You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4net-dev@logging.apache.org by ni...@apache.org on 2004/09/14 23:00:36 UTC

cvs commit: logging-log4net/src/Appender LocalSyslogAppender.cs RemoteSyslogAppender.cs

nicko       2004/09/14 14:00:36

  Added:       src/Appender LocalSyslogAppender.cs RemoteSyslogAppender.cs
  Log:
  Added LocalSyslogAppender and RemoteSyslogAppender based on Rob Lyon's contribution.
  LocalSyslogAppender uses libc calls to write to the local syslog service.
  RemoteSyslogAppender uses UDP port 514 to send messages to a remote syslogd.
  
  Revision  Changes    Path
  1.1                  logging-log4net/src/Appender/LocalSyslogAppender.cs
  
  Index: LocalSyslogAppender.cs
  ===================================================================
  #region Copyright & License
  /*
   * Copyright 2004 The Apache Software Foundation
   *
   * 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.
   */
  #endregion
  
  using System;
  using System.Runtime.InteropServices;
  
  using log4net.Core;
  using log4net.Appender;
  using log4net.Util;
  using log4net.Layout;
  
  namespace log4net.Appender 
  {
  	/// <summary>
  	/// Logs events to a local syslog service.
  	/// </summary>
  	/// <remarks>
  	/// <note>
  	/// This appender uses the POSIX libc library functions <c>openlog</c>, <c>syslog</c>, and <c>closelog</c>.
  	/// If these functions are not available on the local system then this appender will not work!
  	/// </note>
  	/// <para>
  	/// The functions <c>openlog</c>, <c>syslog</c>, and <c>closelog</c> are specified in SUSv2 and 
  	/// POSIX 1003.1-2001 standards. These are used to log messages to the local syslog service.
  	/// </para>
  	/// <para>
  	/// This appender talks to a local syslog service. If you need to log to a remote syslog
  	/// daemon and you cannot configure your local syslog service to do this you may be
  	/// able to use the <see cref="RemoteSyslogAppender"/> to log via UDP.
  	/// </para>
  	/// <para>
  	/// Syslog messages must have a facility and and a severity. The severity
  	/// is derived from the Level of the logging event.
  	/// The facility must be chosen from the set of defined syslog 
  	/// <see cref="SyslogFacility"/> values. The facilities list is predefined
  	/// and cannot be extended.
  	/// </para>
  	/// <para>
  	/// An identifier is specified with each log message. This can be specified
  	/// by setting the <see cref="Identity"/> property. The identity (also know 
  	/// as the tag) must not contain white space. The default value for the
  	/// identity is the application name (from <see cref="SystemInfo.ApplicationFriendlyName"/>).
  	/// </para>
  	/// </remarks>
  	/// <author>Rob Lyon</author>
  	/// <author>Nicko Cadell</author>
  	public class LocalSyslogAppender : AppenderSkeleton 
  	{
  		#region Enumerations
  
  		/// <summary>
  		/// syslog severities
  		/// </summary>
  		public enum SyslogSeverity
  		{
  			/// <summary>
  			/// system is unusable
  			/// </summary>
  			Emergency = 0,
  
  			/// <summary>
  			/// action must be taken immediately
  			/// </summary>
  			Alert = 1,
  
  			/// <summary>
  			/// critical conditions
  			/// </summary>
  			Critical = 2,
  
  			/// <summary>
  			/// error conditions
  			/// </summary>
  			Error = 3,
  
  			/// <summary>
  			/// warning conditions
  			/// </summary>
  			Warning = 4,
  
  			/// <summary>
  			/// normal but significant condition
  			/// </summary>
  			Notice = 5,
  
  			/// <summary>
  			/// informational
  			/// </summary>
  			Informational = 6,
  
  			/// <summary>
  			/// debug-level messages
  			/// </summary>
  			Debug = 7
  		};
  
  		/// <summary>
  		/// syslog facilities
  		/// </summary>
  		public enum SyslogFacility
  		{
  			/// <summary>
  			/// kernel messages
  			/// </summary>
  			Kernel = 0,
  
  			/// <summary>
  			/// random user-level messages
  			/// </summary>
  			User = 1,
  
  			/// <summary>
  			/// mail system
  			/// </summary>
  			Mail = 2,
  
  			/// <summary>
  			/// system daemons
  			/// </summary>
  			Daemons = 3,
  
  			/// <summary>
  			/// security/authorization messages
  			/// </summary>
  			Authorization = 4,
  
  			/// <summary>
  			/// messages generated internally by syslogd
  			/// </summary>
  			Syslog = 5,
  
  			/// <summary>
  			/// line printer subsystem
  			/// </summary>
  			Printer = 6,
  
  			/// <summary>
  			/// network news subsystem
  			/// </summary>
  			News = 7,
  
  			/// <summary>
  			/// UUCP subsystem
  			/// </summary>
  			Uucp = 8,
  
  			/// <summary>
  			/// clock (cron/at) daemon
  			/// </summary>
  			Clock = 9,
  
  			/// <summary>
  			/// security/authorization  messages (private)
  			/// </summary>
  			Authorization2 = 10,
  
  			/// <summary>
  			/// ftp daemon
  			/// </summary>
  			Ftp = 11,
  
  			/// <summary>
  			/// NTP subsystem
  			/// </summary>
  			Ntp = 12,
  
  			/// <summary>
  			/// log audit
  			/// </summary>
  			Audit = 13,
  
  			/// <summary>
  			/// log alert
  			/// </summary>
  			Alert = 14,
  
  			/// <summary>
  			/// clock daemon
  			/// </summary>
  			Clock2 = 15,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local0 = 16,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local1 = 17,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local2 = 18,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local3 = 19,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local4 = 20,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local5 = 21,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local6 = 22,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local7 = 23
  		}
  
  		#endregion Enumerations
  
  		#region Public Instance Constructors
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="LocalSyslogAppender" /> class.
  		/// </summary>
  		/// <remarks>
  		/// This instance of the <see cref="LocalSyslogAppender" /> class is set up to write 
  		/// to a local syslog service.
  		/// </remarks>
  		public LocalSyslogAppender() 
  		{
  		}
  
  		#endregion Public Instance Constructors
  
  		#region Public Instance Properties
  		
  		/// <summary>
  		/// Message identity
  		/// </summary>
  		/// <remarks>
  		/// <para>
  		/// An identifier is specified with each log message. This can be specified
  		/// by setting the <see cref="Identity"/> property. The identity (also know 
  		/// as the tag) must not contain white space. The default value for the
  		/// identity is the application name (from <see cref="SystemInfo.ApplicationFriendlyName"/>).
  		/// </para>
  		/// </remarks>
  		public string Identity
  		{
  			get { return m_identity; }
  			set { m_identity = value; }
  		}
  
  		/// <summary>
  		/// Syslog facility
  		/// </summary>
  		/// <remarks>
  		/// Set to one of the <see cref="SyslogFacility"/> values. The list of
  		/// facilities is predefined and cannot be extended. The default value
  		/// is <see cref="SyslogFacility.User"/>.
  		/// </remarks>
  		public SyslogFacility Facility
  		{
  			get { return m_facility; }
  			set { m_facility = value; }
  		}
  		
  		#endregion Public Instance Properties
  
  		/// <summary>
  		/// Add a mapping of level to severity
  		/// </summary>
  		/// <param name="mapping">The mapping to add</param>
  		public void AddMapping(LevelSeverity mapping)
  		{
  			m_levelMapping.Add(mapping);
  		}
  
  		#region IOptionHandler Implementation
  
  		/// <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>
  		/// </remarks>
  		public override void ActivateOptions()
  		{
  			base.ActivateOptions();
  			
  			m_levelMapping.ActivateOptions();
  
  			string identString = m_identity;
  			if (identString == null)
  			{
  				// Set to app name by default
  				identString = SystemInfo.ApplicationFriendlyName;
  			}
  
  			// create the native heap ansi string. Note this is a copy of our string
  			// so we do not need to hold on to the string itself, holding on to the
  			// handle will keep the heap ansi string alive.
  			m_handleToIdentity = Marshal.StringToHGlobalAnsi(identString);
  
  			// open syslog
  			openlog(m_handleToIdentity, 1, m_facility);
  		}
  
  		#endregion IOptionHandler Implementation
  
  		#region AppenderSkeleton Implementation
  
  		/// <summary>
  		/// This method is called by the <see cref="AppenderSkeleton.DoAppend"/> method.
  		/// </summary>
  		/// <param name="loggingEvent">The event to log.</param>
  		/// <remarks>
  		/// <para>
  		/// Writes the event to a remote syslog daemon.
  		/// </para>
  		/// <para>
  		/// The format of the output will depend on the appender's layout.
  		/// </para>
  		/// </remarks>
  		protected override void Append(LoggingEvent loggingEvent) 
  		{
  			int priority = GeneratePriority(m_facility, GetSeverity(loggingEvent.Level));
  			string message = RenderLoggingEvent(loggingEvent);
  
  			syslog(priority, message);
  		}
  
  		/// <summary>
  		/// Close the syslog when the appender is closed
  		/// </summary>
  		protected override void OnClose()
  		{
  			base.OnClose();
  
  			try
  			{
  				// close syslog
  				closelog();
  			}
  			catch(DllNotFoundException)
  			{
  				// Ignore dll not found at this point
  			}
  		
  			if (m_handleToIdentity != IntPtr.Zero)
  			{
  				// free global ident
  				Marshal.FreeHGlobal(m_handleToIdentity);
  			}
  		}
  
  		/// <summary>
  		/// This appender requires a <see cref="AppenderSkeleton.Layout"/> to be set.
  		/// </summary>
  		/// <value><c>true</c></value>
  		override protected bool RequiresLayout
  		{
  			get { return true; }
  		}
  
  		#endregion AppenderSkeleton Implementation
  
  		#region Protected Members
  
  		/// <summary>
  		/// Translates a log4net level to a syslog severity.
  		/// </summary>
  		/// <param name="level">A log4net level.</param>
  		/// <returns>A syslog severity.</returns>
  		virtual protected SyslogSeverity GetSeverity(Level level)
  		{
  			LevelSeverity levelSeverity = m_levelMapping.Lookup(level) as LevelSeverity;
  			if (levelSeverity != null)
  			{
  				return levelSeverity.Severity;
  			}
  
  			//
  			// Fallback to sensible default values
  			//
  
  			if (level >= Level.Alert) 
  			{
  				return SyslogSeverity.Alert;
  			} 
  			else if (level >= Level.Critical) 
  			{
  				return SyslogSeverity.Critical;
  			} 
  			else if (level >= Level.Error) 
  			{
  				return SyslogSeverity.Error;
  			} 
  			else if (level >= Level.Warn) 
  			{
  				return SyslogSeverity.Warning;
  			} 
  			else if (level >= Level.Notice) 
  			{
  				return SyslogSeverity.Notice;
  			} 
  			else if (level >= Level.Info) 
  			{
  				return SyslogSeverity.Informational;
  			} 
  			// Default setting
  			return SyslogSeverity.Debug;
  		}
  
  		#endregion Protected Members
  
  		#region Public Static Members
  
  		/// <summary>
  		/// Generate a syslog priority.
  		/// </summary>
  		/// <param name="facility">The syslog facility.</param>
  		/// <param name="severity">The syslog severity.</param>
  		/// <returns>A syslog priority.</returns>
  		public static int GeneratePriority(SyslogFacility facility, SyslogSeverity severity)
  		{
  			return ((int)facility * 8) + (int)severity;
  		}
  
  		#endregion Public Static Members
  
  		#region Private Instances Fields
  
  		/// <summary>
  		/// The facility. The default facility is <see cref="SyslogFacility.User"/>.
  		/// </summary>
  		private SyslogFacility m_facility = SyslogFacility.User;
  
  		/// <summary>
  		/// The message identity
  		/// </summary>
  		private string m_identity;
  
  		/// <summary>
  		/// Marshaled handle to the identity string. We have to hold on to the
  		/// string as the <c>openlog</c> and <c>syslog</c> APIs just hold the
  		/// pointer to the ident and dereference it for each log message.
  		/// </summary>
  		private IntPtr m_handleToIdentity = IntPtr.Zero;
  
  		/// <summary>
  		/// Mapping from level object to syslog severity
  		/// </summary>
  		private LevelMapping m_levelMapping = new LevelMapping();
  
  		#endregion Private Instances Fields
  
  		#region External Members
  		
  		/// <summary>
  		/// Open connection to system logger.
  		/// </summary>
  		[DllImport("libc")]
  		public static extern void openlog(IntPtr ident, int option, SyslogFacility facility);
  
  		/// <summary>
  		/// Generate a log message.
  		/// </summary>
  		[DllImport("libc")]
  		public static extern void syslog(int priority, string message);
  
  		/// <summary>
  		/// Close descriptor used to write to system logger.
  		/// </summary>
  		[DllImport("libc")]
  		public static extern void closelog();
  
  		#endregion 
  
  		#region LevelSeverity LevelMapping Entry
  
  		/// <summary>
  		/// A class to act as a mapping between the level that a logging call is made at and
  		/// the syslog severity that is should be logged at.
  		/// </summary>
  		public class LevelSeverity : LevelMappingEntry
  		{
  			private SyslogSeverity m_severity;
  
  			/// <summary>
  			/// The mapped syslog severity for the specified level
  			/// </summary>
  			public SyslogSeverity Severity
  			{
  				get { return m_severity; }
  				set { m_severity = value; }
  			}
  		}
  
  		#endregion // LevelSeverity LevelMapping Entry
  	}
  }
  
  
  
  1.1                  logging-log4net/src/Appender/RemoteSyslogAppender.cs
  
  Index: RemoteSyslogAppender.cs
  ===================================================================
  #region Copyright & License
  /*
   * Copyright 2004 The Apache Software Foundation
   *
   * 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.
   */
  #endregion
  
  using System;
  
  using log4net.Core;
  using log4net.Appender;
  using log4net.Util;
  using log4net.Layout;
  
  namespace log4net.Appender 
  {
  	/// <summary>
  	/// Logs events to a remote syslog daemon.
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// The BSD syslog protocol is used to remotely log to
  	/// a syslog daemon. The syslogd listens for for messages
  	/// on UDP port 514.
  	/// </para>
  	/// <para>
  	/// The syslog UDP protocol is not authenticated. Most syslog daemons
  	/// do not accept remote log messages because of the security implications.
  	/// You may be able to use the LocalSyslogAppender to talk to a local
  	/// syslog service.
  	/// </para>
  	/// <para>
  	/// There is an RFC 3164 that claims to document the BSD Syslog Protocol.
  	/// This RFC can be seen here: http://www.faqs.org/rfcs/rfc3164.html.
  	/// This appender generates what the RFC calls an "Original Device Message",
  	/// i.e. does not include the TIMESTAMP or HOSTNAME fields. By observation
  	/// this format of message will be accepted by all current syslog daemon
  	/// implementations. The daemon will attach the current time and the source
  	/// hostname or IP address to any messages received.
  	/// </para>
  	/// <para>
  	/// Syslog messages must have a facility and and a severity. The severity
  	/// is derived from the Level of the logging event.
  	/// The facility must be chosen from the set of defined syslog 
  	/// <see cref="SyslogFacility"/> values. The facilities list is predefined
  	/// and cannot be extended.
  	/// </para>
  	/// <para>
  	/// An identifier is specified with each log message. This can be specified
  	/// by setting the <see cref="Identity"/> property. The identity (also know 
  	/// as the tag) must not contain white space. The default value for the
  	/// identity is the application name (from <see cref="LoggingEvent.Domain"/>).
  	/// </para>
  	/// </remarks>
  	/// <author>Rob Lyon</author>
  	/// <author>Nicko Cadell</author>
  	public class RemoteSyslogAppender : UdpAppender 
  	{
  		/// <summary>
  		/// Syslog port 514
  		/// </summary>
  		private const int DefaultSyslogPort = 514;
  
  		#region Enumerations
  
  		/// <summary>
  		/// syslog severities
  		/// </summary>
  		public enum SyslogSeverity
  		{
  			/// <summary>
  			/// system is unusable
  			/// </summary>
  			Emergency = 0,
  
  			/// <summary>
  			/// action must be taken immediately
  			/// </summary>
  			Alert = 1,
  
  			/// <summary>
  			/// critical conditions
  			/// </summary>
  			Critical = 2,
  
  			/// <summary>
  			/// error conditions
  			/// </summary>
  			Error = 3,
  
  			/// <summary>
  			/// warning conditions
  			/// </summary>
  			Warning = 4,
  
  			/// <summary>
  			/// normal but significant condition
  			/// </summary>
  			Notice = 5,
  
  			/// <summary>
  			/// informational
  			/// </summary>
  			Informational = 6,
  
  			/// <summary>
  			/// debug-level messages
  			/// </summary>
  			Debug = 7
  		};
  
  		/// <summary>
  		/// syslog facilities
  		/// </summary>
  		public enum SyslogFacility
  		{
  			/// <summary>
  			/// kernel messages
  			/// </summary>
  			Kernel = 0,
  
  			/// <summary>
  			/// random user-level messages
  			/// </summary>
  			User = 1,
  
  			/// <summary>
  			/// mail system
  			/// </summary>
  			Mail = 2,
  
  			/// <summary>
  			/// system daemons
  			/// </summary>
  			Daemons = 3,
  
  			/// <summary>
  			/// security/authorization messages
  			/// </summary>
  			Authorization = 4,
  
  			/// <summary>
  			/// messages generated internally by syslogd
  			/// </summary>
  			Syslog = 5,
  
  			/// <summary>
  			/// line printer subsystem
  			/// </summary>
  			Printer = 6,
  
  			/// <summary>
  			/// network news subsystem
  			/// </summary>
  			News = 7,
  
  			/// <summary>
  			/// UUCP subsystem
  			/// </summary>
  			Uucp = 8,
  
  			/// <summary>
  			/// clock (cron/at) daemon
  			/// </summary>
  			Clock = 9,
  
  			/// <summary>
  			/// security/authorization  messages (private)
  			/// </summary>
  			Authorization2 = 10,
  
  			/// <summary>
  			/// ftp daemon
  			/// </summary>
  			Ftp = 11,
  
  			/// <summary>
  			/// NTP subsystem
  			/// </summary>
  			Ntp = 12,
  
  			/// <summary>
  			/// log audit
  			/// </summary>
  			Audit = 13,
  
  			/// <summary>
  			/// log alert
  			/// </summary>
  			Alert = 14,
  
  			/// <summary>
  			/// clock daemon
  			/// </summary>
  			Clock2 = 15,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local0 = 16,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local1 = 17,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local2 = 18,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local3 = 19,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local4 = 20,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local5 = 21,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local6 = 22,
  
  			/// <summary>
  			/// reserved for local use
  			/// </summary>
  			Local7 = 23
  		}
  
  		#endregion Enumerations
  
  		#region Public Instance Constructors
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="RemoteSyslogAppender" /> class.
  		/// </summary>
  		/// <remarks>
  		/// This instance of the <see cref="RemoteSyslogAppender" /> class is set up to write 
  		/// to a remote syslog daemon.
  		/// </remarks>
  		public RemoteSyslogAppender() 
  		{
  			// syslog udp defaults
  			this.RemotePort = DefaultSyslogPort;
  			this.RemoteAddress = System.Net.IPAddress.Parse("127.0.0.1");
  			this.Encoding = System.Text.Encoding.ASCII;
  		}
  
  		#endregion Public Instance Constructors
  
  		#region Public Instance Properties
  		
  		/// <summary>
  		/// Message identity
  		/// </summary>
  		/// <remarks>
  		/// <para>
  		/// An identifier is specified with each log message. This can be specified
  		/// by setting the <see cref="Identity"/> property. The identity (also know 
  		/// as the tag) must not contain white space. The default value for the
  		/// identity is the application name (from <see cref="LoggingEvent.Domain"/>).
  		/// </para>
  		/// </remarks>
  		public PatternLayout Identity
  		{
  			get { return m_identity; }
  			set { m_identity = value; }
  		}
  
  		/// <summary>
  		/// Syslog facility
  		/// </summary>
  		/// <remarks>
  		/// Set to one of the <see cref="SyslogFacility"/> values. The list of
  		/// facilities is predefined and cannot be extended. The default value
  		/// is <see cref="SyslogFacility.User"/>.
  		/// </remarks>
  		public SyslogFacility Facility
  		{
  			get { return m_facility; }
  			set { m_facility = value; }
  		}
  		
  		#endregion Public Instance Properties
  
  		/// <summary>
  		/// Add a mapping of level to severity
  		/// </summary>
  		/// <param name="mapping">The mapping to add</param>
  		public void AddMapping(LevelSeverity mapping)
  		{
  			m_levelMapping.Add(mapping);
  		}
  
  		#region AppenderSkeleton Implementation
  
  		/// <summary>
  		/// This method is called by the <see cref="AppenderSkeleton.DoAppend"/> method.
  		/// </summary>
  		/// <param name="loggingEvent">The event to log.</param>
  		/// <remarks>
  		/// <para>
  		/// Writes the event to a remote syslog daemon.
  		/// </para>
  		/// <para>
  		/// The format of the output will depend on the appender's layout.
  		/// </para>
  		/// </remarks>
  		protected override void Append(LoggingEvent loggingEvent) 
  		{
  			try 
  			{
  				System.IO.StringWriter writer = new System.IO.StringWriter();
  
  				// Priority
  				int priority = GeneratePriority(m_facility, GetSeverity(loggingEvent.Level));
  				writer.Write('<');
  				writer.Write(priority);
  				writer.Write('>');
  
  				// Identity
  				if (m_identity != null)
  				{
  					m_identity.Format(writer, loggingEvent);
  				}
  				else
  				{
  					writer.Write(loggingEvent.Domain);
  				}
  				writer.Write(": ");
  
  				// Message. The message goes after the tag/identity
  				RenderLoggingEvent(writer, loggingEvent);
  
  				// Grab as a byte array
  				string fullMessage = writer.ToString();
  				Byte [] buffer = this.Encoding.GetBytes(fullMessage.ToCharArray());
  
  				this.Client.Send(buffer, buffer.Length, this.RemoteEndPoint);
  			} 
  			catch (Exception e) 
  			{
  				ErrorHandler.Error(
  					"Unable to send logging event to remote syslog " + 
  					this.RemoteAddress.ToString() + 
  					" on port " + 
  					this.RemotePort + ".", 
  					e, 
  					ErrorCode.WriteFailure);
  			}
  		}
  
  		/// <summary>
  		/// Initialise the options for this appender
  		/// </summary>
  		/// <remarks>
  		/// <para>
  		/// Initialise the level to syslog severity mappings set on this appender.
  		/// </para>
  		/// </remarks>
  		public override void ActivateOptions()
  		{
  			base.ActivateOptions();
  			m_levelMapping.ActivateOptions();
  		}
  
  		#endregion AppenderSkeleton Implementation
  
  		#region Protected Members
  
  		/// <summary>
  		/// Translates a log4net level to a syslog severity.
  		/// </summary>
  		/// <param name="level">A log4net level.</param>
  		/// <returns>A syslog severity.</returns>
  		virtual protected SyslogSeverity GetSeverity(Level level)
  		{
  			LevelSeverity levelSeverity = m_levelMapping.Lookup(level) as LevelSeverity;
  			if (levelSeverity != null)
  			{
  				return levelSeverity.Severity;
  			}
  
  			//
  			// Fallback to sensible default values
  			//
  
  			if (level >= Level.Alert) 
  			{
  				return SyslogSeverity.Alert;
  			} 
  			else if (level >= Level.Critical) 
  			{
  				return SyslogSeverity.Critical;
  			} 
  			else if (level >= Level.Error) 
  			{
  				return SyslogSeverity.Error;
  			} 
  			else if (level >= Level.Warn) 
  			{
  				return SyslogSeverity.Warning;
  			} 
  			else if (level >= Level.Notice) 
  			{
  				return SyslogSeverity.Notice;
  			} 
  			else if (level >= Level.Info) 
  			{
  				return SyslogSeverity.Informational;
  			} 
  			// Default setting
  			return SyslogSeverity.Debug;
  		}
  
  		#endregion Protected Members
  
  		#region Public Static Members
  
  		/// <summary>
  		/// Generate a syslog priority.
  		/// </summary>
  		/// <param name="facility">The syslog facility.</param>
  		/// <param name="severity">The syslog severity.</param>
  		/// <returns>A syslog priority.</returns>
  		public static int GeneratePriority(SyslogFacility facility, SyslogSeverity severity)
  		{
  			return ((int)facility * 8) + (int)severity;
  		}
  
  		#endregion Public Static Members
  
  		#region Private Instances Fields
  
  		/// <summary>
  		/// The facility. The default facility is <see cref="SyslogFacility.User"/>.
  		/// </summary>
  		private SyslogFacility m_facility = SyslogFacility.User;
  
  		/// <summary>
  		/// The message identity
  		/// </summary>
  		private PatternLayout m_identity;
  
  		/// <summary>
  		/// Mapping from level object to syslog severity
  		/// </summary>
  		private LevelMapping m_levelMapping = new LevelMapping();
  
  		#endregion Private Instances Fields
  
  		#region LevelSeverity LevelMapping Entry
  
  		/// <summary>
  		/// A class to act as a mapping between the level that a logging call is made at and
  		/// the syslog severity that is should be logged at.
  		/// </summary>
  		public class LevelSeverity : LevelMappingEntry
  		{
  			private SyslogSeverity m_severity;
  
  			/// <summary>
  			/// The mapped syslog severity for the specified level
  			/// </summary>
  			public SyslogSeverity Severity
  			{
  				get { return m_severity; }
  				set { m_severity = value; }
  			}
  		}
  
  		#endregion // LevelSeverity LevelMapping Entry
  	}
  }