Compartilhar via


Adicionar registos à sua aplicação do Service Fabric

Seu aplicativo deve fornecer informações suficientes para depurá-lo forense quando surgirem problemas. O registro em log é uma das coisas mais importantes que você pode adicionar ao seu aplicativo do Service Fabric. Quando ocorre uma falha, um bom registo pode dar-lhe uma forma de investigar falhas. Ao analisar padrões de log, você pode encontrar maneiras de melhorar o desempenho ou o design do seu aplicativo. Este documento demonstra algumas opções de registro diferentes.

Fluxo de Eventos

O pacote de bibliotecas EventFlow permite que os aplicativos definam quais dados de diagnóstico coletar e para onde eles devem ser produzidos. Os dados de diagnóstico podem ser qualquer coisa, desde contadores de desempenho até rastreamentos de aplicativos. Ele é executado no mesmo processo que o aplicativo, portanto, a sobrecarga de comunicação é minimizada. Para obter mais informações sobre o EventFlow e o Service Fabric, consulte Agregação de eventos do Azure Service Fabric com EventFlow.

Usando eventos EventSource estruturados

Definir eventos de mensagem por caso de uso permite empacotar dados sobre o evento, no contexto do evento. Você pode pesquisar e filtrar mais facilmente com base nos nomes ou valores das propriedades de evento especificadas. Estruturar a saída da instrumentação facilita a leitura, mas requer mais pensamento e tempo para definir um evento para cada caso de uso.

Algumas definições de evento podem ser compartilhadas em todo o aplicativo. Por exemplo, um evento de início ou parada de método seria reutilizado em muitos serviços dentro de um aplicativo. Um serviço específico de domínio, como um sistema de pedidos, pode ter um evento CreateOrder , que tem seu próprio evento exclusivo. Essa abordagem pode gerar muitos eventos e, potencialmente, exigir a coordenação de identificadores entre as equipes de projeto.

[EventSource(Name = "MyCompany-VotingState-VotingStateService")]
internal sealed class ServiceEventSource : EventSource
{
    public static readonly ServiceEventSource Current = new ServiceEventSource();

    // The instance constructor is private to enforce singleton semantics.
    private ServiceEventSource() : base() { }

    ...

    // The ServiceTypeRegistered event contains a unique identifier, an event attribute that defined the event, and the code implementation of the event.
    private const int ServiceTypeRegisteredEventId = 3;
    [Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)]
    public void ServiceTypeRegistered(int hostProcessId, string serviceType)
    {
        WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType);
    }

    // The ServiceHostInitializationFailed event contains a unique identifier, an event attribute that defined the event, and the code implementation of the event.
    private const int ServiceHostInitializationFailedEventId = 4;
    [Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)]
    public void ServiceHostInitializationFailed(string exception)
    {
        WriteEvent(ServiceHostInitializationFailedEventId, exception);
    }

    ...

Usando EventSource genericamente

Como a definição de eventos específicos pode ser difícil, muitas pessoas definem alguns eventos com um conjunto comum de parâmetros que geralmente produzem suas informações como uma cadeia de caracteres. Grande parte do aspeto estruturado é perdido, e é mais difícil pesquisar e filtrar os resultados. Nessa abordagem, alguns eventos que geralmente correspondem aos níveis de log são definidos. O trecho a seguir define uma mensagem de depuração e erro:

[EventSource(Name = "MyCompany-VotingState-VotingStateService")]
internal sealed class ServiceEventSource : EventSource
{
    public static readonly ServiceEventSource Current = new ServiceEventSource();

    // The Instance constructor is private, to enforce singleton semantics.
    private ServiceEventSource() : base() { }

    ...

    private const int DebugEventId = 10;
    [Event(DebugEventId, Level = EventLevel.Verbose, Message = "{0}")]
    public void Debug(string msg)
    {
        WriteEvent(DebugEventId, msg);
    }

    private const int ErrorEventId = 11;
    [Event(ErrorEventId, Level = EventLevel.Error, Message = "Error: {0} - {1}")]
    public void Error(string error, string msg)
    {
        WriteEvent(ErrorEventId, error, msg);
    }

    ...

Usar um híbrido de instrumentação estruturada e genérica também pode funcionar bem. A instrumentação estruturada é usada para relatar erros e métricas. Eventos genéricos podem ser usados para o log detalhado que é consumido pelos engenheiros para solução de problemas.

Microsoft.Extensões.Logging

O log ASP.NET Core (pacote NuGet Microsoft.Extensions.Logging) é uma estrutura de log que fornece uma API de log padrão para seu aplicativo. O suporte para outros back-ends de log pode ser conectado ao log ASP.NET Core. Isso lhe dá uma ampla variedade de suporte para que seu aplicativo seja processado, sem ter que alterar muito código.

  1. Adicione o pacote NuGet Microsoft.Extensions.Logging ao projeto que você deseja instrumentar. Além disso, adicione quaisquer pacotes de provedor. Para obter mais informações, consulte Efetuando login no ASP.NET Core.

  2. Adicione uma diretiva using para Microsoft.Extensions.Logging ao seu arquivo de serviço.

  3. Defina uma variável privada dentro da sua classe de serviço.

    private ILogger _logger = null;
    
  4. No construtor de sua classe de serviço, adicione este código:

    _logger = new LoggerFactory().CreateLogger<Stateless>();
    
  5. Comece a instrumentar seu código em seus métodos. Aqui estão alguns exemplos:

    _logger.LogDebug("Debug-level event from Microsoft.Logging");
    _logger.LogInformation("Informational-level event from Microsoft.Logging");
    
    // In this variant, we're adding structured properties RequestName and Duration, which have values MyRequest and the duration of the request.
    // Later in the article, we discuss why this step is useful.
    _logger.LogInformation("{RequestName} {Duration}", "MyRequest", requestDuration);
    

Usando outros provedores de registro em log

Alguns provedores de terceiros usam a abordagem descrita na seção anterior, incluindo Serilog, NLog e Loggr. Você pode conectar cada um deles ao registro do ASP.NET Core ou usá-los separadamente. Serilog tem um recurso que enriquece todas as mensagens enviadas de um logger. Esse recurso pode ser útil para gerar as informações de nome, tipo e partição do serviço. Para usar esse recurso na infraestrutura ASP.NET Core, execute estas etapas:

  1. Adicione os pacotes Serilog, Serilog.Extensions.Logging, Serilog.Sinks.Literate e Serilog.Sinks.Observable NuGet ao projeto.

  2. Crie uma LoggerConfiguration instância e o logger.

    Log.Logger = new LoggerConfiguration().WriteTo.LiterateConsole().CreateLogger();
    
  3. Adicione um Serilog.ILogger argumento ao construtor de serviço e passe o registrador recém-criado.

    ServiceRuntime.RegisterServiceAsync("StatelessType", context => new Stateless(context, Log.Logger)).GetAwaiter().GetResult();
    
  4. No construtor de serviço, cria enriquecedores de propriedade para ServiceTypeName, ServiceName, PartitionId e InstanceId.

    public Stateless(StatelessServiceContext context, Serilog.ILogger serilog)
        : base(context)
    {
        PropertyEnricher[] properties = new PropertyEnricher[]
        {
            new PropertyEnricher("ServiceTypeName", context.ServiceTypeName),
            new PropertyEnricher("ServiceName", context.ServiceName),
            new PropertyEnricher("PartitionId", context.PartitionId),
            new PropertyEnricher("InstanceId", context.ReplicaOrInstanceId),
        };
    
        serilog.ForContext(properties);
    
        _logger = new LoggerFactory().AddSerilog(serilog.ForContext(properties)).CreateLogger<Stateless>();
    }
    
  5. Instrumente o código da mesma forma como se estivesse usando ASP.NET Core sem Serilog.

    Nota

    Recomendamos que você não use a estática Log.Logger com o exemplo anterior. O Service Fabric pode hospedar várias instâncias do mesmo tipo de serviço em um único processo. Se você usar o estático Log.Logger, o último gravador dos enriquecedores de propriedade mostrará valores para todas as instâncias que estão em execução. Esta é uma razão pela qual a variável _logger é uma variável membro privada da classe de serviço. Além disso, você deve disponibilizar o _logger código comum, que pode ser usado em todos os serviços.

Próximos passos