Правильный способ использования Serilog с WebApi2 - PullRequest
3 голосов
/ 25 марта 2019

Я в поиске правильного способа использования serilog с aspnet webapi2. На данный момент я инициализирую глобальное свойство 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 }
        );
    }

Есть ли более чистый способ сделать это? Мне интересно, может ли это быть проблемой, если мне придется провести какой-то тест для моих контроллеров.

1 Ответ

2 голосов
/ 26 марта 2019

Я довольно часто использовал следующую организацию кода для приложений с Web API (и / или MVC).(обратите внимание, что он может быть немного устаревшим, так как он основан на старых версиях некоторых пакетов, но вы сможете адаптировать его без особой работы ...)

Вам понадобится немало пакетов, что вы должны быть в состоянии угадать из пространств имен, но самое главное, установить пакет WebActivatorEx, который обеспечивает способ выполнения кода в другой момент жизненного цикла приложения

Наш 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):

App_Start folder structure

  • 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();
        }
    }
}

, а затем считайте переменные части конфигурации журнала из 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)"/>

Некоторые из наиболее важных частей Tего конфигурация:

  • сконфигурируйте ваш регистратор как можно скорее
  • вызовите Log.CloseAndFlush(); в самом конце, чтобы убедиться, что все ваши события журнала сохранены / отправлены
  • добавьте Enrich.FromLogContext() из Serilog и некоторые обогащения из SerilogWeb.Classic и SerilogWeb.WebApi, они могут оказаться очень полезными.
  • регистрация на сервере журналов, который должным образом поддерживает структурированное ведение журнала (запись в файлы простоимеет слишком много недостатков) ... мы использовали Seq и были очень довольны этим (устанавливались локально на каждую машину разработчика, а затем производили централизованный экземпляр).Он поддерживает поиск / запросы и информационные панели, а также динамический контроль уровня журнала.
...