Skip to content
Closed
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
31 changes: 31 additions & 0 deletions server/cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,29 @@ func main() {
}
}

// Optional OTLP export sink. Independent of S2; both can run together.
var otlpWriter *events.OTLPStorageWriter
if config.OTLPEndpoint != "" {
headers := map[string]string{}
if config.OTLPInstanceJWT != "" {
headers["Authorization"] = "Bearer " + config.OTLPInstanceJWT
}
slogger.Info("OTLP export enabled", "endpoint", config.OTLPEndpoint, "path", config.OTLPPath)
otlpWriter = events.NewOTLPStorageWriter(eventStream, events.OTLPConfig{
Endpoint: config.OTLPEndpoint,
URLPath: config.OTLPPath,
Insecure: config.OTLPInsecure,
Headers: headers,
ServiceName: config.OTLPServiceName,
InstanceName: config.OTLPInstanceName,
Metro: config.OTLPMetro,
}, slogger)
if err := otlpWriter.Start(ctx); err != nil {
slogger.Error("failed to start OTLP storage writer", "err", err)
os.Exit(1)
}
}

apiService, err := api.New(
recorder.NewFFmpegManager(),
recorder.NewFFmpegRecorderFactory(config.PathToFFmpeg, defaultParams, stz),
Expand Down Expand Up @@ -306,6 +329,14 @@ func main() {
slogger.Error("s2 storage writer stop failed", "err", err)
}
}

if otlpWriter != nil {
stopCtx, stopCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer stopCancel()
if err := otlpWriter.Stop(stopCtx); err != nil {
slogger.Error("otlp storage writer stop failed", "err", err)
}
}
}

func mustFFmpeg() {
Expand Down
22 changes: 22 additions & 0 deletions server/cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ type Config struct {
S2Basin string `envconfig:"S2_BASIN" default:""`
S2AccessToken string `envconfig:"S2_ACCESS_TOKEN" default:""`
S2Stream string `envconfig:"S2_STREAM" default:""`

// OTLP telemetry export. OTLPEndpoint (host[:port]) must be set to enable
// the OTLP sink, which forwards telemetry events to the metro-api relay (or
// a collector in development).
OTLPEndpoint string `envconfig:"OTLP_RELAY_ENDPOINT" default:""`
OTLPPath string `envconfig:"OTLP_RELAY_PATH" default:"/otlp-relay/v1/logs"`
OTLPInsecure bool `envconfig:"OTLP_RELAY_INSECURE" default:"false"`
OTLPInstanceJWT string `envconfig:"KERNEL_INSTANCE_JWT" default:""`
OTLPServiceName string `envconfig:"OTLP_SERVICE_NAME" default:"kernel-browser"`
OTLPInstanceName string `envconfig:"OTLP_INSTANCE_NAME" default:""`
OTLPMetro string `envconfig:"OTLP_METRO" default:""`
}

// LogValue implements slog.LogValuer, redacting secret fields.
Expand All @@ -55,6 +66,10 @@ func (c *Config) LogValue() slog.Value {
if c.S2AccessToken != "" {
s2AccessToken = "[redacted]"
}
otlpJWT := ""
if c.OTLPInstanceJWT != "" {
otlpJWT = "[redacted]"
}
return slog.GroupValue(
slog.Int("port", c.Port),
slog.Int("frame_rate", c.FrameRate),
Expand All @@ -73,6 +88,13 @@ func (c *Config) LogValue() slog.Value {
slog.String("s2_basin", c.S2Basin),
slog.String("s2_access_token", s2AccessToken),
slog.String("s2_stream", c.S2Stream),
slog.String("otlp_endpoint", c.OTLPEndpoint),
slog.String("otlp_path", c.OTLPPath),
slog.Bool("otlp_insecure", c.OTLPInsecure),
slog.String("otlp_instance_jwt", otlpJWT),
slog.String("otlp_service_name", c.OTLPServiceName),
slog.String("otlp_instance_name", c.OTLPInstanceName),
slog.String("otlp_metro", c.OTLPMetro),
)
}

Expand Down
24 changes: 24 additions & 0 deletions server/dev/otelcol/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Local OTel Collector for testing the OTLP telemetry sink (Phase 1).
# Receives OTLP/HTTP on :4318 and prints decoded records to stdout.
#
# docker run --rm -p 4318:4318 \
# -v "$PWD/server/dev/otelcol/config.yaml":/etc/otelcol-contrib/config.yaml \
# otel/opentelemetry-collector-contrib:latest
#
# Then run the server with:
# OTLP_RELAY_ENDPOINT=localhost:4318 OTLP_RELAY_PATH=/v1/logs OTLP_RELAY_INSECURE=true
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318

exporters:
debug:
verbosity: detailed

service:
pipelines:
logs:
receivers: [otlp]
exporters: [debug]
40 changes: 22 additions & 18 deletions server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,16 @@ require (
github.com/samber/lo v1.52.0
github.com/stretchr/testify v1.11.1
github.com/testcontainers/testcontainers-go v0.40.0
golang.org/x/sync v0.18.0
golang.org/x/sys v0.39.0
golang.org/x/term v0.37.0
go.opentelemetry.io/otel v1.44.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0
go.opentelemetry.io/otel/log v0.20.0
go.opentelemetry.io/otel/sdk v1.44.0
go.opentelemetry.io/otel/sdk/log v0.20.0
go.opentelemetry.io/proto/otlp v1.10.0
golang.org/x/sync v0.20.0
golang.org/x/sys v0.45.0
golang.org/x/term v0.43.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -39,6 +46,7 @@ require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
Expand All @@ -58,7 +66,7 @@ require (
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand Down Expand Up @@ -100,21 +108,17 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.39.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/sdk v1.39.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect
golang.org/x/crypto v0.44.0 // indirect
golang.org/x/mod v0.29.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/tools v0.38.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.1 // indirect
google.golang.org/protobuf v1.36.10 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect
golang.org/x/crypto v0.51.0 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/text v0.37.0 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/grpc v1.81.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/gorm v1.25.7 // indirect
modernc.org/libc v1.22.5 // indirect
Expand Down
Loading
Loading