• 引入Jaeger——封装


      随着微服务的普及,微服务间的调用全链路跟踪也火了起来,Jaeger(https://www.jaegertracing.io/)是CNCF孵化的全链路跟踪型项目,在.net core中,提供了一个Jaeger的Nuget(https://github.com/jaegertracing/jaeger-client-csharp)包来使用。

      本例封装就是用这个Nuget包来做成asp.net core中间件和action过滤器:中间件可以把所有经过管道的请求都跟踪起来,如果觉得这样的跟踪太重,或者没有必要收集所有请求,只跟踪有链路关联的请求,那就可以用action过滤器。

     定义一个Options,这是自定义跟踪收集数据的配置属性

    namespace JaegerSharp
    {
        /// <summary>
        /// Jaeger选项
        /// </summary>
        public class JaegerOptions
        {
            /// <summary>
            /// 是否启用Form数据转成Span
            /// </summary>
            public bool IsFormSpan { get; set; } = false;
            /// <summary>
            /// Form数据最大长度
            /// </summary>
            public int FormValueMaxLength { get; set; } = 100;
            /// <summary>
            /// 是否启用Query数据转成Span
            /// </summary>
            public bool IsQuerySpan { get; set; } = false;
            /// <summary>
            /// Query最大长度
            /// </summary>
            public int QueryValueMaxLength { get; set; } = 100;
            /// <summary>
            /// Form或Query中不作为Span的key集合
            /// </summary>
            public string[] NoSpanKeys { get; set; } = new string[] { "password", "pwd" };
    
        }
    }

      跟踪中间件,把所有的请求都收集起来

    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using OpenTracing;
    using OpenTracing.Propagation;
    using System;
    using System.IO;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace JaegerSharp
    {
        /// <summary>
        /// Jaeger中间件
        /// </summary>
        public class JaegerSharpMiddleware
        {
            /// <summary>
            /// jaeger选项
            /// </summary>
            private readonly JaegerOptions _jaegerOptions;
            /// <summary>
            /// 日志
            /// </summary>
            private readonly ILogger<JaegerSharpMiddleware> _logger;
            /// <summary>
            /// 请求代理
            /// </summary>
            private readonly RequestDelegate _next;
            public JaegerSharpMiddleware(RequestDelegate next, ILogger<JaegerSharpMiddleware> logger, JaegerOptions jaegerOptions = null)
            {
                _next = next;
                _jaegerOptions = jaegerOptions;
                _logger = logger;
            }
            /// <summary>
            /// 调用管道
            /// </summary>
            /// <param name="context">上下文</param>
            /// <param name="tracer">跟踪器</param>
            /// <returns></returns>
            public async Task InvokeAsync(HttpContext context, ITracer tracer)
            {
                _logger.LogInformation("jaeger调用");
                var path = context.Request.Path;
                if (Path.HasExtension(path))
                {
                    await _next(context);
                }
                else
                {
                    //接收传入的Headers
                    var callingHeaders = new TextMapExtractAdapter(context.Request.Headers.ToDictionary(m => m.Key, m => m.Value.ToString()));
                    var spanContex = tracer.Extract(BuiltinFormats.HttpHeaders, callingHeaders);
                    ISpanBuilder builder = null;
                    if (spanContex != null)
                    {
                        builder = tracer.BuildSpan("中间件Span").AsChildOf(spanContex);
                    }
                    else
                    {
                        builder = tracer.BuildSpan("中间件Span");
                    }
                    //开始设置Span
                    using (IScope scope = builder.StartActive(true))
                    {
                        scope.Span.SetOperationName(path);             
                        // 记录请求信息到span
                        if (_jaegerOptions.IsQuerySpan)
                        {
                            foreach (var query in context.Request.Query)
                            {
                                //包含敏感词跳出
                                if (_jaegerOptions.NoSpanKeys.Contains(query.Key))
                                {
                                    continue;
                                }
                                var value = query.Value.ToString().Length > _jaegerOptions.QueryValueMaxLength ? query.Value.ToString()?.Substring(0, _jaegerOptions.QueryValueMaxLength) : query.Value.ToString();
                                scope.Span.SetTag(query.Key, value);
                            }
                        }
                        if (_jaegerOptions.IsFormSpan && context.Request.HasFormContentType)
                        {
                            foreach (var form in context.Request.Form)
                            {
                                //包含敏感词跳出
                                if (_jaegerOptions.NoSpanKeys.Contains(form.Key))
                                {
                                    continue;
                                }
                                var value = form.Value.ToString().Length > _jaegerOptions.FormValueMaxLength ? form.Value.ToString()?.Substring(0, _jaegerOptions.FormValueMaxLength) : form.Value.ToString();
                                scope.Span.SetTag(form.Key, value);
                            }
                        }
                        await _next(context);
                    }
                }
            }
        }
    }

      扩展中间件

    using Microsoft.AspNetCore.Builder;
    
    namespace JaegerSharp
    {
        /// <summary>
        /// 使用JaegerSharp中间件
        /// </summary>
        public static class JaegerSharpMiddlewareExtensions
        {
            public static IApplicationBuilder UseJaegerSharp(this IApplicationBuilder builder)
            {
                return builder.UseMiddleware<JaegerSharpMiddleware>(new JaegerOptions());
            }
            public static IApplicationBuilder UseJaegerSharp(this IApplicationBuilder builder, JaegerOptions jaegerOptions)
            {
                return builder.UseMiddleware<JaegerSharpMiddleware>(jaegerOptions);
            }
        }
    }

      action过滤器,用来准确收集跟踪信息

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using OpenTracing;
    using OpenTracing.Propagation;
    using System.Linq;
    
    namespace JaegerSharp
    {
        /// <summary>
        /// 使用Jaeger的Action过滤器添加Jaeger实列
        /// </summary>
        public class JaegerSharpAttribute : TypeFilterAttribute
        {
            public JaegerSharpAttribute() : base(typeof(JaegerFilterAttributeImpl))
            {
            }
            /// <summary>
            /// 真正实现Jaeger Scope的类
            /// </summary>
            private class JaegerFilterAttributeImpl : IActionFilter
            {
                private readonly ITracer _tracer;
    
                public JaegerFilterAttributeImpl(ITracer tracer)
                {
                    _tracer = tracer;
                }
                IScope _scope = null;
                /// <summary>
                /// 开始执行Action
                /// </summary>
                /// <param name="context"></param>
                public void OnActionExecuting(ActionExecutingContext context)
                {
                    var path = context.HttpContext.Request.Path;
                    var callingHeaders = new TextMapExtractAdapter(context.HttpContext.Request.Headers.ToDictionary(m => m.Key, m => m.Value.ToString()));
                    var spanContex = _tracer.Extract(BuiltinFormats.HttpHeaders, callingHeaders);
                    ISpanBuilder builder = null;
                    if (spanContex != null)
                    {
                        builder = _tracer.BuildSpan("中间件Span").AsChildOf(spanContex);
                    }
                    else
                    {
                        builder = _tracer.BuildSpan("中间件Span");
                    }
                    _scope = builder.StartActive(true);
                    _scope.Span.SetOperationName(path);
                    // 记录请求信息到span
                    foreach (var query in context.HttpContext.Request.Query)
                    {
                        _scope.Span.SetTag(query.Key, query.Value);
                    }
                    if (context.HttpContext.Request.HasFormContentType)
                    {
                        foreach (var form in context.HttpContext.Request.Form)
                        {
                            _scope.Span.SetTag(form.Key, form.Value);
                        }
                    }
                }
                /// <summary>
                /// Action结束执行
                /// </summary>
                /// <param name="context"></param>
                public void OnActionExecuted(ActionExecutedContext context)
                {
                    _scope?.Dispose();
                }
            }
        }
    }

      封装Jaeger在asp.net core中的注放步骤,使注入简单

    using Jaeger;
    using Jaeger.Reporters;
    using Jaeger.Samplers;
    using Jaeger.Senders.Thrift;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using OpenTracing;
    using OpenTracing.Util;
    using System.Reflection;
    
    namespace JaegerSharp
    {
        public static class JaegerSharpExtensions
        {
            /// <summary>
            /// 注入Jaeger
            /// </summary>
            /// <param name="services">服务容器</param>
            /// <param name="host">Jaeger agent host</param>
            /// <param name="port">Jaeger agent port</param>
            /// <param name="maxPacketSize">Jaeger agent maxpacketsize</param>
            public static void AddJaegerSharp(this IServiceCollection services, string host, int port, int maxPacketSize)
            {     
                services.AddSingleton<ITracer>(serviceProvider =>
                {
                    var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();              
                    var reporter = new RemoteReporter.Builder()
                        .WithLoggerFactory(loggerFactory)
                        .WithSender(new UdpSender(string.IsNullOrEmpty(host) ? UdpSender.DefaultAgentUdpHost : host,
                                                        port <= 100 ? UdpSender.DefaultAgentUdpCompactPort : port,
                                                        maxPacketSize <= 0 ? 0 : maxPacketSize))
                              .Build();
                    ITracer tracer = new Tracer.Builder(Assembly.GetEntryAssembly().GetName().Name)
                       .WithReporter(reporter)
                       .WithLoggerFactory(loggerFactory)
                       .WithSampler(new ConstSampler(true))
                       .Build();
                    GlobalTracer.Register(tracer);
                    return tracer;
                });
            }
        }
    }
      想要更快更方便的了解相关知识,可以关注微信公众号 
     

     

  • 相关阅读:
    HDU5446 Unknown Treasure
    C语言课设——通讯录操作系统
    判断设备是否横屏
    禁止用户横屏
    用javascript编写ps脚本
    用户鼠标指针的样式
    如何使用typescript开发node(自动编译,自动重启)
    CSS3:box-sizing:不再为匣子模型而烦恼
    命令行开启自动编译scss(webstrom可开启自动编译)
    TypeError: Cannot read property 'replace' of undefined ----angular2
  • 原文地址:https://www.cnblogs.com/axzxs2001/p/15860671.html
Copyright © 2020-2023  润新知