#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to you 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.
//
#endregion
using System;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Text;
using log4net.Core;
namespace log4net.Appender
{
///
/// Sends logging events as connectionless UDP datagrams to a remote host or a
/// multicast group using an .
///
///
///
/// UDP guarantees neither that messages arrive, nor that they arrive in the correct order.
///
///
/// To view the logging results, a custom application can be developed that listens for logging
/// events.
///
///
/// When decoding events send via this appender remember to use the same encoding
/// to decode the events as was used to send the events. See the
/// property to specify the encoding to use.
///
///
///
/// This example shows how to log receive logging events that are sent
/// on IP address 244.0.0.1 and port 8080 to the console. The event is
/// encoded in the packet as a unicode string and it is decoded as such.
///
/// IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
/// UdpClient udpClient;
/// byte[] buffer;
/// string loggingEvent;
///
/// try
/// {
/// udpClient = new UdpClient(8080);
///
/// while(true)
/// {
/// buffer = udpClient.Receive(ref remoteEndPoint);
/// loggingEvent = System.Text.Encoding.Unicode.GetString(buffer);
/// Console.WriteLine(loggingEvent);
/// }
/// }
/// catch(Exception e)
/// {
/// Console.WriteLine(e.ToString());
/// }
///
///
/// Dim remoteEndPoint as IPEndPoint
/// Dim udpClient as UdpClient
/// Dim buffer as Byte()
/// Dim loggingEvent as String
///
/// Try
/// remoteEndPoint = new IPEndPoint(IPAddress.Any, 0)
/// udpClient = new UdpClient(8080)
///
/// While True
/// buffer = udpClient.Receive(ByRef remoteEndPoint)
/// loggingEvent = System.Text.Encoding.Unicode.GetString(buffer)
/// Console.WriteLine(loggingEvent)
/// Wend
/// Catch e As Exception
/// Console.WriteLine(e.ToString())
/// End Try
///
///
/// An example configuration section to log information using this appender to the
/// IP 224.0.0.1 on port 8080:
///
///
///
///
///
///
///
///
///
/// Gert Driesen
/// Nicko Cadell
public class UdpAppender : AppenderSkeleton
{
#region Public Instance Constructors
///
/// Initializes a new instance of the class.
///
///
/// The default constructor initializes all fields to their default values.
///
public UdpAppender()
{
}
#endregion Public Instance Constructors
#region Public Instance Properties
///
/// Gets or sets the IP address of the remote host or multicast group to which
/// the underlying should sent the logging event.
///
///
/// The IP address of the remote host or multicast group to which the logging event
/// will be sent.
///
///
///
/// Multicast addresses are identified by IP class D addresses (in the range 224.0.0.0 to
/// 239.255.255.255). Multicast packets can pass across different networks through routers, so
/// it is possible to use multicasts in an Internet scenario as long as your network provider
/// supports multicasting.
///
///
/// Hosts that want to receive particular multicast messages must register their interest by joining
/// the multicast group. Multicast messages are not sent to networks where no host has joined
/// the multicast group. Class D IP addresses are used for multicast groups, to differentiate
/// them from normal host addresses, allowing nodes to easily detect if a message is of interest.
///
///
/// Static multicast addresses that are needed globally are assigned by IANA. A few examples are listed in the table below:
///
///
///
///
/// IP Address
/// Description
///
/// -
/// 224.0.0.1
///
///
/// Sends a message to all system on the subnet.
///
///
///
/// -
/// 224.0.0.2
///
///
/// Sends a message to all routers on the subnet.
///
///
///
/// -
/// 224.0.0.12
///
///
/// The DHCP server answers messages on the IP address 224.0.0.12, but only on a subnet.
///
///
///
///
///
///
/// A complete list of actually reserved multicast addresses and their owners in the ranges
/// defined by RFC 3171 can be found at the IANA web site.
///
///
/// The address range 239.0.0.0 to 239.255.255.255 is reserved for administrative scope-relative
/// addresses. These addresses can be reused with other local groups. Routers are typically
/// configured with filters to prevent multicast traffic in this range from flowing outside
/// of the local network.
///
///
public IPAddress RemoteAddress
{
get { return m_remoteAddress; }
set { m_remoteAddress = value; }
}
///
/// Gets or sets the TCP port number of the remote host or multicast group to which
/// the underlying should sent the logging event.
///
///
/// An integer value in the range to
/// indicating the TCP port number of the remote host or multicast group to which the logging event
/// will be sent.
///
///
/// The underlying will send messages to this TCP port number
/// on the remote host or multicast group.
///
/// The value specified is less than or greater than .
public int RemotePort
{
get { return m_remotePort; }
set
{
if (value < IPEndPoint.MinPort || value > IPEndPoint.MaxPort)
{
throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("value", (object)value,
"The value specified is less than " +
IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
" or greater than " +
IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
}
else
{
m_remotePort = value;
}
}
}
///
/// Gets or sets the TCP port number from which the underlying will communicate.
///
///
/// An integer value in the range to
/// indicating the TCP port number from which the underlying will communicate.
///
///
///
/// The underlying will bind to this port for sending messages.
///
///
/// Setting the value to 0 (the default) will cause the udp client not to bind to
/// a local port.
///
///
/// The value specified is less than or greater than .
public int LocalPort
{
get { return m_localPort; }
set
{
if (value != 0 && (value < IPEndPoint.MinPort || value > IPEndPoint.MaxPort))
{
throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("value", (object)value,
"The value specified is less than " +
IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
" or greater than " +
IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
}
else
{
m_localPort = value;
}
}
}
///
/// Gets or sets used to write the packets.
///
///
/// The used to write the packets.
///
///
///
/// The used to write the packets.
///
///
public Encoding Encoding
{
get { return m_encoding; }
set { m_encoding = value; }
}
#endregion Public Instance Properties
#region Protected Instance Properties
///
/// Gets or sets the underlying .
///
///
/// The underlying .
///
///
/// creates a to send logging events
/// over a network. Classes deriving from can use this
/// property to get or set this . Use the underlying
/// returned from if you require access beyond that which
/// provides.
///
protected UdpClient Client
{
get { return this.m_client; }
set { this.m_client = value; }
}
///
/// Gets or sets the cached remote endpoint to which the logging events should be sent.
///
///
/// The cached remote endpoint to which the logging events will be sent.
///
///
/// The method will initialize the remote endpoint
/// with the values of the and
/// properties.
///
protected IPEndPoint RemoteEndPoint
{
get { return this.m_remoteEndPoint; }
set { this.m_remoteEndPoint = value; }
}
#endregion Protected Instance Properties
#region Implementation of IOptionHandler
///
/// Initialize the appender based on the options set.
///
///
///
/// This is part of the delayed object
/// activation scheme. The method must
/// be called on this object after the configuration properties have
/// been set. Until is called this
/// object is in an undefined state and must not be used.
///
///
/// If any of the configuration properties are modified then
/// must be called again.
///
///
/// The appender will be ignored if no was specified or
/// an invalid remote or local TCP port number was specified.
///
///
/// The required property was not specified.
/// The TCP port number assigned to or is less than or greater than .
public override void ActivateOptions()
{
base.ActivateOptions();
if (this.RemoteAddress == null)
{
throw new ArgumentNullException("The required property 'Address' was not specified.");
}
else if (this.RemotePort < IPEndPoint.MinPort || this.RemotePort > IPEndPoint.MaxPort)
{
throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("this.RemotePort", (object)this.RemotePort,
"The RemotePort is less than " +
IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
" or greater than " +
IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
}
else if (this.LocalPort != 0 && (this.LocalPort < IPEndPoint.MinPort || this.LocalPort > IPEndPoint.MaxPort))
{
throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("this.LocalPort", (object)this.LocalPort,
"The LocalPort is less than " +
IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
" or greater than " +
IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
}
else
{
this.RemoteEndPoint = new IPEndPoint(this.RemoteAddress, this.RemotePort);
this.InitializeClientConnection();
}
}
#endregion
#region Override implementation of AppenderSkeleton
///
/// This method is called by the method.
///
/// The event to log.
///
///
/// Sends the event using an UDP datagram.
///
///
/// Exceptions are passed to the .
///
///
protected override void Append(LoggingEvent loggingEvent)
{
try
{
Byte [] buffer = m_encoding.GetBytes(RenderLoggingEvent(loggingEvent).ToCharArray());
#if NETSTANDARD
Client.SendAsync(buffer, buffer.Length, RemoteEndPoint).Wait();
#else
this.Client.Send(buffer, buffer.Length, this.RemoteEndPoint);
#endif
}
catch (Exception ex)
{
ErrorHandler.Error(
"Unable to send logging event to remote host " +
this.RemoteAddress.ToString() +
" on port " +
this.RemotePort + ".",
ex,
ErrorCode.WriteFailure);
}
}
///
/// This appender requires a to be set.
///
/// true
///
///
/// This appender requires a to be set.
///
///
protected override bool RequiresLayout
{
get { return true; }
}
///
/// Closes the UDP connection and releases all resources associated with
/// this instance.
///
///
///
/// Disables the underlying and releases all managed
/// and unmanaged resources associated with the .
///
///
protected override void OnClose()
{
base.OnClose();
if (this.Client != null)
{
this.Client.Close();
}
}
#endregion Override implementation of AppenderSkeleton
#region Protected Instance Methods
///
/// Initializes the underlying connection.
///
///
///
/// The underlying is initialized and binds to the
/// port number from which you intend to communicate.
///
///
/// Exceptions are passed to the .
///
///
protected virtual void InitializeClientConnection()
{
try
{
if (this.LocalPort == 0)
{
#if NETCF || NET_1_0 || SSCLI_1_0 || CLI_1_0
this.Client = new UdpClient();
#else
this.Client = new UdpClient(RemoteAddress.AddressFamily);
#endif
}
else
{
#if NETCF || NET_1_0 || SSCLI_1_0 || CLI_1_0
this.Client = new UdpClient(this.LocalPort);
#else
this.Client = new UdpClient(this.LocalPort, RemoteAddress.AddressFamily);
#endif
}
}
catch (Exception ex)
{
ErrorHandler.Error(
"Could not initialize the UdpClient connection on port " +
this.LocalPort.ToString(NumberFormatInfo.InvariantInfo) + ".",
ex,
ErrorCode.GenericFailure);
this.Client = null;
}
}
#endregion Protected Instance Methods
#region Private Instance Fields
///
/// The IP address of the remote host or multicast group to which
/// the logging event will be sent.
///
private IPAddress m_remoteAddress;
///
/// The TCP port number of the remote host or multicast group to
/// which the logging event will be sent.
///
private int m_remotePort;
///
/// The cached remote endpoint to which the logging events will be sent.
///
private IPEndPoint m_remoteEndPoint;
///
/// The TCP port number from which the will communicate.
///
private int m_localPort;
///
/// The instance that will be used for sending the
/// logging events.
///
private UdpClient m_client;
///
/// The encoding to use for the packet.
///
#if NETSTANDARD
private Encoding m_encoding = Encoding.Unicode;
#else
private Encoding m_encoding = Encoding.Default;
#endif
#endregion Private Instance Fields
}
}