Microsoft Service Fabric и ведение журнала - PullRequest
1 голос
/ 09 мая 2019

Я изучаю лучший способ реализовать ведение журнала для моего API Service Stateless без учета состояния и был несколько ошеломлен различными решениями для того, что представляется относительно простым требованием.

Я реализовал ведение журнала с использованиемWebHostBuilder().ConfigureLogging и успешно зарегистрировал мои сообщения трассировки в окне отладки и через Serilog.Extensions.Logging.File Мне также удалось вывести этот журнал в файл, все это контролируется с помощью директивы #if DEBUG, и я был доволен.

Затем мне нужно было настроить, что будет происходить при развертывании в кластере в Azure, и это когда я перегружен !!!

Я подумал, что могу таким же образом зарегистрировать регистратор типа ServiceEventSourceкак я делал с AddDebug, но это было не так просто.

Поэтому мне удалось заставить мои журналы появляться в окне диагностики, используя ServiceEventSource.Current.Message, но эти журналы не интегрированы в ASP.NETрамки ведения журнала: /

Мое продолжающееся расследование привело меня к пониманию того, чтоВедение журнала Service Fabric должно быть направлено на Application Insights, хотя многие и многие статьи имеют различную степень детализации и применимы к последней платформе.

В настоящее время я думаю, что мне нужно удалить протоколирование ASP.NET и реализовать что-то подобноекак EventFlow, чтобы мои сообщения трассировки могли быть сгенерированы и впоследствии переданы в Application Insights для допроса на более поздний срок, верно ли мое мышление ??

Или я сейчас ухожу по касательной?

1 Ответ

1 голос
/ 13 мая 2019

ОБНОВЛЕНИЕ 15/05/2019
После развертывания этого в Azure Service Fabric файлы журнала не были заполнены, это, похоже, является несовместимостью между пакетом Serilog.Sinks.AzureBlobStorage NUGET и .NET Coreверсия 2.2.0, на которую был нацелен мой проект.

Я разместил тикет на странице GitHub и жду ответа, в краткосрочной перспективе вы можете скачать исходный код и перенести проект в Microsoft.NETCore.App 2.2.0 и прямо ссылаться на это, и все работает отлично.

ОРИГИНАЛЬНЫЙ ОТВЕТ
Кажется, я делаю это довольно часто, отвечая на мой собственный вопрос, но здесь снова.Мне потребовался день или два, чтобы разобраться в этом, и я решил поделиться своими выводами и решениями с сообществом в случае, если это может помочь кому-то еще в будущем и / или кому-то будет что добавить или даже противоречитья, что я бы приветствовал любой ввод.

Моя среда разработки выглядит следующим образом: -

Microsoft Visual Studio 15.9.11
Windows 10 Professional SDK: Microsoft.NETCore.App 2.2.0

Iсоздал новую Service State Stateless Service, целью которой является предоставление RESTful конечных точек интерфейсному веб-приложению Angular 7.

Моё требование состояло в том, чтобы предоставлять информацию журналирования как в моей среде разработки через окно Debug, так и длятакже предоставлять аналогичные данные журналов, пока мои приложения размещаются в кластере Service Fabric в Azure.

Установки пакетов NUGET
Microsoft.Extensions.Logging (2.2.0)
Serilog.AspNetCore (2.1.1)
Serilog.Enrichers.Environment (2.1.3)
Serilog.Settings.Configuration (3.0.1)
Serilog.Sinks.Debug (1.0.1)
Serilog.Sinks.AzureBlobStorage (1.3.0)

Управление средой разработки и производства
Я управляю дСреды разработки и производства, использующие директиву препроцессора DEBUG для включения файла appsettings.json или appsettings.Development.json.

Мой файл appSettings.Development.json выглядит следующим образом: -

{
  "AppSettings": {
     // My app settings not applicable to this
  },
  "Serilog": {
    "Using": [ "Serilog.Sinks.Debug" ],
    "MinimumLevel": {
      "Default": "Verbose",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "Debug",
        "Args": {
          "outputTemplate": "[{Timestamp:HH:mm:ss} {MachineName} {Level:u3}] {Message:lj}{NewLine}{Exception}"
        }
      }
    ],
    "Enrich": ["WithMachineName"]
  } 
}

Мой файл appSettings.json выглядит следующим образом: -

{
  "AppSettings": {
     // My app settings not applicable to this
  },
  "Serilog": {
    "Using": [ "Serilog.Sinks.AzureBlobStorage" ],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "AzureBlobStorage",
        "Args": {
          "outputTemplate": "[{Timestamp:HH:mm:ss} {MachineName} {Level:u3}] {Message:lj}{NewLine}{Exception}",
          "connectionString": "[Connection String]",
          "storageContainerName": "app",
          "storageFileName": "{yyyy}-{MM}-{dd}.log"
        }
      }
    ],
    "Enrich": [ "WithMachineName" ]
  }
}

Как видно из приведенных выше файлов настроек, я выводлю в окно отладки в процессе разработки и выбрал вывод в хранилище BLOB-объектов Azure.при развертывании в кластере Service Fabric в Azure.

Чтобы реализовать простое ведение журнала Serilog, ознакомьтесь с приведенной ниже реализацией класса обслуживания без сохранения состояния, в которой показано, как переключать два разных файла appSettings.json в зависимости от среды, а также какРегистратор Serilog вставляется в систему внедрения зависимостей с помощью метода расширения UseSerilog.

using System.Collections.Generic;
using System.Fabric;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.ServiceFabric.Services.Communication.AspNetCore;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
using Serilog;

namespace Caboodal.Manatee.ServiceFabric.Api.Identity
{
    internal sealed class Identity : StatelessService
    {
        public Identity(StatelessServiceContext context)
            : base(context)
        {
        }

        private string AppSettingsFilename
        {
            get
            {
#if DEBUG
                return "appsettings.Development.json";
#else
                return "appsettings.json";
#endif
            }
        }

        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        {
            var appSettings = GetAppSettings();

            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(appSettings)
                .CreateLogger();

            return new[]
            {
                new ServiceInstanceListener(
                    serviceContext =>
                        new KestrelCommunicationListener(
                            serviceContext,
                            "ServiceEndpoint",
                            (url, listener) =>
                            {
                                ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

                                return new WebHostBuilder()
                                    .UseKestrel()
                                    .ConfigureAppConfiguration(
                                        (builderContext, config) =>
                                        {
                                            config.AddJsonFile(AppSettingsFilename, false, true);
                                        })
                                    .ConfigureServices(
                                        services => services
                                            .AddSingleton(serviceContext))
                                    .UseContentRoot(Directory.GetCurrentDirectory())
                                    .UseSerilog()
                                    .UseStartup<Startup>()
                                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                                    .UseUrls(url)
                                    .Build();
                            }))
            };
        }

        private IConfigurationRoot GetAppSettings()
        {
            return new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile(AppSettingsFilename)
                .Build();
        }
    }
}

Использование регистратора в контроллере
Поскольку экземпляр ILogger настроенкак экземпляр Dependency Injected, к нему можно просто получить доступ в вашем классе контроллераКак и любая другая зависимость, например

    [Authorize]
    [ApiController]
    [Route("[controller]")]
    public class UserController : ApiController
    {
        private readonly IUserService _userService;
        private readonly ILogger<UserController> _logger;

        public UserController(IUserService userService, ILogger<UserController> logger)
        {
            _userService = userService;
            _logger = logger;
        }

        [AllowAnonymous]
        [HttpPost("authenticate")]
        public IActionResult Authenticate([FromBody] DtoAuthenticateRequest request)
        {
            // Adding log entries
            _logger.Log(LogLevel.Debug, "Here is a log entry");

            // Some code in here
            return Ok(response);
        }
    }

Я очень отвлекся на класс ServiceEventSource.cs, но с использованием Serilog я проигнорировал этот аспект шаблона проекта.

Если выВы хотите вывести свои журналы другим потребителям данных или просто в другие форматы, а затем просто просмотрите веб-сайт Serilog здесь для получения полного списка доступных приемников, причем Application Insights является одним из многих.

...