#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
using System;
using System.Runtime.Serialization;
using System.Collections;
using System.IO;
#if (!NETCF)
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
///
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
{
#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("LoggingEvent: 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("LoggingEvent: 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(System.Security.SecurityException)
{
// This security exception will occur if the caller does not have
// some undefined set of SecurityPermission flags.
LogLog.Debug("LoggingEvent: 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.
///
///
[System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.Demand, SerializationFormatter=true)]
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
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
}
}