internal/telemetry: add gRPC transport for OTLP trace export

The OTLP specification defines two transports: HTTP (port 4318) and gRPC
(port 4317). The existing exporter setup only supports HTTP/HTTPS schemes.
This adds gRPC support via URL scheme dispatch:

  - grpc://host:port  → plaintext gRPC
  - grpcs://host:port → TLS-secured gRPC

Many observability backends (Jaeger, Tempo, Datadog) prefer gRPC for its
lower overhead. Both otlptracehttp and otlptracegrpc return the same
*otlptrace.Exporter type, so only the exporter construction changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Barnabas Busa 2026-03-03 12:53:53 +01:00
parent 1c9ddee16f
commit 0d1fb19613
No known key found for this signature in database
GPG key ID: D7AF98F214C59E4E
4 changed files with 20 additions and 3 deletions

View file

@ -1063,19 +1063,19 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
RPCTelemetryEndpointFlag = &cli.StringFlag{
Name: "rpc.telemetry.endpoint",
Usage: "Defines where RPC telemetry is sent (e.g., http://localhost:4318)",
Usage: "Defines where RPC telemetry is sent (e.g., http://localhost:4318 or grpc://localhost:4317)",
Category: flags.APICategory,
}
RPCTelemetryUserFlag = &cli.StringFlag{
Name: "rpc.telemetry.username",
Usage: "HTTP Basic Auth username for OpenTelemetry",
Usage: "Basic Auth username for OpenTelemetry",
Category: flags.APICategory,
}
RPCTelemetryPasswordFlag = &cli.StringFlag{
Name: "rpc.telemetry.password",
Usage: "HTTP Basic Auth password for OpenTelemetry",
Usage: "Basic Auth password for OpenTelemetry",
Category: flags.APICategory,
}

1
go.mod
View file

@ -63,6 +63,7 @@ require (
github.com/urfave/cli/v2 v2.27.5
go.opentelemetry.io/otel v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0
go.opentelemetry.io/otel/sdk v1.40.0
go.opentelemetry.io/otel/trace v1.40.0

2
go.sum
View file

@ -384,6 +384,8 @@ go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=

View file

@ -30,6 +30,7 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
@ -101,6 +102,19 @@ func SetupTelemetry(cfg node.OpenTelemetryConfig, stack *node.Node) error {
}))
}
exporter = otlptracehttp.NewUnstarted(opts...)
case "grpc", "grpcs":
opts := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(u.Host),
}
if u.Scheme == "grpc" {
opts = append(opts, otlptracegrpc.WithInsecure())
}
if cfg.AuthUser != "" {
opts = append(opts, otlptracegrpc.WithHeaders(map[string]string{
"Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte(cfg.AuthUser+":"+cfg.AuthPassword)),
}))
}
exporter = otlptracegrpc.NewUnstarted(opts...)
default:
return fmt.Errorf("unsupported telemetry url scheme: %s", u.Scheme)
}