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
}
}