zoukankan      html  css  js  c++  java
  • 玩转ASP.NET Core中的日志组件

    简介

    日志组件,作为程序员使用频率最高的组件,给程序员开发调试程序提供了必要的信息。ASP.NET Core中内置了一个通用日志接口ILogger,并实现了多种内置的日志提供器,例如

    • Console
    • Debug
    • EventSource
    • EventLog
    • TraceSource
    • Azure App Service

    除了内置的日志提供器,ASP.NET Core还支持了多种第三方日志工具,例如

    开发人员在ASP.Net Core中可以自由指定日志提供器,并将日志发送到指定的位置。
    本篇博文中,我们将由浅入深的介绍ASP.Net Core中通用日志接口,最后我们将实现一些自定义的日志提供器(Log Provider)。

    使用系统提供的内置日志提供器

    日志级别(Log Level)

    ASP.NET Core中提供了6种日志级别,分别是Trace, Debug, Information, Warning, Error, Critical。以下是他们的具体使用场景

    日志级别 常用场景
    Trace 记录一些对程序员调试问题有帮助的信息,
    其中可能包含一些敏感信息, 所以应该避免在
    生产环境中启用Trace日志
    Debug 记录一些在开发和调试阶段有用的短时变
    量(Short-term usefulness), 所以除非为了临时排除生产环境的
    故障,开发人员应该尽量避免在生产环境中启用Debug日志
    Information 记录应用程序的一些流程, 例如,记录当前api请求的url
    Warning 记录应用程序中发生的不正常或者未预期的事件信息。
    这些信息中可能包含错误消息或者错误产生的条件, 例如, 文件未找到
    Error 记录应用程序中某个操作产生的错误和异常信息。
    Critical 记录一些需要立刻修复的问题。例如数据丢失,磁盘空间不足。

    如何创建日志

    为了创建一个日志,我们首先需要通过依赖注入获取一个实现ILogger<日志类别>的泛型接口对象。

    已以下代码为例, 在ValuesController的构造函数中,我们注入了一个ILogger的日志记录器

        [Route("api/[controller]")]
        [ApiController]
        public class ValuesController : ControllerBase
        {
            private readonly ILogger<ValuesController> _logger = null;
    
            public ValuesController(ILogger<ValuesController> logger)
            {
                _logger = logger;
            }
    
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                _logger.LogInformation("[Test Log]Getting items.");
                return new string[] { "value1", "value2" };
            }
    
    
        }
    

    然后我们使用ILogger接口提供的LogInformation方法添加了一个Information类型日志"[Test Log]Getting items"。

    注:ILogger为了提供了6个可用的输出日志方法,分别对应了6个不同的日志级别

    • LogTrace
    • LogDebug
    • LogInformation
    • LogWarning
    • LogError
    • LogCritical!

    下面我们使用Kestral服务器启动项目

    项目产生的日志如下,我们手动输出的日志出现在控制台中。

    日志配置

    可能针对以上的代码,有些同学可能有疑问,我们没有在Startup.cs中注入任何日志提供器,但是日志却正常产生了。这是由于Program.cs中默认使用WebHost.CreateDefaultBuilder方法添加了2个日志提供器。

    默认日志提供器

    当创建一个新的ASP.NET Core WebApi项目,我们通常在Program.cs中看到以下代码。

        public class Program
        {
            public static void Main(string[] args)
            {
                CreateWebHostBuilder(args).Build().Run();
            }
    
            public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                    .UseStartup<Startup>();
        }
    

    下面我们看一下WebHost.CreateDefaultBuilder的源代码

        public static IWebHostBuilder CreateDefaultBuilder(string[] args)
        {
            var builder = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    var env = hostingContext.HostingEnvironment;
    
                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                          .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
    
                    if (env.IsDevelopment())
                    {
                        var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
                        if (appAssembly != null)
                        {
                            config.AddUserSecrets(appAssembly, optional: true);
                        }
                    }
    
                    config.AddEnvironmentVariables();
    
                    if (args != null)
                    {
                        config.AddCommandLine(args);
                    }
                })
                .ConfigureLogging((hostingContext, logging) =>
                {
                    logging.UseConfiguration(hostingContext.Configuration.GetSection("Logging"));
                    logging.AddConsole();
                    logging.AddDebug();
                })
                .UseIISIntegration()
                .UseDefaultServiceProvider((context, options) =>
                {
                    options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
                })
                .ConfigureServices(services =>
                {
                    services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
                });
    
            return builder;
        }
    

    你会发现代码中通过logging.AddConsolelogging.AddDebug默认配置了Console和Debug类型的日志提供器,这也就是为什么我们没有注入任何日志提供器,日志却能正常产生了。

    手动添加日志提供器

    看了以上代码后,你应该可以也清楚了如何自己添加其他内置的日志提供器。我们只需要在Program.cs中使用ConfigureLogging方法就可以配置我们需要的日志提供器了。

    例:

        public class Program
        {
            public static void Main(string[] args)
            {
                CreateWebHostBuilder(args).Build().Run();
            }
    
            public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                .ConfigureLogging((hostingContext, logging) =>
                {
                    logging.AddConsole();
                    logging.AddDebug();
                    logging.AddEventSourceLogger();
                })
                .UseStartup<Startup>();
        }
    

    除了在Program.cs添加日志提供器之外,我们还可以在Startup.cs中添加日志提供器。
    在Startup.cs中,我们可以为Configure方法添加第三个参数ILoggerFactory loggerFactory, 并使用该参数添加日志提供器。

    例:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            loggerFactory.AddConsole();
            loggerFactory.AddDebug();
            
            app.UseMvc();
        }
    

    配置文件及日志级别过滤

    ASP.NET Core默认会从appSetting.json中的Logging属性读取日志的配置(当然你也可以从其他文件中读取配置),这里设置了不同的日志提供器产生的最低的日志级别,配置样例如下。

    {
      "Logging": {
        "Debug": {
          "LogLevel": {
            "Default": "Information"
          }
        },
        "Console": {
          "LogLevel": {
            "Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning",
            "Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug",
            "Microsoft.AspNetCore.Mvc.Razor": "Error",
            "Default": "Trace"
          }
        },
        "LogLevel": {
          "Default": "Debug"
        }
      }
    }
    

    以上代码中的Debug表示Debug日志提供器, Console表示Console日志提供器, 最后一个LogLevel表示其他日志提供器通用。
    Debug中的Default设置为Information, 即Debug中产生的日志最低级别是Information, 低于Information级别的日志不会输出。Console中的配置同理。

    自定义日志组件

    在学习了以上基础知识之后,我们应该对内置的日志提供器有了简单的认识。下面我们尝试自定义2个日志提供器。
    在ASP.NET Core中,我们可以通过实现ILogger, ILoggerProvider2个接口来创建我们自己的日志提供器。

    编写一个自定义样式的控制台日志组件

    这里我们希望添加一个在控制台中输出的日志,但是和内置Console类型日志的区别是我们为不同的日志类型设置了不同的颜色。

    首先我们创建一个新的Api项目ColoredConsoleLoggerSample

    然后我们创建一个针对不同日志级别的字体颜色配置类ColoredConsoleLoggerConfiguration, 代码如下

        public class ColoredConsoleLoggerConfiguration
        {
            public LogLevel LogLevel { get; set; } = LogLevel.Warning;
            public ConsoleColor Color { get; set; } = ConsoleColor.Yellow;
        }
    

    这个类中定义了针对不同日志类型设置不同的字体颜色。

    然后我们创建一个日志类ColoredConsoleLogger, 它实现了ILogger接口,代码如下

        public class ColoredConsoleLogger : ILogger
        {
            private readonly string _name;
            private readonly ColoredConsoleLoggerConfiguration _config;
    
            public ColoredConsoleLogger(string name, ColoredConsoleLoggerConfiguration config)
            {
                _name = name;
                _config = config;
            }
    
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }
    
            public bool IsEnabled(LogLevel logLevel)
            {
                return logLevel == _config.LogLevel;
            }
    
            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                if (!IsEnabled(logLevel))
                {
                    return;
                }
                
                var color = Console.ForegroundColor;
                Console.ForegroundColor = _config.Color;
                Console.WriteLine($"{logLevel.ToString()} - {_name} - {formatter(state, exception)}");
                Console.ForegroundColor = color;
            }
        }
    

    代码解释

    • ColoredConsoleLogger仅针对一种日志级别
    • 只要当前产生的日志级别和ColoredConsoleLogger中定义的日志级别一样时,日志才会输出,这里我们是用IsEnable方法判断的
    • Log是ILogger接口中定义的方法,我们就是在这个方法中输出日志的
    • 这里我们在输入日志前记录下了当前控制台的原始字体颜色, 当输出日志完成之后,我们将字体颜色恢复成了原来的颜色

    然后我们添加一个Logger提供器类ColoredConsoleLoggerProvider,代码如下

        public class ColoredConsoleLoggerProvider : ILoggerProvider
        {
            private readonly ColoredConsoleLoggerConfiguration _config;
    
            public ColoredConsoleLoggerProvider(ColoredConsoleLoggerConfiguration config)
            {
                _config = config;
            }
    
            public ILogger CreateLogger(string categoryName)
            {
                return new ColoredConsoleLogger(categoryName, _config);
            }
    
            public void Dispose()
            {
    
            }
        }
    

    代码解释

    • ColoredConsoleLoggerProvider仅针对一种日志级别
    • CreateLoggerILoggerProvider接口中定义的方法,它是用来返回一个日志生成器的

    最后我们修改Startup.cs中的Configure方法, 代码如下

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            
            loggerFactory.AddProvider(new ColoredConsoleLoggerProvider(new ColoredConsoleLoggerConfiguration
            {
                LogLevel = LogLevel.Information,
                Color = ConsoleColor.Blue
            }));
            loggerFactory.AddProvider(new ColoredConsoleLoggerProvider(new ColoredConsoleLoggerConfiguration
            {
                LogLevel = LogLevel.Debug,
                Color = ConsoleColor.Gray
            }));
    
            app.UseMvc();
        }
    

    这里我们添加了2个日志日志提供器,分别是针对Information级别日志和Debug级别日志的

    最终效果

    我们的日志根据我们预设的字体颜色正确的显示了出来

    编写一个与SignalR集成的实时日志组件

    下面我们再来自定义一个与SignalR集成的日志提供器,我们希望产生的日志通过一个SignalR服务器推送到一个网页中。

    首先我们创建一个ASP.NET Core WebApi项目,命名为SignalrServer, 创建之后,我们右键项目属性,修改App Url为http://localhost:5000

    然后我们创建一个LogHub类,它集成自Hub类,代码如下

        public class LogHub : Hub
        {
            public async Task WriteLog(Log log)
            {
                await Clients.All.SendAsync("showLog", log);
            }
        }
    
        public class Log
        {
            public LogLevel Level { get; set; }
    
            public string Content { get; set; }
        }
    

    代码解释

    • 这里我们创建了一个写日志的方法,它会把日志推送到所有连接到SignalR服务器的客户端,并调用客户端的showLog方法来展示推送的日志信息。

    然后我们修改Startup.cs文件,代码如下

        public class Startup
        {
            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.AddCors();
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
                
                services.AddSignalR();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                app.UseCors(p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());
                app.UseSignalR(routes =>
                {
                    routes.MapHub<LogHub>("/logHub");
                });
    
                
    
                app.UseMvc();
            }
        }
    

    代码解释

    • 我们通过service.AddSignalR注册了SignalR服务
    • 我们通过app.UserSignalR方法注册一个logHub
    • 这里我们启用了CORS, 因为需要提供跨域访问

    然后我们创建一个另外一个ASP.NET Core WebApi项目, SignalRLoggerSample

    项目创建成功之后,我们右键点击项目属性,并设置App URL为http://localhost:5001

    然后我们使用Package Console Manager, 安装Microsoft.AspNetCore.SignalR.Client

    PM> install-package Microsoft.AspNetCore.SignalR.Client

    为了创建一个SignalR日志提供器, 我们分别创建一个SignalRLogger类和一个SignalRLoggerProvider类, 代码如下

    SignalRLogger.cs

        public class SignalRLogger : ILogger
        {
            HubConnection connection;
    
            public SignalRLogger()
            {
                connection = new HubConnectionBuilder()
                 .WithUrl("http://localhost:5000/LogHub")
                 .Build();
            }
    
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }
    
            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }
    
            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                if (!IsEnabled(logLevel))
                {
                    return;
                }
    
                connection.StartAsync().Wait();
                var task = connection.SendAsync("writeLog", new { Level = logLevel, Content = formatter(state, exception) });
                task.Wait();
            }
        }
    

    SignalRLoggerProvider.cs

        public class SignalRLoggerProvider : ILoggerProvider
        {
            public SignalRLoggerProvider()
            {
            }
    
            public ILogger CreateLogger(string categoryName)
            {
                return new SignalRLogger();
            }
    
            public void Dispose()
            {
    
            }
        }
    

    代码解释

    • 这里使用HubConnectionBuilder创建了一个SignalR连接
    • 连接启动成功之后,我们使用connection.SendAsync方法,将当前产生的日志信息发送到SignalR服务器

    添加完成之后,我们在wwwroot文件夹中创建一个index.html, 在其中引入jquery和signalr的js库,并指定连接的signalR服务器是http://localhost:5000/logHub

    index.html

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script src="jquery-1.10.2.min.js"></script>
        <script src="signalr.min.js"></script>
    </head>
    <body>
        <h1>Logs</h1>
        <div id="content" style="border:1px solid #0094ff">
    
        </div>
        <script type="text/javascript">
            var levels = [
                { level: 0, name: 'Trace', backgroundColor: 'gray' },
                { level: 1, name: 'Debug', backgroundColor: 'green' },
                { level: 2, name: 'Information', backgroundColor: 'blue' },
                { level: 3, name: 'Warning', backgroundColor: 'yellow' },
                { level: 4, name: 'Error', backgroundColor: 'orange' },
                { level: 5, name: 'Critical', backgroundColor: 'red' },
            ];
    
            function getLevelName(level) {
                return levels.find(function (o) {
                    return o.level == level;
                }).name;
            }
    
            function getLevelColor(level) {
                return levels.find(function (o) {
                    return o.level == level;
                }).backgroundColor;
            }
    
            var connection = new signalR.HubConnectionBuilder().withUrl("http://localhost:5000/logHub").build();
    
            connection.on("showLog", function (message) {
                var div = "<div style='background-color:" + getLevelColor(message.level)+"'>[" + getLevelName(message.level) + "]:" + message.content + "</div>";
                $("#content").append(div);
            });
    
            connection.start().catch(function (err) {
                return console.error(err.toString());
            });
        </script>
    </body>
    </html>
    

    然后我们修改ValuesController文件,代码如下

        [Route("api/[controller]")]
        [ApiController]
        public class ValuesController : ControllerBase
        {
            private ILogger<ValuesController> _logger = null;
    
            public ValuesController(ILogger<ValuesController> logger)
            {
                _logger = logger;
            }
    
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                _logger.LogTrace("User call the /api/values api");
                _logger.LogDebug("User call the /api/values api");
                _logger.LogInformation("User call the /api/values api");
                _logger.LogWarning("User call the /api/values api");
                _logger.LogError("User call the /api/values api");
                _logger.LogCritical("User call the /api/values api");
                return new string[] { "value1", "value2" };
            }
        }
    

    代码解释

    • 我们创建了一个ValueController类的日志
    • 当用户请求/api/values时,我们输出了6个不同级别的日志

    最后我们修改Startup.cs中的Configure方法,使用我们之前介绍的方法,将SignalRLoggerProvider添加到管道中

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseStaticFiles();
    
            loggerFactory.AddProvider(new SignalRLoggerProvider());
    
            app.UseMvc();
    
    
        }
    

    最终效果

    最后我们按照顺序,先启动SignalRServer, 再启动SignalRLoggerSample, 效果如下

    本篇源代码

    参考文献

  • 相关阅读:
    指针变量的值和指针变量地址
    定时备份docker容器中mysql的数据
    sql server表外键查询
    Go语言strings包基本操作
    centos安装字体库处理中文乱码
    codeforces 1610D
    汽车配件生产加工企业管理软件出售生产加工刹车盘、刹车鼓、刹车蹄、刹车片等企业通用
    关于document.onmousemove报错Invalid or unexpected token问题
    SilverLight支持的DataTable可以直接绑定
    我的工作我的状态
  • 原文地址:https://www.cnblogs.com/lwqlun/p/9683482.html
Copyright © 2011-2022 走看看