Udostępnij za pośrednictwem


Add logging to your Service Fabric application (Dodawanie rejestrowania do aplikacji usługi Service Fabric)

Aplikacja musi podać wystarczającą ilość informacji, aby śledczo debugować ją w przypadku wystąpienia problemów. Rejestrowanie jest jedną z najważniejszych rzeczy, które można dodać do aplikacji usługi Service Fabric. Gdy wystąpi awaria, dobre rejestrowanie może dać możliwość zbadania błędów. Analizując wzorce dzienników, możesz znaleźć sposoby poprawy wydajności lub projektowania aplikacji. W tym dokumencie przedstawiono kilka różnych opcji rejestrowania.

EventFlow

Pakiet bibliotek EventFlow umożliwia aplikacjom definiowanie danych diagnostycznych do zbierania i miejsca ich wyprowadzania. Dane diagnostyczne mogą być niczym, od liczników wydajności po ślady aplikacji. Działa on w tym samym procesie co aplikacja, więc obciążenie komunikacji jest zminimalizowane. Aby uzyskać więcej informacji o przepływie zdarzeń i usłudze Service Fabric, zobacz Agregacja zdarzeń usługi Azure Service Fabric za pomocą rozwiązania EventFlow.

Używanie zdarzeń ze strukturą EventSource

Definiowanie zdarzeń komunikatów przy użyciu przypadku użycia umożliwia spakować dane o zdarzeniu w kontekście zdarzenia. Można łatwiej wyszukiwać i filtrować na podstawie nazw lub wartości określonych właściwości zdarzenia. Struktura danych wyjściowych instrumentacji ułatwia odczytywanie, ale wymaga więcej przemyśleń i czasu na zdefiniowanie zdarzenia dla każdego przypadku użycia.

Niektóre definicje zdarzeń można udostępniać w całej aplikacji. Na przykład zdarzenie uruchamiania lub zatrzymywania metody będzie ponownie używane w wielu usługach w aplikacji. Usługa specyficzna dla domeny, podobnie jak system zamówień, może mieć zdarzenie CreateOrder , które ma własne unikatowe zdarzenie. Takie podejście może generować wiele zdarzeń i potencjalnie wymagać koordynacji identyfikatorów w zespołach projektów.

[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);
    }

    ...

Ogólne używanie usługi EventSource

Definiowanie określonych zdarzeń może być trudne, dlatego wiele osób definiuje kilka zdarzeń ze wspólnym zestawem parametrów, które zazwyczaj generują informacje jako ciąg. Większość aspektu strukturalnego jest utracona i trudniej jest przeszukiwać i filtrować wyniki. W tym podejściu zdefiniowano kilka zdarzeń, które zwykle odpowiadają poziomom rejestrowania. Poniższy fragment kodu definiuje debugowanie i komunikat o błędzie:

[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);
    }

    ...

Użycie hybrydowej instrumentacji strukturalnej i ogólnej może również działać dobrze. Instrumentacja ustrukturyzowana służy do raportowania błędów i metryk. Zdarzenia ogólne mogą służyć do szczegółowego rejestrowania używanego przez inżynierów do rozwiązywania problemów.

Microsoft.Extensions.Logging

ASP.NET Core logging (Pakiet NuGet Microsoft.Extensions.Logging) to struktura rejestrowania, która udostępnia standardowy interfejs API rejestrowania dla aplikacji. Obsługę innych zapleczy rejestrowania można podłączyć do rejestrowania ASP.NET Core. Zapewnia to szeroką obsługę rejestrowania w aplikacji, bez konieczności zmiany kodu.

  1. Dodaj pakiet NuGet Microsoft.Extensions.Logging do projektu, który chcesz instrumentować. Ponadto dodaj wszystkie pakiety dostawcy. Aby uzyskać więcej informacji, zobacz Rejestrowanie w usłudze ASP.NET Core.

  2. Dodaj dyrektywę using dla pliku usługi Microsoft.Extensions.Logging .

  3. Zdefiniuj zmienną prywatną w klasie usługi.

    private ILogger _logger = null;
    
  4. W konstruktorze klasy usługi dodaj następujący kod:

    _logger = new LoggerFactory().CreateLogger<Stateless>();
    
  5. Rozpocznij instrumentację kodu w metodach. Oto kilka przykładów:

    _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);
    

Korzystanie z innych dostawców rejestrowania

Niektórzy dostawcy innych firm używają podejścia opisanego w poprzedniej sekcji, w tym Serilog, NLog i Loggr. Każdy z tych elementów można podłączyć do rejestrowania ASP.NET Core lub użyć ich oddzielnie. Serilog ma funkcję, która wzbogaca wszystkie komunikaty wysyłane z rejestratora. Ta funkcja może być przydatna do wyprowadzania informacji o nazwie, typie i partycji usługi. Aby użyć tej funkcji w infrastrukturze ASP.NET Core, wykonaj następujące kroki:

  1. Dodaj do projektu pakiety Serilog, Serilog.Extensions.Logging, Serilog.Sinks.Literate i Serilog.Sinks.Observable NuGet.

  2. LoggerConfiguration Utwórz wystąpienie rejestratora i .

    Log.Logger = new LoggerConfiguration().WriteTo.LiterateConsole().CreateLogger();
    
  3. Serilog.ILogger Dodaj argument do konstruktora usługi i przekaż nowo utworzony rejestrator.

    ServiceRuntime.RegisterServiceAsync("StatelessType", context => new Stateless(context, Log.Logger)).GetAwaiter().GetResult();
    
  4. W konstruktorze usługi tworzy wzbogacacze właściwości dla parametrów ServiceTypeName, ServiceName, PartitionId i 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. Instrumentacja kodu w taki sam sposób, jak w przypadku używania ASP.NET Core bez serilogu.

    Uwaga

    Zalecamy, aby nie używać statycznego Log.Logger z poprzednim przykładem. Usługa Service Fabric może hostować wiele wystąpień tego samego typu usługi w ramach jednego procesu. Jeśli używasz statycznego Log.Loggerobiektu , ostatni składnik zapisywania właściwości będzie wyświetlać wartości dla wszystkich uruchomionych wystąpień. Jest to jeden z powodów, dla których zmienna _logger jest prywatną zmienną składową klasy usługi. Ponadto należy udostępnić _logger wspólny kod, który może być używany w usługach.

Następne kroki