Nicko,

I mis-read your comments and I went ahead and re-wrote to the RFC you sent.  I 
did have to remove the options enumeration * but probably not a problem.

The newer syslog daemons by default do not accept network traffic.  The 
SyslogAppender will only work (like the log4j version) if you enable network 
traffic with the "-r" option.  This could be considered a security risk.  The 
native version uses a unix socket and will work without reconfiguring the 
syslogd options.

The timestamp and host are being added automatically by the daemon (I tried to 
override with the event timestamp, but was unsuccessful).  I did add, per your 
comment, a tag with a pid.  The Udp and Native versions now appear the same in 
the log.

Thanks,
Rob

>>> [EMAIL PROTECTED] 8/24/04 03:03 PM >>>
Rob,

This looks great. A couple of things. 

I would rather not have to include the BSD licence as it does complicate
things. I will go back to the RFC
(http://www.faqs.org/rfcs/rfc3164.html) and rewrite those portions as
necessary. 

Does the native mono appender do anything that the SyslogAppender (Udp
based) cannot do?
Reading through the man page it looks like it is following exactly the
same protocol. Are there times when a call to syslog(3) will work but
there isn't a syslog deamon listening?

Looking at the syslog protocol it looks as though we should be including
the date, hostname and a tag in the content of the message. I know that
we can specify this in the layout, but it might be better to do it in
code because it has a fixed format.

Cheers,

Nicko

> -----Original Message-----
> From: Rob Lyon [mailto:[EMAIL PROTECTED] 
> Sent: 24 August 2004 21:30
> To: [email protected] 
> Subject: RE: syslog log4net appender
> 
> This is a cleaned-up version that includes a native version 
> for Mono/Linux.
> 
> Rob
> 
> >>> [EMAIL PROTECTED] 8/24/04 11:11 AM >>>
> I attached an implementation of a syslog log4net appender.  
> Please review.  I added a BSD license header because I used 
> the defines from syslog.h.  Is that correct?  Please review 
> the copyright and license.
> 
> Thanks,
> Rob
> 
> >>> [EMAIL PROTECTED] 8/22/04 10:16 AM >>>
> Rob,
> 
> You may want to look at the log4j syslog implementation:
> 
> http://cvs.apache.org/viewcvs.cgi/logging-log4j/src/java/org/a 
> pache/log4
> 
> 
> j/net/SyslogAppender.java?rev=1.18&view=auto
> 
> http://cvs.apache.org/viewcvs.cgi/logging-log4j/src/java/org/a 
> pache/log4
> 
> 
> j/helpers/SyslogWriter.java?rev=1.5&view=auto
> 
> http://cvs.apache.org/viewcvs.cgi/logging-log4j/src/java/org/a 
> pache/log4
> 
> 
> j/helpers/SyslogQuietWriter.java?rev=1.5&view=auto
> 
> Nicko 
> 
> > -----Original Message-----
> > From: Rob Lyon [mailto:[EMAIL PROTECTED] 
> > Sent: 21 August 2004 16:06
> > To: [email protected] 
> > Subject: syslog log4net appender
> > 
> > I need a syslog log4net appender.  Has anyone started an 
> > implementation?
> > Does anyone have any suggestions as I start an implementation?
> > 
> > Thanks,
> > Rob
> > 
> > 
> 

#region Copyright & License
/*
 * Copyright 2001-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.IO;
using System.Net;
using System.Runtime.InteropServices;

using log4net.spi;

namespace log4net.Appender 
{
        /// <summary>
        /// Logs entries to a remote syslog daemon.
        /// </summary>
        /// <author>Rob Lyon</author>
        public class SyslogNativeAppender : AppenderSkeleton 
        {
                #region Public Instance Constructors

                /// <summary>
                /// Initializes a new instance of the <see 
cref="SyslogAppender" /> class.
                /// </summary>
                /// <remarks>
                /// The instance of the <see cref="SyslogAppender" /> class is 
set up to write 
                /// to a remote syslog daemon.
                /// </remarks>
                public SyslogNativeAppender() 
                {
                }

                #endregion Public Instance Constructors

                #region Public Instance Properties
                
                /// <summary>
                /// syslog identity
                /// </summary>
                public string Identity
                {
                        get { return identity; }
                        set { identity = value; }
                }
                
                /// <summary>
                /// syslog facility
                /// </summary>
                public Syslog.Facility Facility
                {
                        get { return facility; }
                        set { facility = value; }
                }
                
                #endregion Public Instance Properties
                
                #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();
                        
                        // check identity
                        if (identity == null)
                        {
                                // create an identity
                                string[] args = 
Environment.GetCommandLineArgs();
        
                                if ((args.Length > 0) && (args[0] != null) && 
(args[0].Length > 0)
                                        && !args[0].StartsWith("mono"))
                                {
                                        identity = 
Path.GetFileNameWithoutExtension(args[0]);
                                }
                                else if ((args.Length > 1) && (args[1] != null) 
&& (args[1].Length > 0))
                                {
                                        identity = 
Path.GetFileNameWithoutExtension(args[1]);
                                }
                                else
                                {
                                        identity = "";
                                }
                        }

                        // global ident
                        handleIdent = Marshal.StringToHGlobalAnsi(identity);

                        // open syslog
                        openlog(handleIdent, 1, 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) 
                {
                        string message = RenderLoggingEvent(loggingEvent);
                                                        
                        int priority = Syslog.GeneratePriority(facility, 
Syslog.TranslateLevel(loggingEvent.Level));

                        syslog(priority, message);
                }

                /// <summary>
                /// On closing
                /// </summary>
                public override void OnClose()
                {
                        base.OnClose();

                        // close syslog
                        closelog();
                
                        // free global ident
                        Marshal.FreeHGlobal(handleIdent);
                }

                /// <summary>
                /// This appender requires a <see cref="Layout"/> to be set.
                /// </summary>
                /// <value><c>true</c></value>
                override protected bool RequiresLayout
                {
                        get { return true; }
                }

                #endregion AppenderSkeleton Implementation

                #region Private Instances Fields

                // identity
                string identity;

                // default to user
                Syslog.Facility facility = Syslog.Facility.User;

                // global ident
                IntPtr handleIdent;
        
                #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, 
Syslog.Facility facility);

                /// <summary>
                /// Generate a log message.
                /// </summary>
                [DllImport("libc")]
                public static extern void syslog(int priority, string message);

                /// <summary>
                /// Close desriptor used to write to system logger.
                /// </summary>
                [DllImport("libc")]
                public static extern void closelog();

                #endregion 
        }
}
#region Copyright & License
/*
 * Copyright 2001-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.Net;

using log4net;

namespace log4net.Appender 
{
        /// <summary>
        /// Syslog constants and utilities for log4net
        /// </summary>
        /// <remarks>syslog rfc: http://www.faqs.org/rfcs/rfc3164.html</remarks>
        /// <author>Rob Lyon</author>
        public class Syslog
        {
                #region Public Enums

                /// <summary>
                /// syslog levels
                /// </summary>
                [Flags]
                public enum Level
                {
                        /// <summary>
                        /// emergency: system is unusable
                        /// </summary>
                        Emergency = 0,

                        /// <summary>
                        /// alert: action must be taken immediately
                        /// </summary>
                        Alert = 1,

                        /// <summary>
                        /// critical: critical conditions
                        /// </summary>
                        Critical = 2,

                        /// <summary>
                        /// error: error conditions
                        /// </summary>
                        Error = 3,

                        /// <summary>
                        /// warning: warning conditions
                        /// </summary>
                        Warning = 4,

                        /// <summary>
                        /// notice: normal but significant condition
                        /// </summary>
                        Notice = 5,

                        /// <summary>
                        /// informational: informational messages
                        /// </summary>
                        Info = 6,

                        /// <summary>
                        /// debug: debug-level messages
                        /// </summary>
                        Debug = 7
                };

                /// <summary>
                /// syslog facilities
                /// </summary>
                [Flags]
                public enum Facility
                {
                        /// <summary>
                        /// kernel messages
                        /// </summary>
                        Kernal = 0,

                        /// <summary>
                        /// user-level messages
                        /// </summary>
                        User = 1,

                        /// <summary>
                        /// mail system
                        /// </summary>
                        Mail = 2,

                        /// <summary>
                        /// system daemons
                        /// </summary>
                        Daemon = 3,

                        /// <summary>
                        /// security/authorization messages
                        /// </summary>
                        Authorization = 4,

                        /// <summary>
                        /// messages generated internally by syslogd
                        /// </summary>
                        Internal = 5,

                        /// <summary>
                        /// line printer subsystem
                        /// </summary>
                        Lpr = 6,

                        /// <summary>
                        /// network news subsystem
                        /// </summary>
                        News = 7,

                        /// <summary>
                        /// UUCP subsystem
                        /// </summary>
                        Uucp = 8,

                        /// <summary>
                        /// clock daemon
                        /// </summary>
                        Clock = 9,

                        /// <summary>
                        /// security/authorization messages
                        /// </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>
                        /// local use 0
                        /// </summary>
                        Local0 = 16,

                        /// <summary>
                        /// local use 1
                        /// </summary>
                        Local1 = 17,

                        /// <summary>
                        /// local use 2
                        /// </summary>
                        Local2 = 18,

                        /// <summary>
                        /// local use 3
                        /// </summary>
                        Local3 = 19,

                        /// <summary>
                        /// local use 4
                        /// </summary>
                        Local4 = 20,

                        /// <summary>
                        /// local use 5
                        /// </summary>
                        Local5 = 21,

                        /// <summary>
                        /// local use 6
                        /// </summary>
                        Local6 = 22,

                        /// <summary>
                        /// local use 7
                        /// </summary>
                        Local7 = 23
                }
                
                #endregion Public Enums

                #region Public Static Fields

                /// <summary>
                /// syslog port
                /// </summary>
                public static int Port = 514;

                #endregion Public Static Fields

                #region Public Static Members

                /// <summary>
                /// Generate a syslog priority.
                /// </summary>
                /// <param name="facility">The syslog facility.</param>
                /// <param name="level">The syslog level.</param>
                /// <returns>A syslog priority.</returns>
                public static int GeneratePriority(Facility facility, Level 
level)
                {
                        return ((int)facility << 3) | (int)level;
                }

                /// <summary>
                /// Translates a log4net level to a syslog level.
                /// </summary>
                /// <param name="level">A log4net level.</param>
                /// <returns>A syslog level.</returns>
                public static Level TranslateLevel(log4net.spi.Level level)
                {
                        Level result = Level.Debug;

                        if (level == log4net.spi.Level.OFF)
                        {
                                result = Level.Emergency;
                        }
                        else if (level == log4net.spi.Level.FATAL)
                        {
                                result = Level.Emergency;
                        }
                        else if (level == log4net.spi.Level.ERROR)
                        {
                                result = Level.Error;
                        }
                        else if (level == log4net.spi.Level.WARN)
                        {
                                result = Level.Warning;
                        }
                        else if (level == log4net.spi.Level.INFO)
                        {
                                result = Level.Info;
                        }

                        return result;
                }

                #endregion Public Static Members
        }
}
#region Copyright & License
/*
 * Copyright 2001-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.IO;
using System.Net;
using System.Diagnostics;

using log4net.spi;

namespace log4net.Appender 
{
        /// <summary>
        /// Logs entries to a remote syslog daemon.
        /// </summary>
        /// <author>Rob Lyon</author>
        public class SyslogAppender : UdpAppender 
        {
                #region Public Instance Constructors

                /// <summary>
                /// Initializes a new instance of the <see 
cref="SyslogAppender" /> class.
                /// </summary>
                /// <remarks>
                /// The instance of the <see cref="SyslogAppender" /> class is 
set up to write 
                /// to a remote syslog daemon.
                /// </remarks>
                public SyslogAppender() 
                {
                        // syslog udp defaults
                        this.RemotePort = Syslog.Port;
                        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>
                /// syslog identity
                /// </summary>
                public string Identity
                {
                        get { return identity; }
                        set { identity = value; }
                }
                
                /// <summary>
                /// syslog facility
                /// </summary>
                public Syslog.Facility Facility
                {
                        get { return facility; }
                        set { facility = value; }
                }
                
                #endregion Public Instance Properties

                #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();
                        
                        // check identity
                        if (identity == null)
                        {
                                // create an identity
                                string[] args = 
Environment.GetCommandLineArgs();
                                
                                if ((args.Length > 0) && (args[0] != null) && 
(args[0].Length > 0)
                                        && !args[0].StartsWith("mono"))
                                {
                                        identity = 
Path.GetFileNameWithoutExtension(args[0]);
                                }
                                else if ((args.Length > 1) && (args[1] != null) 
&& (args[1].Length > 0))
                                {
                                        identity = 
Path.GetFileNameWithoutExtension(args[1]);
                                }
                                else
                                {
                                        identity = "";
                                }
                        }

                        // pid
                        pid = Process.GetCurrentProcess().Id;
                }

                #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) 
                {
                        try 
                        {
                                string message = 
RenderLoggingEvent(loggingEvent);
                                
                                int priority = 
Syslog.GeneratePriority(facility, Syslog.TranslateLevel(loggingEvent.Level));
                                
                                message = String.Format("<{0}>{1}[{2}]: {3}", 
priority, identity, pid, message);

                                Byte [] buffer = 
this.Encoding.GetBytes(message.ToCharArray());
                                
                                this.Client.Send(buffer, buffer.Length, 
this.RemoteAddress.ToString(), this.RemotePort);
                        } 
                        catch (Exception e) 
                        {
                                ErrorHandler.Error(
                                        "Unable to send logging event to remote 
host " + 
                                        this.RemoteAddress.ToString() + 
                                        " on port " + 
                                        this.RemotePort + ".", 
                                        e, 
                                        ErrorCodes.WriteFailure);
                        }
                }

                #endregion AppenderSkeleton Implementation

                #region Private Instances Fields

                // identity
                string identity;

                // pid
                int pid;

                // default to user
                Syslog.Facility facility = Syslog.Facility.User;

                #endregion Private Instances Fields
        }
}

Reply via email to