Serilog 紀錄檔同步到redis佇列中 後續可以通過佇列同步到資料庫、騰訊阿里等紀錄檔元件中,這裡redis庫用的新生命團隊的NewLife.Redis元件 可以實現輕量級訊息佇列(輕量級訊息佇列RedisQueue (newlifex.com)),也可以自行替換熟悉的元件
類庫目錄 該類庫需新增 Microsoft.AspNetCore.Http.Abstractions、NewLife.Redis、Newtonsoft.Json、Serilog包
RedisStreamSink.cs 中的程式碼 定義RedisSink 將紀錄檔記錄到redis佇列中
using Microsoft.AspNetCore.Http; using NewLife.Caching; using Newtonsoft.Json; using Serilog.Core; using Serilog.Events; using Serilog.Formatting; using Serilog.Parsing; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; namespace SeriLog.Sinks.RedisStream.Ms { /// <summary> /// 用於序列化資料 /// </summary> public class LogData { public DateTimeOffset Timestamp { get; set; } public LogEventLevel Level { get; set; } public string Message { get; set; } public string RequestIP { get; set; } public string HostName { get; set; } public static LogData LogEventToLogData(LogEvent logEvent) { var data = new LogData(); data.Timestamp = logEvent.Timestamp; data.Level = logEvent.Level; return data; } } public class RedisStreamSink : ILogEventSink { private readonly ITextFormatter _formatter; private readonly FullRedis _redis; private readonly string _redisStreamName; public RedisStreamSink(FullRedis fullRedis, string redisStreamName, ITextFormatter textFormatter) { _redis = fullRedis; _redisStreamName = redisStreamName; _formatter = textFormatter; } public void Emit(LogEvent logEvent) { string message =string.Empty; using (var writer = new StringWriter()) { _formatter.Format(logEvent, writer); message = writer.ToString(); } var data = LogData.LogEventToLogData(logEvent); data.Message = message.Replace("\r\n",""); //獲取自定義需要記錄的資訊例如使用者端ip地址和主機名 data.RequestIP = logEvent.Properties.TryGetValue("RequestIP", out LogEventPropertyValue? propertyIpValue) ? propertyIpValue.ToString() : string.Empty; data.HostName = logEvent.Properties.TryGetValue("HostName", out LogEventPropertyValue? propertyHostNameValue) ? propertyHostNameValue.ToString() : string.Empty; Console.WriteLine("===================================\r\n" + JsonConvert.SerializeObject(data)); //新增到redis 佇列中 var queue = _redis.GetQueue<string>(_redisStreamName); queue.Add(JsonConvert.SerializeObject(data)); } } }
RedisStreamSinkExtensions.cs 中的程式碼
using Serilog.Configuration; using Serilog; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using NewLife.Caching; using Serilog.Formatting; namespace SeriLog.Sinks.RedisStream.Ms { public static class RedisStreamSinkExtensions { //序列化時message中顯示的內容 簡化輸出 private const string DefaultOutputTemplate = "(RequestId:{RequestId}){Message:j}{Exception}"; public static LoggerConfiguration RedisStreamSink( this LoggerSinkConfiguration loggerConfiguration, FullRedis redis, string redisStreamName, string outputTemplate = DefaultOutputTemplate, IFormatProvider formatProvider = null ) { var formatter = new Serilog.Formatting.Display.MessageTemplateTextFormatter(outputTemplate, formatProvider); return loggerConfiguration.Sink(new RedisStreamSink(redis, redisStreamName, formatter)); } } }
RequestInfoEnricher.cs 中的程式碼 自定義新增RequestIP和Referer資訊
using Microsoft.AspNetCore.Http; using NewLife.Model; using Serilog.Core; using Serilog.Events; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SeriLog.Sinks.RedisStream.Ms { public class RequestInfoEnricher : ILogEventEnricher { private readonly IServiceProvider _serviceProvider; public RequestInfoEnricher(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { var httpContext = _serviceProvider.GetService<IHttpContextAccessor>()?.HttpContext; if (null != httpContext) { //這裡新增自定義需記錄的資訊 logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", httpContext.Connection.RemoteIpAddress)); logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Referer", httpContext.Request.Headers["Referer"])); } } } }
EnricherExtensions.cs 中的程式碼
using Serilog.Configuration; using Serilog; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SeriLog.Sinks.RedisStream.Ms { public static class EnricherExtensions { public static LoggerConfiguration WithRequestInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider) { if (enrich == null) throw new ArgumentNullException(nameof(enrich)); return enrich.With(new RequestInfoEnricher(serviceProvider)); } } }
在需要用到的專案中新增 SeriLog.Sinks.RedisStream.Ms 專案參照
public static void Main(string[] args) { var fullRedis = FullRedis.Create($"server=127.0.0.1:6379,db=1"); var builder = WebApplication.CreateBuilder(args); //這一步必須放在CreateLogger之前否則 RequestInfoEnricher中獲取不到HttpContextAccessor builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .Enrich.WithProperty("HostName", Dns.GetHostName()) .WriteTo.RedisStreamSink(fullRedis, "logger") //logger 為佇列的名稱 .Enrich.WithRequestInfo(builder.Services.BuildServiceProvider()) .CreateLogger(); builder.Host.UseSerilog();
.......後續忽略自行修改
}