为什么在使用 Serilog 日志记录时会出现 Serilog SelfLog 错误?
Why do I get Serilog SelfLog error while using Serilog logging?
我在使用 Serilog SelfLog 时遇到一个有趣的错误。错误是:
Application: XXX.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.IO.IOException
at System.IO.__Error.WinIOError(Int32, System.String)
at System.IO.FileStream.Init(System.String, System.IO.FileMode, System.IO.FileAccess, Int32, Boolean, System.IO.FileShare, Int32, System.IO.FileOptions, SECURITY_ATTRIBUTES, System.String, Boolean, Boolean, Boolean)
at System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare, Int32, System.IO.FileOptions, System.String, Boolean, Boolean, Boolean)
at System.IO.StreamWriter.CreateFile(System.String, Boolean, Boolean)
at System.IO.StreamWriter..ctor(System.String, Boolean, System.Text.Encoding, Int32, Boolean)
at System.IO.StreamWriter..ctor(System.String, Boolean, System.Text.Encoding)
at System.IO.File.InternalAppendAllText(System.String, System.String, System.Text.Encoding)
at Serilog.Debugging.SelfLog.WriteLine(System.String, System.Object, System.Object, System.Object)
at Serilog.Sinks.PeriodicBatching.PeriodicBatchingSink+<OnTick>d__16.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
at Serilog.Sinks.PeriodicBatching.PortableTimer+<OnTick>d__8.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
我不知道会发生什么。我有 .Net framework 4.6.1,Serilog 版本 2.9.0,我无法再次产生错误。
你能帮我描述一下错误吗?为什么我会收到错误消息?您知道此错误的任何解决方案吗?
我使用以下库:
- Serilog v2.9.0
- Serilog.Enrichers.Context v2.4.0
- Serilog.Extensions.Logging v2.0.4
- Serilog.Settings.AppSettings v2.2.2
- Serilog.Sinks.Console v3.1.1
- Serilog.Sinks.File v4.1.0
- Serilog.Sinks.Http v5.2.1
- Serilog.Sinks.PeriodicBatching v2.2.0
- Serilog.Sinks.RollingFile v3.3.0
- Microsoft.Extensions.Logging.Abstractions v1.0.0
编辑:
我的应用配置在这里:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="serilog:minimum-level" value="Verbose" />
<add key="serilog:using:Console" value="Serilog.Sinks.Console" />
<add key="serilog:write-to:Console" />
<add key="serilog:using:Http" value="Serilog.Sinks.Http" />
<add key="serilog:write-to:Http.requestUri" value="http://log.XXX.com:8080/TestEngine" />
<add key="serilog:write-to:Http.batchPostingLimit" value="1" />
<add key="serilog:write-to:Http.batchFormatter" value="Serilog.Sinks.Http.BatchFormatters.ArrayBatchFormatter, Serilog.Sinks.Http" />
<add key="serilog:enrich:with-property:application" value="TestEngine" />
</appSettings>
<startup>
...
此处集成 Serilog:
var config = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithUserName()
.ReadFrom.AppSettings();
if (CustomLogContext == null)
{
CustomLogContext = new Dictionary<string, object>();
}
foreach (var logContext in CustomLogContext)
{
config.Enrich.WithProperty(logContext.Key, logContext.Value);
}
if (!string.IsNullOrEmpty(CustomLogFileFullName))
{
config = config.WriteTo.File($"{CustomLogFileFullName}");
}
Serilog.Log.Logger = config.CreateLogger();
Serilog.Debugging.SelfLog.Enable(msg => File.AppendAllText("internallog.log", msg, Encoding.UTF8));
ILoggerProvider loggerProvider = new SerilogLoggerProvider(Serilog.Log.Logger);
_logger = loggerProvider.CreateLogger("XXX.Logging.Serilog");
解决方案:
正如@RubenBartelink 所说,如果你使用 Selflog 但你使用自定义日志文件,你会冒一些风险。因为Serilog有可能没有写入自定义文件,有可能Serilog抛出IO异常。这个问题不属于 Serilog。问题实际上是File.AppendAlltext操作。解决方法如下:
Serilog.Debugging.SelfLog.Enable(msg =>
{
try
{
File.AppendAllText("internallog.log", msg, Encoding.UTF8);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
});
正如@Daniel A. White 在评论中提到的那样,您正在做一件冒险的事情——尝试在“如果所有其他方法都失败”的基础上提供的处理程序中写入文件。一些例子:
- 如果磁盘已满且文件记录器无法写入(文件接收器不一定会这样做,但如果他们想要通信失败,则可以通过
SelfLog
)
- 如果消息字符串格式错误(Serilog 日志记录调用永远不会抛出;这是基于 wiki 中关于日志记录的重要性不足以阻止正在运行的应用程序运行的基本原则)
虽然 Serilog debugging and diagnostics Wiki page 当前显示了一个写入文件的示例,但这根本不是合约的性质——它不能失败。
因此,如果您真的想将 SelfLog
发送到文件,则需要添加 try
/catch
并在处理程序中吞下异常。
当文件接收器是您的主要输出时,我会说将文件作为备份的价值值得怀疑。这是双重情况,因为您没有任何机制来确保它被修剪(假设您有一个带有格式错误的消息字符串的紧密循环记录 - 它最终会填满您的磁盘)。
基于操作环境(想想 Kubernetes),我个人通过将 SelfLog
回显到我的 Console
接收器(根据上述,日志记录不会抛出)来处理这种权衡可以信任捕获它,或警报。
我在使用 Serilog SelfLog 时遇到一个有趣的错误。错误是:
Application: XXX.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.IO.IOException
at System.IO.__Error.WinIOError(Int32, System.String)
at System.IO.FileStream.Init(System.String, System.IO.FileMode, System.IO.FileAccess, Int32, Boolean, System.IO.FileShare, Int32, System.IO.FileOptions, SECURITY_ATTRIBUTES, System.String, Boolean, Boolean, Boolean)
at System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare, Int32, System.IO.FileOptions, System.String, Boolean, Boolean, Boolean)
at System.IO.StreamWriter.CreateFile(System.String, Boolean, Boolean)
at System.IO.StreamWriter..ctor(System.String, Boolean, System.Text.Encoding, Int32, Boolean)
at System.IO.StreamWriter..ctor(System.String, Boolean, System.Text.Encoding)
at System.IO.File.InternalAppendAllText(System.String, System.String, System.Text.Encoding)
at Serilog.Debugging.SelfLog.WriteLine(System.String, System.Object, System.Object, System.Object)
at Serilog.Sinks.PeriodicBatching.PeriodicBatchingSink+<OnTick>d__16.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
at Serilog.Sinks.PeriodicBatching.PortableTimer+<OnTick>d__8.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
我不知道会发生什么。我有 .Net framework 4.6.1,Serilog 版本 2.9.0,我无法再次产生错误。
你能帮我描述一下错误吗?为什么我会收到错误消息?您知道此错误的任何解决方案吗?
我使用以下库:
- Serilog v2.9.0
- Serilog.Enrichers.Context v2.4.0
- Serilog.Extensions.Logging v2.0.4
- Serilog.Settings.AppSettings v2.2.2
- Serilog.Sinks.Console v3.1.1
- Serilog.Sinks.File v4.1.0
- Serilog.Sinks.Http v5.2.1
- Serilog.Sinks.PeriodicBatching v2.2.0
- Serilog.Sinks.RollingFile v3.3.0
- Microsoft.Extensions.Logging.Abstractions v1.0.0
编辑: 我的应用配置在这里:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="serilog:minimum-level" value="Verbose" />
<add key="serilog:using:Console" value="Serilog.Sinks.Console" />
<add key="serilog:write-to:Console" />
<add key="serilog:using:Http" value="Serilog.Sinks.Http" />
<add key="serilog:write-to:Http.requestUri" value="http://log.XXX.com:8080/TestEngine" />
<add key="serilog:write-to:Http.batchPostingLimit" value="1" />
<add key="serilog:write-to:Http.batchFormatter" value="Serilog.Sinks.Http.BatchFormatters.ArrayBatchFormatter, Serilog.Sinks.Http" />
<add key="serilog:enrich:with-property:application" value="TestEngine" />
</appSettings>
<startup>
...
此处集成 Serilog:
var config = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithUserName()
.ReadFrom.AppSettings();
if (CustomLogContext == null)
{
CustomLogContext = new Dictionary<string, object>();
}
foreach (var logContext in CustomLogContext)
{
config.Enrich.WithProperty(logContext.Key, logContext.Value);
}
if (!string.IsNullOrEmpty(CustomLogFileFullName))
{
config = config.WriteTo.File($"{CustomLogFileFullName}");
}
Serilog.Log.Logger = config.CreateLogger();
Serilog.Debugging.SelfLog.Enable(msg => File.AppendAllText("internallog.log", msg, Encoding.UTF8));
ILoggerProvider loggerProvider = new SerilogLoggerProvider(Serilog.Log.Logger);
_logger = loggerProvider.CreateLogger("XXX.Logging.Serilog");
解决方案: 正如@RubenBartelink 所说,如果你使用 Selflog 但你使用自定义日志文件,你会冒一些风险。因为Serilog有可能没有写入自定义文件,有可能Serilog抛出IO异常。这个问题不属于 Serilog。问题实际上是File.AppendAlltext操作。解决方法如下:
Serilog.Debugging.SelfLog.Enable(msg =>
{
try
{
File.AppendAllText("internallog.log", msg, Encoding.UTF8);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
});
正如@Daniel A. White 在评论中提到的那样,您正在做一件冒险的事情——尝试在“如果所有其他方法都失败”的基础上提供的处理程序中写入文件。一些例子:
- 如果磁盘已满且文件记录器无法写入(文件接收器不一定会这样做,但如果他们想要通信失败,则可以通过
SelfLog
) - 如果消息字符串格式错误(Serilog 日志记录调用永远不会抛出;这是基于 wiki 中关于日志记录的重要性不足以阻止正在运行的应用程序运行的基本原则)
虽然 Serilog debugging and diagnostics Wiki page 当前显示了一个写入文件的示例,但这根本不是合约的性质——它不能失败。
因此,如果您真的想将 SelfLog
发送到文件,则需要添加 try
/catch
并在处理程序中吞下异常。
当文件接收器是您的主要输出时,我会说将文件作为备份的价值值得怀疑。这是双重情况,因为您没有任何机制来确保它被修剪(假设您有一个带有格式错误的消息字符串的紧密循环记录 - 它最终会填满您的磁盘)。
基于操作环境(想想 Kubernetes),我个人通过将 SelfLog
回显到我的 Console
接收器(根据上述,日志记录不会抛出)来处理这种权衡可以信任捕获它,或警报。