log4net 的一个简单封装






4.50/5 (9投票s)
log4net 的一个封装,允许用一行代码完成日志记录操作。
引言
log4net 是一个适用于 .NET 的高度灵活的日志记录框架。 首先,我想说明的是,本文假设您对 log4net 框架有基本的了解,并非 log4net 的入门教程,也不是关于如何配置和使用 log4net 的指南。
如果您不熟悉 log4net,可以在这里找到相关文档,或者在 CodeProject 上查看这篇文章。
问题
我对 log4net 的主要批评之一是,我发现它的语法有些笨拙。例如,添加一行简单的调试日志的代码如下
// C#
ILog log = LogManager.GetLogger("MyLog");
if (log.IsDebugEnabled)
{
using (ThreadContext.Stacks["MyProperty"].Push("MyValue"))
{
using(ThreadContext.Stacks["MyOtherProperty"].Push("MyOtherValue"))
{
log.Debug("This is a debugging message.");
}
}
}
' Visual Basic
Dim log As ILog = LogManager.GetLogger("MyLog")
If log.IsDebugEnabled Then
Using ThreadContext.Stacks("MyProperty").Push("MyValue")
Using ThreadContext.Stacks("MyOtherProperty").Push("MyOtherValue")
log.Debug("This is a debugging message.")
End Using
End Using
End If
正如您所看到的,执行一个简单的日志记录操作需要大约六行代码。 显然,即使对于最小的应用程序,仅出于日志记录目的,将代码中布满像上面所示的语句,也绝对是不希望看到的,并且随着日志代码开始淹没逻辑代码,很快就会变得难以阅读。
需要的是一种可以通过一行代码完成日志记录操作的解决方案。
解决方案
我的解决方案是一个名为 Logger
的静态类,它有两个方法(加上重载):Initialize()
用于初始化 log4net,Log()
实际执行日志记录操作
首先,让我们看看 Initialize()
方法。 它有两个重载:一个接受单个参数,指定 log4net 使用的配置文件。 无参数重载指定 log4net 应从 *web.config* 或 *app.config* 中提取其配置
// C#
private static bool isInitialized;
public static void Initialize()
{
Initialize(null);
}
public static void Initialize(string configFile)
{
if (!isInitialized)
{
if (!String.IsNullOrEmpty(configFile))
XmlConfigurator.ConfigureAndWatch(new FileInfo(configFile));
else
XmlConfigurator.Configure();
isInitialized = true;
}
else
throw new LoggingInitializationException(
"Logging has already been initialized.");
}
' Visual Basic
Private _isInitialized As Boolean
Public Sub Initialize()
Initialize(Nothing)
End Sub
Public Sub Initialize(ByVal configFile As String)
If Not _isInitialized Then
If Not String.IsNullOrEmpty(configFile) Then
XmlConfigurator.ConfigureAndWatch(New FileInfo(configFile))
Else
XmlConfigurator.Configure()
End If
_isInitialized = True
Else
Throw New LoggingInitializationException("Logging has already been initialized.")
End If
End Sub
接下来,我们将看看 Log()
方法及其重载。 这些方法接受以下参数的各种组合
参数 | 描述 |
---|---|
logName |
要将事件写入的日志的名称。 如果省略,则事件将写入所有日志。 |
loggingLevel |
事件的级别(调试、信息、警告、错误或致命)。 |
message |
消息。 |
loggingProperties |
其属性映射到 log4net PatternLayout 属性的对象(例如:%property{Foo} ,其中 Foo 是属性的名称)。 |
exception |
要记录的异常(如果有)。 |
所有 Log()
方法重载最终都会调用 LogBase()
,这是实际调用 log4net 的地方。 此方法的代码如下
// C#
private static void LogBase(ILog log, LoggingLevel loggingLevel,
string message, object loggingProperties, Exception exception)
{
if (ShouldLog(log, loggingLevel))
{
PushLoggingProperties(loggingProperties);
switch (loggingLevel)
{
case LoggingLevel.Debug: log.Debug(message, exception); break;
case LoggingLevel.Info: log.Info(message, exception); break;
case LoggingLevel.Warning: log.Warn(message, exception); break;
case LoggingLevel.Error: log.Error(message, exception); break;
case LoggingLevel.Fatal: log.Fatal(message, exception); break;
}
PopLoggingProperties(loggingProperties);
}
}
' Visual Basic
Private Sub LogBase(ByVal log As ILog, ByVal loggingLevel As LoggingLevel, _
ByVal message As String, ByVal loggingProperties _
As Object, ByVal exception As Exception)
If ShouldLog(log, loggingLevel) Then
PushLoggingProperties(loggingProperties)
Select Case loggingLevel
Case VisualBasic.LoggingLevel.Debug
log.Debug(message, exception)
Case VisualBasic.LoggingLevel.Info
log.Info(message, exception)
Case VisualBasic.LoggingLevel.Warning
log.Warn(message, exception)
Case VisualBasic.LoggingLevel.Error
log.Error(message, exception)
Case VisualBasic.LoggingLevel.Fatal
log.Fatal(message, exception)
End Select
PopLoggingProperties(loggingProperties)
End If
End Sub
正如您所看到的,此方法发出的第一个调用是 ShouldLog()
方法,该方法根据当前的日志记录级别确定是否应记录事件
// C#
private static bool ShouldLog(ILog log, LoggingLevel loggingLevel)
{
switch (loggingLevel)
{
case LoggingLevel.Debug: return log.IsDebugEnabled;
case LoggingLevel.Info: return log.IsInfoEnabled;
case LoggingLevel.Warning: return log.IsWarnEnabled;
case LoggingLevel.Error: return log.IsErrorEnabled;
case LoggingLevel.Fatal: return log.IsFatalEnabled;
default: return false;
}
}
' Visual Basic
Private Function ShouldLog(ByVal log As ILog, _
ByVal loggingLevel As LoggingLevel) As Boolean
Select Case loggingLevel
Case VisualBasic.LoggingLevel.Debug
Return log.IsDebugEnabled
Case VisualBasic.LoggingLevel.Info
Return log.IsInfoEnabled
Case VisualBasic.LoggingLevel.Warning
Return log.IsWarnEnabled
Case VisualBasic.LoggingLevel.Error
Return log.IsErrorEnabled
Case VisualBasic.LoggingLevel.Fatal
Return log.IsFatalEnabled
Case Else
Return False
End Select
End Function
如果此方法返回 true
,则要调用的下一个方法是 PushLoggingProperties()
。 这需要 loggingProperties
参数,并使用反射将每个属性的值推送到 log4net ThreadContext
堆栈上
// C#
private static void PushLoggingProperties(object loggingProperties)
{
if (loggingProperties != null)
{
Type attrType = loggingProperties.GetType();
PropertyInfo[] properties = attrType.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < properties.Length; i++)
{
object value = properties[i].GetValue(loggingProperties, null);
if (value != null)
ThreadContext.Stacks[properties[i].Name].Push(value.ToString());
}
}
}
' Visual Basic
Private Sub PushLoggingProperties(ByVal loggingProperties As Object)
If Not loggingProperties Is Nothing Then
Dim attrType As Type = loggingProperties.GetType()
Dim properties() As PropertyInfo = attrType.GetProperties(
BindingFlags.Public Or BindingFlags.Instance)
For i As Integer = 0 To properties.Length - 1 Step +1
Dim value As Object = properties(i).GetValue(loggingProperties, Nothing)
If Not value Is Nothing Then
ThreadContext.Stacks(properties(i).Name).Push(value.ToString())
End If
Next
End If
End Sub
接下来,代码基于当前的日志记录级别在 log4net ILog
对象上调用适当的日志记录方法。 接下来调用 PopLoggingProperties()
,它只是执行 PushLoggingProperties()
的相反操作,并将属性从堆栈中弹出。
一些示例
以下是一些 Logger
类在实际操作中的示例。 在这些示例中,我们使用以下 PatternLayout
<layout type="log4net.Layout.PatternLayout">
<conversionPattern
value="%date [%thread] %-5level %logger [%property{User}]:
%{property{Environment} - %message%newline%exception" />
</layout>
// C#
// Initialise log4net
Logger.Initialize();
// Write a debugging event to all logs.
Logger.Log(LoggingLevel.Debug, "Calling method Foo().",
new { User = "Joe Bloggs", Environment = "UAT" });
// Write an exception to the log called "ErrorLog".
try
{
int i = 25;
int j = 0;
int foo = i / j;
}
catch (DivideByZeroException ex)
{
Logger.Log("ErrorLog", LoggingLevel.Error, "Attempted to divide by zero.",
new { User = "Fred Bloggs", Environment = "Production" }, ex);
}
' Visual Basic
' Initialise log4net
Logger.Initialize()
' Write a debugging event to all logs.
Logger.Log(LoggingLevel.Debug, "Calling method Foo().", _
New With {.User = "Joe Bloggs", .Environment = "UAT"})
' Write an exception to the log called "ErrorLog".
Try
Dim i As Integer = 25
Dim j As Integer = 0
Dim foo As Integer = CInt(i / j)
Catch ex As DivideByZeroException
Logger.Log("ErrorLog", LoggingLevel.Error, "Attempted to divide by zero.", _
New With {.User = "Fred Bloggs", .Environment = "Production"}, ex)
End Try
摘要
通过一个简单的封装器,可以抽象出与 log4net 框架交互的代码,同时通过将日志记录操作减少到单行来提高代码的可读性。
您可以从这里下载完整的代码。