#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
// .NET Compact Framework 1.0 has no support for Marshal.StringToHGlobalAnsi
// SSCLI 1.0 has no support for Marshal.StringToHGlobalAnsi
#if !NETCF && !SSCLI
using System;
using System.Runtime.InteropServices;
using log4net.Core;
using log4net.Appender;
using log4net.Util;
using log4net.Layout;
namespace log4net.Appender
{
///
/// Logs events to a local syslog service.
///
///
///
/// This appender uses the POSIX libc library functions openlog, syslog, and closelog.
/// If these functions are not available on the local system then this appender will not work!
///
///
/// The functions openlog, syslog, and closelog are specified in SUSv2 and
/// POSIX 1003.1-2001 standards. These are used to log messages to the local syslog service.
///
///
/// 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 to log via UDP.
///
///
/// 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
/// values. The facilities list is predefined
/// and cannot be extended.
///
///
/// An identifier is specified with each log message. This can be specified
/// by setting the property. The identity (also know
/// as the tag) must not contain white space. The default value for the
/// identity is the application name (from ).
///
///
/// Rob Lyon
/// Nicko Cadell
public class LocalSyslogAppender : AppenderSkeleton
{
#region Enumerations
///
/// syslog severities
///
///
///
/// The log4net Level maps to a syslog severity using the
/// method and the
/// class. The severity is set on .
///
///
public enum SyslogSeverity
{
///
/// system is unusable
///
Emergency = 0,
///
/// action must be taken immediately
///
Alert = 1,
///
/// critical conditions
///
Critical = 2,
///
/// error conditions
///
Error = 3,
///
/// warning conditions
///
Warning = 4,
///
/// normal but significant condition
///
Notice = 5,
///
/// informational
///
Informational = 6,
///
/// debug-level messages
///
Debug = 7
};
///
/// syslog facilities
///
///
///
/// The syslog facility defines which subsystem the logging comes from.
/// This is set on the property.
///
///
public enum SyslogFacility
{
///
/// kernel messages
///
Kernel = 0,
///
/// random user-level messages
///
User = 1,
///
/// mail system
///
Mail = 2,
///
/// system daemons
///
Daemons = 3,
///
/// security/authorization messages
///
Authorization = 4,
///
/// messages generated internally by syslogd
///
Syslog = 5,
///
/// line printer subsystem
///
Printer = 6,
///
/// network news subsystem
///
News = 7,
///
/// UUCP subsystem
///
Uucp = 8,
///
/// clock (cron/at) daemon
///
Clock = 9,
///
/// security/authorization messages (private)
///
Authorization2 = 10,
///
/// ftp daemon
///
Ftp = 11,
///
/// NTP subsystem
///
Ntp = 12,
///
/// log audit
///
Audit = 13,
///
/// log alert
///
Alert = 14,
///
/// clock daemon
///
Clock2 = 15,
///
/// reserved for local use
///
Local0 = 16,
///
/// reserved for local use
///
Local1 = 17,
///
/// reserved for local use
///
Local2 = 18,
///
/// reserved for local use
///
Local3 = 19,
///
/// reserved for local use
///
Local4 = 20,
///
/// reserved for local use
///
Local5 = 21,
///
/// reserved for local use
///
Local6 = 22,
///
/// reserved for local use
///
Local7 = 23
}
#endregion // Enumerations
#region Public Instance Constructors
///
/// Initializes a new instance of the class.
///
///
/// This instance of the class is set up to write
/// to a local syslog service.
///
public LocalSyslogAppender()
{
}
#endregion // Public Instance Constructors
#region Public Instance Properties
///
/// Message identity
///
///
///
/// An identifier is specified with each log message. This can be specified
/// by setting the property. The identity (also know
/// as the tag) must not contain white space. The default value for the
/// identity is the application name (from ).
///
///
public string Identity
{
get { return m_identity; }
set { m_identity = value; }
}
///
/// Syslog facility
///
///
/// Set to one of the values. The list of
/// facilities is predefined and cannot be extended. The default value
/// is .
///
public SyslogFacility Facility
{
get { return m_facility; }
set { m_facility = value; }
}
#endregion // Public Instance Properties
///
/// Add a mapping of level to severity
///
/// The mapping to add
///
///
/// Adds a to this appender.
///
///
public void AddMapping(LevelSeverity mapping)
{
m_levelMapping.Add(mapping);
}
#region IOptionHandler Implementation
///
/// 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.
///
///
#if NET_4_0
[System.Security.SecuritySafeCritical]
#endif
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
///
/// This method is called by the method.
///
/// The event to log.
///
///
/// Writes the event to a remote syslog daemon.
///
///
/// The format of the output will depend on the appender's layout.
///
///
#if NET_4_0
[System.Security.SecuritySafeCritical]
#endif
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, UnmanagedCode = true)]
protected override void Append(LoggingEvent loggingEvent)
{
int priority = GeneratePriority(m_facility, GetSeverity(loggingEvent.Level));
string message = RenderLoggingEvent(loggingEvent);
// Call the local libc syslog method
// The second argument is a printf style format string
syslog(priority, "%s", message);
}
///
/// Close the syslog when the appender is closed
///
///
///
/// Close the syslog when the appender is closed
///
///
#if NET_4_0
[System.Security.SecuritySafeCritical]
#endif
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);
}
}
///
/// This appender requires a to be set.
///
/// true
///
///
/// This appender requires a to be set.
///
///
override protected bool RequiresLayout
{
get { return true; }
}
#endregion // AppenderSkeleton Implementation
#region Protected Members
///
/// Translates a log4net level to a syslog severity.
///
/// A log4net level.
/// A syslog severity.
///
///
/// Translates a log4net level to a syslog severity.
///
///
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
///
/// Generate a syslog priority.
///
/// The syslog facility.
/// The syslog severity.
/// A syslog priority.
private static int GeneratePriority(SyslogFacility facility, SyslogSeverity severity)
{
return ((int)facility * 8) + (int)severity;
}
#endregion // Public Static Members
#region Private Instances Fields
///
/// The facility. The default facility is .
///
private SyslogFacility m_facility = SyslogFacility.User;
///
/// The message identity
///
private string m_identity;
///
/// Marshaled handle to the identity string. We have to hold on to the
/// string as the openlog and syslog APIs just hold the
/// pointer to the ident and dereference it for each log message.
///
private IntPtr m_handleToIdentity = IntPtr.Zero;
///
/// Mapping from level object to syslog severity
///
private LevelMapping m_levelMapping = new LevelMapping();
#endregion // Private Instances Fields
#region External Members
///
/// Open connection to system logger.
///
[DllImport("libc")]
private static extern void openlog(IntPtr ident, int option, SyslogFacility facility);
///
/// Generate a log message.
///
///
///
/// The libc syslog method takes a format string and a variable argument list similar
/// to the classic printf function. As this type of vararg list is not supported
/// by C# we need to specify the arguments explicitly. Here we have specified the
/// format string with a single message argument. The caller must set the format
/// string to "%s".
///
///
[DllImport("libc", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
private static extern void syslog(int priority, string format, string message);
///
/// Close descriptor used to write to system logger.
///
[DllImport("libc")]
private static extern void closelog();
#endregion // External Members
#region LevelSeverity LevelMapping Entry
///
/// 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.
///
///
///
/// 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.
///
///
public class LevelSeverity : LevelMappingEntry
{
private SyslogSeverity m_severity;
///
/// The mapped syslog severity for the specified level
///
///
///
/// Required property.
/// The mapped syslog severity for the specified level
///
///
public SyslogSeverity Severity
{
get { return m_severity; }
set { m_severity = value; }
}
}
#endregion // LevelSeverity LevelMapping Entry
}
}
#endif