Serilog MSSqlServer 没有记录错误
Serilog MSSqlServer is not logging errors
在进入问题之前,我必须解释一下我在哪里以及为什么按照我的方式创建代码。
我正在学习关于 PluralSight 的教程,其中解释了日志记录:
所以,我创建了一个静态的 class 并稍微修改了它(因为课程已经过时了)并想出了这个:
public static class Logger
{
private static ILogger _diagnosticLogger;
private static ILogger _errorLogger;
private static ILogger _performanceLogger;
private static ILogger _usageLogger;
public static void AddLogging(this IServiceCollection services, IConfiguration configuration, string sectionName = "Logging")
{
services.Configure<LoggerConfig>(configuration.GetSection(sectionName));
services.AddSingleton(m => m.GetRequiredService<IOptions<LoggerConfig>>().Value);
var scope = services.BuildServiceProvider().CreateScope();
var config = scope.ServiceProvider.GetRequiredService<LoggerConfig>();
_diagnosticLogger = CreateLogger("DiagnosticLogs", config.ConnectionString);
_errorLogger = CreateLogger("ErrorLogs", config.ConnectionString);
_performanceLogger = CreateLogger("PerformanceLogs", config.ConnectionString);
_usageLogger = CreateLogger("UsageLogs", config.ConnectionString);
}
public static void LogDiagnostic(Log log)
{
var shouldWrite = Convert.ToBoolean(Environment.GetEnvironmentVariable("LOG_DIAGNOSTICS"));
if (!shouldWrite) return;
_diagnosticLogger.Write(LogEventLevel.Information, "{@Log}", log);
}
public static void LogError(Log log)
{
log.Message = GetMessageFromException(log.Exception);
_errorLogger.Write(LogEventLevel.Information, "{@Log}", log);
}
public static void LogPerformance(Log log) =>
_performanceLogger.Write(LogEventLevel.Information, "{@Log}", log);
public static void LogUsage(Log log) =>
_usageLogger.Write(LogEventLevel.Information, "{@Log}", log);
private static string GetMessageFromException(Exception exception)
{
while (true)
{
if (exception.InnerException == null) return exception.Message;
exception = exception.InnerException;
}
}
private static ILogger CreateLogger(string name, string connectionString) =>
new LoggerConfiguration()
//.WriteTo.File(path: Environment.GetEnvironmentVariable(name))
.WriteTo.MSSqlServer(connectionString,
sinkOptions: GetSinkOptions(name),
columnOptions: GetColumnOptions())
.CreateLogger();
private static ColumnOptions GetColumnOptions()
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Exception);
columnOptions.Store.Remove(StandardColumn.Level);
columnOptions.Store.Remove(StandardColumn.Message);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.TimeStamp);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "AdditionalInformation"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CorrelationId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CustomException"},
new SqlColumn { DataType = SqlDbType.Int, ColumnName = "ElapsedMilliseconds"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Exception"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Hostname"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Layer"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Location"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Message"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Model"},
new SqlColumn { DataType = SqlDbType.DateTime, ColumnName = "Timestamp"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserEmail"}
};
return columnOptions;
}
private static SinkOptions GetSinkOptions(string name)
{
return new SinkOptions
{
TableName = name,
AutoCreateSqlTable = true,
BatchPostingLimit = 1
};
}
}
在我的 Startup.cs
中,我通过调用 AddLogging
方法初始化了 Loggers:
services.AddLogging(Configuration, nameof(DataConfig));
哪个执行这个方法:
public static void AddLogging(this IServiceCollection services, IConfiguration configuration, string sectionName = "Logging")
{
services.Configure<LoggerConfig>(configuration.GetSection(sectionName));
services.AddSingleton(m => m.GetRequiredService<IOptions<LoggerConfig>>().Value);
var scope = services.BuildServiceProvider().CreateScope();
var config = scope.ServiceProvider.GetRequiredService<LoggerConfig>();
_diagnosticLogger = CreateLogger("DiagnosticLogs", config.ConnectionString);
_errorLogger = CreateLogger("ErrorLogs", config.ConnectionString);
_performanceLogger = CreateLogger("PerformanceLogs", config.ConnectionString);
_usageLogger = CreateLogger("UsageLogs", config.ConnectionString);
}
private static ILogger CreateLogger(string name, string connectionString) =>
new LoggerConfiguration()
//.WriteTo.File(path: Environment.GetEnvironmentVariable(name))
.WriteTo.MSSqlServer(connectionString,
sinkOptions: GetSinkOptions(name),
columnOptions: GetColumnOptions())
.CreateLogger();
如您所见,应该可以很好地创建记录器。
然后我添加了一个自定义中间件:
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
switch (exception)
{
case NotFoundException _:
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
break;
case BadRequestException _:
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
break;
}
WebHelper.LogWebError(null, "Core API", exception, context);
var errorId = Activity.Current?.Id ?? context.TraceIdentifier;
var response = JsonConvert.SerializeObject(new ErrorResponse
{
ErrorId = errorId,
Message = "An error occurred in the API."
});
await context.Response.WriteAsync(response, Encoding.UTF8);
}
}
我有一个扩展方法可以添加到我的项目中:
public static class ErrorHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder)
=> builder.UseMiddleware<ErrorHandlerMiddleware>();
}
当我使用 postman 调用一个方法得到一个错误时,我得到一个格式很好的错误,如下所示:
{
"ErrorId": "|e55d889c-463ad56fc704d7fb.",
"Message": "An error occurred in the API."
}
我在我的代码中设置了断点,逐步执行了所有步骤,日志显示它已创建(通过调用 LogError
方法)并且一切似乎都很好。
如果我检查我的数据库;我可以看到表已经创建,但是没有记录。
我在我的 LogError
方法中添加了一个 try/catch 块,如下所示:
public static void LogError(Log log)
{
try
{
log.Message = GetMessageFromException(log.Exception);
_errorLogger.Write(LogEventLevel.Information, "{@Log}", log);
}
catch (Exception ex)
{
}
}
并且没有抛出任何错误,一切都按预期执行,但我的数据库中没有添加任何记录。
有谁知道为什么?
更新
玩了一会儿后我注意到它实际上是在尝试记录数据,只是所有列都是空的。
如果我删除了我的列选项,它实际上会将日志添加到数据库中。所以这与映射有关。有人可以帮我吗?
这是我的日志的样子:
public class Log
{
public DateTime TimeStamp { get; }
public string Message { get; set; }
public string Model { get; set; }
public string Layer { get; set; }
public string Location { get; set; }
public string HostName { get; set; }
public string UserId { get; set; }
public string UserEmail { get; set; }
public long? ElapsedMilliseconds { get; set; }
public string CorrelationId { get; set; }
public Exception Exception { get; set; }
public Dictionary<string, string> AdditionalInformation { get; set; }
public Log() => TimeStamp = DateTime.UtcNow;
}
如您所见,我正在尝试将其记录到此:
private static ColumnOptions GetColumnOptions()
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Exception);
columnOptions.Store.Remove(StandardColumn.Level);
columnOptions.Store.Remove(StandardColumn.Message);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.TimeStamp);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "AdditionalInformation"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CorrelationId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CustomException"},
new SqlColumn { DataType = SqlDbType.Int, ColumnName = "ElapsedMilliseconds", AllowNull = true},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Exception"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Hostname"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Layer"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Location"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Message"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Model"},
new SqlColumn { DataType = SqlDbType.DateTime, ColumnName = "Timestamp"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserEmail"}
};
return columnOptions;
}
我设法让它工作了。我不确定为什么我在任何地方都找不到它的任何文档,但这就是我所做的。
我做的第一件事是将我的列选项修改为:
private static ColumnOptions GetColumnOptions()
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Level);
columnOptions.Store.Remove(StandardColumn.Message);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "AdditionalInformation"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CorrelationId"},
new SqlColumn { DataType = SqlDbType.Int, ColumnName = "ElapsedMilliseconds", AllowNull = true},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Hostname"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Layer"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Location"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Message"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Model"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserEmail"}
};
return columnOptions;
}
我打算稍后设置 DataLength,但如您所见,我删除了一些不感兴趣的列,然后添加了我自己的列。
然后我修改了我写日志的方式:
public static void LogError(Log log)
{
log.Message = GetMessageFromException(log.Exception);
_errorLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
}
这里有两件事发生了变化,首先我在调用中添加了 Exception,其次,我添加了一个名为 PopulateCustomColumns
的新方法,如下所示:
private static ILogger PopulateCustomColumns(this ILogger logger, Log log)
{
var additionalInformation = JsonConvert.SerializeObject(log.AdditionalInformation);
return logger
.ForContext("AdditionalInformation", additionalInformation)
.ForContext("CorrelationId", log.CorrelationId)
.ForContext("ElapsedMilliseconds", log.ElapsedMilliseconds)
.ForContext("Hostname", log.HostName)
.ForContext("Layer", log.Layer)
.ForContext("Location", log.Location)
.ForContext("Message", log.Message)
.ForContext("Model", log.Model)
.ForContext("UserId", log.UserId)
.ForContext("UserEmail", log.UserEmail);
}
值得注意的是,如果不添加 messageTemplate,它不会记录任何内容(时间戳除外)。我希望这对其他人有帮助。
为了完成,这里是整个 Logger
class:
public static class Logger
{
private static ILogger _diagnosticLogger;
private static ILogger _errorLogger;
private static ILogger _performanceLogger;
private static ILogger _usageLogger;
public static void AddLogging(this IServiceCollection services, IConfiguration configuration, string sectionName = "Logging")
{
services.Configure<LoggerConfig>(configuration.GetSection(sectionName));
services.AddSingleton(m => m.GetRequiredService<IOptions<LoggerConfig>>().Value);
var scope = services.BuildServiceProvider().CreateScope();
var config = scope.ServiceProvider.GetRequiredService<LoggerConfig>();
var t = Path.Combine(Directory.GetCurrentDirectory(), "self.log");
var file = File.CreateText(Path.Combine(Directory.GetCurrentDirectory(), "self.log"));
Serilog.Debugging.SelfLog.Enable(TextWriter.Synchronized(file));
_diagnosticLogger = CreateLogger("DiagnosticLogs", config.ConnectionString);
_errorLogger = CreateLogger("ErrorLogs", config.ConnectionString);
_performanceLogger = CreateLogger("PerformanceLogs", config.ConnectionString);
_usageLogger = CreateLogger("UsageLogs", config.ConnectionString);
}
public static void LogDiagnostic(Log log)
{
var shouldWrite = Convert.ToBoolean(Environment.GetEnvironmentVariable("LOG_DIAGNOSTICS"));
if (!shouldWrite) return;
_diagnosticLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
}
public static void LogError(Log log)
{
log.Message = GetMessageFromException(log.Exception);
_errorLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
}
public static void LogPerformance(Log log) =>
_performanceLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
public static void LogUsage(Log log) =>
_usageLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
private static string GetMessageFromException(Exception exception)
{
while (true)
{
if (exception.InnerException == null) return exception.Message;
exception = exception.InnerException;
}
}
private static ILogger CreateLogger(string name, string connectionString) =>
new LoggerConfiguration()
//.WriteTo.File(path: Environment.GetEnvironmentVariable(name))
.WriteTo.MSSqlServer(connectionString,
sinkOptions: GetSinkOptions(name),
columnOptions: GetColumnOptions())
.CreateLogger();
private static ColumnOptions GetColumnOptions()
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Level);
columnOptions.Store.Remove(StandardColumn.Message);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "AdditionalInformation"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CorrelationId"},
new SqlColumn { DataType = SqlDbType.Int, ColumnName = "ElapsedMilliseconds", AllowNull = true},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Hostname"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Layer"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Location"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Message"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Model"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserEmail"}
};
return columnOptions;
}
private static ILogger PopulateCustomColumns(this ILogger logger, Log log)
{
var additionalInformation = JsonConvert.SerializeObject(log.AdditionalInformation);
return logger
.ForContext("AdditionalInformation", additionalInformation)
.ForContext("CorrelationId", log.CorrelationId)
.ForContext("ElapsedMilliseconds", log.ElapsedMilliseconds)
.ForContext("Hostname", log.HostName)
.ForContext("Layer", log.Layer)
.ForContext("Location", log.Location)
.ForContext("Message", log.Message)
.ForContext("Model", log.Model)
.ForContext("UserId", log.UserId)
.ForContext("UserEmail", log.UserEmail);
}
private static SinkOptions GetSinkOptions(string name)
{
return new SinkOptions
{
TableName = name,
AutoCreateSqlTable = true,
BatchPostingLimit = 1
};
}
}
在进入问题之前,我必须解释一下我在哪里以及为什么按照我的方式创建代码。 我正在学习关于 PluralSight 的教程,其中解释了日志记录:
所以,我创建了一个静态的 class 并稍微修改了它(因为课程已经过时了)并想出了这个:
public static class Logger
{
private static ILogger _diagnosticLogger;
private static ILogger _errorLogger;
private static ILogger _performanceLogger;
private static ILogger _usageLogger;
public static void AddLogging(this IServiceCollection services, IConfiguration configuration, string sectionName = "Logging")
{
services.Configure<LoggerConfig>(configuration.GetSection(sectionName));
services.AddSingleton(m => m.GetRequiredService<IOptions<LoggerConfig>>().Value);
var scope = services.BuildServiceProvider().CreateScope();
var config = scope.ServiceProvider.GetRequiredService<LoggerConfig>();
_diagnosticLogger = CreateLogger("DiagnosticLogs", config.ConnectionString);
_errorLogger = CreateLogger("ErrorLogs", config.ConnectionString);
_performanceLogger = CreateLogger("PerformanceLogs", config.ConnectionString);
_usageLogger = CreateLogger("UsageLogs", config.ConnectionString);
}
public static void LogDiagnostic(Log log)
{
var shouldWrite = Convert.ToBoolean(Environment.GetEnvironmentVariable("LOG_DIAGNOSTICS"));
if (!shouldWrite) return;
_diagnosticLogger.Write(LogEventLevel.Information, "{@Log}", log);
}
public static void LogError(Log log)
{
log.Message = GetMessageFromException(log.Exception);
_errorLogger.Write(LogEventLevel.Information, "{@Log}", log);
}
public static void LogPerformance(Log log) =>
_performanceLogger.Write(LogEventLevel.Information, "{@Log}", log);
public static void LogUsage(Log log) =>
_usageLogger.Write(LogEventLevel.Information, "{@Log}", log);
private static string GetMessageFromException(Exception exception)
{
while (true)
{
if (exception.InnerException == null) return exception.Message;
exception = exception.InnerException;
}
}
private static ILogger CreateLogger(string name, string connectionString) =>
new LoggerConfiguration()
//.WriteTo.File(path: Environment.GetEnvironmentVariable(name))
.WriteTo.MSSqlServer(connectionString,
sinkOptions: GetSinkOptions(name),
columnOptions: GetColumnOptions())
.CreateLogger();
private static ColumnOptions GetColumnOptions()
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Exception);
columnOptions.Store.Remove(StandardColumn.Level);
columnOptions.Store.Remove(StandardColumn.Message);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.TimeStamp);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "AdditionalInformation"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CorrelationId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CustomException"},
new SqlColumn { DataType = SqlDbType.Int, ColumnName = "ElapsedMilliseconds"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Exception"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Hostname"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Layer"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Location"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Message"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Model"},
new SqlColumn { DataType = SqlDbType.DateTime, ColumnName = "Timestamp"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserEmail"}
};
return columnOptions;
}
private static SinkOptions GetSinkOptions(string name)
{
return new SinkOptions
{
TableName = name,
AutoCreateSqlTable = true,
BatchPostingLimit = 1
};
}
}
在我的 Startup.cs
中,我通过调用 AddLogging
方法初始化了 Loggers:
services.AddLogging(Configuration, nameof(DataConfig));
哪个执行这个方法:
public static void AddLogging(this IServiceCollection services, IConfiguration configuration, string sectionName = "Logging")
{
services.Configure<LoggerConfig>(configuration.GetSection(sectionName));
services.AddSingleton(m => m.GetRequiredService<IOptions<LoggerConfig>>().Value);
var scope = services.BuildServiceProvider().CreateScope();
var config = scope.ServiceProvider.GetRequiredService<LoggerConfig>();
_diagnosticLogger = CreateLogger("DiagnosticLogs", config.ConnectionString);
_errorLogger = CreateLogger("ErrorLogs", config.ConnectionString);
_performanceLogger = CreateLogger("PerformanceLogs", config.ConnectionString);
_usageLogger = CreateLogger("UsageLogs", config.ConnectionString);
}
private static ILogger CreateLogger(string name, string connectionString) =>
new LoggerConfiguration()
//.WriteTo.File(path: Environment.GetEnvironmentVariable(name))
.WriteTo.MSSqlServer(connectionString,
sinkOptions: GetSinkOptions(name),
columnOptions: GetColumnOptions())
.CreateLogger();
如您所见,应该可以很好地创建记录器。 然后我添加了一个自定义中间件:
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
switch (exception)
{
case NotFoundException _:
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
break;
case BadRequestException _:
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
break;
}
WebHelper.LogWebError(null, "Core API", exception, context);
var errorId = Activity.Current?.Id ?? context.TraceIdentifier;
var response = JsonConvert.SerializeObject(new ErrorResponse
{
ErrorId = errorId,
Message = "An error occurred in the API."
});
await context.Response.WriteAsync(response, Encoding.UTF8);
}
}
我有一个扩展方法可以添加到我的项目中:
public static class ErrorHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder)
=> builder.UseMiddleware<ErrorHandlerMiddleware>();
}
当我使用 postman 调用一个方法得到一个错误时,我得到一个格式很好的错误,如下所示:
{
"ErrorId": "|e55d889c-463ad56fc704d7fb.",
"Message": "An error occurred in the API."
}
我在我的代码中设置了断点,逐步执行了所有步骤,日志显示它已创建(通过调用 LogError
方法)并且一切似乎都很好。
如果我检查我的数据库;我可以看到表已经创建,但是没有记录。
我在我的 LogError
方法中添加了一个 try/catch 块,如下所示:
public static void LogError(Log log)
{
try
{
log.Message = GetMessageFromException(log.Exception);
_errorLogger.Write(LogEventLevel.Information, "{@Log}", log);
}
catch (Exception ex)
{
}
}
并且没有抛出任何错误,一切都按预期执行,但我的数据库中没有添加任何记录。 有谁知道为什么?
更新
玩了一会儿后我注意到它实际上是在尝试记录数据,只是所有列都是空的。
如果我删除了我的列选项,它实际上会将日志添加到数据库中。所以这与映射有关。有人可以帮我吗?
这是我的日志的样子:
public class Log
{
public DateTime TimeStamp { get; }
public string Message { get; set; }
public string Model { get; set; }
public string Layer { get; set; }
public string Location { get; set; }
public string HostName { get; set; }
public string UserId { get; set; }
public string UserEmail { get; set; }
public long? ElapsedMilliseconds { get; set; }
public string CorrelationId { get; set; }
public Exception Exception { get; set; }
public Dictionary<string, string> AdditionalInformation { get; set; }
public Log() => TimeStamp = DateTime.UtcNow;
}
如您所见,我正在尝试将其记录到此:
private static ColumnOptions GetColumnOptions()
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Exception);
columnOptions.Store.Remove(StandardColumn.Level);
columnOptions.Store.Remove(StandardColumn.Message);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.TimeStamp);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "AdditionalInformation"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CorrelationId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CustomException"},
new SqlColumn { DataType = SqlDbType.Int, ColumnName = "ElapsedMilliseconds", AllowNull = true},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Exception"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Hostname"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Layer"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Location"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Message"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Model"},
new SqlColumn { DataType = SqlDbType.DateTime, ColumnName = "Timestamp"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserEmail"}
};
return columnOptions;
}
我设法让它工作了。我不确定为什么我在任何地方都找不到它的任何文档,但这就是我所做的。 我做的第一件事是将我的列选项修改为:
private static ColumnOptions GetColumnOptions()
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Level);
columnOptions.Store.Remove(StandardColumn.Message);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "AdditionalInformation"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CorrelationId"},
new SqlColumn { DataType = SqlDbType.Int, ColumnName = "ElapsedMilliseconds", AllowNull = true},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Hostname"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Layer"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Location"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Message"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Model"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserEmail"}
};
return columnOptions;
}
我打算稍后设置 DataLength,但如您所见,我删除了一些不感兴趣的列,然后添加了我自己的列。
然后我修改了我写日志的方式:
public static void LogError(Log log)
{
log.Message = GetMessageFromException(log.Exception);
_errorLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
}
这里有两件事发生了变化,首先我在调用中添加了 Exception,其次,我添加了一个名为 PopulateCustomColumns
的新方法,如下所示:
private static ILogger PopulateCustomColumns(this ILogger logger, Log log)
{
var additionalInformation = JsonConvert.SerializeObject(log.AdditionalInformation);
return logger
.ForContext("AdditionalInformation", additionalInformation)
.ForContext("CorrelationId", log.CorrelationId)
.ForContext("ElapsedMilliseconds", log.ElapsedMilliseconds)
.ForContext("Hostname", log.HostName)
.ForContext("Layer", log.Layer)
.ForContext("Location", log.Location)
.ForContext("Message", log.Message)
.ForContext("Model", log.Model)
.ForContext("UserId", log.UserId)
.ForContext("UserEmail", log.UserEmail);
}
值得注意的是,如果不添加 messageTemplate,它不会记录任何内容(时间戳除外)。我希望这对其他人有帮助。
为了完成,这里是整个 Logger
class:
public static class Logger
{
private static ILogger _diagnosticLogger;
private static ILogger _errorLogger;
private static ILogger _performanceLogger;
private static ILogger _usageLogger;
public static void AddLogging(this IServiceCollection services, IConfiguration configuration, string sectionName = "Logging")
{
services.Configure<LoggerConfig>(configuration.GetSection(sectionName));
services.AddSingleton(m => m.GetRequiredService<IOptions<LoggerConfig>>().Value);
var scope = services.BuildServiceProvider().CreateScope();
var config = scope.ServiceProvider.GetRequiredService<LoggerConfig>();
var t = Path.Combine(Directory.GetCurrentDirectory(), "self.log");
var file = File.CreateText(Path.Combine(Directory.GetCurrentDirectory(), "self.log"));
Serilog.Debugging.SelfLog.Enable(TextWriter.Synchronized(file));
_diagnosticLogger = CreateLogger("DiagnosticLogs", config.ConnectionString);
_errorLogger = CreateLogger("ErrorLogs", config.ConnectionString);
_performanceLogger = CreateLogger("PerformanceLogs", config.ConnectionString);
_usageLogger = CreateLogger("UsageLogs", config.ConnectionString);
}
public static void LogDiagnostic(Log log)
{
var shouldWrite = Convert.ToBoolean(Environment.GetEnvironmentVariable("LOG_DIAGNOSTICS"));
if (!shouldWrite) return;
_diagnosticLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
}
public static void LogError(Log log)
{
log.Message = GetMessageFromException(log.Exception);
_errorLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
}
public static void LogPerformance(Log log) =>
_performanceLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
public static void LogUsage(Log log) =>
_usageLogger.PopulateCustomColumns(log).Write(LogEventLevel.Information, log.Exception, "{@Log}", log);
private static string GetMessageFromException(Exception exception)
{
while (true)
{
if (exception.InnerException == null) return exception.Message;
exception = exception.InnerException;
}
}
private static ILogger CreateLogger(string name, string connectionString) =>
new LoggerConfiguration()
//.WriteTo.File(path: Environment.GetEnvironmentVariable(name))
.WriteTo.MSSqlServer(connectionString,
sinkOptions: GetSinkOptions(name),
columnOptions: GetColumnOptions())
.CreateLogger();
private static ColumnOptions GetColumnOptions()
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Level);
columnOptions.Store.Remove(StandardColumn.Message);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "AdditionalInformation"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "CorrelationId"},
new SqlColumn { DataType = SqlDbType.Int, ColumnName = "ElapsedMilliseconds", AllowNull = true},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Hostname"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Layer"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Location"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Message"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "Model"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserId"},
new SqlColumn { DataType = SqlDbType.VarChar, ColumnName = "UserEmail"}
};
return columnOptions;
}
private static ILogger PopulateCustomColumns(this ILogger logger, Log log)
{
var additionalInformation = JsonConvert.SerializeObject(log.AdditionalInformation);
return logger
.ForContext("AdditionalInformation", additionalInformation)
.ForContext("CorrelationId", log.CorrelationId)
.ForContext("ElapsedMilliseconds", log.ElapsedMilliseconds)
.ForContext("Hostname", log.HostName)
.ForContext("Layer", log.Layer)
.ForContext("Location", log.Location)
.ForContext("Message", log.Message)
.ForContext("Model", log.Model)
.ForContext("UserId", log.UserId)
.ForContext("UserEmail", log.UserEmail);
}
private static SinkOptions GetSinkOptions(string name)
{
return new SinkOptions
{
TableName = name,
AutoCreateSqlTable = true,
BatchPostingLimit = 1
};
}
}