NetCore微服务实战体系:日志管理
阅读原文时间:2022年03月31日阅读:1

一. 起始

进入NetCore时代,日志的使用有了很大的变化,因为跨平台以及虚拟化技术的使用,日志不能够再像Framework的方式直接记录在文本,文本其实也可以,但是日志的管理以及查看都不太方便。Linux都是指令化,对于开发来说并不友好。

后来Exceptionless进入了视线,选择这个组件的原因很简单,可视化、数据统计、实时性以及对于.net开发人员相当友好。在当前网络上,有很多关于Exceptionless的使用文档,但是个人觉得并不友好,在使用上并不方便。

在微软体系中,Microsoft.Extesions.Logging是官方日志框架,它允许我们自己去实现ILoggerProvider / ILogging,那么根据这两者得特性,由此开发了组件 Overt.Core.Logging

二. 组件Overt.Core.Logging

https://github.com/overtly/logging

三. 简单介绍使用

1. Nuget包引用

  • Nuget版本:V 1.0.4.1

  • 框架支持: NetStandard 2.0

Install-Package Overt.Core.Logging -Version 1.0.4.1

2. 配置信息

  • NetCore配置案例 appsettings.json

{

  "Exceptionless": {
    "ServerUrl": "http://exless.g.lan", // 私有化域名(Exceptionless私有化的文档网上很多)
    "ApiKey": "ugGFmeeriaj1BG12itWWURfiJjwqiyi2o71ll4mm", // 项目Key 如何创建网上文档也很多
    "Tags": "Local" // 日志所属项目环境
  }

}

3. 具体使用

(1)服务注入

  • Web服务使用

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)  
    {// Logging  
        app.AddExlessLogging();
    
    }
  • IHost 普通服务使用 

    static void Main(string\[\] args)  
    {  
        var host = new HostBuilder()  
           .UseConsoleLifetime() //使用控制台生命周期  
           .ConfigureAppConfiguration((context, configuration) =>  
           {  
               configuration  
               .AddJsonFile("appsettings.json", optional: true)  
               .AddEnvironmentVariables();  
           })  
           .ConfigureLogging(logger =>  
           {
       })  
       .ConfigureServices(ConfigureServices)  
       .Build();
    
    **host.Services.AddExlessLogging();**  
    ThreadPool.SetMinThreads(100, 100);
    
    AppDomain.CurrentDomain.UnhandledException += (sender, e) =>  
    {  
        **var logFactory = host.Services.GetService<ILoggerFactory>();  
        var logger = logFactory.CreateLogger<Program>****();**  
        logger.LogError(e.ExceptionObject as Exception, $"UnhandledException");  
    };
    
    host.Run();  
    }

(2)日志使用

using Microsoft.Extensions.Logging;

private readonly ILogger _logger;

public Test(ILogger logger)
{
  _logger = logger;
}

_logger.LogInformation("你好");

4. 源码详解

(1)实现ILogger

using Exceptionless;
using Microsoft.Extensions.Logging;
using System;

namespace Overt.Core.Logging
{
///

/// Logger实现 ///
public class ExlessLogger : ILogger
{
private readonly string _categoryName;
public ExlessLogger(string categoryName)
{
_categoryName = categoryName;
}

    /// <summary>  
    ///  
    /// </summary>  
    /// <typeparam name="TState"></typeparam>  
    /// <param name="state"></param>  
    /// <returns></returns>  
    public IDisposable BeginScope<TState>(TState state)  
    {  
        return new NoopDisposable();  
    }

    /// <summary>  
    /// 是否可用  
    /// </summary>  
    /// <param name="logLevel"></param>  
    /// <returns></returns>  
    public bool IsEnabled(LogLevel logLevel)  
    {  
        return true;  
    }

    /// <summary>  
    /// 记录日志  
    /// </summary>  
    /// <typeparam name="TState"></typeparam>  
    /// <param name="logLevel"></param>  
    /// <param name="eventId"></param>  
    /// <param name="state"></param>  
    /// <param name="exception"></param>  
    /// <param name="formatter"></param>  
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)  
    {  
        try  
        {  
            var message = formatter(state, exception);  
            var source = $"{\_categoryName}";  
            var exlessLogLevel = Exceptionless.Logging.LogLevel.Trace;  
            switch (logLevel)  
            {  
                case LogLevel.Trace:  
                    exlessLogLevel = Exceptionless.Logging.LogLevel.Trace;  
                    break;  
                case LogLevel.Information:  
                    exlessLogLevel = Exceptionless.Logging.LogLevel.Info;  
                    break;  
                case LogLevel.Warning:  
                    exlessLogLevel = Exceptionless.Logging.LogLevel.Warn;  
                    break;  
                case LogLevel.Error:  
                    exlessLogLevel = Exceptionless.Logging.LogLevel.Error;  
                    break;  
                case LogLevel.Critical:  
                    exlessLogLevel = Exceptionless.Logging.LogLevel.Fatal;  
                    break;  
                default:  
                    exlessLogLevel = Exceptionless.Logging.LogLevel.Debug;  
                    break;  
            }  
            var eventBuilder = ExceptionlessClient.Default  
                           .CreateLog(message, exlessLogLevel)  
                           .SetSource(source)  
                           .SetException(exception);

            if (eventId != null)  
                eventBuilder.SetProperty("Event", $"{eventId.ToString()}");

            var serverAndPoint = LoggingUtility.GetAddressIP();  
            if (!string.IsNullOrEmpty(serverAndPoint))  
                eventBuilder.SetProperty("ServerEndPoint", serverAndPoint);

            eventBuilder.Submit();  
        }  
        catch { }  
    }

    private class NoopDisposable : IDisposable  
    {  
        public void Dispose()  
        {  
        }  
    }  
}  

}

(2)实现ILoggerProvider

using Microsoft.Extensions.Logging;

namespace Overt.Core.Logging
{
///

/// LoggerProvider ///
public class ExlessLoggerProvider : ILoggerProvider
{
/// /// Contructor ///
///
///
public ILogger CreateLogger(string categoryName)
{
return new ExlessLogger(categoryName);
}

    /// <summary>  
    ///  
    /// </summary>  
    public void Dispose()  
    {  
    }  
}  

}

(3)依赖注入

using Exceptionless;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using Overt.Core.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionExtensions
{

    #region ExLess  
    /// <summary>  
    /// 注入  
    /// </summary>  
    /// <param name="loggingBuilder"></param>  
    /// <param name="configFile"></param>  
    public static void AddExlessLogging(this IServiceProvider provider)  
    {  
        var configuration = provider.GetService<IConfiguration>();  
        var loggerFactory = provider.GetService<ILoggerFactory>();

        var client = ExceptionlessClient.Default;  
        client.InitExlessTags(configuration);  
        client.Configuration.ReadFromConfiguration(configuration);  
        client.Configuration.ReadFromEnvironmentalVariables();  
        client.Configuration.UseInMemoryStorage();  
        client.Startup();

        loggerFactory.AddProvider(new ExlessLoggerProvider());  
    }

    /// <summary>  
    /// 注入  
    /// </summary>  
    /// <param name="loggingBuilder"></param>  
    /// <param name="configFile"></param>  
    public static void AddExlessLogging(this IApplicationBuilder app)  
    {  
        var provider = app.ApplicationServices;  
        var configuration = provider.GetService<IConfiguration>();  
        var loggerFactory = provider.GetService<ILoggerFactory>();

        app.UseExceptionless(configuration);  
        var client = ExceptionlessClient.Default;  
        client.InitExlessTags(configuration);  
        client.Configuration.UseInMemoryStorage();

        loggerFactory.AddProvider(new ExlessLoggerProvider());  
    }

    /// <summary>  
    /// tags  
    /// </summary>  
    /// <param name="client"></param>  
    /// <param name="configuration"></param>  
    private static void InitExlessTags(this ExceptionlessClient client, IConfiguration configuration)  
    {  
        var tags = configuration?\["Exceptionless:Tags"\]?.Split(",", StringSplitOptions.RemoveEmptyEntries)?.ToList();  
        foreach (var tag in tags ?? new List<string>())  
        {  
            client.Configuration.DefaultTags.Add(tag);  
        }  
    }  
    #endregion  
}  

}

5. 最终效果

可实时查看日志信息