diff --git a/.gitignore b/.gitignore index 94420dc..9121657 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ bld/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ +# JetBrains config directory +.idea/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b324a..70aea10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ This project adheres to [Semantic Versioning](http://semver.org/) and is followi ## Unreleased +### :syringe: Fixed + +- [#29](https://github.com/FantasticFiasco/serilog-sinks-udp/issues/29) Fix remote hostname implementation bug (contribution by [Nisheeth Barthwal](https://github.com/nbaztec)) + ## [4.1.0] - 2018-06-06 ### :zap: Added @@ -32,13 +36,13 @@ This project adheres to [Semantic Versioning](http://semver.org/) and is followi ### :zap: Added -- Text formatter complient with log4net XML schema, thus compatible with [Log4View](http://www.log4view.com) (contribution by [jvanrhyn](https://github.com/jvanrhyn)) +- Text formatter compliant with log4net XML schema, thus compatible with [Log4View](http://www.log4view.com) (contribution by [jvanrhyn](https://github.com/jvanrhyn)) ## [3.2.0] - 2017-08-26 ### :zap: Added -- Text formatter complient with log4j XML schema, thus compatible with [Log2Console](https://github.com/Statyk7/log2console) +- Text formatter compliant with log4j XML schema, thus compatible with [Log2Console](https://github.com/Statyk7/log2console) ## [3.1.0] - 2017-08-20 diff --git a/serilog-sinks-udp.sln.DotSettings b/serilog-sinks-udp.sln.DotSettings index c09b201..2bf72ba 100644 --- a/serilog-sinks-udp.sln.DotSettings +++ b/serilog-sinks-udp.sln.DotSettings @@ -365,4 +365,7 @@ public void SetUp() 0 True 2.0 - InCSharpFile \ No newline at end of file + InCSharpFile + True + True + True \ No newline at end of file diff --git a/src/Serilog.Sinks.Udp/LoggerSinkConfigurationExtensions.cs b/src/Serilog.Sinks.Udp/LoggerSinkConfigurationExtensions.cs index 18bfe22..94f8e15 100644 --- a/src/Serilog.Sinks.Udp/LoggerSinkConfigurationExtensions.cs +++ b/src/Serilog.Sinks.Udp/LoggerSinkConfigurationExtensions.cs @@ -1,11 +1,11 @@ // Copyright 2015-2018 Serilog Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,7 +13,6 @@ // limitations under the License. using System; -using System.ComponentModel; using System.Net; using Serilog.Configuration; using Serilog.Events; @@ -32,11 +31,45 @@ public static class LoggerSinkConfigurationExtensions { private const string DefaultOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"; + // NOTE: + // This overload that accepts the remote address as a string must come first in the + // class, otherwise Serilog.Settings.Configuration won't work. + /// - /// Extension method providing JSON support via - /// Serilog.Settings.Configuration. + /// Adds a sink that sends log events as UDP packages over the network. /// - [EditorBrowsable(EditorBrowsableState.Never)] + /// + /// Logger sink configuration. + /// + /// + /// The hostname of the remote host or multicast group to which the UDP client should sent + /// the logging event. + /// + /// + /// The TCP port of the remote host or multicast group to which the UDP client should sent + /// the logging event. + /// + /// + /// The TCP port from which the UDP client will communicate. The default is 0 and will + /// cause the UDP client not to bind to a local port. + /// + /// + /// The minimum level for events passed through the sink. The default is + /// . + /// + /// + /// A switch allowing the pass-through minimum level to be changed at runtime. + /// + /// + /// A message template describing the format used to write to the sink. The default is + /// "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}". + /// + /// + /// Supplies culture-specific formatting information, or null. + /// + /// + /// Logger configuration, allowing configuration to continue. + /// public static LoggerConfiguration Udp( this LoggerSinkConfiguration sinkConfiguration, string remoteAddress, @@ -47,21 +80,27 @@ public static LoggerConfiguration Udp( string outputTemplate = DefaultOutputTemplate, IFormatProvider formatProvider = null) { + if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration)); + if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate)); + + var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider); + return Udp( sinkConfiguration, - remoteAddress.ToIPAddress(), + remoteAddress, remotePort, + formatter, localPort, restrictedToMinimumLevel, - levelSwitch, - outputTemplate, - formatProvider); + levelSwitch); } /// /// Adds a sink that sends log events as UDP packages over the network. /// - /// Logger sink configuration. + /// + /// Logger sink configuration. + /// /// /// The of the remote host or multicast group to which the UDP /// client should sent the logging event. @@ -88,7 +127,9 @@ public static LoggerConfiguration Udp( /// /// Supplies culture-specific formatting information, or null. /// - /// Logger configuration, allowing configuration to continue. + /// + /// Logger configuration, allowing configuration to continue. + /// public static LoggerConfiguration Udp( this LoggerSinkConfiguration sinkConfiguration, IPAddress remoteAddress, @@ -99,27 +140,54 @@ public static LoggerConfiguration Udp( string outputTemplate = DefaultOutputTemplate, IFormatProvider formatProvider = null) { - if (sinkConfiguration == null) - throw new ArgumentNullException(nameof(sinkConfiguration)); - if (outputTemplate == null) - throw new ArgumentNullException(nameof(outputTemplate)); - - var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider); return Udp( sinkConfiguration, - remoteAddress, + remoteAddress.ToString(), remotePort, - formatter, localPort, restrictedToMinimumLevel, - levelSwitch); + levelSwitch, + outputTemplate, + formatProvider + ); } + // NOTE: + // This overload that accepts the remote address as a string must come first in the + // class, otherwise Serilog.Settings.Configuration won't work. + /// - /// Extension method providing JSON support via - /// Serilog.Settings.Configuration. + /// Adds a sink that sends log events as UDP packages over the network. /// - [EditorBrowsable(EditorBrowsableState.Never)] + /// + /// Logger sink configuration. + /// + /// + /// The hostname of the remote host or multicast group to which the UDP client should sent + /// the logging event. + /// + /// + /// The TCP port of the remote host or multicast group to which the UDP client should sent + /// the logging event. + /// + /// + /// Controls the rendering of log events into text, for example to log JSON. To control + /// plain text formatting, use the overload that accepts an output template. + /// + /// + /// The TCP port from which the UDP client will communicate. The default is 0 and will + /// cause the UDP client not to bind to a local port. + /// + /// + /// The minimum level for events passed through the sink. The default is + /// . + /// + /// + /// A switch allowing the pass-through minimum level to be changed at runtime. + /// + /// + /// Logger configuration, allowing configuration to continue. + /// public static LoggerConfiguration Udp( this LoggerSinkConfiguration sinkConfiguration, string remoteAddress, @@ -129,20 +197,28 @@ public static LoggerConfiguration Udp( LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null) { - return Udp( - sinkConfiguration, - remoteAddress.ToIPAddress(), - remotePort, - formatter, - localPort, - restrictedToMinimumLevel, - levelSwitch); + if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration)); + + try + { + var client = UdpClientFactory.Create(localPort); + var sink = new UdpSink(client, remoteAddress, remotePort, formatter); + + return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); + } + catch (Exception e) + { + SelfLog.WriteLine("Unable to create UDP sink: {0}", e); + return sinkConfiguration.Sink(new NullSink(), LevelAlias.Maximum, null); + } } /// /// Adds a sink that sends log events as UDP packages over the network. /// - /// Logger sink configuration. + /// + /// Logger sink configuration. + /// /// /// The of the remote host or multicast group to which the UDP /// client should sent the logging event. @@ -166,7 +242,9 @@ public static LoggerConfiguration Udp( /// /// A switch allowing the pass-through minimum level to be changed at runtime. /// - /// Logger configuration, allowing configuration to continue. + /// + /// Logger configuration, allowing configuration to continue. + /// public static LoggerConfiguration Udp( this LoggerSinkConfiguration sinkConfiguration, IPAddress remoteAddress, @@ -176,20 +254,15 @@ public static LoggerConfiguration Udp( LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null) { - if (sinkConfiguration == null) - throw new ArgumentNullException(nameof(sinkConfiguration)); - - try - { - var client = UdpClientFactory.Create(localPort, remoteAddress); - var sink = new UdpSink(client, remoteAddress, remotePort, formatter); - return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); - } - catch (Exception e) - { - SelfLog.WriteLine("Unable to create UDP sink: {0}", e); - return sinkConfiguration.Sink(new NullSink(), LevelAlias.Maximum, null); - } + return Udp( + sinkConfiguration, + remoteAddress.ToString(), + remotePort, + formatter, + localPort, + restrictedToMinimumLevel, + levelSwitch + ); } } } \ No newline at end of file diff --git a/src/Serilog.Sinks.Udp/Serilog.Sinks.Udp.csproj b/src/Serilog.Sinks.Udp/Serilog.Sinks.Udp.csproj index 096c58d..c014aa8 100755 --- a/src/Serilog.Sinks.Udp/Serilog.Sinks.Udp.csproj +++ b/src/Serilog.Sinks.Udp/Serilog.Sinks.Udp.csproj @@ -1,5 +1,4 @@  - 4.1.0 Serilog.Sinks.Udp @@ -21,22 +20,17 @@ http://www.apache.org/licenses/LICENSE-2.0 For release notes, please see the change log on GitHub. - - - - $(DefineConstants);NET4 - - + \ No newline at end of file diff --git a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/IUdpClient.cs b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/IUdpClient.cs index d592a3a..dab9fed 100644 --- a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/IUdpClient.cs +++ b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/IUdpClient.cs @@ -1,18 +1,17 @@ // Copyright 2015-2018 Serilog Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Net; using System.Threading.Tasks; namespace Serilog.Sinks.Udp.Private @@ -25,17 +24,23 @@ public interface IUdpClient /// /// Sends a UDP datagram asynchronously to a remote host. /// - /// The datagram. + /// /// An array of type that specifies the UDP datagram that you intend to /// send represented as an array of bytes. /// - /// The number of bytes in the datagram. - /// - /// An that represents the host and port to which to send the - /// datagram. + /// + /// The number of bytes in the datagram. /// - /// Returns . - Task SendAsync(byte[] datagram, int bytes, IPEndPoint endPoint); + /// + /// The name of the remote host to which you intend to send the datagram. + /// + /// + /// The remote port number with which you intend to communicate. + /// + /// + /// Returns . + /// + Task SendAsync(byte[] datagram, int bytes, string hostname, int port); #if NET4 /// diff --git a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/StringExtensions.cs b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/StringExtensions.cs deleted file mode 100644 index f8cbfe2..0000000 --- a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/StringExtensions.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015-2018 Serilog Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using Serilog.Debugging; - -namespace Serilog.Sinks.Udp.Private -{ - /// - /// Class containing extensions methods to . - /// - public static class StringExtensions - { - /// - /// Converts specified address or hostname into an . - /// - /// The address to convert. - /// - /// An if address is found to be a valid IP address or DNS name; - /// otherwise null. - /// - public static IPAddress ToIPAddress(this string address) - { - if (IPAddress.TryParse(address, out var ipAddress)) - { - return ipAddress; - } - - try - { - // TODO: Use Dns.GetHostEntry when moving to .NET Standard 2.0 - var hostEntry = Dns.GetHostEntryAsync(address).Result; - - return hostEntry - .AddressList - .FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork); - } - catch (Exception e) - { - SelfLog.WriteLine("Unable to lookup hostname {0}: {1}", address, e); - } - - return null; - } - } -} diff --git a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpClientFactory.cs b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpClientFactory.cs index b600c1f..4dc156b 100644 --- a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpClientFactory.cs +++ b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpClientFactory.cs @@ -1,11 +1,11 @@ // Copyright 2015-2018 Serilog Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,7 +13,6 @@ // limitations under the License. using System; -using System.Net; namespace Serilog.Sinks.Udp.Private { @@ -25,7 +24,7 @@ public static class UdpClientFactory /// /// Gets or sets the factory creating instances of . /// - public static Func Create { get; set; } - = (localPort, remoteAddress) => new UdpClientWrapper(localPort, remoteAddress); + public static Func Create { get; set; } + = localPort => new UdpClientWrapper(localPort); } } diff --git a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpClientWrapper.cs b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpClientWrapper.cs index 78aae3e..daa8980 100644 --- a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpClientWrapper.cs +++ b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpClientWrapper.cs @@ -1,11 +1,11 @@ // Copyright 2015-2018 Serilog Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,23 +23,18 @@ internal class UdpClientWrapper : IUdpClient { private readonly UdpClient client; - public UdpClientWrapper( - int localPort, - IPAddress remoteAddress) + public UdpClientWrapper(int localPort) { - if (localPort < IPEndPoint.MinPort || localPort > IPEndPoint.MaxPort) - throw new ArgumentOutOfRangeException(nameof(localPort)); - if (remoteAddress == null) - throw new ArgumentNullException(nameof(remoteAddress)); + if (localPort < IPEndPoint.MinPort || localPort > IPEndPoint.MaxPort) throw new ArgumentOutOfRangeException(nameof(localPort)); client = localPort == 0 - ? new UdpClient(remoteAddress.AddressFamily) - : new UdpClient(localPort, remoteAddress.AddressFamily); + ? new UdpClient(AddressFamily.InterNetwork) + : new UdpClient(localPort, AddressFamily.InterNetwork); } - public Task SendAsync(byte[] datagram, int bytes, IPEndPoint endPoint) + public Task SendAsync(byte[] datagram, int bytes, string hostname, int port) { - return client.SendAsync(datagram, bytes, endPoint); + return client.SendAsync(datagram, bytes, hostname, port); } #if NET4 diff --git a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpSink.cs b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpSink.cs index 96ea21b..ff854aa 100644 --- a/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpSink.cs +++ b/src/Serilog.Sinks.Udp/Sinks/Udp/Private/UdpSink.cs @@ -1,11 +1,11 @@ // Copyright 2015-2018 Serilog Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,49 +30,29 @@ namespace Serilog.Sinks.Udp.Private /// internal class UdpSink : PeriodicBatchingSink { - private readonly IPEndPoint remoteEndPoint; + private readonly IUdpClient client; + private readonly string remoteAddress; + private readonly int remotePort; private readonly ITextFormatter formatter; - private IUdpClient client; - - /// - /// Construct a . - /// - /// - /// The UDP client responsible for sending multicast messages. - /// - /// - /// The of the remote host or multicast group to which the UDP - /// client should sent the logging event. - /// - /// - /// The TCP port of the remote host or multicast group to which the UDP client should sent - /// the logging event. - /// - /// Formatter used to convert log events to text. public UdpSink( IUdpClient client, - IPAddress remoteAddress, + string remoteAddress, int remotePort, ITextFormatter formatter) : base(1000, TimeSpan.FromSeconds(0.5)) { - if (remoteAddress == null) - throw new ArgumentNullException(nameof(remoteAddress)); - if (remotePort < IPEndPoint.MinPort || remotePort > IPEndPoint.MaxPort) - throw new ArgumentOutOfRangeException(nameof(remotePort)); + if (remotePort < IPEndPoint.MinPort || remotePort > IPEndPoint.MaxPort) throw new ArgumentOutOfRangeException(nameof(remotePort)); - remoteEndPoint = new IPEndPoint(remoteAddress, remotePort); - this.formatter = formatter ?? throw new ArgumentNullException(nameof(formatter)); this.client = client ?? throw new ArgumentNullException(nameof(client)); + this.remoteAddress = remoteAddress ?? throw new ArgumentNullException(nameof(remoteAddress)); + this.remotePort = remotePort; + this.formatter = formatter ?? throw new ArgumentNullException(nameof(formatter)); } #region PeriodicBatchingSink Members - /// - /// Emit a batch of log events, running asynchronously. - /// - /// The events to emit. + /// protected override async Task EmitBatchAsync(IEnumerable events) { foreach (LogEvent logEvent in events) @@ -90,7 +70,7 @@ protected override async Task EmitBatchAsync(IEnumerable events) .ToCharArray()); await client - .SendAsync(buffer, buffer.Length, remoteEndPoint) + .SendAsync(buffer, buffer.Length, remoteAddress, remotePort) .ConfigureAwait(false); } } @@ -101,13 +81,7 @@ await client } } - /// - /// Free resources held by the sink. - /// - /// - /// If true, called because the object is being disposed; if false, the object is being - /// disposed from the finalizer. - /// + /// protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -121,10 +95,7 @@ protected override void Dispose(bool disposing) client?.Close(); #else client?.Dispose(); - #endif - client = null; - } } diff --git a/src/Serilog.Sinks.Udp/Sinks/Udp/TextFormatters/Log4netTextFormatter.cs b/src/Serilog.Sinks.Udp/Sinks/Udp/TextFormatters/Log4netTextFormatter.cs index d509e21..4dff36a 100644 --- a/src/Serilog.Sinks.Udp/Sinks/Udp/TextFormatters/Log4netTextFormatter.cs +++ b/src/Serilog.Sinks.Udp/Sinks/Udp/TextFormatters/Log4netTextFormatter.cs @@ -19,7 +19,7 @@ namespace Serilog.Sinks.Udp.TextFormatters { /// - /// Text formatter serializing log events into log4net complient XML. + /// Text formatter serializing log events into log4net compliant XML. /// public class Log4netTextFormatter : ITextFormatter { diff --git a/test/Serilog.Sinks.Udp.Tests/CodeConfigurationTest.cs b/test/Serilog.Sinks.Udp.Tests/CodeConfigurationTest.cs deleted file mode 100644 index 2067c8b..0000000 --- a/test/Serilog.Sinks.Udp.Tests/CodeConfigurationTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Net; - -namespace Serilog -{ - public class CodeConfigurationTest : SinkFixture - { - public CodeConfigurationTest() - { - RemoteAddress = IPAddress.Loopback; - RemotePort = 7071; - - Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo - .Udp(RemoteAddress, RemotePort) - .CreateLogger(); - } - } -} diff --git a/test/Serilog.Sinks.Udp.Tests/SettingsFileUsingOutputTemplateTest.cs b/test/Serilog.Sinks.Udp.Tests/OutputTemplateGivenAppSettingsShould.cs similarity index 53% rename from test/Serilog.Sinks.Udp.Tests/SettingsFileUsingOutputTemplateTest.cs rename to test/Serilog.Sinks.Udp.Tests/OutputTemplateGivenAppSettingsShould.cs index 494cb81..c849add 100644 --- a/test/Serilog.Sinks.Udp.Tests/SettingsFileUsingOutputTemplateTest.cs +++ b/test/Serilog.Sinks.Udp.Tests/OutputTemplateGivenAppSettingsShould.cs @@ -1,18 +1,17 @@ -using System.Globalization; -using Microsoft.Extensions.Configuration; -using Serilog.Sinks.Udp.Private; +using Microsoft.Extensions.Configuration; +using Serilog.Core; namespace Serilog { - public class SettingsFileUsingOutputTemplateTest : SinkFixture + public class OutputTemplateGivenAppSettingsShould : SinkFixture { - public SettingsFileUsingOutputTemplateTest() + public OutputTemplateGivenAppSettingsShould() { var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings_output_template.json") .Build(); - RemoteAddress = configuration["Serilog:WriteTo:0:Args:remoteAddress"].ToIPAddress(); + RemoteAddress = configuration["Serilog:WriteTo:0:Args:remoteAddress"]; RemotePort = int.Parse(configuration["Serilog:WriteTo:0:Args:remotePort"]); Logger = new LoggerConfiguration() @@ -20,12 +19,10 @@ public SettingsFileUsingOutputTemplateTest() .CreateLogger(); } - public class FormatProvider : CultureInfo - { - public FormatProvider() - : base("en-US") - { - } - } + protected override string RemoteAddress { get; } + + protected override int RemotePort { get; } + + protected override Logger Logger { get; } } } diff --git a/test/Serilog.Sinks.Udp.Tests/OutputTemplateGivenCodeConfigurationShould.cs b/test/Serilog.Sinks.Udp.Tests/OutputTemplateGivenCodeConfigurationShould.cs new file mode 100644 index 0000000..f8fb026 --- /dev/null +++ b/test/Serilog.Sinks.Udp.Tests/OutputTemplateGivenCodeConfigurationShould.cs @@ -0,0 +1,32 @@ +using System.Net; +using Serilog.Core; +using Serilog.Support; + +namespace Serilog +{ + public class OutputTemplateGivenCodeConfigurationShould : SinkFixture + { + private const string OutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message} - {Exception}"; + + public OutputTemplateGivenCodeConfigurationShould() + { + var remoteAddress = IPAddress.Loopback.ToString(); + var remotePort = 7071; + + RemoteAddress = remoteAddress; + RemotePort = remotePort; + + Logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo + .Udp(remoteAddress, remotePort, outputTemplate: OutputTemplate, formatProvider: new FormatProvider()) + .CreateLogger(); + } + + protected override string RemoteAddress { get; } + + protected override int RemotePort { get; } + + protected override Logger Logger { get; } + } +} diff --git a/test/Serilog.Sinks.Udp.Tests/Serilog.Sinks.Udp.Tests.csproj b/test/Serilog.Sinks.Udp.Tests/Serilog.Sinks.Udp.Tests.csproj index a617c85..1f50874 100755 --- a/test/Serilog.Sinks.Udp.Tests/Serilog.Sinks.Udp.Tests.csproj +++ b/test/Serilog.Sinks.Udp.Tests/Serilog.Sinks.Udp.Tests.csproj @@ -1,5 +1,4 @@  - netcoreapp2.0 Serilog @@ -7,11 +6,9 @@ true - - @@ -21,11 +18,9 @@ - - PreserveNewest @@ -34,5 +29,4 @@ PreserveNewest - diff --git a/test/Serilog.Sinks.Udp.Tests/SinkFixture.cs b/test/Serilog.Sinks.Udp.Tests/SinkFixture.cs index cac7783..969b042 100644 --- a/test/Serilog.Sinks.Udp.Tests/SinkFixture.cs +++ b/test/Serilog.Sinks.Udp.Tests/SinkFixture.cs @@ -1,5 +1,4 @@ using System; -using System.Net; using Moq; using Serilog.Core; using Serilog.Events; @@ -16,15 +15,15 @@ public abstract class SinkFixture : IDisposable protected SinkFixture() { client = new UdpClientMock(); - UdpClientFactory.Create = (_, __) => client.Object; + UdpClientFactory.Create = _ => client.Object; } - protected IPAddress RemoteAddress { get; set; } + protected abstract string RemoteAddress { get; } - protected int RemotePort { get; set; } - - protected Logger Logger { get; set; } + protected abstract int RemotePort { get; } + protected abstract Logger Logger { get; } + [Theory] [InlineData(LogEventLevel.Verbose)] [InlineData(LogEventLevel.Debug)] @@ -32,7 +31,7 @@ protected SinkFixture() [InlineData(LogEventLevel.Warning)] [InlineData(LogEventLevel.Error)] [InlineData(LogEventLevel.Fatal)] - public void Level(LogEventLevel level) + public void WriteLogEvent(LogEventLevel level) { // Arrange var counter = new Counter(1); @@ -52,12 +51,12 @@ public void Level(LogEventLevel level) } [Theory] - [InlineData(1)] // 1 batch - [InlineData(10)] // 1 batch - [InlineData(100)] // 1 batch - [InlineData(1000)] // ~1 batch - [InlineData(10000)] // ~10 batches - public void Batches(int numberOfEvents) + [InlineData(1)] // 1 batch assuming batch size is 1000 + [InlineData(10)] // 1 batch assuming batch size is 1000 + [InlineData(100)] // 1 batch assuming batch size is 1000 + [InlineData(1000)] // ~1 batch assuming batch size is 1000 + [InlineData(10000)] // ~10 batches assuming batch size is 1000 + public void WriteBatches(int numberOfEvents) { // Arrange var counter = new Counter(numberOfEvents); @@ -78,8 +77,7 @@ public void Batches(int numberOfEvents) public void Dispose() { - Logger?.Dispose(); - + Logger.Dispose(); UdpClientFactory.Create = null; } } diff --git a/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/Private/StringExtensionsTest.cs b/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/Private/StringExtensionsTest.cs deleted file mode 100644 index c3af34c..0000000 --- a/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/Private/StringExtensionsTest.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Net; -using Serilog.Debugging; -using Shouldly; -using Xunit; - -namespace Serilog.Sinks.Udp.Private -{ - public class StringExtensionsTest - { - [Fact] - public void ShouldConvertIPAddress() - { - // Arrange - var expected = IPAddress.Loopback; - - // Act - var actual = "127.0.0.1".ToIPAddress(); - - // Assert - actual.ShouldBe(expected); - } - - [Fact] - public void ShouldConvertHostname() - { - // Arrange - var expected = IPAddress.Loopback; - - // Act - var actual = "localhost".ToIPAddress(); - - // Assert - actual.ShouldBe(expected); - } - - [Fact] - public void ShouldNotConvertInvalidHostname() - { - // Arrange - string actualErrorMessage = null; - - SelfLog.Enable(output => { actualErrorMessage = output; }); - - // Act - var actual = "invalid-host-name".ToIPAddress(); - - // Assert - actual.ShouldBeNull(); - actualErrorMessage.ShouldNotBeNull(); - } - } -} diff --git a/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/Private/UdpClientMock.cs b/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/Private/UdpClientMock.cs deleted file mode 100644 index fcb220f..0000000 --- a/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/Private/UdpClientMock.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Net; -using System.Threading.Tasks; -using Moq; -using Moq.Language.Flow; - -namespace Serilog.Sinks.Udp.Private -{ - internal class UdpClientMock : Mock - { - internal ISetup> SetupSendAsync( - IPAddress remoteAddress, - int remotePort) - { - return Setup( - mock => mock.SendAsync( - It.IsAny(), - It.IsAny(), - It.Is(RemoteEndPointCriteria(remoteAddress, remotePort)))); - } - - internal void VerifySendAsync( - IPAddress remoteAddress, - int remotePort, - Times times) - { - Verify( - mock => mock.SendAsync( - It.IsAny(), - It.IsAny(), - It.Is(RemoteEndPointCriteria(remoteAddress, remotePort))), - times); - } - - private static Expression> RemoteEndPointCriteria( - IPAddress remoteAddress, - int remotePort) - { - return remoteEndpoint => - remoteEndpoint.Address.Equals(remoteAddress) && - remoteEndpoint.Port == remotePort && - remoteEndpoint.AddressFamily == remoteAddress.AddressFamily; - } - } -} diff --git a/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4jTextFormatterTest.cs b/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4jTextFormatterShould.cs similarity index 88% rename from test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4jTextFormatterTest.cs rename to test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4jTextFormatterShould.cs index dcb1e8c..fac75f7 100644 --- a/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4jTextFormatterTest.cs +++ b/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4jTextFormatterShould.cs @@ -8,21 +8,21 @@ namespace Serilog.Sinks.Udp.TextFormatters { - public class Log4jTextFormatterTest + public class Log4jTextFormatterShould { private static readonly XNamespace Namespace = "http://jakarta.apache.org/log4j/"; private readonly Log4jTextFormatter formatter; private readonly TextWriter output; - public Log4jTextFormatterTest() + public Log4jTextFormatterShould() { formatter = new Log4jTextFormatter(); output = new StringWriter(); } [Fact] - public void Logger() + public void WriteLoggerAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -36,7 +36,7 @@ public void Logger() } [Fact] - public void Timestamp() + public void WriteTimestampAttribute() { // Act formatter.Format(Some.LogEvent(), output); @@ -53,7 +53,7 @@ public void Timestamp() [InlineData(LogEventLevel.Warning, "WARN")] [InlineData(LogEventLevel.Error, "ERROR")] [InlineData(LogEventLevel.Fatal, "FATAL")] - public void Level(LogEventLevel actual, string expected) + public void WriteLevelAttribute(LogEventLevel actual, string expected) { // Act formatter.Format(Some.LogEvent(level: actual), output); @@ -63,7 +63,7 @@ public void Level(LogEventLevel actual, string expected) } [Fact] - public void Thead() + public void WriteTheadAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -77,7 +77,7 @@ public void Thead() } [Fact] - public void Message() + public void WriteMessageElement() { // Arrange var logEvent = Some.LogEvent(message: "Some message"); @@ -90,7 +90,7 @@ public void Message() } [Fact] - public void Exception() + public void WriteExceptionElement() { // Arrange var logEvent = Some.LogEvent(exception: new DivideByZeroException()); diff --git a/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4netTextFormatterTest.cs b/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4netTextFormatterShould.cs similarity index 89% rename from test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4netTextFormatterTest.cs rename to test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4netTextFormatterShould.cs index 39d36fd..9c52093 100644 --- a/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4netTextFormatterTest.cs +++ b/test/Serilog.Sinks.Udp.Tests/Sinks/Udp/TextFormatters/Log4netTextFormatterShould.cs @@ -8,21 +8,21 @@ namespace Serilog.Sinks.Udp.TextFormatters { - public class Log4netTextFormatterTest + public class Log4netTextFormatterShould { private static readonly XNamespace Namespace = "http://logging.apache.org/log4net/schemas/log4net-events-1.2/"; private readonly Log4netTextFormatter formatter; private readonly TextWriter output; - public Log4netTextFormatterTest() + public Log4netTextFormatterShould() { formatter = new Log4netTextFormatter(); output = new StringWriter(); } [Fact] - public void Logger() + public void WriteLoggerAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -36,7 +36,7 @@ public void Logger() } [Fact] - public void Timestamp() + public void WriteTimestampAttribute() { // Act formatter.Format(Some.LogEvent(), output); @@ -53,7 +53,7 @@ public void Timestamp() [InlineData(LogEventLevel.Warning, "WARN")] [InlineData(LogEventLevel.Error, "ERROR")] [InlineData(LogEventLevel.Fatal, "FATAL")] - public void Level(LogEventLevel actual, string expected) + public void WriteLevelAttribute(LogEventLevel actual, string expected) { // Act formatter.Format(Some.LogEvent(level: actual), output); @@ -63,7 +63,7 @@ public void Level(LogEventLevel actual, string expected) } [Fact] - public void Thead() + public void WriteTheadAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -77,7 +77,7 @@ public void Thead() } [Fact] - public void Username() + public void WriteUsernameAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -91,7 +91,7 @@ public void Username() } [Fact] - public void Domain() + public void WriteDomainAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -105,7 +105,7 @@ public void Domain() } [Fact] - public void Class() + public void WriteClassAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -119,7 +119,7 @@ public void Class() } [Fact] - public void Method() + public void WriteMethodAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -133,7 +133,7 @@ public void Method() } [Fact] - public void MachineName() + public void WriteMachineNameAttribute() { // Arrange var logEvent = Some.LogEvent(); @@ -147,7 +147,7 @@ public void MachineName() } [Fact] - public void Message() + public void WriteMessageElement() { // Arrange var logEvent = Some.LogEvent(message: "Some message"); @@ -160,7 +160,7 @@ public void Message() } [Fact] - public void Exception() + public void WriteExceptionElement() { // Arrange var logEvent = Some.LogEvent(exception: new DivideByZeroException()); diff --git a/test/Serilog.Sinks.Udp.Tests/Support/Counter.cs b/test/Serilog.Sinks.Udp.Tests/Support/Counter.cs index 303b19c..d2f215c 100644 --- a/test/Serilog.Sinks.Udp.Tests/Support/Counter.cs +++ b/test/Serilog.Sinks.Udp.Tests/Support/Counter.cs @@ -13,8 +13,7 @@ internal class Counter public Counter(int expected) { - if (expected <= 0) - throw new ArgumentException("expected must be at least 1"); + if (expected <= 0) throw new ArgumentException("expected must be at least 1"); this.expected = expected; resetEvent = new ManualResetEventSlim(); diff --git a/test/Serilog.Sinks.Udp.Tests/Support/FormatProvider.cs b/test/Serilog.Sinks.Udp.Tests/Support/FormatProvider.cs new file mode 100644 index 0000000..fa0c234 --- /dev/null +++ b/test/Serilog.Sinks.Udp.Tests/Support/FormatProvider.cs @@ -0,0 +1,12 @@ +using System.Globalization; + +namespace Serilog.Support +{ + public class FormatProvider : CultureInfo + { + public FormatProvider() + : base("en-US") + { + } + } +} diff --git a/test/Serilog.Sinks.Udp.Tests/Support/TextFormatters/TextFormatter.cs b/test/Serilog.Sinks.Udp.Tests/Support/TextFormatters/TextFormatter.cs new file mode 100644 index 0000000..9876acc --- /dev/null +++ b/test/Serilog.Sinks.Udp.Tests/Support/TextFormatters/TextFormatter.cs @@ -0,0 +1,12 @@ +using Serilog.Formatting.Display; + +namespace Serilog.Support.TextFormatters +{ + public class TextFormatter : MessageTemplateTextFormatter + { + public TextFormatter() + : base("{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message} - {Exception}", null) + { + } + } +} diff --git a/test/Serilog.Sinks.Udp.Tests/Support/UdpClientMock.cs b/test/Serilog.Sinks.Udp.Tests/Support/UdpClientMock.cs new file mode 100644 index 0000000..752a1a2 --- /dev/null +++ b/test/Serilog.Sinks.Udp.Tests/Support/UdpClientMock.cs @@ -0,0 +1,36 @@ +using System.Threading.Tasks; +using Moq; +using Moq.Language.Flow; +using Serilog.Sinks.Udp.Private; + +namespace Serilog.Support +{ + internal class UdpClientMock : Mock + { + internal ISetup> SetupSendAsync( + string remoteHost, + int remotePort) + { + return Setup( + mock => mock.SendAsync( + It.IsAny(), + It.IsAny(), + It.Is(x => x == remoteHost), + It.Is(x => x == remotePort))); + } + + internal void VerifySendAsync( + string remoteHost, + int remotePort, + Times times) + { + Verify( + mock => mock.SendAsync( + It.IsAny(), + It.IsAny(), + It.Is(x => x == remoteHost), + It.Is(x => x == remotePort)), + times); + } + } +} diff --git a/test/Serilog.Sinks.Udp.Tests/SettingsFileUsingTextFormatterTest.cs b/test/Serilog.Sinks.Udp.Tests/TextFormatterGivenAppSettingsShould.cs similarity index 53% rename from test/Serilog.Sinks.Udp.Tests/SettingsFileUsingTextFormatterTest.cs rename to test/Serilog.Sinks.Udp.Tests/TextFormatterGivenAppSettingsShould.cs index 7997ca5..77c07ce 100644 --- a/test/Serilog.Sinks.Udp.Tests/SettingsFileUsingTextFormatterTest.cs +++ b/test/Serilog.Sinks.Udp.Tests/TextFormatterGivenAppSettingsShould.cs @@ -1,18 +1,17 @@ using Microsoft.Extensions.Configuration; -using Serilog.Formatting.Display; -using Serilog.Sinks.Udp.Private; +using Serilog.Core; namespace Serilog { - public class SettingsFileUsingTextFormatterTest : SinkFixture + public class TextFormatterGivenAppSettingsShould : SinkFixture { - public SettingsFileUsingTextFormatterTest() + public TextFormatterGivenAppSettingsShould() { var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings_text_formatter.json") .Build(); - RemoteAddress = configuration["Serilog:WriteTo:0:Args:remoteAddress"].ToIPAddress(); + RemoteAddress = configuration["Serilog:WriteTo:0:Args:remoteAddress"]; RemotePort = int.Parse(configuration["Serilog:WriteTo:0:Args:remotePort"]); Logger = new LoggerConfiguration() @@ -20,12 +19,10 @@ public SettingsFileUsingTextFormatterTest() .CreateLogger(); } - public class TextFormatter : MessageTemplateTextFormatter - { - public TextFormatter() - : base("{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}", null) - { - } - } + protected override string RemoteAddress{ get; } + + protected override int RemotePort { get; } + + protected override Logger Logger { get; } } } diff --git a/test/Serilog.Sinks.Udp.Tests/TextFormatterGivenCodeConfigurationShould.cs b/test/Serilog.Sinks.Udp.Tests/TextFormatterGivenCodeConfigurationShould.cs new file mode 100644 index 0000000..0b3f883 --- /dev/null +++ b/test/Serilog.Sinks.Udp.Tests/TextFormatterGivenCodeConfigurationShould.cs @@ -0,0 +1,30 @@ +using System.Net; +using Serilog.Core; +using Serilog.Support.TextFormatters; + +namespace Serilog +{ + public class TextFormatterGivenCodeConfigurationShould : SinkFixture + { + public TextFormatterGivenCodeConfigurationShould() + { + var remoteAddress = IPAddress.Loopback.ToString(); + var remotePort = 7071; + + RemoteAddress = remoteAddress; + RemotePort = remotePort; + + Logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo + .Udp(remoteAddress, remotePort, new TextFormatter()) + .CreateLogger(); + } + + protected override string RemoteAddress { get; } + + protected override int RemotePort { get; } + + protected override Logger Logger { get; } + } +} diff --git a/test/Serilog.Sinks.Udp.Tests/appsettings_output_template.json b/test/Serilog.Sinks.Udp.Tests/appsettings_output_template.json index 7d9ab4e..34a47f3 100644 --- a/test/Serilog.Sinks.Udp.Tests/appsettings_output_template.json +++ b/test/Serilog.Sinks.Udp.Tests/appsettings_output_template.json @@ -10,8 +10,8 @@ "localPort": 0, "restrictedToMinimumLevel": "Verbose", "levelSwitch": "Verbose", - "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}", - "formatProvider": "Serilog.SettingsFileUsingOutputTemplateTest+FormatProvider, Serilog.Sinks.Udp.Tests" + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message} - {Exception}", + "formatProvider": "Serilog.Support.FormatProvider, Serilog.Sinks.Udp.Tests" } } ] diff --git a/test/Serilog.Sinks.Udp.Tests/appsettings_text_formatter.json b/test/Serilog.Sinks.Udp.Tests/appsettings_text_formatter.json index 21c1b78..0a86616 100644 --- a/test/Serilog.Sinks.Udp.Tests/appsettings_text_formatter.json +++ b/test/Serilog.Sinks.Udp.Tests/appsettings_text_formatter.json @@ -7,7 +7,7 @@ "Args": { "remoteAddress": "localhost", "remotePort": 7071, - "formatter": "Serilog.SettingsFileUsingTextFormatterTest+TextFormatter, Serilog.Sinks.Udp.Tests", + "formatter": "Serilog.Support.TextFormatters.TextFormatter, Serilog.Sinks.Udp.Tests", "localPort": 0, "restrictedToMinimumLevel": "Verbose", "levelSwitch": "Verbose"