Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

distro: Add opt-in logs support #3673

Merged
merged 3 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

This release adds experimental logs support. Set `OTEL_LOGS_EXPORTER=otlp` to
enable logs support. However, take notice that, as of now, the OpenTelemetry Go
Logs API is not stable.

### Added
ysolomchenko marked this conversation as resolved.
Show resolved Hide resolved

- Add logs support and `OTEL_LOGS_EXPORTER` environment variable.
`OTEL_LOGS_EXPORTER` accepts:
- `none` - logs disabled,
- `otlp` - OTLP gRPC exporter.

Currently, `OTEL_LOGS_EXPORTER` defaults to `none` as the OpenTelemetry Go
Logs API and SDK are not stable yet. Set `OTEL_LOGS_EXPORTER=otlp` to enable
logs support. (#3673)

## [1.24.0] - 2025-01-22

This release upgrades [OpenTelemetry Go to v1.34.0/v0.56.0/v0.10.0][otel-v1.34.0]
Expand Down
5 changes: 5 additions & 0 deletions distro/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ const (
// OpenTelemetry exporter to use.
otelTracesExporterKey = "OTEL_TRACES_EXPORTER"
otelMetricsExporterKey = "OTEL_METRICS_EXPORTER"
otelLogsExporterKey = "OTEL_LOGS_EXPORTER"

// OpenTelemetry exporter endpoints.
otelExporterJaegerEndpointKey = "OTEL_EXPORTER_JAEGER_ENDPOINT"
otelExporterOTLPEndpointKey = "OTEL_EXPORTER_OTLP_ENDPOINT"
otelExporterOTLPTracesEndpointKey = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
otelExporterOTLPMetricsEndpointKey = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"
otelExporterOTLPLogsEndpointKey = "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"

// Logging level to set when using the default logger.
otelLogLevelKey = "OTEL_LOG_LEVEL"
Expand All @@ -54,6 +56,7 @@ const (
defaultAccessToken = ""
defaultTraceExporter = "otlp"
defaultMetricsExporter = "otlp"
defaultLogsExporter = "none"
defaultLogLevel = "info"

jaegerDefaultEndpoint = "http://127.0.0.1:9080/v1/trace"
Expand All @@ -80,6 +83,7 @@ type config struct {
ExportConfig *exporterConfig
TracesExporterFunc traceExporterFunc
MetricsExporterFunc metricsExporterFunc
LogsExporterFunc logsExporterFunc
}

// newConfig returns a validated config with Splunk defaults.
Expand All @@ -97,6 +101,7 @@ func newConfig(opts ...Option) *config {
}
c.TracesExporterFunc = tracesExporter(c.Logger)
c.MetricsExporterFunc = metricsExporter(c.Logger)
c.LogsExporterFunc = logsExporter(c.Logger)
return c
}

Expand Down
48 changes: 48 additions & 0 deletions distro/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import (

"github.com/go-logr/logr"
"go.opentelemetry.io/otel/exporters/jaeger" //nolint:staticcheck // Jaeger is deprecated, but we still support it to not break existing users.
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/trace"
"google.golang.org/grpc/credentials"
Expand Down Expand Up @@ -236,6 +238,52 @@ func newOTLPMetricsExporter(c *exporterConfig) (metric.Exporter, error) {
return otlpmetricgrpc.New(ctx, opts...)
}

type logsExporterFunc func(*exporterConfig) (log.Exporter, error)

// logsExporters maps environment variable values to logs exporter creation
// functions.
var logsExporters = map[string]logsExporterFunc{
// OTLP gRPC exporter.
"otlp": newOTLPlogExporter,
// None, explicitly do not set an exporter.
"none": nil,
}

func logsExporter(log logr.Logger) logsExporterFunc {
key := envOr(otelLogsExporterKey, defaultLogsExporter)
lef, ok := logsExporters[key]
if !ok {
err := fmt.Errorf("invalid %s: %q", otelLogsExporterKey, key)
log.Error(err, "using default %s: %q", otelLogsExporterKey, defaultLogsExporter)

return logsExporters[defaultLogsExporter]
}
return lef
}

func newOTLPlogExporter(c *exporterConfig) (log.Exporter, error) {
ctx := context.Background()

var opts []otlploggrpc.Option
MrAlias marked this conversation as resolved.
Show resolved Hide resolved

// SPLUNK_REALM is not supported, Splunk Observability ingest does not support OTLP.
if c.AccessToken != "" {
opts = append(opts, otlploggrpc.WithHeaders(map[string]string{
"X-Sf-Token": c.AccessToken,
}))
}

if c.TLSConfig != nil {
tlsCreds := credentials.NewTLS(c.TLSConfig)
opts = append(opts, otlploggrpc.WithTLSCredentials(tlsCreds))
} else if noneEnvVarSet(otelExporterOTLPEndpointKey, otelExporterOTLPLogsEndpointKey) {
// Assume that the default endpoint (local collector) is non-TLS.
opts = append(opts, otlploggrpc.WithTLSCredentials(insecure.NewCredentials()))
}

return otlploggrpc.New(ctx, opts...)
}

// noneEnvVarSet returns true if none of provided env vars is set.
func noneEnvVarSet(envs ...string) bool {
for _, env := range envs {
Expand Down
3 changes: 3 additions & 0 deletions distro/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ require (
go.opentelemetry.io/contrib/propagators/autoprop v0.59.0
go.opentelemetry.io/otel v1.34.0
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.34.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0
go.opentelemetry.io/otel/log v0.8.0
go.opentelemetry.io/otel/sdk v1.34.0
go.opentelemetry.io/otel/sdk/log v0.8.0
go.opentelemetry.io/otel/sdk/metric v1.34.0
go.opentelemetry.io/otel/trace v1.34.0
go.opentelemetry.io/proto/otlp v1.5.0
Expand Down
6 changes: 6 additions & 0 deletions distro/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 h1:ajl4QczuJVA2TU9W9AGw++86Xga/RKt//16z/yxPgdk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0/go.mod h1:Vn3/rlOJ3ntf/Q3zAI0V5lDnTbHGaUsNUeF6nZmm7pA=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.34.0 h1:opwv08VbCZ8iecIWs+McMdHRcAXzjAeda3uG2kI/hcA=
Expand All @@ -59,10 +61,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0u
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk=
go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs=
go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
Expand Down
34 changes: 34 additions & 0 deletions distro/otel.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/log/global"
"go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
Expand Down Expand Up @@ -104,6 +106,15 @@ func Run(opts ...Option) (SDK, error) {
sdk.shutdownFuncs = append(sdk.shutdownFuncs, shutdownFn)
}

shutdownFn, err = runLogs(c, res)
if err != nil {
sdk.Shutdown(ctx) //nolint:errcheck // the Shutdown errors are logged
return SDK{}, err
}
if shutdownFn != nil {
sdk.shutdownFuncs = append(sdk.shutdownFuncs, shutdownFn)
}

return sdk, nil
}

Expand Down Expand Up @@ -189,6 +200,29 @@ func runMetrics(c *config, res *resource.Resource) (shutdownFunc, error) {
return provider.Shutdown, nil
}

func runLogs(c *config, res *resource.Resource) (shutdownFunc, error) {
if c.LogsExporterFunc == nil {
c.Logger.V(1).Info("OTEL_LOGS_EXPORTER set to none: Logs disabled")
// "none" exporter configured.
return nil, nil
}

exp, err := c.LogsExporterFunc(c.ExportConfig)
if err != nil {
return nil, err
}

o := []log.LoggerProviderOption{
log.WithResource(res),
log.WithProcessor(log.NewBatchProcessor(exp)),
}

provider := log.NewLoggerProvider(o...)
global.SetLoggerProvider(provider)

return provider.Shutdown, nil
}

func serviceNameDefined(r *resource.Resource) bool {
val, ok := r.Set().Value("service.name")
return ok && val.Type() == attribute.STRING && !strings.HasPrefix(val.AsString(), "unknown_service:")
Expand Down
Loading
Loading