Přidání protokolování do aplikace Service Fabric

Aplikace musí poskytnout dostatek informací k jejímu forenznímu ladění, když dojde k problémům. Protokolování je jednou z nejdůležitějších věcí, které můžete do aplikace Service Fabric přidat. Když dojde k selhání, dobré protokolování vám může poskytnout způsob, jak prošetřit selhání. Analýzou vzorů protokolů můžete najít způsoby, jak zlepšit výkon nebo návrh vaší aplikace. Tento dokument ukazuje několik různých možností protokolování.

EventFlow

Sada knihoven EventFlow umožňuje aplikacím definovat, jaká diagnostická data se mají shromažďovat a kam se mají vysílat. Diagnostická data můžou být cokoli od čítačů výkonu až po trasování aplikací. Běží ve stejném procesu jako aplikace, takže je minimalizovaná komunikační režie. Další informace o EventFlow a Service Fabric najdete v tématu Agregace událostí Azure Service Fabric pomocí EventFlow.

Použití strukturovaných událostí EventSource

Definování událostí zpráv podle případu použití umožňuje zabalit data o události v kontextu události. Můžete snadněji vyhledávat a filtrovat na základě názvů nebo hodnot zadaných vlastností události. Strukturování výstupu instrumentace usnadňuje čtení, ale vyžaduje více promyšlení a času na definování události pro každý případ použití.

Některé definice událostí je možné sdílet napříč celou aplikací. Například událost spuštění nebo zastavení metody by se opakovaně používala napříč mnoha službami v rámci aplikace. Služba specifická pro doménu, jako je systém objednávek, může mít událost CreateOrder , která má svou vlastní jedinečnou událost. Tento přístup může generovat mnoho událostí a potenciálně vyžadovat koordinaci identifikátorů mezi projektovými týmy.

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

    ...

Obecné použití EventSource

Protože definování konkrétních událostí může být obtížné, mnoho lidí definuje několik událostí se společnou sadou parametrů, které obvykle vypíše jejich informace jako řetězec. Většina strukturovaného aspektu se ztratí a je obtížnější výsledky prohledávat a filtrovat. V tomto přístupu se definuje několik událostí, které obvykle odpovídají úrovním protokolování. Následující fragment kódu definuje ladění a chybovou zprávu:

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

    ...

Dobře může fungovat také použití hybridní strukturované a obecné instrumentace. Strukturovaná instrumentace se používá k hlášení chyb a metrik. Obecné události se dají použít pro podrobné protokolování, které využívají technici pro řešení potíží.

Microsoft.Extensions.Logging

Protokolování ASP.NET Core (balíček NuGet Microsoft.Extensions.Logging) je protokolovací architektura, která poskytuje standardní rozhraní API pro protokolování pro vaši aplikaci. Podporu pro další back-endy protokolování je možné připojit k protokolování ASP.NET Core. Díky tomu získáte širokou škálu podpory pro zpracování protokolování ve vaší aplikaci, aniž byste museli měnit mnoho kódu.

  1. Do projektu, který chcete instrumentovat, přidejte balíček NuGet Microsoft.Extensions.Logging . Přidejte také všechny balíčky poskytovatelů. Další informace najdete v tématu Protokolování v ASP.NET Core.

  2. Přidejte do souboru služby direktivu using pro Microsoft.Extensions.Logging .

  3. Definujte privátní proměnnou v rámci třídy služby.

    private ILogger _logger = null;
    
  4. Do konstruktoru vaší třídy služby přidejte tento kód:

    _logger = new LoggerFactory().CreateLogger<Stateless>();
    
  5. Začněte instrumentovat kód v metodách. Tady je několik ukázek:

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

Použití jiných zprostředkovatelů protokolování

Někteří poskytovatelé třetích stran používají přístup popsaný v předchozí části, včetně služeb Serilog, NLog a Loggr. Každou z nich můžete zapojit do protokolování ASP.NET Core nebo je můžete použít samostatně. Serilog má funkci, která rozšiřuje všechny zprávy odeslané protokolovacím nástroje. Tato funkce může být užitečná pro výstup názvu služby, typu a informací o oddílu. Pokud chcete tuto funkci použít v infrastruktuře ASP.NET Core, postupujte takto:

  1. Přidejte do projektu balíčky NuGet Serilog, Serilog.Extensions.Logging, Serilog.Sinks.Literate a Serilog.Sinks.Observable .

  2. LoggerConfiguration Vytvořte instanci protokolovacího nástroje a .

    Log.Logger = new LoggerConfiguration().WriteTo.LiterateConsole().CreateLogger();
    
  3. Serilog.ILogger Přidejte argument do konstruktoru služby a předejte nově vytvořený protokolovací nástroj.

    ServiceRuntime.RegisterServiceAsync("StatelessType", context => new Stateless(context, Log.Logger)).GetAwaiter().GetResult();
    
  4. V konstruktoru služby vytvoří rozšiřovače vlastností pro ServiceTypeName, ServiceName, PartitionId a 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. Instrumentujte kód stejně, jako kdybyste používali ASP.NET Core bez nástroje Serilog.

    Poznámka

    V předchozím příkladu doporučujeme nepoužívat statický Log.Logger . Service Fabric může hostovat více instancí stejného typu služby v rámci jednoho procesu. Pokud použijete statický Log.Loggerobjekt , poslední zapisovač z rozšiřování vlastností zobrazí hodnoty pro všechny spuštěné instance. To je jeden z důvodů, proč je proměnná _logger privátní členovou proměnnou třídy služby. Musíte také zpřístupnit _logger běžný kód, který se může používat napříč službami.

Další kroky