1.Serilog with the 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) become attributes and therefore fields; the rendered message is the log body.2.Microsoft.Extensions.Logging with the 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.Containers: JSON console output
Keep the platform agent in charge. AddJsonConsole writes one JSON object per line; Message is the message and Timestamp the time.
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=TimestampFields you get
These show up in the fields panel and can be used in every filter:
service.name,deployment.environmentseverity_text- message template properties (
OrderId,DurationMs) Category/ logger name- exception details
Tips
- Use message templates (
{OrderId}), never string interpolation — only templates produce fields. - Add
Enrich.WithProperty("env", "prod")if you want a plainenvfield next to the resource attribute.
FAQ
- Does Serilog.Sinks.Http work too?
- Yes, with a custom
IHttpClientthat adds theAuthorizationheader andrequestUriset to/v1/ingest/jsonline?_msg_field=RenderedMessage&_time_field=Timestamp. The OpenTelemetry sink needs no custom code, so it is the recommended path. - Where do I get the ingest key?
- In the app under API Keys → New API Key → type “Log ingest”. The key starts with
ue_ingest_and is shown once. Management keys (ue_live_) are refused by the ingest endpoint. - How do I check that logs arrive?
- Open Logs in the app, pick the 15m range and search for
service:=<your service>. New lines are searchable within about a second; Live tail shows them with a ~6 s delay.
Related
OpenTelemetryPoint an OpenTelemetry Collector or any OTel SDK at UptimeEye Logs over OTLP/HTTP.HTTP API / curlThe UptimeEye Logs ingest API: send JSON lines with curl or your own code, choose stream fields, compress with gzip and read the response codes..DockerSend the logs of all containers on a Docker host to UptimeEye Logs: one Vector container on the Docker socket, or the Loki logging driver per container..Searching logsNow that lines arrive: filters, fields, time ranges and live tail.