我在MVC5应用程序中使用Serilog
和Serilog.Exceptions
进行结构化日志记录。
我想从& Exception"的值中删除堆栈跟踪。日志中的字段。我知道我在RollingFile
接收器中使用new JsonFormatter()
,因此异常对象,例如ex
,被格式化为ex.ToString()
,整个对象被原样写入。
我使用new DestructuringOptionsBuilder().WithDefaultDestructurers()
在"ExceptionDetails"中显示堆栈跟踪
是否可能只看到异常的名称作为" exception "字段的日志,而不是整个堆栈跟踪和其他细节作为ex.ToString()
,而写到JSON文件接收器?
下面是我的异常日志:
{
"Timestamp": "2021-09-02T15:04:02.4469999+05:00",
"Level": "Error",
"MessageTemplate": "Unhandled Exception",
"Exception": "System.NullReferenceException: Object reference not set to an instance of an object.rn at AppV2.Controllers.BaseController.Initialize(RequestContext requestContext) in E:\Workspace\AppV2\AppV2\Controllers\BaseController.cs:line 115rn at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)rn at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)rn at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)rn at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)rn at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()rn at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)rn at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)",
"Properties": {
"UserName": "adminuser@mail.com",
"ThreadId": 5,
"Caller": "AppV2.Extensions.Logger.LogError(System.Exception, System.String, System.Object[])",
"MachineName": "DESKTOP-GHV3V41",
"HttpRequestId": "e9922caf-7e25-47f8-9941-263ba1ec4278",
"HttpRequestNumber": 1,
"HttpRequestClientHostIP": "::1",
"HttpRequestType": "GET",
"HttpRequestRawUrl": "/",
"ExceptionDetails": {
"Type": "System.NullReferenceException",
"HResult": -2147467261,
"Message": "Object reference not set to an instance of an object.",
"Source": "Boilerplate.Web.Mvc5.Sample",
"StackTrace": " at AppV2.Controllers.BaseController.Initialize(RequestContext requestContext) in E:\Workspace\AppV2\AppV2\Controllers\BaseController.cs:line 115rn at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)rn at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)rn at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)rn at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)rn at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()rn at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)rn at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)",
"TargetSite": "Void Initialize(System.Web.Routing.RequestContext)"
}
}
}
这是我的Logger
类:
public class Logger
{
private static readonly ILogger logger;
static Logger()
{
logger = new LoggerConfiguration()
.Enrich.WithUserName(anonymousUsername: "Not Authenticated")
.Enrich.FromLogContext()
.Enrich.With(new ThreadEnrich())
.Enrich.WithCaller()
.Enrich.WithMachineName()
.Enrich.WithHttpRequestId()
.Enrich.WithHttpRequestNumber()
.Enrich.WithHttpRequestClientHostIP()
.Enrich.WithHttpRequestType()
.Enrich.WithHttpRequestRawUrl()
.Enrich.WithMvcRouteData()
.Enrich.WithExceptionDetails(
new DestructuringOptionsBuilder()
.WithDefaultDestructurers())
.Enrich.WithDemystifiedStackTraces()
.WriteTo.RollingFile(new JsonFormatter(),
HttpContext.Current.Server.MapPath($"~/logs/log-.json"),
LogEventLevel.Debug,
fileSizeLimitBytes: 655360)
.CreateLogger();
}
public static void LogInformation(string info, object[] data = null)
{
logger.Information(info, data);
}
public static void LogDebug(string debug, object[] data = null)
{
logger.Debug(debug, data);
}
public static void LogWarning(string warning, object[] data = null, Exception e = null)
{
logger.Warning(e, warning, data);
}
public static void LogError(Exception e, string error, object[] data = null)
{
logger.Error(e, error, data);
}
}
任何关于我的Logger
课程的建议也欢迎。
无论存在什么日志属性,"formatter "Serilog如何将标准日志信息(包括异常)转换为文本。
为了摆脱冗余,你需要使用JsonFormatter
以外的东西。
如果您使用Serilog.Expressions
,那么使用ExpressionTemplate
很容易做到这一点。Serilog.Sinks.File
的示例用法(其他接收器类似):
const string logTemplate = "{ { Timestamp: @t, Message: @m, Level: @l, Exception: ExceptionDetail, ..Rest() } }n";
Log.Logger = new LoggerConfiguration()
.Enrich.WithExceptionDetails()
.WriteTo.File(new ExpressionTemplate(logTemplate), "app.log")
.CreateLogger();
旧答案(不含Serilog.Expressions
):
这是我使用的一个格式化器,它只是移动了&;exceptiondetails &;
namespace Serilog.Exceptions.Formatting;
using System.IO;
using Serilog.Events;
using Serilog.Exceptions.Core;
using Serilog.Formatting;
using Serilog.Formatting.Json;
/// <summary>
/// A JSON text formatter using structured properties for exceptions.
/// </summary>
/// <remarks>
/// Avoids the redundancy of <see cref="JsonFormatter"/> when used with <see cref="ExceptionEnricher"/>.
/// </remarks>
public class StructuredExceptionFormatter : ITextFormatter
{
private readonly string rootName;
private readonly JsonValueFormatter valueFormatter = new(typeTagName: null);
/// <summary>
/// Initializes a new instance of the <see cref="StructuredExceptionFormatter"/> class.
/// </summary>
/// <param name="rootName">The root name used by the enricher, if different from the default.</param>
public StructuredExceptionFormatter(string? rootName = null)
{
this.rootName = rootName ?? new DestructuringOptionsBuilder().RootName;
}
public void Format(LogEvent logEvent, TextWriter output)
{
output.Write("{"Timestamp":"");
output.Write(logEvent.Timestamp.UtcDateTime.ToString("O"));
output.Write("","Message":");
var message = logEvent.MessageTemplate.Render(logEvent.Properties);
JsonValueFormatter.WriteQuotedJsonString(message, output);
output.Write(","Level":"");
output.Write(logEvent.Level);
output.Write('"');
var propCount = logEvent.Properties.Count;
if (logEvent.Properties.TryGetValue(this.rootName, out var exceptionProperty))
{
output.Write(","Exception":");
this.valueFormatter.Format(exceptionProperty, output);
propCount--;
}
if (propCount > 0)
{
output.Write(","Properties":{");
var comma = false;
foreach (var property in logEvent.Properties)
{
if (property.Key == this.rootName)
{
continue;
}
if (comma)
{
output.Write(',');
}
else
{
comma = true;
}
JsonValueFormatter.WriteQuotedJsonString(property.Key, output);
output.Write(':');
this.valueFormatter.Format(property.Value, output);
}
output.Write("}");
}
output.WriteLine('}');
}
}
它将显示你的异常如下:
{
"Timestamp": "2021-09-02T15:04:02.4469999+05:00",
"Level": "Error",
"Message": "Unhandled Exception",
"Exception": {
"Type": "System.NullReferenceException",
"HResult": -2147467261,
"Message": "Object reference not set to an instance of an object.",
"Source": "Boilerplate.Web.Mvc5.Sample",
"StackTrace": " at AppV2.Controllers.BaseController.Initialize(RequestContext requestContext) in E:\Workspace\AppV2\AppV2\Controllers\BaseController.cs:line 115rn at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)rn at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)rn at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)rn at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)rn at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)rn at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()rn at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)rn at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)",
"TargetSite": "Void Initialize(System.Web.Routing.RequestContext)"
},
"Properties": {
"UserName": "adminuser@mail.com",
"ThreadId": 5,
"Caller": "AppV2.Extensions.Logger.LogError(System.Exception, System.String, System.Object[])",
"MachineName": "DESKTOP-GHV3V41",
"HttpRequestId": "e9922caf-7e25-47f8-9941-263ba1ec4278",
"HttpRequestNumber": 1,
"HttpRequestClientHostIP": "::1",
"HttpRequestType": "GET",
"HttpRequestRawUrl": "/"
}
}
我研究了一下,我认为你应该检查一下Serilog过滤器。例如,您可以过滤事件字段、源,甚至过滤具有特定值的数据。你可以通过在你的日志配置代码或JSON文件中配置相应的过滤器来做到这一点。
更多信息请参考此链接https://github.com/serilog/serilog-filters-expressions