.net 如何在Web API c#中使用Serilog自定义日志

m4pnthwp  于 2023-06-25  发布在  .NET
关注(0)|答案(1)|浏览(210)

我需要创建一个JSON格式的日志与下面的格式我的Web API项目。

{
   "LEVEL":"ERROR",
   "TIMESTAMP":"2023-06-07 14:07:59,983",
   "CALLER" : "CLIENT NAME",
   "EVENT_NAME":"WHICH METHOD CALLED HERE",
   "ERRORID":"10001",
   "MESSAGE":"INVALID REQUEST/REQUEST COMPLETED"
 }

下面是我的appsetting.json文件。

"Serilog": {
    "Using": ["Serilog.Sinks.Console"],
    "MinimumLevel": {
      "Default": "Information"
    },
    "Enrich": [ "FromLogContext"],
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
          "outputTemplate": "[{Level}][{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] Message:{Message}{NewLine}{Exception}"
        }
      }
    ]
  }

我的logConfig.cs文件是

public static void ConfigureSerilogServices(this IServiceCollection services)
        {
            var configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();

            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .CreateLogger();                   

        }

我在控制台上可以看到serilog中的一些值,比如timestamp,level,message,但是我不知道如何将它转换成json格式和添加一些运行时值

x3naxklr

x3naxklr1#

我可以展示我是如何为我自己的用例实现自定义 Package 器的,你可以用它作为编写自己的日志 Package 器的例子。
我的用例是,我想在每个日志消息前面加上文件和方法名称,说明日志来自何处。
Program.cs

LogRegister.CreateLogger(Constants.AppInfo.AppName, Constants.LogFilePath);

try
{
    ApplicationConfiguration.Initialize();
    ...
    Application.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Error setting up ... service. Exiting application");
}
finally
{
    LogRegister.DisposeLogger();
}

LogRegister.cs:

public class LogRegister
{
    public static void CreateLogger(string appName, string logFilePath)
    {
        var outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] [{CallerFileName}.{CallerMethodName}] {Message}{NewLine}{Exception}";
        Serilog.Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .WriteTo.File(logFilePath, rollOnFileSizeLimit: true, fileSizeLimitBytes: 1_048_576, outputTemplate: outputTemplate)
            //.WriteTo.EventLog(appName, manageEventSource: true, outputTemplate: outputTemplate, logName: appName, restrictedToMinimumLevel: LogEventLevel.Information)
            .Enrich.FromLogContext()
            .CreateLogger();
    }

    public static void DisposeLogger()
    {
        Serilog.Log.CloseAndFlush();
    }
}

注意上面的[{CallerFileName}.{CallerMethodName}]。这就是我注射的东西。在您的情况下,您可能希望将整行替换为占位符,如下所示:var outputTemplate = "{MyJsonMessage}";
Package 器Log.cs

// We have a Log wrapper in a separate file so that we could enrich all messages with ClassName and MethodName in an efficient non-reflective way.
// Unfortunately there is no simple and native way to do this.
// That does mean that we need to wrap every single logging method we plan on using such as Information or Error.
public static class Log
{
    private static ILogger GetLogger(string sourceFilePath, string memberName, int sourceLineNumber)
    {
        var fileName = Path.GetFileNameWithoutExtension(sourceFilePath);
        var enricher = new ClassAndMethodContextEnricher(fileName, memberName, sourceLineNumber);
        var logger = Serilog.Log.Logger.ForContext(enricher);
        return logger;
    }

    public static void Debug(
        string message,
        [CallerFilePath] string sourceFilePath = "",
        [CallerMemberName] string memberName = "",
        [CallerLineNumber] int sourceLineNumber = 0)
    {
        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Debug(message);
    }

    public static void Information(
       string message,
       [CallerFilePath] string sourceFilePath = "",
       [CallerMemberName] string memberName = "",
       [CallerLineNumber] int sourceLineNumber = 0)
    {
        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Information(message);
    }

    public static void Information<T0>(
        string message,
        T0 p0,
        [CallerFilePath] string sourceFilePath = "",
        [CallerMemberName] string memberName = "",
        [CallerLineNumber] int sourceLineNumber = 0)
    {
        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Information(message, p0);
    }

    public static void Information<T0, T1>(
        string message,
        T0 p0, T1 p1,
        [CallerFilePath] string sourceFilePath = "",
        [CallerMemberName] string memberName = "",
        [CallerLineNumber] int sourceLineNumber = 0)
    {
        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Information(message, p0, p1);
    }

    // example of calling methods with multiple parameters (for structured logging)
    public static void Information<T0, T1, T2>(
        string message,
        T0 p0, T1 p1, T2 p2,
        [CallerFilePath] string sourceFilePath = "",
        [CallerMemberName] string memberName = "",
        [CallerLineNumber] int sourceLineNumber = 0)
    {
        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Information(message, p0, p1, p2);
    }
    
    public static void Warn(
       string message,
       [CallerMemberName] string memberName = "",
       [CallerFilePath] string sourceFilePath = "",
       [CallerLineNumber] int sourceLineNumber = 0)
    {
        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Warning(message);
    }

    public static void Error(
        Exception ex,
        string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0)
    {
        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Error(ex, message);
    }

    public static void Error(
       string message,
       [CallerMemberName] string memberName = "",
       [CallerFilePath] string sourceFilePath = "",
       [CallerLineNumber] int sourceLineNumber = 0)
    {
        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Error(message);
    }

    public static void Fatal(
       string message,
       [CallerMemberName] string memberName = "",
       [CallerFilePath] string sourceFilePath = "",
       [CallerLineNumber] int sourceLineNumber = 0)
    {
        FatalAction();

        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Fatal(message);
    }

    public static void Fatal(
        Exception ex,
        string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0)
    {
        FatalAction();

        var logger = GetLogger(sourceFilePath, memberName, sourceLineNumber);
        logger.Fatal(ex, message);
    }

    private static void FatalAction()
    {
        Environment.ExitCode = -1;
    }
}

ClassAndMethodContextEnricher.cs:

public class ClassAndMethodContextEnricher : ILogEventEnricher
{
    public ClassAndMethodContextEnricher(string callerFileName, string callerMethodName, int callerLineNumber)
    {
        CallerFileName = callerFileName;
        CallerMethodName = callerMethodName;
        CallerLineNumber = callerLineNumber;
    }

    public string CallerMethodName { get; protected set; }
    public string CallerFileName { get; protected set; }
    public int CallerLineNumber { get; protected set; }
    public static string CallerFileNamePropertyName { get; } = "CallerFileName";
    public static string CallerMethodNamePropertyName { get; } = "CallerMethodName";
    public static string CallerLineNumberPropertyName { get; } = "CallerLineNumber";

    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        logEvent.AddPropertyIfAbsent(new LogEventProperty(CallerFileNamePropertyName, new ScalarValue(CallerFileName)));
        logEvent.AddPropertyIfAbsent(new LogEventProperty(CallerMethodNamePropertyName, new ScalarValue(CallerMethodName)));
        logEvent.AddPropertyIfAbsent(new LogEventProperty(CallerLineNumberPropertyName, new ScalarValue(CallerLineNumber)));
    }
}

用法:只需在应用程序中的任何位置使用Log.即可。不需要注射任何东西:

Log.Information("Starting Service");
Log.Debug("Error getting response from server.");
Log.Error(message);
Log.Error(ex, message);
Log.Fatal(ex, "Error in the main .. service. Exiting application");

在本例中,您需要编写用于在Enricher类中创建和注入JSON的主要逻辑。实际上,有很多方法可以将您自己的自定义Message封装为json。我的日志 Package 器对于您的需要来说可能太复杂了,但这是一种方法。如果你想要更简单的方法,你将需要深入挖掘Serilog肠道。
祝你好运

相关问题