| #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.Layout; |
| using log4net.Core; |
| using log4net.Util; |
| |
| namespace log4net.Appender |
| { |
| /// <summary> |
| /// Sends logging events as connectionless UDP datagrams to a remote host or a |
| /// multicast group using an <see cref="UdpClient" />. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// UDP guarantees neither that messages arrive, nor that they arrive in the correct order. |
| /// </para> |
| /// <para> |
| /// To view the logging results, a custom application can be developed that listens for logging |
| /// events. |
| /// </para> |
| /// <para> |
| /// 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 <see cref="Encoding"/> |
| /// property to specify the encoding to use. |
| /// </para> |
| /// </remarks> |
| /// <example> |
| /// 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. |
| /// <code lang="C#"> |
| /// 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()); |
| /// } |
| /// </code> |
| /// <code lang="Visual Basic"> |
| /// 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 |
| /// </code> |
| /// <para> |
| /// An example configuration section to log information using this appender to the |
| /// IP 224.0.0.1 on port 8080: |
| /// </para> |
| /// <code lang="XML" escaped="true"> |
| /// <appender name="UdpAppender" type="log4net.Appender.UdpAppender"> |
| /// <remoteAddress value="224.0.0.1" /> |
| /// <remotePort value="8080" /> |
| /// <layout type="log4net.Layout.PatternLayout" value="%-5level %logger [%ndc] - %message%newline" /> |
| /// </appender> |
| /// </code> |
| /// </example> |
| /// <author>Gert Driesen</author> |
| /// <author>Nicko Cadell</author> |
| public class UdpAppender : AppenderSkeleton |
| { |
| #region Public Instance Constructors |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="UdpAppender" /> class. |
| /// </summary> |
| /// <remarks> |
| /// The default constructor initializes all fields to their default values. |
| /// </remarks> |
| public UdpAppender() |
| { |
| } |
| |
| #endregion Public Instance Constructors |
| |
| #region Public Instance Properties |
| |
| /// <summary> |
| /// Gets or sets the IP address of the remote host or multicast group to which |
| /// the underlying <see cref="UdpClient" /> should sent the logging event. |
| /// </summary> |
| /// <value> |
| /// The IP address of the remote host or multicast group to which the logging event |
| /// will be sent. |
| /// </value> |
| /// <remarks> |
| /// <para> |
| /// Multicast addresses are identified by IP class <b>D</b> 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. |
| /// </para> |
| /// <para> |
| /// 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 <b>D</b> 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. |
| /// </para> |
| /// <para> |
| /// Static multicast addresses that are needed globally are assigned by IANA. A few examples are listed in the table below: |
| /// </para> |
| /// <para> |
| /// <list type="table"> |
| /// <listheader> |
| /// <term>IP Address</term> |
| /// <description>Description</description> |
| /// </listheader> |
| /// <item> |
| /// <term>224.0.0.1</term> |
| /// <description> |
| /// <para> |
| /// Sends a message to all system on the subnet. |
| /// </para> |
| /// </description> |
| /// </item> |
| /// <item> |
| /// <term>224.0.0.2</term> |
| /// <description> |
| /// <para> |
| /// Sends a message to all routers on the subnet. |
| /// </para> |
| /// </description> |
| /// </item> |
| /// <item> |
| /// <term>224.0.0.12</term> |
| /// <description> |
| /// <para> |
| /// The DHCP server answers messages on the IP address 224.0.0.12, but only on a subnet. |
| /// </para> |
| /// </description> |
| /// </item> |
| /// </list> |
| /// </para> |
| /// <para> |
| /// A complete list of actually reserved multicast addresses and their owners in the ranges |
| /// defined by RFC 3171 can be found at the <A href="http://www.iana.org/assignments/multicast-addresses">IANA web site</A>. |
| /// </para> |
| /// <para> |
| /// 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. |
| /// </para> |
| /// </remarks> |
| public IPAddress RemoteAddress |
| { |
| get { return m_remoteAddress; } |
| set { m_remoteAddress = value; } |
| } |
| |
| /// <summary> |
| /// Gets or sets the TCP port number of the remote host or multicast group to which |
| /// the underlying <see cref="UdpClient" /> should sent the logging event. |
| /// </summary> |
| /// <value> |
| /// An integer value in the range <see cref="IPEndPoint.MinPort" /> to <see cref="IPEndPoint.MaxPort" /> |
| /// indicating the TCP port number of the remote host or multicast group to which the logging event |
| /// will be sent. |
| /// </value> |
| /// <remarks> |
| /// The underlying <see cref="UdpClient" /> will send messages to this TCP port number |
| /// on the remote host or multicast group. |
| /// </remarks> |
| /// <exception cref="ArgumentOutOfRangeException">The value specified is less than <see cref="IPEndPoint.MinPort" /> or greater than <see cref="IPEndPoint.MaxPort" />.</exception> |
| 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; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Gets or sets the TCP port number from which the underlying <see cref="UdpClient" /> will communicate. |
| /// </summary> |
| /// <value> |
| /// An integer value in the range <see cref="IPEndPoint.MinPort" /> to <see cref="IPEndPoint.MaxPort" /> |
| /// indicating the TCP port number from which the underlying <see cref="UdpClient" /> will communicate. |
| /// </value> |
| /// <remarks> |
| /// <para> |
| /// The underlying <see cref="UdpClient" /> will bind to this port for sending messages. |
| /// </para> |
| /// <para> |
| /// Setting the value to 0 (the default) will cause the udp client not to bind to |
| /// a local port. |
| /// </para> |
| /// </remarks> |
| /// <exception cref="ArgumentOutOfRangeException">The value specified is less than <see cref="IPEndPoint.MinPort" /> or greater than <see cref="IPEndPoint.MaxPort" />.</exception> |
| 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; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Gets or sets <see cref="Encoding"/> used to write the packets. |
| /// </summary> |
| /// <value> |
| /// The <see cref="Encoding"/> used to write the packets. |
| /// </value> |
| /// <remarks> |
| /// <para> |
| /// The <see cref="Encoding"/> used to write the packets. |
| /// </para> |
| /// </remarks> |
| public Encoding Encoding |
| { |
| get { return m_encoding; } |
| set { m_encoding = value; } |
| } |
| |
| #endregion Public Instance Properties |
| |
| #region Protected Instance Properties |
| |
| /// <summary> |
| /// Gets or sets the underlying <see cref="UdpClient" />. |
| /// </summary> |
| /// <value> |
| /// The underlying <see cref="UdpClient" />. |
| /// </value> |
| /// <remarks> |
| /// <see cref="UdpAppender" /> creates a <see cref="UdpClient" /> to send logging events |
| /// over a network. Classes deriving from <see cref="UdpAppender" /> can use this |
| /// property to get or set this <see cref="UdpClient" />. Use the underlying <see cref="UdpClient" /> |
| /// returned from <see cref="Client" /> if you require access beyond that which |
| /// <see cref="UdpAppender" /> provides. |
| /// </remarks> |
| protected UdpClient Client |
| { |
| get { return this.m_client; } |
| set { this.m_client = value; } |
| } |
| |
| /// <summary> |
| /// Gets or sets the cached remote endpoint to which the logging events should be sent. |
| /// </summary> |
| /// <value> |
| /// The cached remote endpoint to which the logging events will be sent. |
| /// </value> |
| /// <remarks> |
| /// The <see cref="ActivateOptions" /> method will initialize the remote endpoint |
| /// with the values of the <see cref="RemoteAddress" /> and <see cref="RemotePort"/> |
| /// properties. |
| /// </remarks> |
| protected IPEndPoint RemoteEndPoint |
| { |
| get { return this.m_remoteEndPoint; } |
| set { this.m_remoteEndPoint = value; } |
| } |
| |
| #endregion Protected Instance Properties |
| |
| #region Implementation of IOptionHandler |
| |
| /// <summary> |
| /// Initialize the appender based on the options set. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// This is part of the <see cref="IOptionHandler"/> delayed object |
| /// activation scheme. The <see cref="ActivateOptions"/> method must |
| /// be called on this object after the configuration properties have |
| /// been set. Until <see cref="ActivateOptions"/> is called this |
| /// object is in an undefined state and must not be used. |
| /// </para> |
| /// <para> |
| /// If any of the configuration properties are modified then |
| /// <see cref="ActivateOptions"/> must be called again. |
| /// </para> |
| /// <para> |
| /// The appender will be ignored if no <see cref="RemoteAddress" /> was specified or |
| /// an invalid remote or local TCP port number was specified. |
| /// </para> |
| /// </remarks> |
| /// <exception cref="ArgumentNullException">The required property <see cref="RemoteAddress" /> was not specified.</exception> |
| /// <exception cref="ArgumentOutOfRangeException">The TCP port number assigned to <see cref="LocalPort" /> or <see cref="RemotePort" /> is less than <see cref="IPEndPoint.MinPort" /> or greater than <see cref="IPEndPoint.MaxPort" />.</exception> |
| 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 |
| |
| /// <summary> |
| /// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent)"/> method. |
| /// </summary> |
| /// <param name="loggingEvent">The event to log.</param> |
| /// <remarks> |
| /// <para> |
| /// Sends the event using an UDP datagram. |
| /// </para> |
| /// <para> |
| /// Exceptions are passed to the <see cref="AppenderSkeleton.ErrorHandler"/>. |
| /// </para> |
| /// </remarks> |
| protected override void Append(LoggingEvent loggingEvent) |
| { |
| try |
| { |
| Byte [] buffer = m_encoding.GetBytes(RenderLoggingEvent(loggingEvent).ToCharArray()); |
| #if NETSTANDARD1_3 |
| 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); |
| } |
| } |
| |
| /// <summary> |
| /// This appender requires a <see cref="Layout"/> to be set. |
| /// </summary> |
| /// <value><c>true</c></value> |
| /// <remarks> |
| /// <para> |
| /// This appender requires a <see cref="Layout"/> to be set. |
| /// </para> |
| /// </remarks> |
| override protected bool RequiresLayout |
| { |
| get { return true; } |
| } |
| |
| /// <summary> |
| /// Closes the UDP connection and releases all resources associated with |
| /// this <see cref="UdpAppender" /> instance. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Disables the underlying <see cref="UdpClient" /> and releases all managed |
| /// and unmanaged resources associated with the <see cref="UdpAppender" />. |
| /// </para> |
| /// </remarks> |
| override protected void OnClose() |
| { |
| base.OnClose(); |
| |
| if (this.Client != null) |
| { |
| this.Client.Close(); |
| this.Client = null; |
| } |
| } |
| |
| #endregion Override implementation of AppenderSkeleton |
| |
| #region Protected Instance Methods |
| |
| /// <summary> |
| /// Initializes the underlying <see cref="UdpClient" /> connection. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// The underlying <see cref="UdpClient"/> is initialized and binds to the |
| /// port number from which you intend to communicate. |
| /// </para> |
| /// <para> |
| /// Exceptions are passed to the <see cref="AppenderSkeleton.ErrorHandler"/>. |
| /// </para> |
| /// </remarks> |
| 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 |
| |
| /// <summary> |
| /// The IP address of the remote host or multicast group to which |
| /// the logging event will be sent. |
| /// </summary> |
| private IPAddress m_remoteAddress; |
| |
| /// <summary> |
| /// The TCP port number of the remote host or multicast group to |
| /// which the logging event will be sent. |
| /// </summary> |
| private int m_remotePort; |
| |
| /// <summary> |
| /// The cached remote endpoint to which the logging events will be sent. |
| /// </summary> |
| private IPEndPoint m_remoteEndPoint; |
| |
| /// <summary> |
| /// The TCP port number from which the <see cref="UdpClient" /> will communicate. |
| /// </summary> |
| private int m_localPort; |
| |
| /// <summary> |
| /// The <see cref="UdpClient" /> instance that will be used for sending the |
| /// logging events. |
| /// </summary> |
| private UdpClient m_client; |
| |
| /// <summary> |
| /// The encoding to use for the packet. |
| /// </summary> |
| #if NETSTANDARD1_3 |
| private Encoding m_encoding = Encoding.Unicode; |
| #else |
| private Encoding m_encoding = Encoding.Default; |
| #endif |
| |
| #endregion Private Instance Fields |
| } |
| } |