#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 System.Collections; using System.IO; #if (!NETCF) using System.Runtime.Serialization; using System.Security.Principal; #endif using log4net.Util; using log4net.Repository; namespace log4net.Core { /// /// Portable data structure used by /// /// /// /// Portable data structure used by /// /// /// Nicko Cadell public struct LoggingEventData { #region Public Instance Fields /// /// The logger name. /// /// /// /// The logger name. /// /// public string LoggerName; /// /// Level of logging event. /// /// /// /// Level of logging event. Level cannot be Serializable /// because it is a flyweight. Due to its special serialization it /// cannot be declared final either. /// /// public Level Level; /// /// The application supplied message. /// /// /// /// The application supplied message of logging event. /// /// public string Message; /// /// The name of thread /// /// /// /// The name of thread in which this logging event was generated /// /// public string ThreadName; /// /// The time the event was logged /// /// /// /// The TimeStamp is stored in the local time zone for this computer. /// /// public DateTime TimeStamp; /// /// Location information for the caller. /// /// /// /// Location information for the caller. /// /// public LocationInfo LocationInfo; /// /// String representation of the user /// /// /// /// String representation of the user's windows name, /// like DOMAIN\username /// /// public string UserName; /// /// String representation of the identity. /// /// /// /// String representation of the current thread's principal identity. /// /// public string Identity; /// /// The string representation of the exception /// /// /// /// The string representation of the exception /// /// public string ExceptionString; /// /// String representation of the AppDomain. /// /// /// /// String representation of the AppDomain. /// /// public string Domain; /// /// Additional event specific properties /// /// /// /// A logger or an appender may attach additional /// properties to specific events. These properties /// have a string key and an object value. /// /// public PropertiesDictionary Properties; #endregion Public Instance Fields } /// /// Flags passed to the property /// /// /// /// Flags passed to the property /// /// /// Nicko Cadell [Flags] public enum FixFlags { /// /// Fix the MDC /// [Obsolete("Replaced by composite Properties")] Mdc = 0x01, /// /// Fix the NDC /// Ndc = 0x02, /// /// Fix the rendered message /// Message = 0x04, /// /// Fix the thread name /// ThreadName = 0x08, /// /// Fix the callers location information /// /// /// CAUTION: Very slow to generate /// LocationInfo = 0x10, /// /// Fix the callers windows user name /// /// /// CAUTION: Slow to generate /// UserName = 0x20, /// /// Fix the domain friendly name /// Domain = 0x40, /// /// Fix the callers principal name /// /// /// CAUTION: May be slow to generate /// Identity = 0x80, /// /// Fix the exception text /// Exception = 0x100, /// /// Fix the event properties. Active properties must implement in order to be eligible for fixing. /// Properties = 0x200, /// /// No fields fixed /// None = 0x0, /// /// All fields fixed /// All = 0xFFFFFFF, /// /// Partial fields fixed /// /// /// /// This set of partial fields gives good performance. The following fields are fixed: /// /// /// /// /// /// /// /// /// Partial = Message | ThreadName | Exception | Domain | Properties, } /// /// The internal representation of logging events. /// /// /// /// When an affirmative decision is made to log then a /// instance is created. This instance /// is passed around to the different log4net components. /// /// /// This class is of concern to those wishing to extend log4net. /// /// /// Some of the values in instances of /// are considered volatile, that is the values are correct at the /// time the event is delivered to appenders, but will not be consistent /// at any time afterwards. If an event is to be stored and then processed /// at a later time these volatile values must be fixed by calling /// . There is a performance penalty /// for incurred by calling but it /// is essential to maintaining data consistency. /// /// /// Nicko Cadell /// Gert Driesen /// Douglas de la Torre /// Daniel Cazzulino #if !NETCF [Serializable] #endif public class LoggingEvent #if !NETCF : ISerializable #endif { private readonly static Type declaringType = typeof(LoggingEvent); #region Public Instance Constructors /// /// Initializes a new instance of the class /// from the supplied parameters. /// /// The declaring type of the method that is /// the stack boundary into the logging system for this call. /// The repository this event is logged in. /// The name of the logger of this event. /// The level of this event. /// The message of this event. /// The exception for this event. /// /// /// Except , and , /// all fields of LoggingEvent are filled when actually needed. Call /// to cache all data locally /// to prevent inconsistencies. /// /// This method is called by the log4net framework /// to create a logging event. /// /// public LoggingEvent(Type callerStackBoundaryDeclaringType, log4net.Repository.ILoggerRepository repository, string loggerName, Level level, object message, Exception exception) { m_callerStackBoundaryDeclaringType = callerStackBoundaryDeclaringType; m_message = message; m_repository = repository; m_thrownException = exception; m_data.LoggerName = loggerName; m_data.Level = level; // Store the event creation time m_data.TimeStamp = DateTime.Now; } /// /// Initializes a new instance of the class /// using specific data. /// /// The declaring type of the method that is /// the stack boundary into the logging system for this call. /// The repository this event is logged in. /// Data used to initialize the logging event. /// The fields in the struct that have already been fixed. /// /// /// This constructor is provided to allow a /// to be created independently of the log4net framework. This can /// be useful if you require a custom serialization scheme. /// /// /// Use the method to obtain an /// instance of the class. /// /// /// The parameter should be used to specify which fields in the /// struct have been preset. Fields not specified in the /// will be captured from the environment if requested or fixed. /// /// public LoggingEvent(Type callerStackBoundaryDeclaringType, log4net.Repository.ILoggerRepository repository, LoggingEventData data, FixFlags fixedData) { m_callerStackBoundaryDeclaringType = callerStackBoundaryDeclaringType; m_repository = repository; m_data = data; m_fixFlags = fixedData; } /// /// Initializes a new instance of the class /// using specific data. /// /// The declaring type of the method that is /// the stack boundary into the logging system for this call. /// The repository this event is logged in. /// Data used to initialize the logging event. /// /// /// This constructor is provided to allow a /// to be created independently of the log4net framework. This can /// be useful if you require a custom serialization scheme. /// /// /// Use the method to obtain an /// instance of the class. /// /// /// This constructor sets this objects flags to , /// this assumes that all the data relating to this event is passed in via the /// parameter and no other data should be captured from the environment. /// /// public LoggingEvent(Type callerStackBoundaryDeclaringType, log4net.Repository.ILoggerRepository repository, LoggingEventData data) : this(callerStackBoundaryDeclaringType, repository, data, FixFlags.All) { } /// /// Initializes a new instance of the class /// using specific data. /// /// Data used to initialize the logging event. /// /// /// This constructor is provided to allow a /// to be created independently of the log4net framework. This can /// be useful if you require a custom serialization scheme. /// /// /// Use the method to obtain an /// instance of the class. /// /// /// This constructor sets this objects flags to , /// this assumes that all the data relating to this event is passed in via the /// parameter and no other data should be captured from the environment. /// /// public LoggingEvent(LoggingEventData data) : this(null, null, data) { } #endregion Public Instance Constructors #region Protected Instance Constructors #if !NETCF /// /// Serialization constructor /// /// The that holds the serialized object data. /// The that contains contextual information about the source or destination. /// /// /// Initializes a new instance of the class /// with serialized data. /// /// protected LoggingEvent(SerializationInfo info, StreamingContext context) { m_data.LoggerName = info.GetString("LoggerName"); // Note we are deserializing the whole level object. That is the // name and the value. This value is correct for the source // hierarchy but may not be for the target hierarchy that this // event may be re-logged into. If it is to be re-logged it may // be necessary to re-lookup the level based only on the name. m_data.Level = (Level)info.GetValue("Level", typeof(Level)); m_data.Message = info.GetString("Message"); m_data.ThreadName = info.GetString("ThreadName"); m_data.TimeStamp = info.GetDateTime("TimeStamp"); m_data.LocationInfo = (LocationInfo) info.GetValue("LocationInfo", typeof(LocationInfo)); m_data.UserName = info.GetString("UserName"); m_data.ExceptionString = info.GetString("ExceptionString"); m_data.Properties = (PropertiesDictionary) info.GetValue("Properties", typeof(PropertiesDictionary)); m_data.Domain = info.GetString("Domain"); m_data.Identity = info.GetString("Identity"); // We have restored all the values of this instance, i.e. all the values are fixed // Set the fix flags otherwise the data values may be overwritten from the current environment. m_fixFlags = FixFlags.All; } #endif #endregion Protected Instance Constructors #region Public Instance Properties /// /// Gets the time when the current process started. /// /// /// This is the time when this process started. /// /// /// /// The TimeStamp is stored in the local time zone for this computer. /// /// /// Tries to get the start time for the current process. /// Failing that it returns the time of the first call to /// this property. /// /// /// Note that AppDomains may be loaded and unloaded within the /// same process without the process terminating and therefore /// without the process start time being reset. /// /// public static DateTime StartTime { get { return SystemInfo.ProcessStartTime; } } /// /// Gets the of the logging event. /// /// /// The of the logging event. /// /// /// /// Gets the of the logging event. /// /// public Level Level { get { return m_data.Level; } } /// /// Gets the time of the logging event. /// /// /// The time of the logging event. /// /// /// /// The TimeStamp is stored in the local time zone for this computer. /// /// public DateTime TimeStamp { get { return m_data.TimeStamp; } } /// /// Gets the name of the logger that logged the event. /// /// /// The name of the logger that logged the event. /// /// /// /// Gets the name of the logger that logged the event. /// /// public string LoggerName { get { return m_data.LoggerName; } } /// /// Gets the location information for this logging event. /// /// /// The location information for this logging event. /// /// /// /// The collected information is cached for future use. /// /// /// See the class for more information on /// supported frameworks and the different behavior in Debug and /// Release builds. /// /// public LocationInfo LocationInformation { get { if (m_data.LocationInfo == null && this.m_cacheUpdatable) { m_data.LocationInfo = new LocationInfo(m_callerStackBoundaryDeclaringType); } return m_data.LocationInfo; } } /// /// Gets the message object used to initialize this event. /// /// /// The message object used to initialize this event. /// /// /// /// Gets the message object used to initialize this event. /// Note that this event may not have a valid message object. /// If the event is serialized the message object will not /// be transferred. To get the text of the message the /// property must be used /// not this property. /// /// /// If there is no defined message object for this event then /// null will be returned. /// /// public object MessageObject { get { return m_message; } } /// /// Gets the exception object used to initialize this event. /// /// /// The exception object used to initialize this event. /// /// /// /// Gets the exception object used to initialize this event. /// Note that this event may not have a valid exception object. /// If the event is serialized the exception object will not /// be transferred. To get the text of the exception the /// method must be used /// not this property. /// /// /// If there is no defined exception object for this event then /// null will be returned. /// /// public Exception ExceptionObject { get { return m_thrownException; } } /// /// The that this event was created in. /// /// /// /// The that this event was created in. /// /// public ILoggerRepository Repository { get { return m_repository; } } /// /// Ensure that the repository is set. /// /// the value for the repository internal void EnsureRepository(ILoggerRepository repository) { if (repository != null) { m_repository = repository; } } /// /// Gets the message, rendered through the . /// /// /// The message rendered through the . /// /// /// /// The collected information is cached for future use. /// /// public string RenderedMessage { get { if (m_data.Message == null && this.m_cacheUpdatable) { if (m_message == null) { m_data.Message = ""; } else if (m_message is string) { m_data.Message = (m_message as string); } else if (m_repository != null) { m_data.Message = m_repository.RendererMap.FindAndRender(m_message); } else { // Very last resort m_data.Message = m_message.ToString(); } } return m_data.Message; } } /// /// Write the rendered message to a TextWriter /// /// the writer to write the message to /// /// /// Unlike the property this method /// does store the message data in the internal cache. Therefore /// if called only once this method should be faster than the /// property, however if the message is /// to be accessed multiple times then the property will be more efficient. /// /// public void WriteRenderedMessage(TextWriter writer) { if (m_data.Message != null) { writer.Write(m_data.Message); } else { if (m_message != null) { if (m_message is string) { writer.Write(m_message as string); } else if (m_repository != null) { m_repository.RendererMap.FindAndRender(m_message, writer); } else { // Very last resort writer.Write(m_message.ToString()); } } } } /// /// Gets the name of the current thread. /// /// /// The name of the current thread, or the thread ID when /// the name is not available. /// /// /// /// The collected information is cached for future use. /// /// public string ThreadName { get { if (m_data.ThreadName == null && this.m_cacheUpdatable) { #if NETCF // Get thread ID only m_data.ThreadName = SystemInfo.CurrentThreadId.ToString(System.Globalization.NumberFormatInfo.InvariantInfo); #else m_data.ThreadName = System.Threading.Thread.CurrentThread.Name; if (m_data.ThreadName == null || m_data.ThreadName.Length == 0) { // The thread name is not available. Therefore we // go the the AppDomain to get the ID of the // current thread. (Why don't Threads know their own ID?) try { m_data.ThreadName = SystemInfo.CurrentThreadId.ToString(System.Globalization.NumberFormatInfo.InvariantInfo); } catch(System.Security.SecurityException) { // This security exception will occur if the caller does not have // some undefined set of SecurityPermission flags. LogLog.Debug(declaringType, "Security exception while trying to get current thread ID. Error Ignored. Empty thread name."); // As a last resort use the hash code of the Thread object m_data.ThreadName = System.Threading.Thread.CurrentThread.GetHashCode().ToString(System.Globalization.CultureInfo.InvariantCulture); } } #endif } return m_data.ThreadName; } } /// /// Gets the name of the current user. /// /// /// The name of the current user, or NOT AVAILABLE when the /// underlying runtime has no support for retrieving the name of the /// current user. /// /// /// /// Calls WindowsIdentity.GetCurrent().Name to get the name of /// the current windows user. /// /// /// To improve performance, we could cache the string representation of /// the name, and reuse that as long as the identity stayed constant. /// Once the identity changed, we would need to re-assign and re-render /// the string. /// /// /// However, the WindowsIdentity.GetCurrent() call seems to /// return different objects every time, so the current implementation /// doesn't do this type of caching. /// /// /// Timing for these operations: /// /// /// /// Method /// Results /// /// /// WindowsIdentity.GetCurrent() /// 10000 loops, 00:00:00.2031250 seconds /// /// /// WindowsIdentity.GetCurrent().Name /// 10000 loops, 00:00:08.0468750 seconds /// /// /// /// This means we could speed things up almost 40 times by caching the /// value of the WindowsIdentity.GetCurrent().Name property, since /// this takes (8.04-0.20) = 7.84375 seconds. /// /// public string UserName { get { if (m_data.UserName == null && this.m_cacheUpdatable) { #if (NETCF || SSCLI) // On compact framework there's no notion of current Windows user m_data.UserName = SystemInfo.NotAvailableText; #else try { WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent(); if (windowsIdentity != null && windowsIdentity.Name != null) { m_data.UserName = windowsIdentity.Name; } else { m_data.UserName = ""; } } catch(System.Security.SecurityException) { // This security exception will occur if the caller does not have // some undefined set of SecurityPermission flags. LogLog.Debug(declaringType, "Security exception while trying to get current windows identity. Error Ignored. Empty user name."); m_data.UserName = ""; } #endif } return m_data.UserName; } } /// /// Gets the identity of the current thread principal. /// /// /// The string name of the identity of the current thread principal. /// /// /// /// Calls System.Threading.Thread.CurrentPrincipal.Identity.Name to get /// the name of the current thread principal. /// /// public string Identity { get { if (m_data.Identity == null && this.m_cacheUpdatable) { #if (NETCF || SSCLI) // On compact framework there's no notion of current thread principals m_data.Identity = SystemInfo.NotAvailableText; #else try { if (System.Threading.Thread.CurrentPrincipal != null && System.Threading.Thread.CurrentPrincipal.Identity != null && System.Threading.Thread.CurrentPrincipal.Identity.Name != null) { m_data.Identity = System.Threading.Thread.CurrentPrincipal.Identity.Name; } else { m_data.Identity = ""; } } catch (ObjectDisposedException) { // This exception will occur if System.Threading.Thread.CurrentPrincipal.Identity is not null but // the getter of the property Name tries to access disposed objects. // Seen to happen on IIS 7 or greater with windows authentication. LogLog.Debug(declaringType, "Object disposed exception while trying to get current thread principal. Error Ignored. Empty identity name."); m_data.Identity = ""; } catch (System.Security.SecurityException) { // This security exception will occur if the caller does not have // some undefined set of SecurityPermission flags. LogLog.Debug(declaringType, "Security exception while trying to get current thread principal. Error Ignored. Empty identity name."); m_data.Identity = ""; } #endif } return m_data.Identity; } } /// /// Gets the AppDomain friendly name. /// /// /// The AppDomain friendly name. /// /// /// /// Gets the AppDomain friendly name. /// /// public string Domain { get { if (m_data.Domain == null && this.m_cacheUpdatable) { m_data.Domain = SystemInfo.ApplicationFriendlyName; } return m_data.Domain; } } /// /// Additional event specific properties. /// /// /// Additional event specific properties. /// /// /// /// A logger or an appender may attach additional /// properties to specific events. These properties /// have a string key and an object value. /// /// /// This property is for events that have been added directly to /// this event. The aggregate properties (which include these /// event properties) can be retrieved using /// and . /// /// /// Once the properties have been fixed this property /// returns the combined cached properties. This ensures that updates to /// this property are always reflected in the underlying storage. When /// returning the combined properties there may be more keys in the /// Dictionary than expected. /// /// public PropertiesDictionary Properties { get { // If we have cached properties then return that otherwise changes will be lost if (m_data.Properties != null) { return m_data.Properties; } if (m_eventProperties == null) { m_eventProperties = new PropertiesDictionary(); } return m_eventProperties; } } /// /// The fixed fields in this event /// /// /// The set of fields that are fixed in this event /// /// /// /// Fields will not be fixed if they have previously been fixed. /// It is not possible to 'unfix' a field. /// /// public FixFlags Fix { get { return m_fixFlags; } set { this.FixVolatileData(value); } } #endregion Public Instance Properties #region Implementation of ISerializable #if !NETCF /// /// Serializes this object into the provided. /// /// The to populate with data. /// The destination for this serialization. /// /// /// The data in this event must be fixed before it can be serialized. /// /// /// The method must be called during the /// method call if this event /// is to be used outside that method. /// /// #if NET_4_0 [System.Security.SecurityCritical] #else [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.Demand, SerializationFormatter=true)] #endif public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { // The caller must call FixVolatileData before this object // can be serialized. info.AddValue("LoggerName", m_data.LoggerName); info.AddValue("Level", m_data.Level); info.AddValue("Message", m_data.Message); info.AddValue("ThreadName", m_data.ThreadName); info.AddValue("TimeStamp", m_data.TimeStamp); info.AddValue("LocationInfo", m_data.LocationInfo); info.AddValue("UserName", m_data.UserName); info.AddValue("ExceptionString", m_data.ExceptionString); info.AddValue("Properties", m_data.Properties); info.AddValue("Domain", m_data.Domain); info.AddValue("Identity", m_data.Identity); } #endif #endregion Implementation of ISerializable #region Public Instance Methods /// /// Gets the portable data for this . /// /// The for this event. /// /// /// A new can be constructed using a /// instance. /// /// /// Does a fix of the data /// in the logging event before returning the event data. /// /// public LoggingEventData GetLoggingEventData() { return GetLoggingEventData(FixFlags.Partial); } /// /// Gets the portable data for this . /// /// The set of data to ensure is fixed in the LoggingEventData /// The for this event. /// /// /// A new can be constructed using a /// instance. /// /// public LoggingEventData GetLoggingEventData(FixFlags fixFlags) { Fix = fixFlags; return m_data; } /// /// Returns this event's exception's rendered using the /// . /// /// /// This event's exception's rendered using the . /// /// /// /// Obsolete. Use instead. /// /// [Obsolete("Use GetExceptionString instead")] public string GetExceptionStrRep() { return GetExceptionString(); } /// /// Returns this event's exception's rendered using the /// . /// /// /// This event's exception's rendered using the . /// /// /// /// Returns this event's exception's rendered using the /// . /// /// public string GetExceptionString() { if (m_data.ExceptionString == null && this.m_cacheUpdatable) { if (m_thrownException != null) { if (m_repository != null) { // Render exception using the repositories renderer map m_data.ExceptionString = m_repository.RendererMap.FindAndRender(m_thrownException); } else { // Very last resort m_data.ExceptionString = m_thrownException.ToString(); } } else { m_data.ExceptionString = ""; } } return m_data.ExceptionString; } /// /// Fix instance fields that hold volatile data. /// /// /// /// Some of the values in instances of /// are considered volatile, that is the values are correct at the /// time the event is delivered to appenders, but will not be consistent /// at any time afterwards. If an event is to be stored and then processed /// at a later time these volatile values must be fixed by calling /// . There is a performance penalty /// incurred by calling but it /// is essential to maintaining data consistency. /// /// /// Calling is equivalent to /// calling passing the parameter /// false. /// /// /// See for more /// information. /// /// [Obsolete("Use Fix property")] public void FixVolatileData() { Fix = FixFlags.All; } /// /// Fixes instance fields that hold volatile data. /// /// Set to true to not fix data that takes a long time to fix. /// /// /// Some of the values in instances of /// are considered volatile, that is the values are correct at the /// time the event is delivered to appenders, but will not be consistent /// at any time afterwards. If an event is to be stored and then processed /// at a later time these volatile values must be fixed by calling /// . There is a performance penalty /// for incurred by calling but it /// is essential to maintaining data consistency. /// /// /// The param controls the data that /// is fixed. Some of the data that can be fixed takes a long time to /// generate, therefore if you do not require those settings to be fixed /// they can be ignored by setting the param /// to true. This setting will ignore the /// and settings. /// /// /// Set to false to ensure that all /// settings are fixed. /// /// [Obsolete("Use Fix property")] public void FixVolatileData(bool fastButLoose) { if (fastButLoose) { Fix = FixFlags.Partial; } else { Fix = FixFlags.All; } } /// /// Fix the fields specified by the parameter /// /// the fields to fix /// /// /// Only fields specified in the will be fixed. /// Fields will not be fixed if they have previously been fixed. /// It is not possible to 'unfix' a field. /// /// protected void FixVolatileData(FixFlags flags) { object forceCreation = null; //Unlock the cache so that new values can be stored //This may not be ideal if we are no longer in the correct context //and someone calls fix. m_cacheUpdatable=true; // determine the flags that we are actually fixing FixFlags updateFlags = (FixFlags)((flags ^ m_fixFlags) & flags); if (updateFlags > 0) { if ((updateFlags & FixFlags.Message) != 0) { // Force the message to be rendered forceCreation = this.RenderedMessage; m_fixFlags |= FixFlags.Message; } if ((updateFlags & FixFlags.ThreadName) != 0) { // Grab the thread name forceCreation = this.ThreadName; m_fixFlags |= FixFlags.ThreadName; } if ((updateFlags & FixFlags.LocationInfo) != 0) { // Force the location information to be loaded forceCreation = this.LocationInformation; m_fixFlags |= FixFlags.LocationInfo; } if ((updateFlags & FixFlags.UserName) != 0) { // Grab the user name forceCreation = this.UserName; m_fixFlags |= FixFlags.UserName; } if ((updateFlags & FixFlags.Domain) != 0) { // Grab the domain name forceCreation = this.Domain; m_fixFlags |= FixFlags.Domain; } if ((updateFlags & FixFlags.Identity) != 0) { // Grab the identity forceCreation = this.Identity; m_fixFlags |= FixFlags.Identity; } if ((updateFlags & FixFlags.Exception) != 0) { // Force the exception text to be loaded forceCreation = GetExceptionString(); m_fixFlags |= FixFlags.Exception; } if ((updateFlags & FixFlags.Properties) != 0) { CacheProperties(); m_fixFlags |= FixFlags.Properties; } } // avoid warning CS0219 if (forceCreation != null) { } //Finaly lock everything we've cached. m_cacheUpdatable=false; } #endregion Public Instance Methods #region Protected Instance Methods private void CreateCompositeProperties() { m_compositeProperties = new CompositeProperties(); if (m_eventProperties != null) { m_compositeProperties.Add(m_eventProperties); } #if !NETCF PropertiesDictionary logicalThreadProperties = LogicalThreadContext.Properties.GetProperties(false); if (logicalThreadProperties != null) { m_compositeProperties.Add(logicalThreadProperties); } #endif PropertiesDictionary threadProperties = ThreadContext.Properties.GetProperties(false); if (threadProperties != null) { m_compositeProperties.Add(threadProperties); } // TODO: Add Repository Properties // event properties PropertiesDictionary eventProperties = new PropertiesDictionary(); eventProperties[UserNameProperty] = UserName; eventProperties[IdentityProperty] = Identity; m_compositeProperties.Add(eventProperties); m_compositeProperties.Add(GlobalContext.Properties.GetReadOnlyProperties()); } private void CacheProperties() { if (m_data.Properties == null && this.m_cacheUpdatable) { if (m_compositeProperties == null) { CreateCompositeProperties(); } PropertiesDictionary flattenedProperties = m_compositeProperties.Flatten(); PropertiesDictionary fixedProperties = new PropertiesDictionary(); // Validate properties foreach(DictionaryEntry entry in flattenedProperties) { string key = entry.Key as string; if (key != null) { object val = entry.Value; // Fix any IFixingRequired objects IFixingRequired fixingRequired = val as IFixingRequired; if (fixingRequired != null) { val = fixingRequired.GetFixedObject(); } // Strip keys with null values if (val != null) { fixedProperties[key] = val; } } } m_data.Properties = fixedProperties; } } /// /// Lookup a composite property in this event /// /// the key for the property to lookup /// the value for the property /// /// /// This event has composite properties that combine together properties from /// several different contexts in the following order: /// /// /// this events properties /// /// This event has that can be set. These /// properties are specific to this event only. /// /// /// /// the thread properties /// /// The that are set on the current /// thread. These properties are shared by all events logged on this thread. /// /// /// /// the global properties /// /// The that are set globally. These /// properties are shared by all the threads in the AppDomain. /// /// /// /// /// public object LookupProperty(string key) { if (m_data.Properties != null) { return m_data.Properties[key]; } if (m_compositeProperties == null) { CreateCompositeProperties(); } return m_compositeProperties[key]; } /// /// Get all the composite properties in this event /// /// the containing all the properties /// /// /// See for details of the composite properties /// stored by the event. /// /// /// This method returns a single containing all the /// properties defined for this event. /// /// public PropertiesDictionary GetProperties() { if (m_data.Properties != null) { return m_data.Properties; } if (m_compositeProperties == null) { CreateCompositeProperties(); } return m_compositeProperties.Flatten(); } #endregion Public Instance Methods #region Private Instance Fields /// /// The internal logging event data. /// private LoggingEventData m_data; /// /// The internal logging event data. /// private CompositeProperties m_compositeProperties; /// /// The internal logging event data. /// private PropertiesDictionary m_eventProperties; /// /// The fully qualified Type of the calling /// logger class in the stack frame (i.e. the declaring type of the method). /// private readonly Type m_callerStackBoundaryDeclaringType; /// /// The application supplied message of logging event. /// private readonly object m_message; /// /// The exception that was thrown. /// /// /// This is not serialized. The string representation /// is serialized instead. /// private readonly Exception m_thrownException; /// /// The repository that generated the logging event /// /// /// This is not serialized. /// private ILoggerRepository m_repository = null; /// /// The fix state for this event /// /// /// These flags indicate which fields have been fixed. /// Not serialized. /// private FixFlags m_fixFlags = FixFlags.None; /// /// Indicated that the internal cache is updateable (ie not fixed) /// /// /// This is a seperate flag to m_fixFlags as it allows incrementel fixing and simpler /// changes in the caching strategy. /// private bool m_cacheUpdatable = true; #endregion Private Instance Fields #region Constants /// /// The key into the Properties map for the host name value. /// public const string HostNameProperty = "log4net:HostName"; /// /// The key into the Properties map for the thread identity value. /// public const string IdentityProperty = "log4net:Identity"; /// /// The key into the Properties map for the user name value. /// public const string UserNameProperty = "log4net:UserName"; #endregion } }