#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