AspNet Core中利用Seq组件展示结构化日志功能

目录

在一次.Net Core小项目的开发中,掌握的不够深入,对日志记录并没有好好利用,以至于一出现异常问题,都得跑动服务器上查看,那时一度怀疑自己肯定没学好,不然这一块日志不可能需要自己扒服务器日志来查看,果然,很多东西没掌握,至此,花点时间看了下日志的相关操作。利用日志服务来查看日志数据。

本文地址:https://www.cnblogs.com/CKExp/p/9246788.html

本文Demo的地址:https://gitee.com/530521314/LogPanel.git

 

日志记录的原则

日志是为了方便我们观察应用程序是否正常运行,也是当运行不正常是,能够快速找到出现问题,定位问题的方式。

有几条日志记录原则是我们大多都得遵循的:

  1. 日志记录清晰,内容得让我们能够知道应用程序运行正常或运行不正常下能够有关键信息指明哪里出问题了。

  2. 不要过度记录,一些场景下我们需要预判是否需要记录日志信息,我们能够控制日志记录的数量,同样,日志内容,要精简记录,无关紧要的文字,废话等无需加入。

  3. 控制日志记录级别,或许在一个日志级别下记录了很多信息,可是其中的部分信息才是最为重要的,当调高一个记录级别,这部分最为重要的信息就展示出来了,那就直接调高级别吧。

  4. 隐私保护,日志虽然是让开发运维人员看到,可是我们也不能所有信息都记录下来,用户的隐私信息我们得保护好。

 

内置日志组件

Asp.Net Core中内置了日志组件,功能也很强大,首先来个简单示例:

直接使用内置日志功能,无需在startup中加入另外的代码。

private readonly ILogger<HomeController> _logger;


public HomeController(ILogger<HomeController> logger)
{
    _logger = logger;
}


public IActionResult Index()
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
    _logger.LogDebug($"开始监控整个网站的日志信息_{DateTime.Now}");
    //_logger.LogError($"开始监控整个网站的日志信息Error_{DateTime.Now}");
    return View();
}

在调试框下看到输出的日志信息。 201153475_a06ef302-7aaa-4c5f-8c0b-e9126403f53d 开始控制日志记录级别:

在StartUp下,对内置日志组件做一些处理,在配置文件中将日志记录的级别提高到Information级别:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //默认日志功能
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();
    //loggerFactory.AddSerilog();


    if (env.IsDevelopment())
    {
        app.UseBrowserLink();
        app.UseDeveloperExceptionPage();//更多错误信息的开发者异常详情页
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }


    app.UseStaticFiles();


    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

appsetting.json文件中,将LogLevel的默认级别设为Information,这样一来在之前的示例中_logger.LogDebug将失去作用。

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Information",
      "System": "Error",
      "Microsoft": "Error"
    }
  }
}

对于异常来讲,出现异常,日志肯定得记录下来,不然出现异常,直接抛出,那还要日志有什么用呢。

public IActionResult About()
{
    try
    {
        _logger.LogInformation($"业务逻辑执行前,运行正常");
        //执行其它业务逻辑
        int textInt = 5;
        int result = textInt / 0;
        //执行其它业务逻辑
        _logger.LogInformation($"业务逻辑执行后,运行正常");
    }
    catch (Exception ex)
    {
        _logger.LogError($"出现异常,异常信息{ex.Message}");
        throw;
    }


    return View();
}

异常信息加载出来。 201154792_ec4ceb27-683e-4d7f-90ab-59e405f3a1d5 出现异常,拦截异常,记录日志信息,不单单在这里,在Filter或是几个中间件中,都能够记录下来,此处不在多讨论。

 

第三方日志组件

尽管微软已经内置了日志记录组件,但是一些第三方组件或许是我们中意的,在性能上、记录方式上、操作上等等,如Log4Net、Logger、NLog和Serilog 等。结构化的日志使得我们阅读日志也更加轻松点。

在此,我利用Serilog组件来替换内置的日志组件,添加相应的依赖包(Serilog.Extensions.Logging和Serilog.Sinks.Literate)。

然后来更新Startup类,令Serilog组件开始发挥它的作用。

尽管可以在Program中加入Serilog组件,在这里,我将只在StartUp类中更改代码,为了代码统一存放,对于组件加入有三种方式:

方式一:在StartUp初始化完毕加入进来,利用Log.Logger的全局性,在整个应用程序中发挥作用,而不需要在各处声明注入。

其中的 .WriteTo.Seq(“http://localhost:5341”)是为了后面加入Seq组件而提前写入的,可以忽略。

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
    //方式一:配置Seq服务器的地址(5341端口为默认地址)
    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Debug()
        .MinimumLevel.Override("LoggingService", Serilog.Events.LogEventLevel.Debug)
        .Enrich.FromLogContext()
        .WriteTo.Seq("http://localhost:5341")
        .WriteTo.LiterateConsole()
        .CreateLogger();
}

在应用程序中的使用方式是直接使用Log.Logger写入即可,如在HomeController的Contact方法中,利用且只能利用这种方式写入,当想用最开始那种方式注入时,在LoggerFactory中并没有SerilogProvider,因为我们并没有把Serilog加入到LoggerFactory中,这也使得Serilog和内置日志出现共存的局面,并可以通过两种方式使用日志功能。

public IActionResult Contact()
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
    Log.Debug("Log.Debug:Serilog Start Send Message");
    Log.Information("Log.Information:Serilog Start send Message");
    Log.Logger.Debug("Log.Logger:Serilog Start Send Message");
    Log.Logger.Information("Log.Logger.Information:Serilog Start Send Message");
    return View();
}

利用这种方式下的日志输出。
201155844_2c627fe9-bab3-4b95-b79f-38b6a8f66ce3

 

方式二:在ConfigureServices中将Serilog加入到LoggerFactory中,通过统一的扩展方式加入进来,利用Action委托指定Serilog组件,其中允许我们对Serilog进行相关设置。

public void ConfigureServices(IServiceCollection services)
{
    //方式二
    var serilog = new LoggerConfiguration()
            .MinimumLevel.Information()
            .MinimumLevel.Override("LoggingService", Serilog.Events.LogEventLevel.Debug)
            .Enrich.FromLogContext()
            .WriteTo.Seq("http://localhost:5341")
            .WriteTo.LiterateConsole();


    services.AddLogging(loggerBuilder =>
    {
        loggerBuilder.AddSerilog(serilog.CreateLogger());
    });
    services.AddMvc();
}

这种方式下,我们需要将Logger通过依赖注入的形式加入进来,先来看下是否加入到了LoggerFactory中 201156849_4be05513-ac89-4ef7-a298-573bc2639a0b 加入进来了,并完成了日志的输出。201158036_6874c850-1fbe-45c1-9b6b-5a36da46daa5

 

方式三:和方式二一样都是加入到LoggerFactory中,但是直接利用的是Serilog提供的扩展方法AddSerilog加入进来。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //默认日志功能
    //loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    //loggerFactory.AddDebug();


    #region Serilog
    //方式三
    //默认配置
    //loggerFactory.AddSerilog();


    //配置Seq服务器的地址(5341端口为默认地址)
    //var serilog = new LoggerConfiguration()
    //    .MinimumLevel.Information()
    //    .MinimumLevel.Override("LoggingService", Serilog.Events.LogEventLevel.Debug)
    //    .Enrich.FromLogContext()
    //    .WriteTo.Seq("http://localhost:5341")
    //    .WriteTo.LiterateConsole();
    //loggerFactory.AddSerilog(serilog.CreateLogger());
    #endregion


    if (env.IsDevelopment())
    {
        app.UseBrowserLink();
        app.UseDeveloperExceptionPage();//更多错误信息的开发者异常详情页
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }


    app.UseStaticFiles();


    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Serilog组件同源加入进来了,输出结果就不展示了和方式二一样的。 201159048_58821c51-df70-42e3-8f05-c6598834fdd6 具体Serilog的非常优秀的功能就不介绍了,请看Serilog的官方介绍 https://github.com/serilog

 

Seq组件

到了本文的主题——Seq组件,通过网页UI的形式将日志展现出来,内容更加多样化,并赋予了更多功能日志搜索。

首先,安装Seq组件,Seq下载地址:https://getseq.net/Download

本地开发情形下是免费使用的,如果需要在生产环境中使用,需要商业许可(你懂的,money).在目前的版本中,4.2是只能够在windows下跑,也就是说我们如果是在windows下开发,在测试时可以借助这个方便的查看日志信息,按照给定的安装步骤完成安装。

在应用程序中,我们通过Nuget管理Serilog.Sinks.Seq包,该组件隶属于Serilog旗下。Seq默认的端口是利用5341端口,如果我们想要使用其它端口,我们可以更改在应用程序中的地址

以上一节的方式三为例,在其中加入一行.WriteTo.Seq(“http://localhost:5341”)便可指定Seq服务器地址。

var serilog = new LoggerConfiguration()
        .MinimumLevel.Information()
        .MinimumLevel.Override("LoggingService", Serilog.Events.LogEventLevel.Debug)
        .Enrich.FromLogContext()
        .WriteTo.Seq("http://localhost:5341")
        .WriteTo.LiterateConsole();
    loggerFactory.AddSerilog(serilog.CreateLogger());

在本地windows运行起来看下,启动Seq服务器,当然,你这里或许需要配置一些账号密码之类的,都是小问题。 201200279_594560ad-e1ff-42dd-8487-908a31973175 启动应用程序,并进入那些写了日志的方法中,日志信息已经进来了,我们可以查看这些日志信息,并通过日志搜索功能搜索。

201201410_5de9e143-9ca6-4001-b752-ea305083b1c0 201202702_3913f195-4169-466f-b724-895bfb1407e2 在这里要说明一下,同时也在Seq组件的网站中也写明了。使用这些方式都可以直接使用Seq组件,第三方组件也不是必须的,直接利用Asp.Net Core的内置日志组件一样可以使用Seq组件。

201204543_0e2650ae-7d80-4125-9a13-12d6826c8547

可是,Linux下可以吗? 假如我的开发环境在Linux下岂不是不行。现在有一个预览版5.0的,在Docker Hub中有Seq的镜像,虽然是预览版,可是我还是想试试。

Docker Hub中Seq镜像地址:https://docs.getseq.net/v5.0/docs/docker

现在,开始Linux中弄起来。

首先完成镜像的爬取,之后会自动启动该镜像内部的服务。我们直接输入地址访问即可。默认地址ip:5341端口

docker run -e ACCEPT_EULA=Y -p 5341:80 datalust/seq:latest

页面有了,Linux下看来也是可以的了,把应用程序通过Jenkins上都发布成功了,可是访问出现问题,Docker外部端口无法映射到内部端口,糟心,可是应该不是大问题,就此也就不弄日志信息的展示了。 201205582_9466e813-911b-4eee-ae8b-fc2e9c4a173b 至此,日志相关的一系列知识简单带过,关键是Seq那个可视化日志界面,很nice。

本文地址:https://www.cnblogs.com/CKExp/p/9246788.html

本文Demo的地址:https://gitee.com/530521314/LogPanel.git

 

2018-6-30,望技术有成后能回来看见自己的脚步