ECS and OTel endpoints
Wired streams expose specialized bulk endpoints that control how field names are stored. See Elastic's wired streams field naming for the full conversion table.
| Endpoint | Path | Behavior |
|---|---|---|
WiredStreamIngestEndpoint.Logs |
logs/_bulk |
Default; backward-compatible generic wired path |
WiredStreamIngestEndpoint.LogsEcs |
logs.ecs/_bulk |
Stores ECS field names as sent |
WiredStreamIngestEndpoint.LogsOtel |
logs.otel/_bulk |
Stores OTel semantic conventions; creates ECS aliases for existing queries |
[ElasticsearchMappingContext]
[WiredStream<EcsLog>(
Type = "logs",
Dataset = "dotnet",
IngestEndpoint = WiredStreamIngestEndpoint.LogsEcs)]
public static partial class EcsWiredContext;
Documents should use Elastic Common Schema field names. A minimal shape:
public class EcsLog
{
[Timestamp]
[JsonPropertyName("@timestamp")]
public DateTimeOffset Timestamp { get; set; }
[JsonPropertyName("message")]
public string Message { get; set; }
[JsonPropertyName("log.level")]
public string LogLevel { get; set; }
[JsonPropertyName("service.name")]
public string ServiceName { get; set; }
[JsonPropertyName("host.name")]
public string? HostName { get; set; }
[JsonPropertyName("trace.id")]
public string? TraceId { get; set; }
}
Useful fields for Kibana Streams features (significant events, knowledge indicators, Streamlang conditions):
@timestamp— required for time-based viewsmessage/body.text— free-text used by extraction and queriesservice.*,host.*— entity identity for topology and knowledge indicatorslog.level/severity_text— filtering and alertingtrace.id,transaction.id— correlation with APM
elastic/ecs-dotnet formats logs as ECS JSON and ships via Serilog, NLog, or Microsoft.Extensions.Logging.
Elastic.Serilog.Sinks and related shippers use a classic data stream (logs-dotnet-default by default) with optional template bootstrap. That path maps to [DataStream<T>] in this library and appears as a classic stream in Kibana Streams.
// ecs-dotnet Serilog sink (classic)
.WriteTo.Elasticsearch(nodes, opts =>
{
opts.DataStream = new DataStreamName("logs", "dotnet", "production");
opts.BootstrapMethod = BootstrapMethod.Failure;
})
For Kibana wired streams, send ECS documents through IngestChannel with LogsEcs:
[ElasticsearchMappingContext]
[WiredStream<LogEventEcsDocument>(
Type = "logs",
Dataset = "dotnet",
IngestEndpoint = WiredStreamIngestEndpoint.LogsEcs)]
public static partial class WiredEcsContext;
// ...
var options = new IngestChannelOptions<LogEventEcsDocument>(
transport, WiredEcsContext.LogEventEcsDocument.Context);
using var channel = new IngestChannel<LogEventEcsDocument>(options);
await channel.BootstrapElasticsearchAsync(BootstrapMethod.None);
channel.TryWrite(ecsDocument);
Until ecs-dotnet sinks expose a first-class wired option, Path B is the supported way to target logs.ecs from .NET.
Use IngestEndpoint = LogsOtel when documents follow OTel log semantic conventions (or when you want Streams to normalize ECS → OTel storage). Prefer this for OpenTelemetry Collector / OTLP-shaped payloads rather than ecs-dotnet formatters.