惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
GbyAI
GbyAI
美团技术团队
云风的 BLOG
云风的 BLOG
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
博客园 - 叶小钗
Engineering at Meta
Engineering at Meta
博客园 - 三生石上(FineUI控件)
N
Netflix TechBlog - Medium
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
Google Developers Blog
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
宝玉的分享
宝玉的分享
阮一峰的网络日志
阮一峰的网络日志
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
U
Unit 42
P
Proofpoint News Feed
V
V2EX

博客园 - 落叶子

FreeRedis 实现消费确认队列和消费延时确认队列 快速部署单机k3s+Kuboard控制面板 解决微软鼠标设置宏后桌面有效果 进游戏就没有效果了 .net core 使用QRCoder在linux 下生成带logo的二维码 FreeScheduler 在asp.net core 中使用依赖注入的方式进行注入使用 GC 性能调优相关 .net 中使用OpenCvSharp 判断一张图片中是否包含指定图标 maui BlazorWebView+本地html (vue、uniapp等都可以) 接入支付宝sdk 进行支付宝支付 开发 Android app maui BlazorWebView+本地html (vue、uniapp等都可以) 接入微信sdk 开发 Android app maui BlazorWebView+本地html 打包Android app 实现支付宝H5支付 maui WebView 打包Android app 实现线上网页 支付宝H5支付 maui BlazorWebView Android 中混合使用https和http 原生js+html+css 实现大转盘抽奖效果,实现人员占位,转盘缓慢停止时依次显示奖品图片 c# .net 多类实现同一个接口,动态指定使用哪一个类的实现 C#、 .net 修改和移除 url 中的参数 windows 批量删除docker 镜像 uniapp 原生websocket 使用 signalr k8s docker 中部署think php 并搭建php websocket .net core Unicode 转中文
Serilog日志同步到redis中和自定义Enricher来增加额外的记录信息
落叶子 · 2023-01-11 · via 博客园 - 落叶子

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 NewLife.Reflection;
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.Globalization;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;

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 string Referer { 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().Trim('"') : string.Empty;
            data.HostName = logEvent.Properties.TryGetValue("HostName", out LogEventPropertyValue? propertyHostNameValue) ? propertyHostNameValue.ToString().Trim('"') : string.Empty;
            data.Referer= logEvent.Properties.TryGetValue("Referer", out LogEventPropertyValue? propertyRefererValue) ? propertyRefererValue.ToString().Trim('"') : 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 Microsoft.Extensions.Primitives;
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.ToString()));
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Referer", httpContext.Request.Headers.TryGetValue("Referer",out StringValues refererString)? refererString.ToString():string.Empty));
            }
        }
    }
}

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();

       .......后续忽略自行修改
    }

 效果: