Serilog 记录日志

从 NuGet 安装 Serilog

核心的包是 Serilog 和 Serilog.AspNetCore
建议安装 Serilog.AspNetCore,几乎包含了Serilog常用的所有包
异步写入 Serilog.Sinks.Async
写入MSSQL  Serilog.Sinks.MSSqlServer  

Install-Package Serilog.AspNetCore
Install-Package Serilog
Install-Package Serilog.Sinks.Async
Install-Package Serilog.Sinks.MSSqlServer

 

日志输出

输出到控制台

using Serilog;
using Serilog.Events;

Log.Logger = new LoggerConfiguration()
.WriteTo.Console()  //输出到控制台
.CreateLogger();


Log.Logger = new LoggerConfiguration()
#if DEBUG
    .MinimumLevel.Debug()
#else
    .MinimumLevel.Information()
#endif
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .MinimumLevel.Override("IdentityServer4", LogEventLevel.Information)
    .MinimumLevel.Override("Hangfire", LogEventLevel.Information)
    .WriteTo.Console()
     .CreateLogger();
Log.Information("log");

 

输出本地日志文件

WriteTo.File详解(日志默认路径为当前程序路径)

  • path:默认路径是程序的bin目录+path参数,当然也可以写绝对路径,只需要写入参数就可以了
  • rollingInterval:创建文件的类别,可以是分钟,小时,天,月。 此参数可以让创建的log文件名 + 时间。例如log20191219.log
  • outputTemplate:日志模板,可以自定义
  • retainedFileTimeLimit:日志保存时间,删除过期日志
  • retainedFileCountLimit:设置日志文件个数最大值,默认31,意思就是只保留最近的31个日志文件,等于null时永远保留文件
  • rollOnFileSizeLimit: 是否限制单个文件的最大大小
  • fileSizeLimitBytes: 单个文件最大长度

 

using Serilog;

/*
WriteTo.File详解
    path:默认路径是程序的bin目录+path参数,当然也可以写绝对路径,只需要写入参数就可以了
    rollingInterval:创建文件的类别,可以是分钟,小时,天,月。 此参数可以让创建的log文件名 + 时间。例如log20191219.log
    outputTemplate:日志模板,可以自定义
    retainedFileTimeLimit:日志保存时间,删除过期日志
    retainedFileCountLimit:设置日志文件个数最大值,默认31,意思就是只保留最近的31个日志文件,等于null时永远保留文件
    rollOnFileSizeLimit: 是否限制单个文件的最大大小
    fileSizeLimitBytes: 单个文件最大长度
 */


string SerilogOutputTemplate = "{NewLine}{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);

Log.Logger = new LoggerConfiguration()
  .MinimumLevel.Debug()
  .WriteTo.Console()
  .WriteTo.File("00_Logs//log.log",
                rollingInterval: RollingInterval.Day,
                outputTemplate: SerilogOutputTemplate,
                retainedFileCountLimit: 31,
                retainedFileTimeLimit: TimeSpan.FromDays(2),
                rollOnFileSizeLimit: true,
                fileSizeLimitBytes: 52428800 // 50MB
                )
  .CreateLogger();

//简洁版本
Log.Logger = new LoggerConfiguration()
 .MinimumLevel.Debug()
 .WriteTo.Console()
 .WriteTo.File("00_Logs//log.log", rollingInterval: RollingInterval.Day)
 .CreateLogger();

Log.Information("log");

 

异步输出本地日志文件

需要Serilog.Sinks.Async包,github详解路径:https://github.com/serilog/serilog-sinks-async

 

 1 using Serilog;
 2 
 3 namespace NetCoreConsoleApp
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             //github详解路径:https://github.com/serilog/serilog-sinks-async
10 
11             Log.Logger = new LoggerConfiguration()
12           .WriteTo.Async(a => a.File("00_Logs\\log.log", rollingInterval: RollingInterval.Day))
13           .CreateLogger();
14             Log.Information("log");
15             Log.CloseAndFlush();
16         }
17     }
18 }

 

日志添加自定义字段

自定义字段需要通过自定义模板配置输出,如果是数据库会添加字段

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .Enrich.WithProperty("SystemName", string.Empty)
    .Enrich.WithProperty("ServiceName", string.Empty)
    .Enrich.WithProperty("IP", "8.8.8.8")
    .Enrich.WithProperty("IP2", "9.9.9.9")
    .Enrich.With(serviceProvider.GetService<RequestEnricher>())
    .WriteTo.Console();

 RequestEnricher 代码

using Microsoft.AspNetCore.Http.Extensions;
using Serilog.Core;
using Serilog.Events;

namespace WebApplication1
{
    public class RequestEnricher : ILogEventEnricher
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        public RequestEnricher(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("IpAddress", "127.0.0.1"));
        }       
    }
}

需要注入 RequestEnricher  

builder.Services.AddSingleton<RequestEnricher>();

自定义模板输出日志

输出自定义日志(SystemName和IpAddress)

string SerilogOutputTemplate = "{NewLine}{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}SystemName:{SystemName}IpAddress:{IpAddress}{NewLine}{Exception}" + new string('-', 100);


Log.Logger = new LoggerConfiguration()
    .Enrich.WithProperty("SystemName", 123546)
 .MinimumLevel.Debug()
 .WriteTo.Console()
 .WriteTo.File("00_Logs//log.log", outputTemplate: SerilogOutputTemplate, rollingInterval: RollingInterval.Day)
 .CreateLogger();
Log.Information("log");

或者JSON模板输出。需要Nuget安装 Serilog.Expressions 包

using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Templates;

Log.Logger = new LoggerConfiguration()
    .Enrich.WithProperty("SystemName", 123546)
 .MinimumLevel.Debug()
 .WriteTo.Console()
 .WriteTo.File(
  formatter: new ExpressionTemplate("{ {@timestamp: @t, systemName: SystemName, serviceName: ServiceName, logType: LogType, traceId: TraceId, connectionId: ConnectionId, requestId: RequestId, spanId: SpanId, parentId: ParentId, logTime: @t, sourceContext: SourceContext, ipAddress: IpAddress, userId: UserId, userName: UserName, requestScheme: RequestScheme, requestPath: RequestPath, requestUrl: RequestUrl, requestMethod: RequestMethod, statusCode: StatusCode, elapsed: Elapsed, requestBody: RequestBody, responseBody: ResponseBody} }\n")
 , "00_Logs//log.log", rollingInterval: RollingInterval.Day)
 .CreateLogger();
Log.Information("log");

 

 

日志记录Http请求日志

自定义字段需要通过自定义模板配置输出

注入

builder.Services.AddSingleton<RequestEnricher>();
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();

获取请求信息,然后 Enrich.With 添加

using Microsoft.AspNetCore.Http.Extensions;
using Serilog.Core;
using Serilog.Events;
using System.Security.Claims;

namespace WebApplication1
{
    public class RequestEnricher : ILogEventEnricher
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        public RequestEnricher(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            var context = _httpContextAccessor.HttpContext;
            if (context == null || context.Connection == null || context.Request == null)
            {
                return;
            }
            var ipAddress = context.Connection.RemoteIpAddress?.ToString();
            if (!string.IsNullOrEmpty(ipAddress))
            {
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("IpAddress", ipAddress));
            }
            var requestHost = context.Request.Host.Value;
            if (!string.IsNullOrEmpty(requestHost))
            {
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestHost", requestHost));
            }
            var requestScheme = context.Request.Scheme;
            if (!string.IsNullOrEmpty(requestScheme))
            {
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestScheme", requestScheme));
            }
            var requestUrl = context.Request.GetDisplayUrl();
            if (!string.IsNullOrEmpty(requestUrl))
            {
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestUrl", requestUrl));
            }
            if (context?.User != null)
            {
                var userId = context.User.FindFirstValue("sub");
                if (!string.IsNullOrEmpty(userId))
                {
                    logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("UserId", userId));
                }
                var userName = context.User.Identity?.Name;
                if (!string.IsNullOrEmpty(userName))
                {
                    logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("UserName", userName));
                }
            }
        }
    }
}

Controller中发起请求

using Microsoft.AspNetCore.Mvc;

namespace WebApplication1.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        IHttpClientFactory _httpClientFactory;
        HttpClient _HttpClient;
        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger, IHttpClientFactory httpClientFactory, HttpClient httpClient)
        {
            _logger = logger;
            _httpClientFactory = httpClientFactory;
            _HttpClient = httpClient;
        }

        [HttpGet]
        public string Get()
        {
            var client = _httpClientFactory.CreateClient();
            var result = client.GetStringAsync("https://www.baidu.com/").Result;
            var response = _HttpClient.GetAsync("https://www.taobao.com").Result;
            _logger.LogInformation("我爱中国");
            return Guid.NewGuid().ToString();   
        }
    }
}

 

日志文件夹根据级别分类

 

using Serilog;
using Serilog.Events;
using System;

namespace NetCoreConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string LogFilePath(string LogEvent) => $@"{AppContext.BaseDirectory}00_Logs\{LogEvent}\log.log";
            string SerilogOutputTemplate = "{NewLine}{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 50);

            Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.MinimumLevel.Debug() // 所有Sink的最小记录级别
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Debug).WriteTo.File(LogFilePath("Debug"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Information).WriteTo.File(LogFilePath("Information"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Warning).WriteTo.File(LogFilePath("Warning"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Error).WriteTo.File(LogFilePath("Error"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Fatal).WriteTo.File(LogFilePath("Fatal"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
.CreateLogger();

            Log.Information("log");
            Log.Error("log");
        }
    }
}

 使用 Serilog.Sinks.Map ,需要安装  Install-Package Serilog.Sinks.Map 包

            string LogFilePath(string LogEvent) => System.IO.Path.Combine(AppContext.BaseDirectory, "00_Logs", LogEvent, "log.log");

            Log.Logger = new LoggerConfiguration()
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .MinimumLevel.Debug() // 所有Sink的最小记录级别
            .WriteTo.Map(evt => evt.Level, (level, wt) => wt.File(LogFilePath(level.ToString()), rollingInterval: RollingInterval.Day))
            .CreateLogger();

日志文件根据内容分类

using Serilog;

var requestLogExpression = "StartsWith(SourceContext, 'Serilog.AspNetCore.RequestLoggingMiddleware')";
var systemLogExpression = "StartsWith(SourceContext, 'Microsoft.') " +
    "or StartsWith(SourceContext, 'IdentityServer4.') " +
    "or StartsWith(SourceContext, 'Hangfire.') " +
    "or StartsWith(SourceContext, 'Volo.Abp.')" +
    "or StartsWith(SourceContext, 'System.Net.')";

//可以根据不同的分类写入不同的日志文件
var logger = new LoggerConfiguration();
logger
 .Filter.ByIncludingOnly(requestLogExpression)
 .MinimumLevel.Debug()
 .WriteTo.File("00_Logs\\log-request.log", rollingInterval: RollingInterval.Day)
 .WriteTo.Console();
logger
 .Filter.ByIncludingOnly(systemLogExpression)
 .MinimumLevel.Debug()
 .WriteTo.File("00_Logs\\log-system.log", rollingInterval: RollingInterval.Day)
 .WriteTo.Console();

logger
 .Filter.ByExcluding($"{requestLogExpression} or {systemLogExpression}")
 .MinimumLevel.Debug()
 .WriteTo.File("00_Logs\\log-businesslog.log", rollingInterval: RollingInterval.Day)
 .WriteTo.Console();
Log.Logger = logger.CreateLogger();

 

输出到MSSQL 

需要Serilog.Sinks.MSSqlServer包github详解路径:https://github.com/serilog/serilog-sinks-mssqlserver

参考链接:https://www.cnblogs.com/lonelyxmas/p/11980881.html

using Serilog;
using Serilog.Events;
using Serilog.Sinks.MSSqlServer;
using System;
using System.Collections.ObjectModel;
using System.Data;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {


            string connectionString = @"Server=....";string tableNameA = "LogsA";
            string tableNameB = "LogsB";
            string tableNameC = "LogsC";
            static void Write()
            {
                Log.Information("测试信息");
                Log.Error("Error");
                Log.Write(LogEventLevel.Error, new Exception("错误"), "致命错误");
            };


            //A:默认配置
            //autoCreateSqlTable为true 时 会自动创建日志表
            Log.Logger = new LoggerConfiguration().WriteTo.MSSqlServer(connectionString, tableNameA, autoCreateSqlTable: true).CreateLogger();
            Write();


            //B:移除列
            var options = new ColumnOptions();
            options.Store.Remove(StandardColumn.Properties);
            options.Store.Remove(StandardColumn.MessageTemplate);
            Log.Logger = new LoggerConfiguration()
                         .WriteTo.MSSqlServer(connectionString, tableNameB, columnOptions: options, autoCreateSqlTable: true)
                         .CreateLogger();
            Write();


            //C:添加自定义列
            options = new ColumnOptions();
            options.AdditionalColumns = new Collection<SqlColumn>
                {
                    new SqlColumn { DataType = SqlDbType.NVarChar, DataLength =-1, ColumnName = "IP" },
                    new SqlColumn { DataType = SqlDbType.NVarChar, DataLength =-1, ColumnName = "IP2" }
                };
            Log.Logger = new LoggerConfiguration()
                             .Enrich.WithProperty("IP", "8.8.8.8")
                             .Enrich.WithProperty("IP2", "9.9.9.9")
                             .WriteTo.MSSqlServer(connectionString, tableNameC, columnOptions: options, autoCreateSqlTable: true)
                             .CreateLogger();
            Write();



            Console.WriteLine("Hello World!");
            Console.ReadKey();

        }
    }
}

  

输出到MYSQL 

Serilog的Mysql插件包有两个: Serilog.Sinks.MySQL 和 Serilog.Sinks.MariaDB 。随便选一个使用即可。

代码案例:

using Serilog;
using Serilog.Sinks.MariaDB.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

builder.Host.UseSerilog((hostingContext, loggerConfiguration) =>
{
    var ConnectionString = "Database=sys;Data Source=127.0.0.1;User Id=root;Password=123456;port=3306;pooling=false;SslMode=None;old Guids=true;Charset=utf8;";

    loggerConfiguration
     .Enrich.FromLogContext()
     .Enrich.WithProperty("IP", "8.8.8.8")
     .WriteTo.Console()
     .MinimumLevel.Debug()

     //Serilog.Sinks.MySQL
     .WriteTo.MySQL(ConnectionString, "LogsA")

    //Serilog.Sinks.MariaDB
    .WriteTo.MariaDB(
    connectionString: ConnectionString,
    tableName: "LogsB",
    autoCreateTable: true,
    useBulkInsert: false
    );

});


var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseAuthorization();

app.MapControllers();


app.Run();

输出到PostgreSQL

Serilog的PostgreSQL插件包有两个: Serilog.Sinks.PostgreSQL 和 Serilog.Sinks.Postgresql.Alternative  。随便选一个使用即可。

builder.Host.UseSerilog((hostingContext, loggerConfiguration) =>
{
    var ConnectionString = "Host=192.168.1.5;Port=5432;Uid=postgres;Pwd=123456;Database=postgres;";
    loggerConfiguration
     .Enrich.FromLogContext()
     .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
     .WriteTo.PostgreSQL(ConnectionString, 
        tableName: "serilogs",
        //columnOptions:null,//使用Serilog.Sinks.Postgresql.Alternative包需要使用此参数,不然会报方法冲突错误
        schemaName: "public",
        needAutoCreateTable: true
    );
});

 

过滤Net Core系统日志

using Serilog;
using Serilog.Events;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                    .WriteTo.Console()
                    //Microsoft系统日志最低等级为Warning,过滤掉了所有Information日志
                    //.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                    
                    //记录Net Core系统和EF日志最低级别
                    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                    .MinimumLevel.Override("System", LogEventLevel.Information)
                    .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
                    .CreateLogger();

            Log.Information("log");
        }
    }
} 

 ASP.NET Core 中使用 Serilog

参考链接:https://www.cnblogs.com/MaleDeer/p/10797509.html

在Program.cs程序启动时注入Serilog 加载配置

using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;

namespace WebApplication1
{
    public class Program
    {
        public static void Main(string[] args)
        {

            Log.Logger = new LoggerConfiguration()
             .MinimumLevel.Debug()
             .WriteTo.Console()
             .WriteTo.File($"{AppContext.BaseDirectory}00_Logs\\log.log", rollingInterval: RollingInterval.Day)
             .CreateLogger();

            try
            {
                Log.Information("Starting up");
                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Application start-up failed");
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
            .UseSerilog();
    }
}

或者

using System;using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;

namespace WebApplication1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();

        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.UseSerilog((hostingContext, loggerConfiguration) =>
                    {
                        loggerConfiguration
                       .ReadFrom.Configuration(hostingContext.Configuration)
                       .Enrich.FromLogContext()
                       .WriteTo.File($"{AppContext.BaseDirectory}00_Logs\\log.log", rollingInterval: RollingInterval.Day)
                       .WriteTo.Console();
                    });
                });
    }
}

 或者在 Program.cs 添加 builder.Host.UseSerilog(LogConfigure.ConfigureSerilog); 

using Serilog.Events;
using Serilog;

namespace WebApplication1
{
    public class LogConfigure
    {
        internal static void ConfigureConsoleLogger(LoggerConfiguration logger)
        {
            logger
#if DEBUG
                .MinimumLevel.Debug()
#else
                .MinimumLevel.Information()
#endif
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                .MinimumLevel.Override("IdentityServer4", LogEventLevel.Information)
                .MinimumLevel.Override("Hangfire", LogEventLevel.Information)
                .WriteTo.Async(c => c.Console());
        }

        public static void ConfigureSerilog(HostBuilderContext context, IServiceProvider serviceProvider, LoggerConfiguration logger)
        {
            ConfigureConsoleLogger(logger);
            logger
           .ReadFrom.Configuration(context.Configuration)
           .Enrich.FromLogContext()
           .WriteTo.File(@"00_Logs/log.log", rollingInterval: RollingInterval.Day)
           .WriteTo.Console();
        }
    }
}

 

 

使用 Serilog 时,直接使用 ILogger 即可,因为此服务项目应该是默认注入了,此处需要依赖关系注入知识。如你不了解依赖关系注入,请看 微软官方文档

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace WebApplication1.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        private readonly ILogger<ValuesController> _logger;

        public ValuesController(ILogger<ValuesController> logger)
        {
            _logger = logger;
        }

        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            _logger.LogInformation("test info");
            return new string[] { "value1", "value2" };
        }
    }
}

 

日志请求中间件

官方中间件

app.UseSerilogRequestLogging();

自定义日志请求中间件

app.UseSerilogRequestLoggingPro();

中间件类

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Serilog;

namespace WebApplication1
{
    class SerilogRequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;

        /// <inheritdoc />
        public SerilogRequestLoggingMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        /// <summary>
        /// 读取请求内容
        /// </summary>
        private static async Task<string> ReadRequestBody(HttpRequest request)
        {
            if (request.Method == HttpMethods.Get)
            {
                return $"This is a {request.Method} request";
            }
            if (request.ContentType == null || !request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                return "Request content is not in json format";
            }
            request.EnableBuffering();
            var body = request.Body;

            var buffer = new byte[Convert.ToInt32(request.ContentLength)];

            _ = await request.Body.ReadAsync(buffer, 0, buffer.Length);

            var bodyAsText = Encoding.UTF8.GetString(buffer);

            request.Body = body;
            request.Body.Seek(0, SeekOrigin.Begin);

            return bodyAsText;
        }

        /// <summary>
        /// 读取输出内容
        /// </summary>
        private static async Task<string> ReadResponseBody(HttpResponse response)
        {
            response.Body.Seek(0, SeekOrigin.Begin);

            if (response.ContentType == null || !response.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                return "Response content is not in json format";
            }

            var text = await new StreamReader(response.Body).ReadToEndAsync();

            response.Body.Seek(0, SeekOrigin.Begin);

            return text;
        }

        public async Task Invoke(HttpContext context, IDiagnosticContext diagnosticContext)
        {
            if (context == null)
            {
                return;
            }

            var requestBody = await ReadRequestBody(context.Request);
            diagnosticContext.Set("RequestBody", requestBody);

            var originalBodyStream = context.Response.Body;
            using var responseStream = new MemoryStream();
            context.Response.Body = responseStream;

            await _next(context);

            var responseBody = await ReadResponseBody(context.Response);
            diagnosticContext.Set("ResponseBody", responseBody);
            await responseStream.CopyToAsync(originalBodyStream);
        }
    }

    public static class SerilogRequestBodyMiddlewareExtensions
    {
        public static IApplicationBuilder UseSerilogRequestLoggingPro(this IApplicationBuilder app)
        {
            app.UseSerilogRequestLogging(options =>
            {
                options.MessageTemplate = "HTTP {RequestId} {RequestHost} {IpAddress} {StatusCode} {Elapsed:0} {RequestMethod} {RequestPath} {RequestBody} {ResponseBody}";
            });
            // must after `app.UseSerilogRequestLogging()`
            return app.UseMiddleware<SerilogRequestLoggingMiddleware>();
        }
    }
}

 

日志配置汇总

安装以下Nuget包

Serilog.AspNetCore
Serilog.Expressions
Serilog.Sinks.Async

 Program.cs 注入

builder.Host.UseSerilog(LogConfigure.ConfigureSerilog);
builder.Services.AddSingleton<RequestEnricher>();
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
....
app.UseSerilogRequestLogging(); 

//app.UseSerilogRequestLoggingPro();

 LogConfigure 代码, RequestEnricher 参考上面

using Serilog.Events;
using Serilog;
using Serilog.Templates;

namespace WebApplication1
{

    public static class LogConfigure
    {
        internal static void ConfigureConsoleLogger(LoggerConfiguration logger)
        {
            logger
#if DEBUG
                .MinimumLevel.Debug()
#else
                .MinimumLevel.Information()
#endif
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                .MinimumLevel.Override("IdentityServer4", LogEventLevel.Information)
                .MinimumLevel.Override("Hangfire", LogEventLevel.Information)
                .WriteTo.Async(c => c.Console());
        }

        internal static void ConfigureSerilog(HostBuilderContext context, IServiceProvider serviceProvider, LoggerConfiguration logger)
        {
            ConfigureConsoleLogger(logger);


            var configuration = context.Configuration;
            var systemName = "System";
            var serviceName = "Service";


            logger.Enrich.FromLogContext()
                .Enrich.WithProperty("SystemName", systemName)
                .Enrich.WithProperty("ServiceName", serviceName)
                .Enrich.With(serviceProvider.GetService<RequestEnricher>())
                .WriteTo.Console();


            var requestLogExpression = "StartsWith(SourceContext, 'Serilog.AspNetCore.RequestLoggingMiddleware')";
            var systemLogExpression = "StartsWith(SourceContext, 'Microsoft.') " +
                "or StartsWith(SourceContext, 'IdentityServer4.') " +
                "or StartsWith(SourceContext, 'Hangfire.') " +
                "or StartsWith(SourceContext, 'Volo.Abp.')" +
                "or StartsWith(SourceContext, 'System.Net.')";

            //记录请求日志
            logger.WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(requestLogExpression)
                .Enrich.WithProperty("LogType", "requestlog")
                .WriteTo.Async(c => c.File(
                    formatter: new ExpressionTemplate("{ {@timestamp: @t, systemName: SystemName, serviceName: ServiceName, logType: LogType, traceId: TraceId, connectionId: ConnectionId, requestId: RequestId, spanId: SpanId, parentId: ParentId, logTime: @t, sourceContext: SourceContext, ipAddress: IpAddress, userId: UserId, userName: UserName, requestScheme: RequestScheme, requestPath: RequestPath, requestUrl: RequestUrl, requestMethod: RequestMethod, statusCode: StatusCode, elapsed: Elapsed, requestBody: RequestBody, responseBody: ResponseBody} }\n"),
                    path: $"logs/{systemName}-{serviceName}-requestlog-.log",
                    rollingInterval: RollingInterval.Day,
                    retainedFileTimeLimit: TimeSpan.FromDays(2),
                    rollOnFileSizeLimit: true,
                    fileSizeLimitBytes: 52428800, // 50MB
                    retainedFileCountLimit: 2)));

            //记录系统日志
            logger.WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(systemLogExpression)
                .Enrich.WithProperty("LogType", "systemlog")
                .WriteTo.Async(c => c.File(
                    formatter: new ExpressionTemplate("{ {@timestamp: @t, systemName: SystemName, serviceName: ServiceName, logType: LogType, traceId: TraceId, connectionId: ConnectionId, requestId: RequestId, spanId: SpanId, parentId: ParentId, logTime: @t, ipAddress: IpAddress, userId: UserId, userName: UserName, level: @l, sourceContext: SourceContext, actionId: ActionId, actionName: ActionName, message: @m, exceptions: @x} }\n"),
                    path: $"logs/{systemName}-{serviceName}-systemlog-.log",
                    rollingInterval: RollingInterval.Day,
                    retainedFileTimeLimit: TimeSpan.FromDays(2),
                    rollOnFileSizeLimit: true,
                    fileSizeLimitBytes: 52428800, // 50MB
                    retainedFileCountLimit: 2)));

            //记录自定义日志
            logger.WriteTo.Logger(lc => lc
                    .Filter.ByExcluding($"{requestLogExpression} or {systemLogExpression}")
                    .Enrich.WithProperty("LogType", "businesslog")
                    .WriteTo.Async(c => c.File(
                        formatter: new ExpressionTemplate("{ {@timestamp: @t, systemName: SystemName, serviceName: ServiceName, logType: LogType, traceId: TraceId, connectionId: ConnectionId, requestId: RequestId, spanId: SpanId, parentId: ParentId, logTime: @t, ipAddress: IpAddress, userId: UserId, userName: UserName, level: @l, sourceContext: SourceContext, actionId: ActionId, actionName: ActionName, message: @m, exceptions: @x} }\n"),
                        path: $"logs/{systemName}-{serviceName}-businesslog-.log",
                        rollingInterval: RollingInterval.Day,
                        retainedFileTimeLimit: TimeSpan.FromDays(2),
                        rollOnFileSizeLimit: true,
                        fileSizeLimitBytes: 52428800, // 50MB
                        retainedFileCountLimit: 2)));
        }
    }
}

日志记录相关堆栈信息

        public static void WriteInfo(Type t, string msg)
        {
            //参数
            var type = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
            var methodBase = new StackTrace().GetFrame(1).GetMethod();

            //获取当前方法的命名空间
            var MethodNamespace = MethodBase.GetCurrentMethod().DeclaringType.Namespace;

            //获取当前方法的类名称,包括命名空间
            var MethodFullName = MethodBase.GetCurrentMethod().DeclaringType.FullName;

            //获取当前方法名称
            var MethodName = MethodBase.GetCurrentMethod().Name;

            //获取父方法的命名空间
            var BaseMethodNamespace = methodBase.DeclaringType.Namespace;

            //获取父方法的类名,不包括命名空间
            var BaseMethodClsName = methodBase.DeclaringType.Name;

            //获取父方法名称
            var BaseMethodName = methodBase.Name;

            //获取父方法的类名称,包括命名空间
            var BaseMethodFullName = methodBase.DeclaringType.FullName;

            var message = "方法名称:" + methodBase.DeclaringType.FullName + "\r\n方法名称:" + methodBase.Name + "\r\n日志内容:" + msg;
        }

 

相关链接 

 
 
 
posted @ 2019-12-20 16:08  雨水的命运  阅读(14765)  评论(4编辑  收藏  举报