| #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 |
| } |
| } |