将 Serilog 与 WebApi2 结合使用的正确方法

2024-01-10

我正在寻找将 serilog 与 asp net web api 2 一起使用的正确方法。 至于现在,我像这样初始化全局 Log.Logger 属性:

   public static void Register(HttpConfiguration config)
    {

        Log.Logger = new LoggerConfiguration()
            .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
            {
                IndexFormat = IndexFormat,
                BufferBaseFilename = outputLogPath,
                AutoRegisterTemplate = true,
                AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6,
                CustomFormatter = new ElasticsearchJsonFormatter(renderMessageTemplate: false),
                BufferFileCountLimit = NbDaysRetention
            })
            .MinimumLevel.ControlledBy(new LoggingLevelSwitch() { MinimumLevel = LogEventLevel.Information})
            .Enrich.FromLogContext()
            .Enrich.WithWebApiRouteTemplate()
            .Enrich.WithWebApiActionName()
            .CreateLogger();

        //Trace all requests
        SerilogWebClassic.Configure(cfg => cfg.LogAtLevel(LogEventLevel.Information));


        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }

有没有更干净的方法来做到这一点?我想知道,如果我必须对我的控制器进行一些测试,这是否会成为问题。


对于具有 Web API(和/或 MVC)的应用程序,我经常使用以下代码组织。 (请注意,它可能有点过时,因为它基于某些软件包的旧版本,但您应该能够在不需要太多工作的情况下适应它......)

您将需要相当多的软件包,您应该能够从命名空间中猜测出这些软件包,但最重要的是,安装WebActivatorEx包提供了一种在应用程序生命周期的不同时刻运行代码的方法

Our Global.asax.cs最终看起来像这样:

    public class WebApiApplication : System.Web.HttpApplication
    {
        // rely on the fact that AppPreStart is called before Application_Start
        private static readonly ILogger Logger = Log.ForContext<WebApiApplication>();

        public override void Init()
        {
            base.Init();
        }

        protected void Application_Start()
        {
            // WARNING :  Some code runs even before this method ... see AppPreStart

            Logger.Debug("In Application_Start");
            // Mvc (must be before)
            AreaRegistration.RegisterAllAreas(); 
            // Web API
            // ... snip ...     
            // some DependencyInjection config ...
            // ... snip ...  
            // MVC
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            Logger.Information("App started !");
        }


        protected void Application_End(object sender, EventArgs e)
        {
            Logger.Debug("In Application_End");
            ApplicationShutdownReason shutdownReason = System.Web.Hosting.HostingEnvironment.ShutdownReason;
            Logger.Information("App is shutting down (reason = {@shutdownReason})", shutdownReason);

            // WARNING : Some code runs AFTER Application_End ... see AppPostShutDown
        }
    }

然后是下面的几个类App_Start文件夹(其中一些您可以忽略:P):

  • AppPreStart.cs
using XXX;
using Serilog;


[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(AppPreStart), nameof(AppPreStart.PreApplicationStart))]
namespace XXX
{
    /// <summary>
    /// This runs even before global.asax Application_Start (see WebActivatorConfig)
    /// </summary>
    public class AppPreStart
    {
        public static void PreApplicationStart()
        {
            LogConfig.Configure();
            var logger = Log.ForContext<AppPreStart>();
            logger.Information("App is starting ...");

            // ... snip ...
            // very early things like IoC config, AutoMapper config ...
            // ... snip ...

            logger.Debug("Done with AppPreStart");
        }
    }
}
  • AppPostShutDown.cs
using XXX;
using Serilog;

[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(AppPostShutDown), nameof(AppPostShutDown.PostApplicationShutDown))]
namespace XXX
{
    /// <summary>
    /// This runs even before global.asax Application_Start (see WebActivatorConfig)
    /// </summary>
    public class AppPostShutDown
    {
        private static ILogger Logger = Log.ForContext<AppPostShutDown>();

        public static void PostApplicationShutDown()
        {
            Logger.Debug("PostApplicationShutDown");

            // ... snip ...
            // very late things like IoC dispose ....
            // ... snip ...

            // force flushing the last "not logged" events
            Logger.Debug("Closing the logger! ");
            Log.CloseAndFlush();
        }
    }
}
  • LogConfig.cs
using Serilog;
using Serilog.Events;
using SerilogWeb.Classic;
using SerilogWeb.Classic.Enrichers;
using SerilogWeb.Classic.WebApi.Enrichers;

namespace XXX
{
    public class LogConfig
    {
        static public void Configure()
        {
            ApplicationLifecycleModule.LogPostedFormData = LogPostedFormDataOption.OnlyOnError;
            ApplicationLifecycleModule.FormDataLoggingLevel = LogEventLevel.Debug;
            ApplicationLifecycleModule.RequestLoggingLevel = LogEventLevel.Debug;

            var loggerConfiguration = new LoggerConfiguration().ReadFrom.AppSettings()
                    .Enrich.FromLogContext()
                    .Enrich.With<HttpRequestIdEnricher>()
                    .Enrich.With<UserNameEnricher>()
                    .Enrich.With<HttpRequestUrlEnricher>()
                    .Enrich.With<WebApiRouteTemplateEnricher>()
                    .Enrich.With<WebApiControllerNameEnricher>()
                    .Enrich.With<WebApiRouteDataEnricher>()
                    .Enrich.With<WebApiActionNameEnricher>()
                ;

            Log.Logger = loggerConfiguration.CreateLogger();
        }
    }
}

然后从中读取Log配置的变量部分Web.config有以下键AppSettings :

    <!-- SeriLog-->
    <add key="serilog:level-switch:$controlSwitch" value="Information" />
    <add key="serilog:minimum-level:controlled-by" value="$controlSwitch" />
    <add key="serilog:enrich:with-property:AppName" value="XXXApp" />
    <add key="serilog:enrich:with-property:AppComponent" value="XXXComponent" />
    <add key="serilog:enrich:with-property:Environment" value="Dev" />
    <add key="serilog:enrich:with-property:MachineName" value="%COMPUTERNAME%" />
    <add key="serilog:using:Seq" value="Serilog.Sinks.Seq" />
    <add key="serilog:write-to:Seq.serverUrl" value="http://localhost:5341" />
    <add key="serilog:write-to:Seq.apiKey" value="xxxxxxxxxxx" />
    <add key="serilog:write-to:Seq.controlLevelSwitch" value="$controlSwitch" />

(然后我们有Web.config转换为“标记化”文件以用于生产

Web.Release.config

    <!-- SeriLog-->
    <add key="serilog:enrich:with-property:Environment" value="__Release_EnvironmentName__"
         xdt:Transform="Replace" xdt:Locator="Match(key)"/>

    <add key="serilog:write-to:Seq.serverUrl" value="__app_serilogSeqUrl__"
         xdt:Transform="Replace" xdt:Locator="Match(key)"/>
    <add key="serilog:write-to:Seq.apiKey" value="__app_serilogApiKey__"
         xdt:Transform="Replace" xdt:Locator="Match(key)"/>

此配置的一些最重要的部分是:

  • 尽快配置您的记录器
  • call Log.CloseAndFlush();最后确保所有日志事件都被存储/推送
  • add Enrich.FromLogContext()来自 Serilog,以及一些来自的丰富器SerilogWeb.Classic and SerilogWeb.WebApi,它们可能会变得非常有用。
  • 记录到正确支持结构化日志记录的日志服务器(写入文件有太多缺点)...我们使用Seq https://datalust.co/seq我们对此非常非常高兴(在每台开发机器上本地安装,然后在生产中安装一个集中实例)。它支持搜索/查询和仪表板以及动态日志级别控制。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 Serilog 与 WebApi2 结合使用的正确方法 的相关文章

随机推荐

  • 将 SDK 工具和 ADT 更新至 17,现在出现VerifyErrors

    我有一个引用两个图书馆项目的项目 升级ADT和SDK工具后版本17 http android developers blogspot com 2012 03 updated sdk tools and adt revision 17 htm
  • gnuplot:在交互模式下设置 link 和 x2tics

    在交互模式下 x2tics 在放大时很快就会消失 这个例子被展示了here https stackoverflow com a 60373156 11769765 set tics nomirror set x2tics set link
  • 双问号,它是如何工作的?

    我正在学习 Swift 作为这个过程的一部分 我试图弄清楚这里到底发生了什么 我有一个自定义 Segue 我想在其中放置模态视图控制器以消除转换 Objective c 中以前的内容是 UIViewController sourceView
  • 如何在iPad屏幕上触摸时获取PDF注释

    我开发了一个使用 CATiled Layer 在 ipad 上显示 PDF 的应用程序 到目前为止 一切都很好 但是有一个问题确实让我剪掉了最后的头发 我有一个带有嵌入注释的 PDF 每个注释都有一个 URL 我可以找到触摸区域的坐标 但问
  • 节点脚本永远不会结束

    我有下面的节点脚本来基本上复制一些文件的内容并将它们插入到 mongo 该脚本似乎永远不会结束 即使所有数据都已成功插入 我总是必须执行 Ctrl C 来终止它 我应该在 node js 中使用什么东西来结束脚本吗 var mongoose
  • Jenkins CI 与 Chef 服务器

    有什么方法可以将 Jenkins 与 Chef Server 集成 以便我可以创建要执行的作业吗 我不想去厨师工作站 我需要在单独的服务器或厨师工作站上安装 jenkins 吗 我可以直接从 Jenkins 控制台执行所有食谱吗 是的 你可
  • 是否有针对开发团队的 Web 服务 (API) 标准或最佳实践?

    如果您要开始为您的 Web 应用程序开发 API 是否有任何类型的指南 最佳实践或标准来构建 Web 服务 我已经看到了有关该主题的一些讨论 我想获得更多信息 至少得到关于从哪里获取信息的指示 提前致谢 关于 网络服务 存在着广泛的多样性和
  • Emberjs 使用 sortProperties 按日期对内容进行排序

    我正在尝试使用 Emberjs排序属性按日期对内容进行排序jsfiddle http jsfiddle net CmtpX 2 我的模型有一个开始时间我尝试排序的属性 但没有成功 然后我创建了一个名为的计算属性今日活动在返回与传入日期匹配的
  • 选择具有不同列名的表

    In SQL Select into 将行复制到不同的 备份 表中 如果备份表具有不同的结构 或不同的列名 这可能吗 如果没有 实现这一目标的最佳方法是什么 这是我想做的 TableA有列a1 a2 a3 我想将此表中的一些行复制到另一个表
  • console.log() 中的反斜杠“\”未出现

    我正在尝试使用反斜杠console log 并在其中 p p 但似乎当页面加载时 所有反斜杠都被删除 示例 JS console log m Lets rock m Result m Lets rock m 我怎样才能防止它被删除 EDIT
  • jQuery切换类如果其他不起作用

    我无法在 if else 条件下使用 jQuerytoggleClass 我的 HTML 代码是 ul li class firstLevel a href main cat 01 MAIN CATEGORY 1 a ul class di
  • 从作业中捕获详细流

    我正在努力成为一名优秀的 powerscript 用户 并根据最佳实践使用 Write Verbose 但我无法从正在运行的作业中获取 Verbose 流 Job Start Job Name Scanning Work Item Scri
  • didFailToRegisterForRemoteNotificationsWithError:用户拒绝错误?

    如果用户拒绝启用推送通知 我们将通过 didFailToRegisterForRemoteNotificationsWithError 收到错误 有谁知道在这种情况下 NSError 对象中的代码是什么 以将其与没有可用连接区分开来 我不认
  • SetJmp/LongJmp:为什么会抛出段错误?

    下面的代码总结了我目前遇到的问题 我当前的执行流程如下 我正在 GCC 4 3 中运行 jmp buf a buf jmp buf b buf void b helper printf entering b helper if setjmp
  • Azure AD B2C 与 React Native 博览会

    我是使用 React Native 和 Expo 进行移动应用程序开发的新手 我正在尝试使用 Azure AD B2C 在我的移动应用程序中添加身份验证 授权 但我发现从 Microsoft 文档中理解起来更困难 甚至我在 Google 上
  • Fullcalendar - 将事件保存在数据库中

    我有以下标记 我有一个 fullcalendar 的实例 当点击某一天时 触发dayClick callback 打开引导模式 用户可以在其中输入标题和开始 结束日期 单击 确定 后 提供的那些值将添加到日历中 这是代码 function
  • 访问父对象属性的“rails 方式”是什么?

    假设我有一个模型Doctor 和一个模型Patient A Patient belongs to a Doctor A Doctor有一个属性office 我想 给定一个Patient p 能够说p office并访问office of p
  • 条件编译时大小

    我想定义一个宏 如果条件涉及sizeof为 true 如果为 false 则不执行任何操作 但仍然编译 如果预处理器支持sizeof 它看起来像这样 if sizeof void lt sizeof unsigned int what go
  • 创建 python 优先级队列

    我想在 python 中构建一个优先级队列 其中队列包含不同的字典及其优先级编号 因此 当调用 get函数 时 优先级最高 编号最低 的字典将从队列中拉出 而当调用 add函数 时 新字典将被添加到队列中并根据其排序优先号码 请大家帮忙 提
  • 将 Serilog 与 WebApi2 结合使用的正确方法

    我正在寻找将 serilog 与 asp net web api 2 一起使用的正确方法 至于现在 我像这样初始化全局 Log Logger 属性 public static void Register HttpConfiguration