.NET-Logs an UptimeEye senden (Serilog, OpenTelemetry)

Für .NET ist der Weg über OpenTelemetry der bequemste: Der OpenTelemetry-Sink von Serilog oder das Paket OpenTelemetry.Exporter.OpenTelemetryProtocol schicken strukturierte Records direkt an UptimeEye, und die Properties der Message-Templates werden zu Feldern. In Containern funktioniert JSON auf der Konsole plus der Agent der Plattform genauso gut.

Endpunkt: https://logs.uptimeeye.com/v1/ingest/otlp/v1/logs

1.Serilog mit dem OpenTelemetry-Sink

Program.cs
// dotnet add package Serilog.AspNetCore
// dotnet add package Serilog.Sinks.OpenTelemetry
using Serilog;
using Serilog.Sinks.OpenTelemetry;

builder.Host.UseSerilog((ctx, lc) => lc
    .ReadFrom.Configuration(ctx.Configuration)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.OpenTelemetry(o =>
    {
        o.Endpoint = "https://logs.uptimeeye.com/v1/ingest/otlp/v1/logs";
        o.Protocol = OtlpProtocol.HttpProtobuf;
        o.Headers = new Dictionary<string, string>
        {
            ["Authorization"] = $"Bearer {Environment.GetEnvironmentVariable("UPTIMEEYE_INGEST_KEY")}",
            ["VL-Stream-Fields"] = "service.name,deployment.environment",
        };
        o.ResourceAttributes = new Dictionary<string, object>
        {
            ["service.name"] = "checkout",
            ["deployment.environment"] = "prod",
        };
    }));

// later
Log.Information("payment authorized for {OrderId} in {DurationMs} ms", "4711", 812);
Note: Template-Properties (OrderId, DurationMs) werden zu Attributen und damit zu Feldern; die gerenderte Nachricht ist der Body des Logs.

2.Microsoft.Extensions.Logging mit dem OpenTelemetry SDK

Program.cs
// dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
using OpenTelemetry.Exporter;

builder.Logging.AddOpenTelemetry(logging =>
{
    logging.IncludeFormattedMessage = true;
    logging.SetResourceBuilder(ResourceBuilder.CreateDefault()
        .AddService("checkout")
        .AddAttributes(new[] { new KeyValuePair<string, object>("deployment.environment", "prod") }));
    logging.AddOtlpExporter(o =>
    {
        o.Endpoint = new Uri("https://logs.uptimeeye.com/v1/ingest/otlp/v1/logs");
        o.Protocol = OtlpExportProtocol.HttpProtobuf;
        o.Headers = $"Authorization=Bearer {Environment.GetEnvironmentVariable("UPTIMEEYE_INGEST_KEY")}";
    });
});

3.Container: JSON auf der Konsole

Lass den Agent der Plattform den Versand übernehmen. AddJsonConsole schreibt ein JSON-Objekt pro Zeile; Message ist die Nachricht und Timestamp die Zeit.

Program.cs
builder.Logging.ClearProviders();
builder.Logging.AddJsonConsole(o =>
{
    o.IncludeScopes = true;
    o.TimestampFormat = "yyyy-MM-dd'T'HH:mm:ss.fffZ";
    o.UseUtcTimestamp = true;
});
// agent side: _msg_field=Message&_time_field=Timestamp

Felder, die du bekommst

Diese Felder erscheinen im Panel „Felder“ und funktionieren in jedem Filter:

  • service.name, deployment.environment
  • severity_text
  • Properties der Message-Templates (OrderId, DurationMs)
  • Category / Logger-Name
  • Exception-Details

Tipps

  • Nimm Message-Templates ({OrderId}), nie String-Interpolation – nur Templates erzeugen Felder.
  • Ergänz Enrich.WithProperty("env", "prod"), wenn du neben dem Resource-Attribut ein schlichtes Feld env willst.

FAQ

Funktioniert Serilog.Sinks.Http auch?

Ja, mit einem eigenen IHttpClient, der den Header Authorization setzt, und requestUri auf /v1/ingest/jsonline?_msg_field=RenderedMessage&_time_field=Timestamp. Der OpenTelemetry-Sink braucht keinen eigenen Code und ist deshalb der empfohlene Weg.

Wo bekomme ich den Ingest-Key?

In der App unter API-Keys → Neuer API-Key → Typ „Log-Ingest“. Der Key beginnt mit ue_ingest_ und wird nur einmal angezeigt. Management-Keys (ue_live_) lehnt der Ingest-Endpoint ab.

Wie sehe ich, ob Logs ankommen?

Öffne Logs in der App, wähl den Zeitraum „15 Min.“ und such nach service:=<your service>. Neue Zeilen sind nach etwa einer Sekunde durchsuchbar; Live-Tail zeigt sie mit etwa 6 s Verzögerung.