Log4Net 两次写入异常以使用自定义布局记录
Log4Net Writing Exception Twice To Log with Custom Layout
我正在开发一个 ASP.Net Web 应用程序,并且 运行 遇到了 Log4Net 使用自定义布局写出异常的问题。我通过以下方式在我的 Global.asax 中以编程方式(不使用 web.config 设置)配置滚动文件附加程序:
// Create basic file logging appender for the application.
var fileAppender = new RollingFileAppender
{
Layout = new JsonPatternLayout(),
File = $"c:/logs/web.log",
AppendToFile = true,
RollingStyle = RollingFileAppender.RollingMode.Date,
DatePattern = "yyyyMMdd'.log'",
StaticLogFileName = true,
Threshold = Level.Debug
};
fileAppender.ActivateOptions();
log4net.Config.BasicConfigurator.Configure(fileAppender);
JsonPatternLayout 是一个自定义 class 我写信是为了从 Log4Net 获取日志记录事件并将其序列化为 json 格式以便更容易消化到日志记录服务中:
public class JsonPatternLayout : PatternLayout
{
public override void Format(TextWriter writer, LoggingEvent e)
{
var dic = new Dictionary<string, string>
{
["level"] = e.Level.DisplayName,
["session"] = e.Identity,
["timestamp"] = e.TimeStamp.ToString(),
["message"] = e.RenderedMessage,
["logger"] = e.LoggerName,
["exception"] = e.ExceptionObject == null ? string.Empty : e.ExceptionObject.ToString(),
["thread"] = e.ThreadName,
["machineName"] = Environment.MachineName
};
writer.Write($"{JsonConvert.SerializeObject(dic)}\n");
}
}
这导致一个文件输出,其中异常被写入两次——一次来自我的模式布局 json,另一次作为字符串(为清楚起见已清理消息):
{"level":"INFO","session":"1","timestamp":"2/14/2020 5:07:12 PM","message":"Message","logger":"Logger","exception":"","thread":"7", "machineName":"Machine"}
{"level":"INFO","session":"1","timestamp":"2/14/2020 5:07:14 PM","message":"Message","logger":"Logger","exception":"","thread":"10","machineName":"Machine"}
{"level":"ERROR","session":"1","timestamp":"2/14/2020 5:07:14 PM","message":"Message","logger":"Logger","exception":"System.Exception: Boo!\r\n at {CallStack}","thread":"10","machineName":"Machine"}
System.Exception: Boo!
at {Call Stack}
我想要的是第二次完全不写出异常。据我所知,这不是通过我的格式化程序,因为它不是我明确提出的消息,而是来自 .Net 框架本身。捕获异常并且不重新抛出它们不会阻止异常被第二次记录。
如何阻止这种行为?
我通过用 Serilog 替换 Log4net 解决了这个问题。我能够通过一些调整重新使用我的自定义格式化程序,因为 Serilog 无论如何都实现了许多相同的接口:
Log.Logger = new LoggerConfiguration()
// Configure file sink.
.WriteTo.RollingFile(
new JsonPatternLayout(),
@"c:\path\logs\app-{Date}.log",
LogEventLevel.Information,
retainedFileCountLimit: 90)
.CreateLogger();
public class JsonPatternLayout : ITextFormatter
{
public void Format(LogEvent e, TextWriter writer)
{
var dic = new Dictionary<string, object>
{
["level"] = e.Level.ToString(),
["session"] = System.Threading.Thread.CurrentPrincipal.Identity.Name,
["timestamp"] = e.Timestamp.ToLocalTime().ToString(),
["message"] = e.RenderMessage(),
["Properties"] = e.Properties,
["exception"] = e.Exception == null ? string.Empty : e.Exception.ToString(),
["machineName"] = machineName
};
writer.Write($"{JsonConvert.SerializeObject(dic)}\n");
}
}
我的日志消息的格式与 Log4net 的格式相同,并且不会重复记录异常。整个替换Log4net有点痛苦,但是很顺利。
非常感谢所有帮助和查看此内容的人。
我正在开发一个 ASP.Net Web 应用程序,并且 运行 遇到了 Log4Net 使用自定义布局写出异常的问题。我通过以下方式在我的 Global.asax 中以编程方式(不使用 web.config 设置)配置滚动文件附加程序:
// Create basic file logging appender for the application.
var fileAppender = new RollingFileAppender
{
Layout = new JsonPatternLayout(),
File = $"c:/logs/web.log",
AppendToFile = true,
RollingStyle = RollingFileAppender.RollingMode.Date,
DatePattern = "yyyyMMdd'.log'",
StaticLogFileName = true,
Threshold = Level.Debug
};
fileAppender.ActivateOptions();
log4net.Config.BasicConfigurator.Configure(fileAppender);
JsonPatternLayout 是一个自定义 class 我写信是为了从 Log4Net 获取日志记录事件并将其序列化为 json 格式以便更容易消化到日志记录服务中:
public class JsonPatternLayout : PatternLayout
{
public override void Format(TextWriter writer, LoggingEvent e)
{
var dic = new Dictionary<string, string>
{
["level"] = e.Level.DisplayName,
["session"] = e.Identity,
["timestamp"] = e.TimeStamp.ToString(),
["message"] = e.RenderedMessage,
["logger"] = e.LoggerName,
["exception"] = e.ExceptionObject == null ? string.Empty : e.ExceptionObject.ToString(),
["thread"] = e.ThreadName,
["machineName"] = Environment.MachineName
};
writer.Write($"{JsonConvert.SerializeObject(dic)}\n");
}
}
这导致一个文件输出,其中异常被写入两次——一次来自我的模式布局 json,另一次作为字符串(为清楚起见已清理消息):
{"level":"INFO","session":"1","timestamp":"2/14/2020 5:07:12 PM","message":"Message","logger":"Logger","exception":"","thread":"7", "machineName":"Machine"}
{"level":"INFO","session":"1","timestamp":"2/14/2020 5:07:14 PM","message":"Message","logger":"Logger","exception":"","thread":"10","machineName":"Machine"}
{"level":"ERROR","session":"1","timestamp":"2/14/2020 5:07:14 PM","message":"Message","logger":"Logger","exception":"System.Exception: Boo!\r\n at {CallStack}","thread":"10","machineName":"Machine"}
System.Exception: Boo!
at {Call Stack}
我想要的是第二次完全不写出异常。据我所知,这不是通过我的格式化程序,因为它不是我明确提出的消息,而是来自 .Net 框架本身。捕获异常并且不重新抛出它们不会阻止异常被第二次记录。
如何阻止这种行为?
我通过用 Serilog 替换 Log4net 解决了这个问题。我能够通过一些调整重新使用我的自定义格式化程序,因为 Serilog 无论如何都实现了许多相同的接口:
Log.Logger = new LoggerConfiguration()
// Configure file sink.
.WriteTo.RollingFile(
new JsonPatternLayout(),
@"c:\path\logs\app-{Date}.log",
LogEventLevel.Information,
retainedFileCountLimit: 90)
.CreateLogger();
public class JsonPatternLayout : ITextFormatter
{
public void Format(LogEvent e, TextWriter writer)
{
var dic = new Dictionary<string, object>
{
["level"] = e.Level.ToString(),
["session"] = System.Threading.Thread.CurrentPrincipal.Identity.Name,
["timestamp"] = e.Timestamp.ToLocalTime().ToString(),
["message"] = e.RenderMessage(),
["Properties"] = e.Properties,
["exception"] = e.Exception == null ? string.Empty : e.Exception.ToString(),
["machineName"] = machineName
};
writer.Write($"{JsonConvert.SerializeObject(dic)}\n");
}
}
我的日志消息的格式与 Log4net 的格式相同,并且不会重复记录异常。整个替换Log4net有点痛苦,但是很顺利。
非常感谢所有帮助和查看此内容的人。