Share via


Adicionar registos à sua aplicação do Service Fabric

A sua aplicação tem de fornecer informações suficientes para depurá-la forense quando surgirem problemas. O registo é um dos aspetos mais importantes que pode adicionar à sua aplicação do Service Fabric. Quando ocorre uma falha, um bom registo pode dar-lhe uma forma de investigar falhas. Ao analisar padrões de registo, pode encontrar formas de melhorar o desempenho ou a estrutura da sua aplicação. Este documento demonstra algumas opções de registo diferentes.

Fluxo de Eventos

O conjunto de bibliotecas do EventFlow permite que as aplicações definam os dados de diagnóstico a recolher e para onde devem ser exportados. Os dados de diagnóstico podem ser qualquer coisa, desde contadores de desempenho a rastreios de aplicações. É executada no mesmo processo que a aplicação, pelo que a sobrecarga de comunicação é minimizada. Para obter mais informações sobre o EventFlow e o Service Fabric, veja Agregação de Eventos do Azure Service Fabric com o EventFlow.

Utilizar eventos estruturados do EventSource

Definir eventos de mensagens por caso de utilização permite-lhe empacotar dados sobre o evento, no contexto do evento. Pode procurar e filtrar mais facilmente com base nos nomes ou valores das propriedades do evento especificadas. Estruturar a saída de instrumentação facilita a leitura, mas requer mais tempo e mais reflexão para definir um evento para cada caso de utilização.

Algumas definições de eventos podem ser partilhadas em toda a aplicação. Por exemplo, um evento de início ou paragem de métodos seria reutilizado em muitos serviços numa aplicação. Um serviço específico do domínio, como um sistema de encomendas, pode ter um evento CreateOrder , que tem o seu próprio evento exclusivo. Esta abordagem pode gerar muitos eventos e, potencialmente, exigir coordenação de identificadores entre equipas 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);
    }

    ...

Utilizar o EventSource genericamente

Uma vez que 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 as suas informações como uma cadeia. Grande parte do aspeto estruturado perde-se e é mais difícil procurar e filtrar os resultados. Nesta abordagem, são definidos alguns eventos que normalmente correspondem aos níveis de registo. O fragmento seguinte 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);
    }

    ...

A utilização de um híbrido de instrumentação estruturada e genérica também pode funcionar bem. A instrumentação estruturada é utilizada para comunicar erros e métricas. Os eventos genéricos podem ser utilizados para o registo detalhado que é consumido pelos engenheiros para resolução de problemas.

Microsoft.Extensions.Logging

O registo de ASP.NET Core (pacote NuGet Microsoft.Extensions.Logging) é uma arquitetura de registo que fornece uma API de registo padrão para a sua aplicação. O suporte para outros back-ends de registo pode ser ligado ao registo de ASP.NET Core. Isto dá-lhe uma grande variedade de suporte para o registo na sua aplicação é processado, sem ter de alterar muito código.

  1. Adicione o pacote NuGet Microsoft.Extensions.Logging ao projeto que pretende instrumentar. Além disso, adicione quaisquer pacotes de fornecedor. Para obter mais informações, veja Iniciar sessão no ASP.NET Core.

  2. Adicione uma diretiva de utilização para Microsoft.Extensions.Logging ao seu ficheiro de serviço.

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

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

    _logger = new LoggerFactory().CreateLogger<Stateless>();
    
  5. Comece a instrumentar o código nos seus métodos. Seguem-se 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);
    

Utilizar outros fornecedores de registo

Alguns fornecedores de terceiros utilizam a abordagem descrita na secção anterior, incluindo Serilog, NLog e Loggr. Pode ligar cada um deles ao registo de ASP.NET Core ou pode utilizá-los separadamente. O Serilog tem uma funcionalidade que melhora todas as mensagens enviadas a partir de um logger. Esta funcionalidade pode ser útil para produzir o nome, o tipo e as informações de partição do serviço. Para utilizar esta capacidade na infraestrutura ASP.NET Core, efetue estes passos:

  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ços e transmita o logger recém-criado.

    ServiceRuntime.RegisterServiceAsync("StatelessType", context => new Stateless(context, Log.Logger)).GetAwaiter().GetResult();
    
  4. No construtor de serviços, cria melhoradores de propriedades 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 que se estivesse a utilizar ASP.NET Core sem Serilog.

    Nota

    Recomendamos que não utilize a estática Log.Logger com o exemplo anterior. O Service Fabric pode alojar várias instâncias do mesmo tipo de serviço num único processo. Se utilizar o estático Log.Logger, o último escritor dos melhoradores de propriedades mostrará valores para todas as instâncias em execução. Esta é uma das razões pelas quais a variável _logger é uma variável de membro privado da classe de serviço. Além disso, tem de disponibilizar o _logger código comum, que pode ser utilizado em todos os serviços.

Passos seguintes