123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- #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;
- using System.Text;
- 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="M: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
- {
- // Priority
- int priority = GeneratePriority(m_facility, GetSeverity(loggingEvent.Level));
- // Identity
- string identity;
- if (m_identity != null)
- {
- identity = m_identity.Format(loggingEvent);
- }
- else
- {
- identity = loggingEvent.Domain;
- }
- // Message. The message goes after the tag/identity
- string message = RenderLoggingEvent(loggingEvent);
- Byte[] buffer;
- int i = 0;
- char c;
- StringBuilder builder = new StringBuilder();
- while (i < message.Length)
- {
- // Clear StringBuilder
- builder.Length = 0;
- // Write priority
- builder.Append('<');
- builder.Append(priority);
- builder.Append('>');
- // Write identity
- builder.Append(identity);
- builder.Append(": ");
- for (; i < message.Length; i++)
- {
- c = message[i];
- // Accept only visible ASCII characters and space. See RFC 3164 section 4.1.3
- if (((int)c >= 32) && ((int)c <= 126))
- {
- builder.Append(c);
- }
- // If character is newline, break and send the current line
- else if ((c == '\r') || (c == '\n'))
- {
- // Check the next character to handle \r\n or \n\r
- if ((message.Length > i + 1) && ((message[i + 1] == '\r') || (message[i + 1] == '\n')))
- {
- i++;
- }
- i++;
- break;
- }
- }
- // Grab as a byte array
- buffer = this.Encoding.GetBytes(builder.ToString());
- #if NETSTANDARD1_3
- Client.SendAsync(buffer, buffer.Length, RemoteEndPoint).Wait();
- #else
- this.Client.Send(buffer, buffer.Length, this.RemoteEndPoint);
- #endif
- }
- }
- 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();
- /// <summary>
- /// Initial buffer size
- /// </summary>
- private const int c_renderBufferSize = 256;
- /// <summary>
- /// Maximum buffer size before it is recycled
- /// </summary>
- private const int c_renderBufferMaxCapacity = 1024;
- #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
- }
- }
|