在.NET Core日志记录中使用Trace和TraceSource

目录

介绍

背景

使用代码

基本的

文件中配置

跟踪(Trace)和跟踪源(TraceSource)

内置跟踪侦听器

LoggerTraceListener

Harmony中的Trace,TraceSource和Logger提供程序

兴趣点

扫描二维码关注公众号,回复: 9359041 查看本文章

介绍

本文介绍如何在.NET Core的组件设计中使用TraceTraceSource。如果满足以下条件,这可能会很有用:

  1. 您已经为.NET Framework.NET Core / .NET Standard构建了组件,并且您希望保留TraceTraceSource
  2. 你有使用TraceTraceSource第三方组件。
  3. 您正在将复杂的.NET Framework应用程序迁移到.NET Core,并且现在您不想更改跟踪和日志记录设计。
  4. 您将保持跟踪和日志记录的分离和简单。

而目标读者是那些具有.NET Framework编程经验的程序员,此处讨论的知识是.NET Core 3.0.NET Framework 2.0的最新知识,而.NET Framework 4.8.NET Framework的最新主要版本。

背景

.NET Core中,默认的跟踪和日志记录已升级为ILogger <T>,并且预期通过.NET Core的依赖注入来实例化各个记录器对象。ILogger<T>可以与System.Diagnostics.TraceSource进行比较,附加的ILoggerProvider对象可以与System.Diagnostics.TraceListener进行比较

.NET Core编程的复杂业务的应用程序中,我发现有很少的文章是在.NET Core使用TraceTraceSource,几乎所有关于ILogger<T>文章和例子,我可以在Google上找到,它们描述了如何使用ILogger,其立即被注入到ProgramStartupController,而我一直在寻找在远离Program项目的组件/程序集中使用ILogger的例子或指南。

使用代码

这些代码示例包含多个项目,每个项目代表一个简单的场景和一个技术解决方案。

基本的

代码示例:ConsoleApp0

大概您已经阅读.NET CoreASP.NET Core中的日志记录。日志未包含在.NET Core运行时和启动逻辑中,也未包含在控制台应用程序的脚手架代码中。要使用日志记录,需要包Microsoft.Extensions.Logging

但是,仅此程序包不足以记录到控制台。而是使用Microsoft.Extensions.Logging.Console

并且此软件包包括Microsoft.Extensions.Logging

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;

namespace ConsoleApp0
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World! from console");

            ILogger logger;
            IFooService fooService;

            using (var serviceProvider = new ServiceCollection()
                .AddSingleton<IFooService, FooService>()
                .AddLogging(cfg =>
                {
                    cfg.AddConsole();
                })
                .BuildServiceProvider())
            {

                logger = serviceProvider.GetService<ILogger<Program>>();

                fooService = serviceProvider.GetService<IFooService>();
            }

            logger.LogInformation("logger information");
            logger.LogWarning("logger warning");

            fooService.DoWork();
        }
    }

    public interface IFooService
    {
        void DoWork();
    }

    public class FooService : IFooService
    {
        private readonly ILogger logger;

        public FooService(ILogger<FooService> logger)
        {
            this.logger = logger;
        }

        public void DoWork()
        {
            logger.LogInformation("Doing work.");
            logger.LogWarning("Something warning");
            logger.LogCritical("Something critical");
        }
    }
}

这是执行效果:

并且您可以通过重载AddConsole()来在代码中进行进一步的配置。

.AddLogging(cfg => { cfg.AddConsole(cfg=> cfg.DisableColors=true); })
.BuildServiceProvider())

文件中配置

代码示例:ConsoleApp1

您可能更喜欢通过外部配置文件配置组件和服务,并且通常使用appsettings.json因此,需要包Microsoft.Extensions.Configuration.Json

var configuration = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.json", false, true)
                    .Build();

ILogger logger;
IFooService fooService;

using (var serviceProvider = new ServiceCollection()// thanks to 
                // https://thecodebuzz.com/logging-in-net-core-console-application/
       .AddSingleton<IFooService, FooService>()
       .AddLogging(cfg => 
       {
           cfg.AddConfiguration(configuration.GetSection("Logging"));
           cfg.AddConsole(); 
       })
       .BuildServiceProvider())
       {
           logger = serviceProvider.GetService<ILogger<Program>>();
           //logger = serviceProvider.GetService<ILoggerFactory>().
                            CreateLogger<Program>(); // Factory first. This works too.

           fooService = serviceProvider.GetService<IFooService>();
       }

appsettings.json内容如下

{
    "Logging": {
        "Console": {
            "disableColors": false
        },

        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Information",
            "ConsoleApp1.FooService": "Warning",
            "ConsoleApp1.Program": "Debug"
        }
    }
}

提示

在阅读了appsettings.jsonLogging部分之后,通过DI .NET Core运行时配置控制台记录器提供程序以及诸如MicrosoftConsoleApp1.FooService之类的记录器。

跟踪(Trace)和跟踪源(TraceSource)

System.Diagnostics.TraceSystem.Diagnostics.TraceSource设计用于分离跟踪和日志记录,并且通过附加的跟踪侦听器实现日志记录。

内置跟踪侦听器

代码示例:ConsoleAppTraceListener

.NET Framework上的许多TraceListener派生类.NET Core上可用,但是像IisTraceListener这样的类在.NET Core 上不可用。

.NET Framework上,应用程序可以通过app.config初始化TraceTraceSource,并实例化跟踪侦听器,app.config在执行应用程序代码的第一行之前加载。

.NET Core上,您仍可以使用各种跟踪侦听器,例如ConsoleTraceListener,因为.NET Core默认情况下不会加载配置文件,并且内置配置不关心跟踪侦听器。

using (var listener = new TextWriterTraceListener("c:\\temp\\mylog.txt"))
using (var consoleListener = new ConsoleTraceListener())
{
    Trace.Listeners.Add(listener);
    Trace.Listeners.Add(consoleListener);

因此,您必须实例化跟踪侦听器,并在应用程序startup代码中进行初始化TraceTraceSources对象。不错,但是事情还在继续,有越来越多的第三方组件可以与ILogger<T>接口进行跟踪和日志记录。考虑到各种因素和权衡取舍,最好在TraceSourceILogger<T>之间架起一座桥梁,因此使用TraceTraceSource的遗留组件可以向ILogger<T>发送跟踪消息

LoggerTraceListener

代码示例:ConsoleAppTrace

由于TraceTraceSource只有侦听器与日志连接,因此这里的LoggerTraceListener侦听跟踪并写入ILogger<T>ILogger<T>最终将跟踪发送给记录提供者。

public class LoggerTraceListener : TraceListener
{
    private readonly ILogger logger;

    public LoggerTraceListener(ILogger logger)
    {
        this.logger = logger;
    }

    public override void Write(string message)
    {
        logger.LogInformation(message);
    }

    public override void WriteLine(string message)
    {
        logger.LogInformation(message);
    }

    public override void WriteLine(string message, string category)
    {
        logger.LogInformation(category + ": " + message);
    }

    public override void TraceEvent
           (TraceEventCache eventCache, string source, TraceEventType eventType, int id)
    {
        switch (eventType)
        {
            case TraceEventType.Critical:
                logger.LogCritical(id, source);
                break;
            case TraceEventType.Error:
                logger.LogError(id, source);
                break;
            case TraceEventType.Warning:
                logger.LogWarning(id, source);
                break;
            case TraceEventType.Information:
                logger.LogInformation(id, source);
                break;
            case TraceEventType.Verbose:
                logger.LogTrace(id, source);
                break;
            case TraceEventType.Start:
                logger.LogInformation(id, "Start: " + source);
                break;
            case TraceEventType.Stop:
                logger.LogInformation(id, "Stop: " + source);
                break;
            case TraceEventType.Suspend:
                logger.LogInformation(id, "Suspend: " + source);
                break;
            case TraceEventType.Resume:
                logger.LogInformation(id, "Resume: " + source);
                break;
            case TraceEventType.Transfer:
                logger.LogInformation(id, "Transfer: " + source);
                break;
            default:
                throw new InvalidOperationException("Impossible");
        }
    }

应用程序启动(startup)

ILogger logger;

using (var serviceProvider = new ServiceCollection()
    .AddLogging(cfg =>
    {
        cfg.AddConfiguration(configuration.GetSection("Logging"));
        cfg.AddConsole();
    })
    .BuildServiceProvider())
{
    logger = serviceProvider.GetService<ILogger<Program>>();
}

logger.LogInformation("logger information");
logger.LogWarning("logger warning");

using (var listener = new LoggerTraceListener(logger))
{
    System.Diagnostics.Trace.Listeners.Add(listener);
    TraceSources.Instance.InitLoggerTraceListener(listener);

    TraceLover.DoSomething();
    TraceSourceLover.DoSomething();
}

现在,TraceTraceSource的日志介质是由哪些日志提供程序附加到ILogger决定的。

HarmonyTraceTraceSourceLogger提供程序

代码示例:ConsoleappSeriLog

微软.NET Core开发了很少的具体记录器提供程序,这很可能是根据业务愿景而设计的。周围有很多第三方记录器提供程序:

  • NLog
  • Log4net
  • Serilog

有一篇很好的文章比较了这三个:

整体上我同意Serilog是最好的。

var configuration = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.json", false, true)
                    .Build();

Serilog.Log.Logger = new Serilog.LoggerConfiguration()
                      .Enrich.FromLogContext()
                      //.WriteTo.Console() I prefer plugging through the config file
                      .ReadFrom.Configuration(configuration)
                      .CreateLogger();

Microsoft.Extensions.Logging.ILogger logger;
IFooService fooService;

var services = new ServiceCollection();
services.AddLogging(configure => configure.AddSerilog());

using (var serviceProvider = services
    .AddSingleton<IFooService, FooService>()
    .BuildServiceProvider())
{
    logger = serviceProvider.GetService<ILogger<Program>>();
    fooService = serviceProvider.GetService<IFooService>();
}

try
{
    Log.Information("Starting up");
    logger.LogInformation("logger information");
    logger.LogWarning("logger warning");

    fooService.DoWork();

    using (var listener = new LoggerTraceListener(logger))
    {
        System.Diagnostics.Trace.Listeners.Add(listener);
        TraceSources.Instance.InitLoggerTraceListener(listener);

        TraceLover.DoSomething();
        TraceSourceLover.DoSomething();
    }
}
catch (Exception ex)
{
    Log.Fatal(ex, "Application start-up failed");
}
finally
{
    Log.CloseAndFlush();
}

appsettings.json内容如下

{
    "TraceSource": {
        "WebApi": {
            "SourceLevels": "Information"
        },

        "HouseKeeping": { "SourceLevels": "Warning" },

        "DbAudit": {
            "SourceLevels": "Warning"
        }
    },

    "Serilog": {
        "MinimumLevel": {
            "Default": "Debug",
            "Override": {
                "Microsoft": "Information",
                "System": "Warning",
                "ConsoleApp1.FooService": "Warning",
                "ConsoleApp1.Program": "Information"
            }
        },

        "WriteTo": [
            {
                "Name": "Console"
            },

            {
                "Name": "File",
                "Args": {
                    "path": "%PROGRAMDATA%/my/logs/CloudPosApi_Test.log",
                    "outputTemplate": "{Timestamp:MM-dd HH:mm:ss.fff zzz} 
                           [{Level}] {ThreadId} {Message}{NewLine}{Exception}",
                    "rollingInterval": "Day"
                }
            }
        ]
    }
}

兴趣点

.NET Framework上,运行时将在执行应用程序代码的第一行之前加载app.config并将设置应用于某些内置组件。默认情况下,其他组件(例如SMTPClientSystem.Diagnostics)会读取app.config

.NET Core上,应用程序程序员负责以代码形式或通过加载配置文件进行配置

即使我已经为.NET Core编写了几年的组件,但我刚刚开始开发复杂的.NET Core商业应用程序已有几个月了,而我绝不是精通.NET Core编程的人。请指出我在本文中可能犯的一些错误。在代码示例中,有一个有关TraceSourceLoggerProvider的示例,尚不可用。令人惊讶的是,截至202018TraceSourceLoggerProvider346Google搜索结果(实际上少于60个)中,似乎没有例子。我有一个问题,你知道如何使用TraceSourceLoggerProvider吗?

发布了69 篇原创文章 · 获赞 139 · 访问量 45万+

猜你喜欢

转载自blog.csdn.net/mzl87/article/details/104374787
今日推荐