Util应用框架基础(六) - 日志记录(一) - 正文

这篇具有很好参考价值的文章主要介绍了Util应用框架基础(六) - 日志记录(一) - 正文。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文介绍Util应用框架如何记录日志.

日志记录共分4篇,本文是正文,后续还有3篇分别介绍写入不同日志接收器的安装和配置方法.

概述

日志记录对于了解系统执行情况非常重要.

Asp.Net Core 抽象了日志基础架构,支持使用日志提供程序进行扩展,提供控制台日志等简单实现.

Serilog 是 .Net 流行的第三方日志框架,支持结构化日志,并能与 Asp.Net Core 日志集成.

Serilog 支持多种日志接收器,可以将日志发送到不同的地方.

我们可以将日志写入文本文件,但查看文本文件比较困难,文件如果很大,查找问题非常费力.

对于生产环境,我们需要包含管理界面的日志系统.

Seq 是一个日志系统,可以很好的展示结构化日志数据,并提供模糊搜索功能.

Exceptionless 是基于 Asp.Net Core 开发的日志系统.

与 Seq 相比,Exceptionless 搜索能力较弱.

Seq 和 Exceptionless 都提供了 Serilog 日志接收器,可以使用 Serilog 接入它们.

Util应用框架使用 Serilog 日志框架,同时集成了 SeqExceptionless 日志系统.

Util简化了日志配置,并对常用功能进行扩展.

日志配置

选择日志接收器

Util应用框架默认支持三种 Serilog 日志接收器:

  • 日志文件
  • Seq
  • Exceptionless

你可以从中选择一种或多种,如果都不能满足要求,你也可以引用 Serilog 支持的其它日志接收器,或自行实现.

配置日志接收器

请转到特定日志接收器章节查看配置方法.

配置日志级别

Asp.Net Core 使用日志级别表示日志的严重程度,定义如下:

  • Trace = 0
  • Debug = 1
  • Information = 2
  • Warning = 3
  • Error = 4
  • Critical = 5
  • None = 6

None不开启日志,Trace的严重程度最低,Critical的严重程度最高,需要高度关注.

可以在 appsettings.json 配置文件设置日志级别.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}

Logging 配置节用于配置日志.

LogLevel 为所有日志提供程序配置日志级别.

Default 为所有日志类别设置默认的日志级别.

上面的配置将默认日志级别设置为 Information.

意味着只输出日志级别等于或大于 Information 的日志.

现在 Trace 和 Debug 两个级别的日志被禁用了.

可以为特定日志类别设置日志级别.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
    }
  }
}

配置增加了 Microsoft 日志类别,并设置为 Debug 日志级别.

日志类别用来给日志分类,一般使用带命名空间的类名作为日志类别.

日志类别支持模糊匹配, Microsoft 日志类别不仅匹配 Microsoft ,而且还能匹配以 Microsoft 开头的所有日志类别,比如 Microsoft.AspNetCore .

Serilog 的日志级别

Serilog 定义了自己的日志级别,不支持上面介绍的标准配置方式.

Exceptionless 也是如此.

使用第三方框架的日志级别会导致复杂性.

Util应用框架扩展了 Serilog 和 Exceptionless 的日志级别配置,允许以统一的标准方式进行配置.

记录日志

.Net 提供了标准的日志记录接口 Microsoft.Extensions.Logging.ILogger.

你可以使用 ILogger 记录日志.

Util应用框架还提供了一个 Util.Logging.ILog 接口.

当你需要写入很长的日志时,可能需要使用 StringBuilder 拼接日志内容.

ILog 提供了一种更简单的方式写入长内容日志.

使用 ILogger 记录日志

ILogger 支持泛型参数, 用来指定日志类别,使用带命名空间的类名作为日志类别.

namespace Demo;

public class DemoController : WebApiControllerBase {
    public DemoController( ILogger<DemoController> logger ) {
        logger.LogDebug( "Util" );
    }
}

示例在控制器构造方法注入 ILogger<DemoController> ,日志类别为 Demo.DemoController .

ILogger 扩展了一些以 Log 开头的方法,比如 LogDebug,表示写入日志级别为 Debug 的消息.

logger.LogDebug( "Util" ) 以 Debug 日志级别写入消息'Util'.

使用 ILog 记录日志

Util应用框架定义了 ILog 接口.

ILog 是对 ILogger 接口的简单包装, 对日志内容的设置进行了扩展.

ILog 也使用泛型参数来指定日志类别.

使用 ILog 完成上面相同的示例.

public class DemoController : WebApiControllerBase {
    public DemoController( ILog<DemoController> log ) {
        log.Message( "Util" ).LogDebug();
    }
}

Message 是 ILog 定义的方法, 用来设置日志消息,可以多次调用它拼接内容.

当你需要写比较长的日志内容, ILog 可以帮你拼接内容,这样省去了定义 StringBuilder 的麻烦.

可以在 ILog 添加自定义扩展方法来设置内容, Util应用框架内置了一些设置日志消息的扩展方法, 比如 AppendLine.

public class DemoController : WebApiControllerBase {
    public DemoController( ILog<DemoController> log ) {
        log.AppendLine( "内容1" )
            .AppendLine( "内容2" )
            .LogDebug();
    }
}

你可以定义自己的扩展方法,以更加语义化的方式记录日志.

范例:

public class DemoController : WebApiControllerBase {
    public DemoController( ILog<DemoController> log ) {
        log.Caption( "标题" )
            .Content( "内容" )
            .Sql( "Sql" )
            .LogDebug();
    }
}

ILog 与 ILogger 比较:

ILog 更擅长记录内容很长的日志.

ILog 是有状态服务,不能在多个线程共享使用.

可以使用 ILog 记录业务日志,其它场景应使用 ILogger.

结构化日志支持

Serilog 日志框架对结构化日志提供了支持.

结构化日志使用特定语法的消息模板日志格式,可以从日志文本中提取搜索元素.

结构化日志的优势主要体现在日志系统对日志消息的展示和搜索方式上.

不同的日志系统对结构化日志的展示方式和搜索能力不同.

请参考 Seq 和 Exceptionless 的 结构化日志支持 小节.

日志操作上下文

记录日志时,我们除了需要记录业务内容,还需要知道一些额外的信息,比如操作用户是谁.

我们希望记录日志时仅设置业务内容,这些额外的信息最好能自动记录.

Util应用框架通过日志上下文自动设置这些额外信息.

  • UserId 设置当前操作用户标识

  • Application 设置当前应用程序名称.

  • Environment 设置当前环境名称.

  • TraceId 设置跟踪号.

  • Stopwatch 设置计时器,用于记录请求执行花了多长时间.

在 Asp.Net Core 环境, 日志上下文由日志上下文中间件 Util.Applications.Logging.LogContextMiddleware 创建.

无需手工添加日志上下文中间件,只要引用 Util.Application.WebApi 类库, 就会自动添加到中间件管道.

对于 Web 请求, 跟踪号是一个重要的信息,可以通过查询跟踪号,将相关的请求日志全部查出来.

另外, Exceptionless 会自动收集很多系统信息.

源码解析

ILog 日志操作

ILog 日志操作接口提供链式调用方式设置日志内容.

  • Message 方法设置日志消息.

  • Property 方法设置扩展属性.

  • State 设置日志参数对象.

Log 开头的日志记录方法,将日志操作委托给 ILogger 相关方法.

/// <summary>
/// 日志操作
/// </summary>
/// <typeparam name="TCategoryName">日志类别</typeparam>
public interface ILog<out TCategoryName> : ILog {
}

/// <summary>
/// 日志操作
/// </summary>
public interface ILog {
    /// <summary>
    /// 设置日志事件标识
    /// </summary>
    /// <param name="eventId">日志事件标识</param>
    ILog EventId( EventId eventId );
    /// <summary>
    /// 设置异常
    /// </summary>
    /// <param name="exception">异常</param>
    ILog Exception( Exception exception );
    /// <summary>
    /// 设置自定义扩展属性
    /// </summary>
    /// <param name="propertyName">属性名</param>
    /// <param name="propertyValue">属性值</param>
    ILog Property( string propertyName, string propertyValue );
    /// <summary>
    /// 设置日志状态对象
    /// </summary>
    /// <param name="state">状态对象</param>
    ILog State( object state );
    /// <summary>
    /// 设置日志消息
    /// </summary>
    /// <param name="message">日志消息</param>
    /// <param name="args">日志消息参数</param>
    ILog Message( string message, params object[] args );
    /// <summary>
    /// 是否启用
    /// </summary>
    /// <param name="logLevel">日志级别</param>
    bool IsEnabled( LogLevel logLevel );
    /// <summary>
    /// 开启日志范围
    /// </summary>
    /// <typeparam name="TState">日志状态类型</typeparam>
    /// <param name="state">日志状态</param>
    IDisposable BeginScope<TState>( TState state );
    /// <summary>
    /// 写跟踪日志
    /// </summary>
    ILog LogTrace();
    /// <summary>
    /// 写调试日志
    /// </summary>
    ILog LogDebug();
    /// <summary>
    /// 写信息日志
    /// </summary>
    ILog LogInformation();
    /// <summary>
    /// 写警告日志
    /// </summary>
    ILog LogWarning();
    /// <summary>
    /// 写错误日志
    /// </summary>
    ILog LogError();
    /// <summary>
    /// 写致命日志
    /// </summary>
    ILog LogCritical();
}

ILogExtensions 日志操作扩展

Util应用框架内置了几个日志操作扩展方法,你可以定义自己的扩展方法,以方便内容设置.

/// <summary>
/// 日志操作扩展
/// </summary>
public static class ILogExtensions {
    /// <summary>
    /// 添加消息
    /// </summary>
    /// <param name="log">配置项</param>
    /// <param name="message">消息</param>
    /// <param name="args">日志消息参数</param>
    public static ILog Append( this ILog log,string message, params object[] args ) {
        log.CheckNull( nameof( log ) );
        log.Message( message, args );
        return log;
    }

    /// <summary>
    /// 当条件为true添加消息
    /// </summary>
    /// <param name="log">配置项</param>
    /// <param name="message">消息</param>
    /// <param name="condition">条件,值为true则添加消息</param>
    /// <param name="args">日志消息参数</param>
    public static ILog AppendIf( this ILog log, string message,bool condition, params object[] args ) {
        log.CheckNull( nameof( log ) );
        if ( condition )
            log.Message( message, args );
        return log;
    }

    /// <summary>
    /// 添加消息并换行
    /// </summary>
    /// <param name="log">配置项</param>
    /// <param name="message">消息</param>
    /// <param name="args">日志消息参数</param>
    public static ILog AppendLine( this ILog log, string message, params object[] args ) {
        log.CheckNull( nameof( log ) );
        log.Message( message, args );
        log.Message( Environment.NewLine );
        return log;
    }

    /// <summary>
    /// 当条件为true添加消息并换行
    /// </summary>
    /// <param name="log">配置项</param>
    /// <param name="message">消息</param>
    /// <param name="condition">条件,值为true则添加消息</param>
    /// <param name="args">日志消息参数</param>
    public static ILog AppendLineIf( this ILog log, string message, bool condition, params object[] args ) {
        log.CheckNull( nameof( log ) );
        if ( condition ) {
            log.Message( message, args );
            log.Message( Environment.NewLine );
        }
        return log;
    }

    /// <summary>
    /// 消息换行
    /// </summary>
    /// <param name="log">配置项</param>
    public static ILog Line( this ILog log ) {
        log.CheckNull( nameof(log) );
        log.Message( Environment.NewLine );
        return log;
    }
}

LogContext 日志上下文

通过日志上下文自动记录重要的额外信息.

/// <summary>
/// 日志上下文
/// </summary>
public class LogContext {
    /// <summary>
    /// 初始化日志上下文
    /// </summary>
    public LogContext() {
        Data = new Dictionary<string, object>();
    }

    /// <summary>
    /// 计时器
    /// </summary>
    public Stopwatch Stopwatch { get; set; }
    /// <summary>
    /// 跟踪标识
    /// </summary>
    public string TraceId { get; set; }
    /// <summary>
    /// 用户标识
    /// </summary>
    public string UserId { get; set; }
    /// <summary>
    /// 应用程序
    /// </summary>
    public string Application { get; set; }
    /// <summary>
    /// 执行环境
    /// </summary>
    public string Environment { get; set; }
    /// <summary>
    /// 扩展数据
    /// </summary>
    public IDictionary<string, object> Data { get; }
}

LogContextMiddleware 日志上下文中间件

日志上下文中间件创建日志上下文,并添加到 HttpContext 对象的 Items .

/// <summary>
/// 日志上下文中间件
/// </summary>
public class LogContextMiddleware {
    /// <summary>
    /// 下个中间件
    /// </summary>
    private readonly RequestDelegate _next;

    /// <summary>
    /// 初始化日志上下文中间件
    /// </summary>
    /// <param name="next">下个中间件</param>
    public LogContextMiddleware( RequestDelegate next ) {
        _next = next;
    }

    /// <summary>
    /// 执行中间件
    /// </summary>
    /// <param name="context">Http上下文</param>
    public async Task Invoke( HttpContext context ) {
        var traceId = context.Request.Headers["x-correlation-id"].SafeString();
        if ( traceId.IsEmpty() )
            traceId = context.TraceIdentifier;
        var session = context.RequestServices.GetService<Util.Sessions.ISession>();
        var environment = context.RequestServices.GetService<IWebHostEnvironment>();
        var logContext = new LogContext {
            Stopwatch = Stopwatch.StartNew(), 
            TraceId = traceId, 
            UserId = session?.UserId,
            Application = environment?.ApplicationName,
            Environment = environment?.EnvironmentName
        };
        context.Items[LogContextAccessor.LogContextKey] = logContext;
        await _next( context );
    }
}

ILogContextAccessor 日志上下文访问器

日志上下文访问器从 HttpContext.Items 获取日志上下文.

/// <summary>
/// 日志上下文访问器
/// </summary>
public interface ILogContextAccessor {
    /// <summary>
    /// 日志上下文
    /// </summary>
    LogContext Context { get; set; }
}

/// <summary>
/// 日志上下文访问器
/// </summary>
public class LogContextAccessor : ILogContextAccessor {
    /// <summary>
    /// 日志上下文键名
    /// </summary>
    public const string LogContextKey = "Util.Logging.LogContext";

    /// <summary>
    /// 日志上下文
    /// </summary>
    public LogContext Context {
        get => Util.Helpers.Convert.To<LogContext>( Web.HttpContext.Items[LogContextKey] );
        set => Web.HttpContext.Items[LogContextKey] = value;
    }
}

LogContextEnricher 日志上下文扩展

Serilog 提供 ILogEventEnricher 接口用于设置扩展属性.

LogContextEnricher 使用 Ioc.Create 方法获取依赖服务 ILogContextAccessor.

这是因为不能使用依赖注入,它要求实现类必须是无参构造函数.

Ioc.Create 在 Asp.Net Core 环境获取依赖服务是安全的,但在其它环境则可能获取失败.

如果获取日志上下文失败,也不会对功能造成影响,只是丢失了一些上下文信息.

/// <summary>
/// 日志上下文扩展属性
/// </summary>
public class LogContextEnricher : ILogEventEnricher {
    /// <summary>
    /// 扩展属性
    /// </summary>
    /// <param name="logEvent">日志事件</param>
    /// <param name="propertyFactory">日志事件属性工厂</param>
    public void Enrich( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) {
        var accessor = Ioc.Create<ILogContextAccessor>();
        if ( accessor == null )
            return;
        var context = accessor.Context;
        if ( context == null )
            return;
        if ( logEvent == null )
            return;
        if ( propertyFactory == null )
            return;
        RemoveProperties( logEvent );
        AddDuration( context,logEvent, propertyFactory );
        AddTraceId( context, logEvent, propertyFactory );
        AddUserId( context, logEvent, propertyFactory );
        AddApplication( context, logEvent, propertyFactory );
        AddEnvironment( context, logEvent, propertyFactory );
        AddData( context, logEvent, propertyFactory );
    }

    /// <summary>
    /// 移除默认设置的部分属性
    /// </summary>
    private void RemoveProperties( LogEvent logEvent ) {
        logEvent.RemovePropertyIfPresent( "ActionId" );
        logEvent.RemovePropertyIfPresent( "ActionName" );
        logEvent.RemovePropertyIfPresent( "RequestId" );
        logEvent.RemovePropertyIfPresent( "RequestPath" );
        logEvent.RemovePropertyIfPresent( "ConnectionId" );
    }

    /// <summary>
    /// 添加执行持续时间
    /// </summary>
    private void AddDuration( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) {
        if ( context?.Stopwatch == null )
            return;
        var property = propertyFactory.CreateProperty( "Duration", context.Stopwatch.Elapsed.Description() );
        logEvent.AddOrUpdateProperty( property );
    }

    /// <summary>
    /// 添加跟踪号
    /// </summary>
    private void AddTraceId( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) {
        if ( context == null || context.TraceId.IsEmpty() )
            return;
        var property = propertyFactory.CreateProperty( "TraceId", context.TraceId );
        logEvent.AddOrUpdateProperty( property );
    }

    /// <summary>
    /// 添加用户标识
    /// </summary>
    private void AddUserId( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) {
        if ( context == null || context.UserId.IsEmpty() )
            return;
        var property = propertyFactory.CreateProperty( "UserId", context.UserId );
        logEvent.AddOrUpdateProperty( property );
    }

    /// <summary>
    /// 添加应用程序
    /// </summary>
    private void AddApplication( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) {
        if ( context == null || context.Application.IsEmpty() )
            return;
        var property = propertyFactory.CreateProperty( "Application", context.Application );
        logEvent.AddOrUpdateProperty( property );
    }

    /// <summary>
    /// 添加执行环境
    /// </summary>
    private void AddEnvironment( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) {
        if ( context == null || context.Environment.IsEmpty() )
            return;
        var property = propertyFactory.CreateProperty( "Environment", context.Environment );
        logEvent.AddOrUpdateProperty( property );
    }

    /// <summary>
    /// 添加扩展数据
    /// </summary>
    private void AddData( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) {
        if ( context?.Data == null || context.Data.Count == 0 )
            return;
        foreach ( var item in context.Data ) {
            var property = propertyFactory.CreateProperty( item.Key, item.Value );
            logEvent.AddOrUpdateProperty( property );
        }
    }
}

LoggerEnrichmentConfigurationExtensions

将 LogContextEnricher 扩展到 LoggerEnrichmentConfiguration 上.

/// <summary>
/// Serilog扩展属性配置扩展
/// </summary>
public static class LoggerEnrichmentConfigurationExtensions {
    /// <summary>
    /// 添加日志上下文扩展属性
    /// </summary>
    /// <param name="source">日志扩展配置</param>
    public static LoggerConfiguration WithLogContext( this LoggerEnrichmentConfiguration source ) {
        source.CheckNull( nameof( source ) );
        return source.With<LogContextEnricher>();
    }

    /// <summary>
    /// 添加日志级别扩展属性
    /// </summary>
    /// <param name="source">日志扩展配置</param>
    public static LoggerConfiguration WithLogLevel( this LoggerEnrichmentConfiguration source ) {
        source.CheckNull( nameof( source ) );
        return source.With<LogLevelEnricher>();
    }
}

AddSerilog 配置方法

AddSerilog 配置方法封装了 Serilog 的配置.

  • 配置 ILog 接口服务依赖.

  • 将 Asp.Net Core 日志级别转换为 Serilog 日志级别.

  • 设置日志上下文扩展.文章来源地址https://www.toymoban.com/news/detail-746083.html

/// <summary>
/// Serilog日志操作扩展
/// </summary>
public static class AppBuilderExtensions {
    /// <summary>
    /// 配置Serilog日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    public static IAppBuilder AddSerilog( this IAppBuilder builder ) {
        return builder.AddSerilog( false );
    }

    /// <summary>
    /// 配置Serilog日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    /// <param name="isClearProviders">是否清除默认设置的日志提供程序</param>
    public static IAppBuilder AddSerilog( this IAppBuilder builder, bool isClearProviders ) {
        return builder.AddSerilog( options => {
            options.IsClearProviders = isClearProviders;
        } );
    }

    /// <summary>
    /// 配置Serilog日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    /// <param name="appName">应用程序名称</param>
    public static IAppBuilder AddSerilog( this IAppBuilder builder, string appName ) {
        return builder.AddSerilog( options => {
            options.Application = appName;
        } );
    }

    /// <summary>
    /// 配置Serilog日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    /// <param name="setupAction">日志配置操作</param>
    public static IAppBuilder AddSerilog( this IAppBuilder builder, Action<LogOptions> setupAction ) {
        builder.CheckNull( nameof( builder ) );
        var options = new LogOptions();
        setupAction?.Invoke( options );
        builder.Host.ConfigureServices( ( context, services ) => {
            services.AddSingleton<ILogFactory, LogFactory>();
            services.AddTransient( typeof( ILog<> ), typeof( Log<> ) );
            services.AddTransient( typeof( ILog ), t => t.GetService<ILogFactory>()?.CreateLog( "default" ) ?? NullLog.Instance );
            var configuration = context.Configuration;
            services.AddLogging( loggingBuilder => {
                if ( options.IsClearProviders )
                    loggingBuilder.ClearProviders();
                SerilogLog.Logger = new LoggerConfiguration()
                    .Enrich.WithProperty( "Application", options.Application )
                    .Enrich.FromLogContext()
                    .Enrich.WithLogLevel()
                    .Enrich.WithLogContext()
                    .ReadFrom.Configuration( configuration )
                    .ConfigLogLevel( configuration )
                    .CreateLogger();
                loggingBuilder.AddSerilog();
            } );
        } );
        return builder;
    }
}

AddExceptionless 配置方法

AddExceptionless 配置方法封装了 Serilog 和 Exceptionless 的配置.

  • 配置 ILog 接口服务依赖.

  • 将 Asp.Net Core 日志级别转换为 Exceptionless 日志级别.

  • 设置日志上下文扩展.

/// <summary>
/// Exceptionless日志操作扩展
/// </summary>
public static class AppBuilderExtensions {
    /// <summary>
    /// 配置Exceptionless日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    /// <param name="isClearProviders">是否清除默认设置的日志提供程序</param>
    public static IAppBuilder AddExceptionless( this IAppBuilder builder, bool isClearProviders = false ) {
        return builder.AddExceptionless( null, isClearProviders );
    }

    /// <summary>
    /// 配置Exceptionless日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    /// <param name="appName">应用程序名称</param>
    public static IAppBuilder AddExceptionless( this IAppBuilder builder, string appName ) {
        return builder.AddExceptionless( null, appName );
    }

    /// <summary>
    /// 配置Exceptionless日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    /// <param name="configAction">Exceptionless日志配置操作</param>
    /// <param name="isClearProviders">是否清除默认设置的日志提供程序</param>
    public static IAppBuilder AddExceptionless( this IAppBuilder builder, Action<ExceptionlessConfiguration> configAction, bool isClearProviders = false ) {
        return builder.AddExceptionless( configAction, t => t.IsClearProviders = isClearProviders );
    }

    /// <summary>
    /// 配置Exceptionless日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    /// <param name="configAction">Exceptionless日志配置操作</param>
    /// <param name="appName">应用程序名称</param>
    public static IAppBuilder AddExceptionless( this IAppBuilder builder, Action<ExceptionlessConfiguration> configAction, string appName ) {
        return builder.AddExceptionless( configAction, t => t.Application = appName );
    }

    /// <summary>
    /// 配置Exceptionless日志操作
    /// </summary>
    /// <param name="builder">应用生成器</param>
    /// <param name="configAction">Exceptionless日志配置操作</param>
    /// <param name="setupAction">日志配置</param>
    public static IAppBuilder AddExceptionless( this IAppBuilder builder, Action<ExceptionlessConfiguration> configAction, Action<LogOptions> setupAction ) {
        builder.CheckNull( nameof( builder ) );
        var options = new LogOptions();
        setupAction?.Invoke( options );
        builder.Host.ConfigureServices( ( context, services ) => {
            services.AddSingleton<ILogFactory, LogFactory>();
            services.AddTransient( typeof( ILog<> ), typeof( Log<> ) );
            services.AddTransient( typeof( ILog ), t => t.GetService<ILogFactory>()?.CreateLog( "default" ) ?? NullLog.Instance );
            var configuration = context.Configuration;
            services.AddLogging( loggingBuilder => {
                if ( options.IsClearProviders )
                    loggingBuilder.ClearProviders();
                ConfigExceptionless( configAction, configuration );
                SerilogLog.Logger = new LoggerConfiguration()
                    .Enrich.WithProperty( "Application", options.Application )
                    .Enrich.FromLogContext()
                    .Enrich.WithLogLevel()
                    .Enrich.WithLogContext()
                    .WriteTo.Exceptionless()
                    .ReadFrom.Configuration( configuration )
                    .ConfigLogLevel( configuration )
                    .CreateLogger();
                loggingBuilder.AddSerilog();
            } );
        } );
        return builder;
    }

    /// <summary>
    /// 配置Exceptionless
    /// </summary>
    private static void ConfigExceptionless( Action<ExceptionlessConfiguration> configAction, IConfiguration configuration ) {
        ExceptionlessClient.Default.Startup();
        if ( configAction != null ) {
            configAction( ExceptionlessClient.Default.Configuration );
            ConfigLogLevel( configuration, ExceptionlessClient.Default.Configuration );
            return;
        }
        ExceptionlessClient.Default.Configuration.ReadFromConfiguration( configuration );
        ConfigLogLevel( configuration, ExceptionlessClient.Default.Configuration );
    }

    /// <summary>
    /// 配置日志级别
    /// </summary>
    private static void ConfigLogLevel( IConfiguration configuration, ExceptionlessConfiguration options ) {
        var section = configuration.GetSection( "Logging:LogLevel" );
        foreach ( var item in section.GetChildren() ) {
            if ( item.Key == "Default" ) {
                options.Settings.Add( "@@log:*", GetLogLevel( item.Value ) );
                continue;
            }
            options.Settings.Add( $"@@log:{item.Key}*", GetLogLevel( item.Value ) );
        }
    }

    /// <summary>
    /// 获取日志级别
    /// </summary>
    private static string GetLogLevel( string logLevel ) {
        switch ( logLevel.ToUpperInvariant() ) {
            case "TRACE":
                return "Trace";
            case "DEBUG":
                return "Debug";
            case "INFORMATION":
                return "Info";
            case "ERROR":
                return "Error";
            case "CRITICAL":
                return "Fatal";
            case "NONE":
                return "Off";
            default:
                return "Warn";
        }
    }
}

到了这里,关于Util应用框架基础(六) - 日志记录(一) - 正文的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Util 应用框架 UI 全新升级

    Util UI 已经开发多年, 并在多家公司的项目使用. 不过一直以来, Util UI 存在一些缺陷, 始终未能解决. 最近几个月, Util 团队下定决心, 终于彻底解决了所有已知缺陷. Util 应用框架 UI 建立在 Angular , Ng-Zorro, Ng-Alain 基础之上, 用于开发企业中后台. 简洁 Util UI 通常可以将复杂组件的

    2024年04月28日
    浏览(36)
  • Util应用框架 UI 开发快速入门

    本文是Util应用框架 Angular UI 开发快速入门教程. Util 应用框架目前仅支持用于开发管理后台的 UI. 本文介绍了 Util UI 的技术特点和功能支持. Js语言 TypeScript TypeScript 是 微软开发的脚本语言, 扩展了弱类型的 Javascript,提供增强的语法和强类型支持. 为编辑器代码提示和语法错误检

    2024年02月08日
    浏览(42)
  • Util应用框架快速入门(4) - 集成测试开发入门

    本文演示Util应用框架开发的项目中如何编写集成测试. 完成 Web Api 快速入门,本文将在之前生成的示例项目上讲解集成测试的开发. 自动化测试对于Util应用框架的开发非常重要,它能保证基础功能的稳定性. 对于使用 Util 开发的业务项目,自动化测试不是必须的,但掌握它可能很有

    2024年02月08日
    浏览(54)
  • Spring Boot中集成各种日志框架Logback、Log4j2和Java Util Logging

    Spring Boot支持多种日志框架,包括Logback、Log4j2和Java Util Logging(JUL)。在Spring Boot中,可以通过简单的配置来集成这些热门的日志框架。 下面将详细说明如何集成Logback、Log4j2和Java Util Logging,并提供相应的源代码示例。 集成Logback: Logback是Spring Boot默认的日志框架,它提供了高

    2024年02月05日
    浏览(54)
  • 设置word目录从正文开始记录页码,并解决word目录正常,但正文页脚处只显示第一页的页码

    问题详情1:如何设置目录从正文开始记录页码 问题详情2:word目录处的页码正常,但正文只有第一页的页脚处显示页码 在设置目录从正文开始记录页码时需在目录后插入分节符(相同的节可以把当前页的页脚链接到上一页,但是不同的节就不行,因此可以利用分节来从 正文

    2024年03月10日
    浏览(115)
  • 深入理解 SpringBoot 日志框架:从入门到高级应用——(一)日志框架原理

    日志框架的历史可以追溯到计算机编程的早期。在早期的编程语言中,如 C 和 Pascal,程序员通常使用 printf 或 fprintf 函数将程序的状态信息输出到控制台或文件中,以便调试和排查问题。 随着计算机应用程序的不断发展,人们对日志管理的需求越来越高。当我们在开发项目时

    2024年02月09日
    浏览(51)
  • 深入理解 SpringBoot 日志框架:从入门到高级应用——(七)SpringBoot日志配置

    SpringBoot 官方文档:https://docs.spring.io/spring-boot/docs/2.7.12/reference/htmlsingle Spring Boot 对所有内部日志记录使用 Commons Logging,但使底层日志实现保持为打开状态。 为 Java Util Logging、Log4J2 和 Logback 提供了默认配置。默认情况下,Logback、 Java Util Logging、Commons Logging、Log4J 或 SLF4J 都能

    2024年02月09日
    浏览(29)
  • 日志文件之间关系和介绍及应用

    Log4j: Log4j是Java中广泛使用的日志框架之一。它提供了灵活的配置选项和丰富的功能,支持日志级别、日志输出目标等。Log4j有1.x版本和2.x版本,其中Log4j 2.x是对1.x的升级和扩展。 Logback: Logback是由Log4j创始人设计的Log4j的后继者,也是一个非常流行的Java日志框架。它具有高性能

    2024年02月10日
    浏览(36)
  • 【前端打怪升级日志之微前端框架篇】微前端qiankun框架子应用间跳转方法

    参考链接 qiankun官网:微应用之间如何跳转? 1.主应用、子应用路由都是hash模式    主应用根据 hash 来判断微应用,无需考虑该问题 2.主应用根据path判断子应用 方法 实现 适用条件 参数传递 存在问题 a标签跳转 a href=\\\"/toA\\\"/a 页面重新刷新,原来的状态丢失,用户体验不好 h

    2024年02月07日
    浏览(43)
  • 深入理解 SpringBoot 日志框架:从入门到高级应用——(六)Log4j2 输出日志到 QQ邮箱

    要实现将 log4j2 输出日志到 QQ 邮箱,需按照以下步骤进行: 在 QQ 邮箱中设置 SMTP 服务,开启 POP3/SMTP 服务,获取 SMTP 服务地址、端口号、登录邮箱账号和密码。 在 Java 项目中添加 Mail 依赖。 在 log4j2.xml 配置文件中,添加 SMTPAppender,指定SMTP服务器地址、端口号、邮箱账号和

    2024年02月09日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包