#region Copyright & License // // Copyright 2001-2005 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 // MONO 1.0 Beta mcs does not like #if !A && !B && !C syntax // .NET Compact Framework 1.0 has no support for EventLog #if !NETCF // SSCLI 1.0 has no support for EventLog #if !SSCLI using System; using System.Diagnostics; using System.Globalization; using log4net.Util; using log4net.Layout; using log4net.Core; namespace log4net.Appender { /// /// Writes events to the system event log. /// /// /// /// The EventID of the event log entry can be /// set using the EventLogEventID property () /// on the . /// /// /// There is a limit of 32K characters for an event log message /// /// /// When configuring the EventLogAppender a mapping can be /// specified to map a logging level to an event log entry type. For example: /// /// /// <mapping> /// <level value="ERROR" /> /// <eventLogEntryType value="Error" /> /// </mapping> /// <mapping> /// <level value="DEBUG" /> /// <eventLogEntryType value="Information" /> /// </mapping> /// /// /// The Level is the standard log4net logging level and eventLogEntryType can be any value /// from the enum, i.e.: /// /// Erroran error event /// Warninga warning event /// Informationan informational event /// /// /// /// Aspi Havewala /// Douglas de la Torre /// Nicko Cadell /// Gert Driesen /// Thomas Voss public class EventLogAppender : AppenderSkeleton { #region Public Instance Constructors /// /// Initializes a new instance of the class. /// /// /// /// Default constructor. /// /// public EventLogAppender() { m_applicationName = System.Threading.Thread.GetDomain().FriendlyName; m_logName = "Application"; // Defaults to application log m_machineName = "."; // Only log on the local machine } /// /// Initializes a new instance of the class /// with the specified . /// /// The to use with this appender. /// /// /// Obsolete constructor. /// /// [Obsolete("Instead use the default constructor and set the Layout property")] public EventLogAppender(ILayout layout) : this() { Layout = layout; } #endregion // Public Instance Constructors #region Public Instance Properties /// /// The name of the log where messages will be stored. /// /// /// The string name of the log where messages will be stored. /// /// /// This is the name of the log as it appears in the Event Viewer /// tree. The default value is to log into the Application /// log, this is where most applications write their events. However /// if you need a separate log for your application (or applications) /// then you should set the appropriately. /// This should not be used to distinguish your event log messages /// from those of other applications, the /// property should be used to distinguish events. This property should be /// used to group together events into a single log. /// /// public string LogName { get { return m_logName; } set { m_logName = value; } } /// /// Property used to set the Application name. This appears in the /// event logs when logging. /// /// /// The string used to distinguish events from different sources. /// /// /// Sets the event log source property. /// public string ApplicationName { get { return m_applicationName; } set { m_applicationName = value; } } /// /// This property is used to return the name of the computer to use /// when accessing the event logs. Currently, this is the current /// computer, denoted by a dot "." /// /// /// The string name of the machine holding the event log that /// will be logged into. /// /// /// This property cannot be changed. It is currently set to '.' /// i.e. the local machine. This may be changed in future. /// public string MachineName { get { return m_machineName; } set { /* Currently we do not allow the machine name to be changed */; } } /// /// Add a mapping of level to - done by the config file /// /// The mapping to add /// /// /// Add a mapping to this appender. /// Each mapping defines the event log entry type for a level. /// /// public void AddMapping(Level2EventLogEntryType mapping) { m_levelMapping.Add(mapping); } /// /// Gets or sets the used to write to the EventLog. /// /// /// The used to write to the EventLog. /// /// /// /// The system security context used to write to the EventLog. /// /// /// Unless a specified here for this appender /// the is queried for the /// security context to use. The default behavior is to use the security context /// of the current thread. /// /// public SecurityContext SecurityContext { get { return m_securityContext; } set { m_securityContext = value; } } #endregion // Public Instance Properties #region Implementation of IOptionHandler /// /// 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. /// /// override public void ActivateOptions() { base.ActivateOptions(); if (m_securityContext == null) { m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this); } bool sourceAlreadyExists = false; string currentLogName = null; using(SecurityContext.Impersonate(this)) { sourceAlreadyExists = EventLog.SourceExists(m_applicationName); if (sourceAlreadyExists) { currentLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName); } } if (sourceAlreadyExists && currentLogName != m_logName) { LogLog.Debug("EventLogAppender: Changing event source [" + m_applicationName + "] from log [" + currentLogName + "] to log [" + m_logName + "]"); } else if (!sourceAlreadyExists) { LogLog.Debug("EventLogAppender: Creating event source Source [" + m_applicationName + "] in log " + m_logName + "]"); } string registeredLogName = null; using(SecurityContext.Impersonate(this)) { if (sourceAlreadyExists && currentLogName != m_logName) { // // Re-register this to the current application if the user has changed // the application / logfile association // EventLog.DeleteEventSource(m_applicationName, m_machineName); CreateEventSource(m_applicationName, m_logName, m_machineName); registeredLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName); } else if (!sourceAlreadyExists) { CreateEventSource(m_applicationName, m_logName, m_machineName); registeredLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName); } } m_levelMapping.ActivateOptions(); LogLog.Debug("EventLogAppender: Source [" + m_applicationName + "] is registered to log [" + registeredLogName + "]"); } #endregion // Implementation of IOptionHandler /// /// Create an event log source /// /// /// Uses different API calls under NET_2_0 /// private static void CreateEventSource(string source, string logName, string machineName) { #if NET_2_0 EventSourceCreationData eventSourceCreationData = new EventSourceCreationData(source, logName); eventSourceCreationData.MachineName = machineName; EventLog.CreateEventSource(eventSourceCreationData); #else EventLog.CreateEventSource(source, logName, machineName); #endif } #region Override implementation of AppenderSkeleton /// /// This method is called by the /// method. /// /// the event to log /// /// Writes the event to the system event log using the /// . /// /// If the event has an EventID property (see ) /// set then this integer will be used as the event log event id. /// /// /// There is a limit of 32K characters for an event log message /// /// override protected void Append(LoggingEvent loggingEvent) { // // Write the resulting string to the event log system // int eventID = 0; // Look for the EventLogEventID property object eventIDPropertyObj = loggingEvent.LookupProperty("EventID"); if (eventIDPropertyObj != null) { if (eventIDPropertyObj is int) { eventID = (int)eventIDPropertyObj; } else { string eventIDPropertyString = eventIDPropertyObj as string; if (eventIDPropertyString != null && eventIDPropertyString.Length > 0) { // Read the string property into a number int intVal; if (SystemInfo.TryParse(eventIDPropertyString, out intVal)) { eventID = intVal; } else { ErrorHandler.Error("Unable to parse event ID property [" + eventIDPropertyString + "]."); } } } } // Write to the event log try { string eventTxt = RenderLoggingEvent(loggingEvent); // There is a limit of 32K characters for an event log message if (eventTxt.Length > 32000) { eventTxt = eventTxt.Substring(0, 32000); } EventLogEntryType entryType = GetEntryType(loggingEvent.Level); using(SecurityContext.Impersonate(this)) { EventLog.WriteEntry(m_applicationName, eventTxt, entryType, eventID); } } catch(Exception ex) { ErrorHandler.Error("Unable to write to event log [" + m_logName + "] using source [" + m_applicationName + "]", ex); } } /// /// This appender requires a to be set. /// /// true /// /// /// This appender requires a to be set. /// /// override protected bool RequiresLayout { get { return true; } } #endregion // Override implementation of AppenderSkeleton #region Protected Instance Methods /// /// Get the equivalent for a /// /// the Level to convert to an EventLogEntryType /// The equivalent for a /// /// Because there are fewer applicable /// values to use in logging levels than there are in the /// this is a one way mapping. There is /// a loss of information during the conversion. /// virtual protected EventLogEntryType GetEntryType(Level level) { // see if there is a specified lookup. Level2EventLogEntryType entryType = m_levelMapping.Lookup(level) as Level2EventLogEntryType; if (entryType != null) { return entryType.EventLogEntryType; } // Use default behavior if (level >= Level.Error) { return EventLogEntryType.Error; } else if (level == Level.Warn) { return EventLogEntryType.Warning; } // Default setting return EventLogEntryType.Information; } #endregion // Protected Instance Methods #region Private Instance Fields /// /// The log name is the section in the event logs where the messages /// are stored. /// private string m_logName; /// /// Name of the application to use when logging. This appears in the /// application column of the event log named by . /// private string m_applicationName; /// /// The name of the machine which holds the event log. This is /// currently only allowed to be '.' i.e. the current machine. /// private string m_machineName; /// /// Mapping from level object to EventLogEntryType /// private LevelMapping m_levelMapping = new LevelMapping(); /// /// The security context to use for privileged calls /// private SecurityContext m_securityContext; #endregion // Private Instance Fields #region Level2EventLogEntryType LevelMapping Entry /// /// A class to act as a mapping between the level that a logging call is made at and /// the color it should be displayed as. /// /// /// /// Defines the mapping between a level and its event log entry type. /// /// public class Level2EventLogEntryType : LevelMappingEntry { private EventLogEntryType m_entryType; /// /// The for this entry /// /// /// /// Required property. /// The for this entry /// /// public EventLogEntryType EventLogEntryType { get { return m_entryType; } set { m_entryType = value; } } } #endregion // LevelColors LevelMapping Entry } } #endif // !SSCLI #endif // !NETCF