#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
|
|
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>
|
/// <remarks>
|
/// <para>
|
/// The syslog severities.
|
/// </para>
|
/// </remarks>
|
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>
|
/// <remarks>
|
/// <para>
|
/// The syslog facilities
|
/// </para>
|
/// </remarks>
|
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>
|
/// <remarks>
|
/// <para>
|
/// Add a <see cref="LevelSeverity"/> mapping to this appender.
|
/// </para>
|
/// </remarks>
|
public void AddMapping(LevelSeverity mapping)
|
{
|
m_levelMapping.Add(mapping);
|
}
|
|
#region AppenderSkeleton Implementation
|
|
/// <summary>
|
/// This method is called by the <see cref="AppenderSkeleton.DoAppend(LoggingEvent)"/> 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(System.Globalization.CultureInfo.InvariantCulture);
|
|
// 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>
|
/// Initialize the options for this appender
|
/// </summary>
|
/// <remarks>
|
/// <para>
|
/// Initialize 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>
|
/// <remarks>
|
/// <para>
|
/// Translates a log4net level to a syslog severity.
|
/// </para>
|
/// </remarks>
|
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>
|
/// <remarks>
|
/// <para>
|
/// Generate a syslog priority.
|
/// </para>
|
/// </remarks>
|
public static int GeneratePriority(SyslogFacility facility, SyslogSeverity severity)
|
{
|
if (facility < SyslogFacility.Kernel || facility > SyslogFacility.Local7)
|
{
|
throw new ArgumentException("SyslogFacility out of range", "facility");
|
}
|
|
if (severity < SyslogSeverity.Emergency || severity > SyslogSeverity.Debug)
|
{
|
throw new ArgumentException("SyslogSeverity out of range", "severity");
|
}
|
|
unchecked
|
{
|
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>
|
/// <remarks>
|
/// <para>
|
/// 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.
|
/// </para>
|
/// </remarks>
|
public class LevelSeverity : LevelMappingEntry
|
{
|
private SyslogSeverity m_severity;
|
|
/// <summary>
|
/// The mapped syslog severity for the specified level
|
/// </summary>
|
/// <remarks>
|
/// <para>
|
/// Required property.
|
/// The mapped syslog severity for the specified level
|
/// </para>
|
/// </remarks>
|
public SyslogSeverity Severity
|
{
|
get { return m_severity; }
|
set { m_severity = value; }
|
}
|
}
|
|
#endregion // LevelSeverity LevelMapping Entry
|
}
|
}
|