#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;
using log4net.Util.PatternStringConverters;
using log4net.Core;
namespace log4net.Util
{
///
/// This class implements a patterned string.
///
///
///
/// This string has embedded patterns that are resolved and expanded
/// when the string is formatted.
///
///
/// This class functions similarly to the
/// in that it accepts a pattern and renders it to a string. Unlike the
/// however the PatternString
/// does not render the properties of a specific but
/// of the process in general.
///
///
/// The recognized conversion pattern names are:
///
///
///
/// Conversion Pattern Name
/// Effect
///
/// -
/// appdomain
///
///
/// Used to output the friendly name of the current AppDomain.
///
///
///
/// -
/// appsetting
///
///
/// Used to output the value of a specific appSetting key in the application
/// configuration file.
///
///
///
/// -
/// date
///
///
/// Used to output the current date and time in the local time zone.
/// To output the date in universal time use the %utcdate pattern.
/// The date conversion
/// specifier may be followed by a date format specifier enclosed
/// between braces. For example, %date{HH:mm:ss,fff} or
/// %date{dd MMM yyyy HH:mm:ss,fff}. If no date format specifier is
/// given then ISO8601 format is
/// assumed ().
///
///
/// The date format specifier admits the same syntax as the
/// time pattern string of the .
///
///
/// For better results it is recommended to use the log4net date
/// formatters. These can be specified using one of the strings
/// "ABSOLUTE", "DATE" and "ISO8601" for specifying
/// ,
/// and respectively
/// . For example,
/// %date{ISO8601} or %date{ABSOLUTE}.
///
///
/// These dedicated date formatters perform significantly
/// better than .
///
///
///
/// -
/// env
///
///
/// Used to output the a specific environment variable. The key to
/// lookup must be specified within braces and directly following the
/// pattern specifier, e.g. %env{COMPUTERNAME} would include the value
/// of the COMPUTERNAME environment variable.
///
///
/// The env pattern is not supported on the .NET Compact Framework.
///
///
///
/// -
/// identity
///
///
/// Used to output the user name for the currently active user
/// (Principal.Identity.Name).
///
///
///
/// -
/// newline
///
///
/// Outputs the platform dependent line separator character or
/// characters.
///
///
/// This conversion pattern name offers the same performance as using
/// non-portable line separator strings such as "\n", or "\r\n".
/// Thus, it is the preferred way of specifying a line separator.
///
///
///
/// -
/// processid
///
///
/// Used to output the system process ID for the current process.
///
///
///
/// -
/// property
///
///
/// Used to output a specific context property. The key to
/// lookup must be specified within braces and directly following the
/// pattern specifier, e.g. %property{user} would include the value
/// from the property that is keyed by the string 'user'. Each property value
/// that is to be included in the log must be specified separately.
/// Properties are stored in logging contexts. By default
/// the log4net:HostName property is set to the name of machine on
/// which the event was originally logged.
///
///
/// If no key is specified, e.g. %property then all the keys and their
/// values are printed in a comma separated list.
///
///
/// The properties of an event are combined from a number of different
/// contexts. These are listed below in the order in which they are searched.
///
///
/// -
/// 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.
///
///
///
///
///
/// -
/// random
///
///
/// Used to output a random string of characters. The string is made up of
/// uppercase letters and numbers. By default the string is 4 characters long.
/// The length of the string can be specified within braces directly following the
/// pattern specifier, e.g. %random{8} would output an 8 character string.
///
///
///
/// -
/// username
///
///
/// Used to output the WindowsIdentity for the currently
/// active user.
///
///
///
/// -
/// utcdate
///
///
/// Used to output the date of the logging event in universal time.
/// The date conversion
/// specifier may be followed by a date format specifier enclosed
/// between braces. For example, %utcdate{HH:mm:ss,fff} or
/// %utcdate{dd MMM yyyy HH:mm:ss,fff}. If no date format specifier is
/// given then ISO8601 format is
/// assumed ().
///
///
/// The date format specifier admits the same syntax as the
/// time pattern string of the .
///
///
/// For better results it is recommended to use the log4net date
/// formatters. These can be specified using one of the strings
/// "ABSOLUTE", "DATE" and "ISO8601" for specifying
/// ,
/// and respectively
/// . For example,
/// %utcdate{ISO8601} or %utcdate{ABSOLUTE}.
///
///
/// These dedicated date formatters perform significantly
/// better than .
///
///
///
/// -
/// %
///
///
/// The sequence %% outputs a single percent sign.
///
///
///
///
///
/// Additional pattern converters may be registered with a specific
/// instance using or
/// .
///
///
/// See the for details on the
/// format modifiers supported by the patterns.
///
///
/// Nicko Cadell
public class PatternString : IOptionHandler
{
#region Static Fields
///
/// Internal map of converter identifiers to converter types.
///
private static Hashtable s_globalRulesRegistry;
#endregion Static Fields
#region Member Variables
///
/// the pattern
///
private string m_pattern;
///
/// the head of the pattern converter chain
///
private PatternConverter m_head;
///
/// patterns defined on this PatternString only
///
private Hashtable m_instanceRulesRegistry = new Hashtable();
#endregion
#region Static Constructor
///
/// Initialize the global registry
///
static PatternString()
{
s_globalRulesRegistry = new Hashtable(18);
s_globalRulesRegistry.Add("appdomain", typeof(AppDomainPatternConverter));
s_globalRulesRegistry.Add("date", typeof(DatePatternConverter));
#if !NETCF
s_globalRulesRegistry.Add("env", typeof(EnvironmentPatternConverter));
#if !NETSTANDARD1_3 // EnvironmentFolderPathPatternConverter not yet supported
s_globalRulesRegistry.Add("envFolderPath", typeof(EnvironmentFolderPathPatternConverter));
#endif
#endif
s_globalRulesRegistry.Add("identity", typeof(IdentityPatternConverter));
s_globalRulesRegistry.Add("literal", typeof(LiteralPatternConverter));
s_globalRulesRegistry.Add("newline", typeof(NewLinePatternConverter));
s_globalRulesRegistry.Add("processid", typeof(ProcessIdPatternConverter));
s_globalRulesRegistry.Add("property", typeof(PropertyPatternConverter));
s_globalRulesRegistry.Add("random", typeof(RandomStringPatternConverter));
s_globalRulesRegistry.Add("username", typeof(UserNamePatternConverter));
s_globalRulesRegistry.Add("utcdate", typeof(UtcDatePatternConverter));
s_globalRulesRegistry.Add("utcDate", typeof(UtcDatePatternConverter));
s_globalRulesRegistry.Add("UtcDate", typeof(UtcDatePatternConverter));
#if !NETCF && !NETSTANDARD1_3
// TODO - have added common variants of casing like utcdate above.
// Wouldn't it be better to use a case-insensitive Hashtable?
s_globalRulesRegistry.Add("appsetting", typeof(AppSettingPatternConverter));
s_globalRulesRegistry.Add("appSetting", typeof(AppSettingPatternConverter));
s_globalRulesRegistry.Add("AppSetting", typeof(AppSettingPatternConverter));
#endif
}
#endregion Static Constructor
#region Constructors
///
/// Default constructor
///
///
///
/// Initialize a new instance of
///
///
public PatternString()
{
}
///
/// Constructs a PatternString
///
/// The pattern to use with this PatternString
///
///
/// Initialize a new instance of with the pattern specified.
///
///
public PatternString(string pattern)
{
m_pattern = pattern;
ActivateOptions();
}
#endregion
///
/// Gets or sets the pattern formatting string
///
///
/// The pattern formatting string
///
///
///
/// The ConversionPattern option. This is the string which
/// controls formatting and consists of a mix of literal content and
/// conversion specifiers.
///
///
public string ConversionPattern
{
get { return m_pattern; }
set { m_pattern = value; }
}
#region Implementation of IOptionHandler
///
/// Initialize object options
///
///
///
/// 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.
///
///
public virtual void ActivateOptions()
{
m_head = CreatePatternParser(m_pattern).Parse();
}
#endregion
///
/// Create the used to parse the pattern
///
/// the pattern to parse
/// The
///
///
/// Returns PatternParser used to parse the conversion string. Subclasses
/// may override this to return a subclass of PatternParser which recognize
/// custom conversion pattern name.
///
///
private PatternParser CreatePatternParser(string pattern)
{
PatternParser patternParser = new PatternParser(pattern);
// Add all the builtin patterns
foreach(DictionaryEntry entry in s_globalRulesRegistry)
{
ConverterInfo converterInfo = new ConverterInfo();
converterInfo.Name = (string)entry.Key;
converterInfo.Type = (Type)entry.Value;
patternParser.PatternConverters.Add(entry.Key, converterInfo);
}
// Add the instance patterns
foreach(DictionaryEntry entry in m_instanceRulesRegistry)
{
patternParser.PatternConverters[entry.Key] = entry.Value;
}
return patternParser;
}
///
/// Produces a formatted string as specified by the conversion pattern.
///
/// The TextWriter to write the formatted event to
///
///
/// Format the pattern to the .
///
///
public void Format(TextWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
PatternConverter c = m_head;
// loop through the chain of pattern converters
while(c != null)
{
c.Format(writer, null);
c = c.Next;
}
}
///
/// Format the pattern as a string
///
/// the pattern formatted as a string
///
///
/// Format the pattern to a string.
///
///
public string Format()
{
using StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
Format(writer);
return writer.ToString();
}
///
/// Add a converter to this PatternString
///
/// the converter info
///
///
/// This version of the method is used by the configurator.
/// Programmatic users should use the alternative method.
///
///
public void AddConverter(ConverterInfo converterInfo)
{
if (converterInfo == null) throw new ArgumentNullException("converterInfo");
if (!typeof(PatternConverter).IsAssignableFrom(converterInfo.Type))
{
throw new ArgumentException("The converter type specified [" + converterInfo.Type + "] must be a subclass of log4net.Util.PatternConverter", "converterInfo");
}
m_instanceRulesRegistry[converterInfo.Name] = converterInfo;
}
///
/// Add a converter to this PatternString
///
/// the name of the conversion pattern for this converter
/// the type of the converter
///
///
/// Add a converter to this PatternString
///
///
public void AddConverter(string name, Type type)
{
if (name == null) throw new ArgumentNullException("name");
if (type == null) throw new ArgumentNullException("type");
ConverterInfo converterInfo = new ConverterInfo();
converterInfo.Name = name;
converterInfo.Type = type;
AddConverter(converterInfo);
}
}
}