程序記錄的日誌通常有兩種做用,故障排查、顯式程序運行狀態,當程序發生故障時,咱們能夠經過日誌定位問題,日誌能夠給咱們留下排查故障的依據。不少時候,每每會認爲日誌記錄很是簡單,例如不少程序只是 try-catch{}
,直接輸出到 .txt
,可是這些日誌每每沒法起到幫助定位問題的做用,甚至日誌充斥了大量垃圾內容;日誌內容全靠人眼一行行掃描,或者 Ctrl+F
搜索,沒法高效率審查日誌;日誌單純輸出到文本文件中,沒有很好地管理日誌。html
接下來,咱們將一步步學習日誌的編寫技巧,以及 OpenTracing API 、Jaeger 分佈式鏈路跟蹤的相關知識。前端
最簡單的日誌,就是控制檯輸出,利用 Console.WriteLine()
函數直接輸出信息。git
下面時一個簡單的信息輸出,當程序調用 SayHello
函數時,SayHello
會打印信息。github
public class Hello { public void SayHello(string content) { var str = $"Hello,{content}"; Console.WriteLine(str); } } class Program { static void Main(string[] args) { Hello hello = new Hello(); hello.SayHello("any one"); Console.Read(); } }
經過控制檯,咱們能夠看到,爲了記錄日誌,咱們必須在函數內編寫輸入日誌的代碼,優缺點這些就很少說了,咱們能夠經過 AOP 框架,實現切面編程,同一記錄日誌。web
這裏可使用筆者開源的 CZGL.AOP 框架,Nuget 中能夠搜索到。docker
編寫統一的切入代碼,這些代碼將在函數被調用時執行。shell
Before
會在被代理的方法執行前或被代理的屬性調用時生效,你能夠經過 AspectContext
上下文,獲取、修改傳遞的參數。編程
After 在方法執行後或屬性調用時生效,你能夠經過上下文獲取、修改返回值。api
public class LogAttribute : ActionAttribute { public override void Before(AspectContext context) { Console.WriteLine($"{context.MethodInfo.Name} 函數被執行前"); } public override object After(AspectContext context) { Console.WriteLine($"{context.MethodInfo.Name} 函數被執行後"); return null; } }
改造 Hello 類,代碼以下:緩存
[Interceptor] public class Hello { [Log] public virtual void SayHello(string content) { var str = $"Hello,{content}"; Console.WriteLine(str); } }
而後建立代理類型:
static void Main(string[] args) { Hello hello = AopInterceptor.CreateProxyOfClass<Hello>(); hello.SayHello("any one"); Console.Read(); }
啓動程序,會輸出:
SayHello 函數被執行前 Hello,any one SayHello 函數被執行後
你徹底不須要擔憂 AOP 框架會給你的程序帶來性能問題,由於 CZGL.AOP 框架採用 EMIT 編寫,而且自帶緩存,當一個類型被代理過,以後無需重複生成。
CZGL.AOP 能夠經過 .NET Core 自帶的依賴注入框架和 Autofac 結合使用,自動代理 CI 容器中的服務。這樣不須要 AopInterceptor.CreateProxyOfClass
手動調用代理接口。
CZGL.AOP 代碼是開源的,能夠參考筆者另外一篇博文:
http://www.javashuo.com/article/p-smjfxqls-ns.html
Microsoft.Extensions.Logging
有些公司無技術管理規範,不一樣的開發人員使用不一樣的日誌框架,一個產品中可能有 .txt
、NLog
、Serilog
等,而且沒有同一的封裝。
.NET Core 中的日誌組件有不少,可是流行的日誌框架基本都會實現 Microsoft.Extensions.Logging.Abstractions
,所以咱們能夠學習Microsoft.Extensions.Logging
。Microsoft.Extensions.Logging.Abstractions
是官方對日誌組件的抽象,若是一個日誌組件並不支持 Microsoft.Extensions.Logging.Abstractions
那麼這個組件很容易跟項目糅合的,後續難以模塊化以及下降耦合程度。
Microsoft.Extensions.Logging
軟件包中包含 Logging API ,這些 Logging API 不能獨立運行。它與一個或多個日誌記錄提供程序一塊兒使用,這些日誌記錄提供程序將日誌存儲或顯示到特定輸出,例如 Console, Debug, TraceListeners。
下圖是 .NET Core 中 Loggin API 的層次結構:
圖片來源:https://www.tutorialsteacher.com/
說實話,Microsoft.Extensions.Logging
剛開始是學着很懵,配置感受很複雜。所以,有一張清晰的結構圖很重要,能夠幫助你們理解裏面的 Logging API。
.NET Core 中不少標準接口都實踐了工廠模式的思想,ILoggerFactory 正是工廠模式的接口,而 LoggerFactory 是工廠模式的實現。
其定義以下:
public interface ILoggerFactory : IDisposable { ILogger CreateLogger(string categoryName); void AddProvider(ILoggerProvider provider); }
ILoggerFactory 工廠接口的做用是建立一個 ILogger 類型的實例,即 CreateLogger
接口。
經過實現ILoggerProvider
接口能夠建立本身的日誌記錄提供程序,表示能夠建立 ILogger 實例的類型。
其定義以下:
public interface ILoggerProvider : IDisposable { ILogger CreateLogger(string categoryName); }
ILogger 接口提供了將日誌記錄到基礎存儲的方法,其定義以下:
public interface ILogger { void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter); bool IsEnabled(LogLevel logLevel); IDisposable BeginScope<TState>(TState state); }
logging providers 稱爲日誌記錄程序。
Logging Providers 將日誌顯示或存儲到特定介質,例如 console, debugging event, event log, trace listener 等。
Microsoft.Extensions.Logging
提供瞭如下類型的 logging providers,咱們能夠經過 Nuget 獲取。
而 Serilog 則有 File、Console、Elasticsearch、Debug、MSSqlServer、Email等。
這些日誌提供程序有不少,咱們沒必要細究;若是一個日誌組件,不提供兼容 Microsoft.Extensions.Logging
的實現,那麼根本不該該引入他。
實際上,不少程序是直接 File.Write("Log.txt")
,這種產品質量能好到哪裏去呢?
前面,介紹了 Microsoft.Extensions.Logging
的組成,這裏將學習如何使用 Logging Provider 輸入日誌。
起碼提到,它只是提供了一個 Logging API,所以爲了輸出日誌,咱們必須選擇合適的 Logging Provider 程序,這裏咱們選擇
Microsoft.Extensions.Logging.Console
,請在 Nuget 中引用這個包。
下圖是 Logging Provider 和 ConsoleLogger 結合使用的結構圖:
從常規方法來弄,筆者發現,無法配置呀。。。
ConsoleLoggerProvider consoleLoggerProvider = new ConsoleLoggerProvider( new OptionsMonitor<ConsoleLoggerOptions>( new OptionsFactory<ConsoleLoggerOptions>( new IEnumerable<IConfigureOptions<TOptions>(... ... ...))));
因此只能使用如下代碼快速建立工廠:
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddSimpleConsole(options => { options.IncludeScopes = true; options.SingleLine = true; options.TimestampFormat = "hh:mm:ss "; }));
或者:
ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
固然工廠中能夠添加其它日誌提供程序,示例:
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddSimpleConsole(...) .AddFile(...) .Add()... );
而後獲取 ILogger 實例:
ILogger logger = loggerFactory.CreateLogger<Program>();
記錄日誌:
logger.LogInformation("記錄信息");
Logging API 中,規定了 7 種日誌等級,其定義以下:
public enum LogLevel { Debug = 1, Verbose = 2, Information = 3, Warning = 4, Error = 5, Critical = 6, None = int.MaxValue }
咱們能夠經過 ILogger 中的函數,輸出如下幾種等級的日誌:
logger.LogInformation("Logging information."); logger.LogCritical("Logging critical information."); logger.LogDebug("Logging debug information."); logger.LogError("Logging error information."); logger.LogTrace("Logging trace"); logger.LogWarning("Logging warning.");
關於 Microsoft.Extensions.Logging
這裏就再也不贅述,讀者能夠等級如下連接,瞭解更多相關知識:
https://www.tutorialsteacher.com/core/fundamentals-of-logging-in-dotnet-core
Debug 、Trace 這兩個類的命名空間爲 System.Diagnostics
,Debug 、Trace 提供一組有助於調試代碼的方法和屬性。
讀者能夠參考筆者的另外一篇文章:
http://www.javashuo.com/article/p-uxojcwwb-ny.html
輸出到控制檯:
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); Debug.WriteLine("信息");
鏈路追蹤能夠幫助開發者快速定位分佈式應用架構下的性能瓶頸,提升微服務時代的開發診斷效率。
前面提到的 Trace 、Debug 是 .NET Core 中提供給開發者用於診斷程序和輸出信息的 API,而接着提到的 trace 只 OpenTracing API 中的 鏈路跟蹤(trace)。
普通的日誌記錄有很大的缺點,就是每一個方法記錄一個日誌,咱們沒法將一個流程中被調用的多個方法聯繫起來。當一個方法出現異常時,咱們很難知道是哪一個任務過程出現的異常。咱們只能看到哪一個方法出現錯誤,已經它的調用者。
在 OpenTracing 中,Trace 是具備 Span(跨度) 的有向無環圖。一個 Span 表明應用程序中完成某些工做的邏輯表示,每一個 Span 都具備如下屬性:
爲了弄清楚,Trace 和 Span 是什麼,OpenTracing 又是什麼,請在 Nuget 中引入 OpenTracing
。
編寫 Hello 類以下:
public class Hello { private readonly ITracer _tracer; private readonly ILogger<Hello> _logger; public Hello(ITracer tracer, ILoggerFactory loggerFactory) { _tracer = tracer; _logger = loggerFactory.CreateLogger<Hello>(); } public void SayHello(string content) { // 建立一個 Span 並開始 var spanBuilder = _tracer.BuildSpan("say-hello"); // ------------------------------- var span = spanBuilder.Start(); // | var str = $"Hello,{content}"; // | _logger.LogInformation(str); // | span.Finish(); // | // --------------------------------- } }
啓動程序,並開始追蹤:
static void Main(string[] args) { using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); Hello hello = new Hello(GlobalTracer.Instance, loggerFactory); hello.SayHello("This trace"); Console.Read(); }
在以上過程當中,咱們使用了 OpenTracing API,下面是關於代碼中一些元素的說明:
say-hello
;Start()
開始一個 Span;使用 Finish()
結束一個 Span;固然,咱們運行上面的程序時,是沒有出現別的信息以及 UI 界面,這是由於 GlobalTracer.Instance
會返回一個無操做的 tracer。當咱們定義一個 Tracer 時,能夠觀察到鏈路追蹤的過程。
在 Nuget 中,引入 Jaeger
。
在 Program 中,添加一個靜態函數,這個函數返回了一個自定義的 Tracer:
private static Tracer InitTracer(string serviceName, ILoggerFactory loggerFactory) { var samplerConfiguration = new Configuration.SamplerConfiguration(loggerFactory) .WithType(ConstSampler.Type) .WithParam(1); var reporterConfiguration = new Configuration.ReporterConfiguration(loggerFactory) .WithLogSpans(true); return (Tracer)new Configuration(serviceName, loggerFactory) .WithSampler(samplerConfiguration) .WithReporter(reporterConfiguration) .GetTracer(); }
修改 Main 函數內容以下:
static void Main(string[] args) { using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); var tracer = InitTracer("hello-world", loggerFactory); Hello hello = new Hello(tracer, loggerFactory); hello.SayHello("This trace"); Console.Read(); }
完整代碼:https://gist.github.com/whuanle/b57fe79c9996988db0a9b812f403f00e
可是,日誌直接輸出 string 是很不友好的,這時,咱們須要結構化日誌。
固然,ISpan 提供告終構化日誌的方法,咱們能夠編寫一個方法,用於格式化日誌。
在 Hello 類中添加如下代碼:
private string FormatString(ISpan rootSpan, string helloTo) { var span = _tracer.BuildSpan("format-string").Start(); try { var helloString = $"Hello, {helloTo}!"; span.Log(new Dictionary<string, object> { [LogFields.Event] = "string.Format", ["value"] = helloString }); return helloString; } finally { span.Finish(); } }
另外,咱們還能夠封裝一個輸出字符串信息的函數:
private void PrintHello(ISpan rootSpan, string helloString) { var span = _tracer.BuildSpan("print-hello").Start(); try { _logger.LogInformation(helloString); span.Log("WriteLine"); } finally { span.Finish(); } }
將 SayHello 方法改爲:
public void SayHello(string content) { var spanBuilder = _tracer.BuildSpan("say-hello"); var span = spanBuilder.Start(); var str = FormatString(span, content); PrintHello(span,str); span.Finish(); }
改以上代碼的緣由是,不要在一個方法中糅合太多代碼,能夠嘗試將一些代碼複用,封裝一個統一的代碼。
可是,本來咱們只須要調用 SayHello 一個方法,這裏一個方法會繼續調用另外兩個方法。本來是一個 Span,最後變成三個 Span。
info: Jaeger.Configuration[0] info: Jaeger.Reporters.LoggingReporter[0] Span reported: 77f1a24676a3ffe1:77f1a24676a3ffe1:0000000000000000:1 - format-string info: ConsoleApp1.Hello[0] Hello, This trace! info: Jaeger.Reporters.LoggingReporter[0] Span reported: cebd31b028a27882:cebd31b028a27882:0000000000000000:1 - print-hello info: Jaeger.Reporters.LoggingReporter[0] Span reported: 44d89e11c8ef51d6:44d89e11c8ef51d6:0000000000000000:1 - say-hello
注:0000000000000000
表示一個 Span 已經結束。
優勢:從代碼上看,SayHello -> FormaString ,SayHello -> PrintHello,咱們能夠清晰知道調用鏈路;
缺點:從輸出來看,Span reported 不一樣,咱們沒法中輸出中判斷三個函數的因果關係;
咱們不可能時時刻刻都盯着代碼來看,運維人員和實施人員也不可能拿着代碼去對比以及查找代碼邏輯。
ITracer 負責建立鏈路追蹤,所以 ITracer 也提供了組合多個 Span 因果關係的 API。
使用方法以下:
var rootSapn = _tracer.BuildSpan("say-hello"); // A var span = _tracer.BuildSpan("format-string").AsChildOf(rootSpan).Start(); // B // A -> B
咱們建立了一個 rootSpan ,接着建立一個延續 rootSpan 的 sapn
,rootSpan -> span
。
info: Jaeger.Reporters.LoggingReporter[0] Span reported: 2f2c7b36f4f6b0b9:3dab62151c641380:2f2c7b36f4f6b0b9:1 - format-string info: ConsoleApp1.Hello[0] Hello, This trace! info: Jaeger.Reporters.LoggingReporter[0] Span reported: 2f2c7b36f4f6b0b9:9824227a41539786:2f2c7b36f4f6b0b9:1 - print-hello info: Jaeger.Reporters.LoggingReporter[0] Span reported: 2f2c7b36f4f6b0b9:2f2c7b36f4f6b0b9:0000000000000000:1 - say-hello
Span reported: 2f2c7b36f4f6b0b9
輸出順序爲執行完畢的順序,say-hello 是最後才執行完成的。
從什麼代碼中,你們發現,代碼比較麻煩,由於:
try-finally{}
確保可以完成 Span爲此, OpenTracing API 提供了一種更好的方法,咱們能夠避免將 Span 做爲參數傳遞給代碼,能夠統一自行調用 _tracer 便可。
修改 FormatString
和 PrintHello
代碼以下:
private string FormatString(string helloTo) { using var scope = _tracer.BuildSpan("format-string").StartActive(true); var helloString = $"Hello, {helloTo}!"; scope.Span.Log(new Dictionary<string, object> { [LogFields.Event] = "string.Format", ["value"] = helloString }); return helloString; } private void PrintHello(string helloString) { using var scope = _tracer.BuildSpan("print-hello").StartActive(true); _logger.LogInformation(helloString); scope.Span.Log(new Dictionary<string, object> { [LogFields.Event] = "WriteLine" }); }
修改 SayHello 代碼以下:
public void SayHello(string helloTo) { using var scope = _tracer.BuildSpan("say-hello").StartActive(true); scope.Span.SetTag("hello-to", helloTo); var helloString = FormatString(helloTo); PrintHello(helloString); }
經過上面的代碼,咱們實現去掉了那些煩人的代碼。
StartActive()
代替Start()
,經過將其存儲在線程本地存儲中來使 span 處於「活動」狀態;StartActive()
返回一個IScope
對象而不是一個對象ISpan
。IScope是當前活動範圍的容器。咱們經過訪問活動跨度scope.Span
,一旦關閉了做用域,先前的做用域將成爲當前做用域,從而從新激活當前線程中的先前活動範圍;IScope
繼承 IDisposable
,它使咱們可使用using
語法;StartActive(true)
告訴Scope,一旦它被處理,它就應該完成它所表明的範圍;StartActive()
自動建立 ChildOf
對先前活動範圍的引用,所以咱們沒必要AsChildOf()
顯式使用 builder 方法;若是運行此程序,咱們將看到全部三個報告的跨度都具備相同的跟蹤ID。
微服務將多個程序分開部署,每一個程序提供不一樣的功能。在前面,咱們已經學會了 OpenTracing 鏈路跟蹤。接下來,咱們將把代碼拆分,控制檯程序將再也不提供 FormatString 函數的實現,咱們使用 一個 Web 程序來實現 FormatString 服務。
建立一個 ASP.NET Core 應用程序,在模板中選擇帶有視圖模型控制器的模板。
添加一個 FormatController
控制器在 Controllers 目錄中,其代碼以下:
using Microsoft.AspNetCore.Mvc; namespace WebApplication1.Controllers { [Route("api/[controller]")] public class FormatController : Controller { [HttpGet] public string Get() { return "Hello!"; } [HttpGet("{helloTo}", Name = "GetFormat")] public string Get(string helloTo) { var formattedHelloString = $"Hello, {helloTo}!"; return formattedHelloString; } } }
Web 應用將做爲微服務中的其中一個服務,而這個服務只有一個 API ,這個 API 很簡單,就是提供字符串的格式化。你也能夠編寫其它 API 來提供服務。
將 Program 的 CreateHostBuilder 改一下,咱們固定這個服務的 端口。
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseUrls("http://*:8081"); webBuilder.UseStartup<Startup>(); });
再到 Startup
中刪除 app.UseHttpsRedirection();
。
修改以前控制檯程序的代碼,把 FormatString
方法改爲:
private string FormatString(string helloTo) { using (var scope = _tracer.BuildSpan("format-string").StartActive(true)) { using WebClient webClient = new WebClient(); var url = $"http://localhost:8081/api/format/{helloTo}"; var helloString = webClient.DownloadString(url); scope.Span.Log(new Dictionary<string, object> { [LogFields.Event] = "string.Format", ["value"] = helloString }); return helloString; } }
啓動 Web 程序後,再啓動 控制檯程序。
控制檯程序輸出:
info: Jaeger.Reporters.LoggingReporter[0] Span reported: c587bd888e8f1c19:2e3273568e6e373b:c587bd888e8f1c19:1 - format-string info: ConsoleApp1.Hello[0] Hello, This trace! info: Jaeger.Reporters.LoggingReporter[0] Span reported: c587bd888e8f1c19:f0416a0130d58924:c587bd888e8f1c19:1 - print-hello info: Jaeger.Reporters.LoggingReporter[0] Span reported: c587bd888e8f1c19:c587bd888e8f1c19:0000000000000000:1 - say-hello
接着,咱們能夠將 Formating 改爲:
private string FormatString(string helloTo) { using (var scope = _tracer.BuildSpan("format-string").StartActive(true)) { using WebClient webClient = new WebClient(); var url = $"http://localhost:8081/api/format/{helloTo}"; var helloString = webClient.DownloadString(url); var span = scope.Span .SetTag(Tags.SpanKind, Tags.SpanKindClient) .SetTag(Tags.HttpMethod, "GET") .SetTag(Tags.HttpUrl, url); var dictionary = new Dictionary<string, string>(); _tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new TextMapInjectAdapter(dictionary)); foreach (var entry in dictionary) webClient.Headers.Add(entry.Key, entry.Value); return helloString; } }
SetTag
能夠設置標籤,咱們爲本次請求到 Web 的 Span,設置一個標籤,而且存儲請求的 URL。
var span = scope.Span .SetTag(Tags.SpanKind, Tags.SpanKindClient) .SetTag(Tags.HttpMethod, "GET") .SetTag(Tags.HttpUrl, url);
經過 Inject
將上下文信息注入。
_tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new TextMapInjectAdapter(dictionary));
這些配置規範,能夠到 https://github.com/opentracing/specification/blob/master/semantic_conventions.md 瞭解。
在上面,咱們實現了 Client 在不一樣進程的追蹤,可是尚未實如今 Server 中跟蹤,咱們能夠修改 Startup.cs 中的代碼,將如下代碼替換進去:
using Jaeger; using Jaeger.Samplers; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OpenTracing.Util; using System; namespace WebApplication1 { public class Startup { private static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); private static readonly Lazy<Tracer> Tracer = new Lazy<Tracer>(() => { return InitTracer("webService", loggerFactory); }); private static Tracer InitTracer(string serviceName, ILoggerFactory loggerFactory) { var samplerConfiguration = new Configuration.SamplerConfiguration(loggerFactory) .WithType(ConstSampler.Type) .WithParam(1); var reporterConfiguration = new Configuration.ReporterConfiguration(loggerFactory) .WithLogSpans(true); return (Tracer)new Configuration(serviceName, loggerFactory) .WithSampler(samplerConfiguration) .WithReporter(reporterConfiguration) .GetTracer(); } public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc(); GlobalTracer.Register(Tracer.Value); services.AddOpenTracing(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }
這樣不一樣的進程各類均可以實現追蹤。
OpenTracing 是開放式分佈式追蹤規範,OpenTracing API 是一致,可表達,與供應商無關的API,用於分佈式跟蹤和上下文傳播。
Jaeger 是 Uber 開源的分佈式跟蹤系統。
OpenTracing 的客戶端庫以及規範,能夠到 Github 中查看:https://github.com/opentracing/
詳細的介紹能夠自行查閱資料。
這裏咱們須要部署一個 Jaeger 實例,以供微服務以及事務跟蹤學習須要。
使用 Docker 部署很簡單,只須要執行下面一條命令便可:
docker run -d -p 5775:5775/udp -p 16686:16686 -p 14250:14250 -p 14268:14268 jaegertracing/all-in-one:latest
訪問 16686 端口,便可看到 UI 界面。
Jaeger 的端口做用以下:
Collector 14250 tcp gRPC 發送 proto 格式數據 14268 http 直接接受客戶端數據 14269 http 健康檢查 Query 16686 http jaeger的UI前端 16687 http 健康檢查
接下來咱們將學習如何經過代碼,將數據上傳到 Jaeger 中。
要注意,數據上傳到 Jaeger ,上傳的是 Span,是不會上傳日誌內容的。
繼續使用上面的控制檯程序,Nuget 中添加 Jaeger.Senders.Grpc
包。
咱們能夠經過 UDP (6831端口)和 gRPC(14250) 端口將數據上傳到 Jaeger 中,這裏咱們使用 gRPC。
修改控制檯程序的 InitTracer
方法,其代碼以下:
private static Tracer InitTracer(string serviceName, ILoggerFactory loggerFactory) { Configuration.SenderConfiguration.DefaultSenderResolver = new SenderResolver(loggerFactory) .RegisterSenderFactory<GrpcSenderFactory>(); var reporter = new RemoteReporter.Builder() .WithLoggerFactory(loggerFactory) .WithSender(new GrpcSender("180.102.130.181:14250", null, 0)) .Build(); var tracer = new Tracer.Builder(serviceName) .WithLoggerFactory(loggerFactory) .WithSampler(new ConstSampler(true)) .WithReporter(reporter); return tracer.Build(); }
分別啓動 Web 和 控制檯程序,而後打開 Jaeger 界面,在 」Service「 中選擇 hello-world
,而後點擊底下的 Find Traces
。
經過 Jaeger ,咱們能夠分析鏈路中函數的執行速度以及服務器性能狀況。