This paper mainly explains how to use ZipKin to build NetCore distributed link tracking
scenario
Because of recent business volume increased, whereas the project also need to increase the number of Pod in K8S basic expansion doubled, has added a number of physical machines, part of the physical machine network communication problems, cause there is something wrong with the request into the physical machine is always a timeout situation, because the system is not used before link tracking, lead to troubleshoot problems slowly, So I studied the link framework on the market and found ZipKin, a lightweight link tracking framework.
The sample code
In this paper, the log system uses Exceplesstion sample code request link for SimpleZipkin(Gateway service)– >WebApi(Api service)– >OrderApi(order service). This document uses version 1.5.0 as an example. If you deploy Zipkin using Mysql as storage, ensure that Mysql version 8.0 is not supported
zipkin4net
zipkin4net.middleware.aspnetcore
Copy the code
Create the ZipKin helper class
public static class ZipKinExtensions
{
public static IServiceCollection AddZipKin(this IServiceCollection services)
{
return services.AddSingleton<HttpDiagnosticSourceObserver >(a); } public static IApplicationBuilder UseZipKin(this IApplicationBuilder app, IHostApplicationLifetime lifetime, ILoggerFactory loggerFactory, string serviceName, string zipKinUrl) { DiagnosticListener.AllListeners.Subscribe(app? .ApplicationServices? .GetService<TraceObserver>()); Lifetime. ApplicationStarted. Register (() = > {TraceManager. SamplingRate = 1.0 f; Var logger = new TracingLogger(loggerFactory, "zipkin4net"); var httpSender = new HttpZipkinSender(zipKinUrl, "application/json"); var tracer = new ZipkinTracer(httpSender, new JSONSpanSerializer(), new Statistics()); var consoleTracer = new zipkin4net.Tracers.ConsoleTracer(); TraceManager.RegisterTracer(tracer); TraceManager.RegisterTracer(consoleTracer); TraceManager.Start(logger); }); lifetime.ApplicationStopped.Register(() => TraceManager.Stop()); app.UseTracing(serviceName); // The name of this side can be customized return app; }}Copy the code
Exceptionless helper classes
/// <summary>/// log extension class ///</summary>
public static class LogHelper
{
/// <summary>/// Record Info logs ///</summary>
/// <param name="logger"></param>
/// <param name="message"></param>public static void InformationToException(this ILogger logger, string message) { var tranceId = Trace.Current? .CurrentSpan.TraceId.ToString("x16"); Logger. LogInformation($@"tranceId={tranceId}, log body: {message}"); } / / /<summary>Record Debug logs ///</summary>
/// <param name="logger"></param>
/// <param name="message"></param>public static void DebugToException(this ILogger logger, string message) { var tranceId = Trace.Current? .CurrentSpan.TraceId.ToString("x16"); Logger. LogDebug($@"tranceId={tranceId}, log body: {message}"); } / / /<summary>// record error logs ///</summary>
/// <param name="logger"></param>
/// <param name="message"></param>public static void ErrorToException(this ILogger logger, string message) { var tranceId = Trace.Current? .CurrentSpan.TraceId.ToString("x16"); Logger. LogError($@"tranceId={tranceId}, log body: {message}"); } / / /<summary>/// record trace logs ///</summary>
/// <param name="logger"></param>
/// <param name="message"></param>public static void TraceToException(this ILogger logger, string message) { var tranceId = Trace.Current? .CurrentSpan.TraceId.ToString("x16"); Logger. LogTrace($@"tranceId={tranceId}, log body: {message}"); } / / /<summary>/// Record warning logs ///</summary>
/// <param name="logger"></param>
/// <param name="message"></param>public static void WarningToException(this ILogger logger, string message) { var tranceId = Trace.Current? .CurrentSpan.TraceId.ToString("x16"); Logger. LogWarning($@"tranceId={tranceId}, log body: {message}"); }}Copy the code
Next, create projects such as SimpleZipkin, WebApi, And OrderApi (this article will only create one because of its consistent structure), first referencing Exceplesstion
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostContext, configLogging) =>
{
configLogging.AddConfiguration(hostContext.Configuration.GetSection("Logging"));
configLogging.AddConsole();
configLogging.AddDebug();
configLogging.AddExceptionless();
ExceptionlessClient.Default.Configuration.SetDefaultMinLogLevel(Exceptionless.Logging.LogLevel.Debug);
configLogging.SetMinimumLevel(LogLevel.Debug);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>(a); }); }Copy the code
Next, introduce ZipKin in Startup
Public void ConfigureServices(IServiceCollection Services) {// Inject Rpc //AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); //services.AddGrpcClient<HelloServer.HelloServerClient>(p, o = > / / {/ / o.A ddress = new Uri (" http://127.0.0.1:3848 "); / /}); //.AddHttpMessageHandler(provider => TracingHandler.WithoutInnerHandler("RpcService")); services.AddControllers(); services.AddZipKin(); services.AddSingleton<IDiagnosticSource, HttpDiagnosticSourceDemo>(); services.AddHttpClient("webApi", client => { client.BaseAddress = new Uri($"http://localhost:5001"); }); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "SimpleZipKin", Version = "v1" }); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory, IHostApplicationLifetime lifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SimpleZipkin v1")); } Configuration.GetSection("ExceptionLess").Bind(ExceptionlessClient.Default.Configuration); ExceptionlessClient.Default.Configuration.SetDefaultMinLogLevel(Exceptionless.Logging.LogLevel.Debug); App. UseZipKin (lifetime, loggerFactory, "SimpleZip", "http://127.0.0.1:9411"); //SimpleZip changed to the corresponding app name, 127.0.0.1 address changed to its own Zipkin address app.userouting (); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }Copy the code
Next, create the corresponding Controller
[Route("/api/Home")]
public class HomeController : Controller
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger _logger;
/// <summary>/// constructor //</summary>
/// <param name="httpClientFactory"></param>
/// <param name="logger"></param>
public HomeController(IHttpClientFactory httpClientFactory, ILogger<HomeController> logger)
{
_httpClientFactory = httpClientFactory;
//_helloServerClient = helloServerClient;
_logger = logger;
}
[HttpGet("GetZipKin")]
public async Task<string>GetZipKin () {_logger. InformationToException ($@ "here is a SimpleZipKinApi"); var httpClient = _httpClientFactory.CreateClient("webApi"); var httpResult = await httpClient.GetAsync($"api/order/getorder"); var result = await httpResult.Content.ReadAsStringAsync(); return result; }}Copy the code
Finally, add the corresponding Exceplesstionless configuration to appSettings. json
"ExceptionLess" : {" ApiKey ":" * * * * * * * * * * * * * * * * * * * * * * * * * * * * ", "ServerUrl" : "http://127.0.0.1:5000"}Copy the code
OrderApi and WebApi do the same, modifying the corresponding request link information
The HttpDiagnosticListener class DiagnosticSource is provided by the Runtime layer. It allows the application layer to interact with system integration, event logging, and performance counters. DiagnosticSource: docs.microsoft.com/zh-cn/dotne… About DiagnosticSource design reference between yi read original bosses article: www.cnblogs.com/wucy/p/1353…
public class HttpDiagnosticSourceDemo : IDiagnosticSourceDemo
{
public string DiagnosticName => "HttpDiagnosticSourceDemo";
private ClientTrace _clientTrace;
private readonly IInjector<HttpHeaders> _injector = Propagations.B3String.Injector<HttpHeaders>((carrier, key, value) => carrier.Add(key, value)); [DiagnosticName("System.Net.Http.Request")] public void HttpRequest(HttpRequestMessage request) { _clientTrace = new ClientTrace("simpleZipKin", request.Method.Method); if (_clientTrace.Trace ! = null) { _injector.Inject(_clientTrace.Trace.CurrentSpan, request.Headers); } } [DiagnosticName("System.Net.Http.Response")] public void HttpResponse(HttpResponseMessage response) { if (_clientTrace.Trace ! = null) { _clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_PATH, response.RequestMessage.RequestUri.LocalPath)); _clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_METHOD, response.RequestMessage.Method.Method)); _clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_HOST, response.RequestMessage.RequestUri.Host)); if (! response.IsSuccessStatusCode) { _clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_STATUS_CODE, ((int)response.StatusCode).ToString())); } } } [DiagnosticName("System.Net.Http.Exception")] public void HttpException(HttpRequestMessage request, Exception exception) { } }Copy the code
Interface information of IDiagnosticSourceDemo is as follows:
public interface IDiagnosticSourceDemo { string DiagnosticName { get; }}Copy the code
HttpDiagnosticSourceObserver method is as follows:
public class HttpDiagnosticSourceObserver : IObserver<DiagnosticListener>
{
private IEnumerable<IDiagnosticSourceDemo> _diagnosticSourceDemo;
public HttpDiagnosticSourceObserver (IEnumerable<IDiagnosticSourceDemo>diagnosticSourceDemo) { _diagnosticSourceDemo= diagnosticSourceDemo; } public void OnCompleted() { } public void OnError(Exception error) { } public void OnNext(DiagnosticListener listener) { var diagnosticSource= _diagnosticSourceDemo.FirstOrDefault(i => i.DiagnosticName == listener.Name); if (traceDiagnostic ! = null) {/ / subscription listener. SubscribeWithAdapter (diagnosticSource); }}}Copy the code
The final result of Zipkin is as follows:
Exceplesstion The log information is
The corresponding machine can also be located through the environment information set by the exception record
reference
DiagnosticSource Official introduction: docs.microsoft.com/zh-cn/dotne… DiagnosticSource reference links: www.cnblogs.com/wucy/p/1353… Sudonull.com/post/3671-U… Andrewlock.net/logging-usi… Docker deployment Zipkin:www.cnblogs.com/binz/p/1265… Docekr deployment exceptionless:www.cnblogs.com/edisonchou/…
Welcome to correct me if I am not very clear or make mistakes
If you like, you might as well point a thumbs-up collection 🙂Copy the code