Skip to content

Commit

Permalink
fix: resolve hostname
Browse files Browse the repository at this point in the history
Fixes issue where the hostname only resolves into an IP address when the
sink is created. The very nature of hostnames is to provide a route to
instances whose IP address may change, and these code changes allows for
this situation.

One limitation with the changes is that currently only IPv4 addresses are
supported, but an upcoming PR will solve that issue.

Closes #29
  • Loading branch information
nbaztec authored and FantasticFiasco committed Sep 9, 2018
1 parent 8f4fc5d commit a7bde60
Show file tree
Hide file tree
Showing 28 changed files with 360 additions and 383 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
5 changes: 4 additions & 1 deletion serilog-sinks-udp.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -365,4 +365,7 @@ public void SetUp()
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=FB31D57BA1B8194CA27B673C3340FD7B/Field/=type/Order/@EntryValue">0</s:Int64>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=FB31D57BA1B8194CA27B673C3340FD7B/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=FB31D57BA1B8194CA27B673C3340FD7B/Scope/=C3001E7C0DA78E4487072B7E050D86C5/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=FB31D57BA1B8194CA27B673C3340FD7B/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=FB31D57BA1B8194CA27B673C3340FD7B/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=datagram/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=multicast/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
171 changes: 122 additions & 49 deletions src/Serilog.Sinks.Udp/LoggerSinkConfigurationExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
// 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.ComponentModel;
using System.Net;
using Serilog.Configuration;
using Serilog.Events;
Expand All @@ -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.

/// <summary>
/// Extension method providing JSON support via
/// <a href="https://github.com/serilog/serilog-settings-configuration">Serilog.Settings.Configuration</a>.
/// Adds a sink that sends log events as UDP packages over the network.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
/// <param name="sinkConfiguration">
/// Logger sink configuration.
/// </param>
/// <param name="remoteAddress">
/// The hostname of the remote host or multicast group to which the UDP client should sent
/// the logging event.
/// </param>
/// <param name="remotePort">
/// The TCP port of the remote host or multicast group to which the UDP client should sent
/// the logging event.
/// </param>
/// <param name="localPort">
/// 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.
/// </param>
/// <param name="restrictedToMinimumLevel">
/// The minimum level for events passed through the sink. The default is
/// <see cref="LevelAlias.Minimum"/>.
/// </param>
/// <param name="levelSwitch">
/// A switch allowing the pass-through minimum level to be changed at runtime.
/// </param>
/// <param name="outputTemplate">
/// 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}".
/// </param>
/// <param name="formatProvider">
/// Supplies culture-specific formatting information, or null.
/// </param>
/// <returns>
/// Logger configuration, allowing configuration to continue.
/// </returns>
public static LoggerConfiguration Udp(
this LoggerSinkConfiguration sinkConfiguration,
string remoteAddress,
Expand All @@ -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);
}

/// <summary>
/// Adds a sink that sends log events as UDP packages over the network.
/// </summary>
/// <param name="sinkConfiguration">Logger sink configuration.</param>
/// <param name="sinkConfiguration">
/// Logger sink configuration.
/// </param>
/// <param name="remoteAddress">
/// The <see cref="IPAddress"/> of the remote host or multicast group to which the UDP
/// client should sent the logging event.
Expand All @@ -88,7 +127,9 @@ public static LoggerConfiguration Udp(
/// <param name="formatProvider">
/// Supplies culture-specific formatting information, or null.
/// </param>
/// <returns>Logger configuration, allowing configuration to continue.</returns>
/// <returns>
/// Logger configuration, allowing configuration to continue.
/// </returns>
public static LoggerConfiguration Udp(
this LoggerSinkConfiguration sinkConfiguration,
IPAddress remoteAddress,
Expand All @@ -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.

/// <summary>
/// Extension method providing JSON support via
/// <a href="https://github.com/serilog/serilog-settings-configuration">Serilog.Settings.Configuration</a>.
/// Adds a sink that sends log events as UDP packages over the network.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
/// <param name="sinkConfiguration">
/// Logger sink configuration.
/// </param>
/// <param name="remoteAddress">
/// The hostname of the remote host or multicast group to which the UDP client should sent
/// the logging event.
/// </param>
/// <param name="remotePort">
/// The TCP port of the remote host or multicast group to which the UDP client should sent
/// the logging event.
/// </param>
/// <param name="formatter">
/// 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.
/// </param>
/// <param name="localPort">
/// 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.
/// </param>
/// <param name="restrictedToMinimumLevel">
/// The minimum level for events passed through the sink. The default is
/// <see cref="LevelAlias.Minimum"/>.
/// </param>
/// <param name="levelSwitch">
/// A switch allowing the pass-through minimum level to be changed at runtime.
/// </param>
/// <returns>
/// Logger configuration, allowing configuration to continue.
/// </returns>
public static LoggerConfiguration Udp(
this LoggerSinkConfiguration sinkConfiguration,
string remoteAddress,
Expand All @@ -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);
}
}

/// <summary>
/// Adds a sink that sends log events as UDP packages over the network.
/// </summary>
/// <param name="sinkConfiguration">Logger sink configuration.</param>
/// <param name="sinkConfiguration">
/// Logger sink configuration.
/// </param>
/// <param name="remoteAddress">
/// The <see cref="IPAddress"/> of the remote host or multicast group to which the UDP
/// client should sent the logging event.
Expand All @@ -166,7 +242,9 @@ public static LoggerConfiguration Udp(
/// <param name="levelSwitch">
/// A switch allowing the pass-through minimum level to be changed at runtime.
/// </param>
/// <returns>Logger configuration, allowing configuration to continue.</returns>
/// <returns>
/// Logger configuration, allowing configuration to continue.
/// </returns>
public static LoggerConfiguration Udp(
this LoggerSinkConfiguration sinkConfiguration,
IPAddress remoteAddress,
Expand All @@ -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
);
}
}
}
8 changes: 1 addition & 7 deletions src/Serilog.Sinks.Udp/Serilog.Sinks.Udp.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>4.1.0</VersionPrefix>
<AssemblyName>Serilog.Sinks.Udp</AssemblyName>
Expand All @@ -21,22 +20,17 @@
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
<PackageReleaseNotes>For release notes, please see the change log on GitHub.</PackageReleaseNotes>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog.Sinks.PeriodicBatching" Version="2.*" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.Net.NameResolution" Version="4.*" />
</ItemGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == 'net45' ">
<DefineConstants>$(DefineConstants);NET4</DefineConstants>
</PropertyGroup>

</Project>
</Project>
27 changes: 16 additions & 11 deletions src/Serilog.Sinks.Udp/Sinks/Udp/Private/IUdpClient.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -25,17 +24,23 @@ public interface IUdpClient
/// <summary>
/// Sends a UDP datagram asynchronously to a remote host.
/// </summary>
/// <param name="datagram">The datagram.
/// <param name="datagram">
/// An array of type <see cref="byte"/> that specifies the UDP datagram that you intend to
/// send represented as an array of bytes.
/// </param>
/// <param name="bytes">The number of bytes in the datagram.</param>
/// <param name="endPoint">
/// An <see cref="IPEndPoint"/> that represents the host and port to which to send the
/// datagram.
/// <param name="bytes">
/// The number of bytes in the datagram.
/// </param>
/// <returns>Returns <see cref="Task{TResult}"/>.</returns>
Task<int> SendAsync(byte[] datagram, int bytes, IPEndPoint endPoint);
/// <param name="hostname">
/// The name of the remote host to which you intend to send the datagram.
/// </param>
/// <param name="port">
/// The remote port number with which you intend to communicate.
/// </param>
/// <returns>
/// Returns <see cref="Task{TResult}"/>.
/// </returns>
Task<int> SendAsync(byte[] datagram, int bytes, string hostname, int port);

#if NET4
/// <summary>
Expand Down
Loading

0 comments on commit a7bde60

Please sign in to comment.