Skip to content

Commit 424e64b

Browse files
committed
feat: add timeout configuration and header parsing for OTLP in telemetry
Signed-off-by: André Silva <[email protected]>
1 parent cf4e826 commit 424e64b

File tree

3 files changed

+75
-2
lines changed

3 files changed

+75
-2
lines changed

core/pkg/telemetry/builder.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"crypto/x509"
77
"fmt"
88
"os"
9+
"strings"
910
"time"
1011

1112
"connectrpc.com/connect"
@@ -40,6 +41,7 @@ type CollectorConfig struct {
4041
CAPath string
4142
Headers string
4243
Protocol string
44+
Timeout time.Duration
4345
}
4446

4547
// Config of the telemetry runtime. These are expected to be mapped to start-up arguments
@@ -162,6 +164,26 @@ func buildTransportCredentials(_ context.Context, cfg CollectorConfig) (credenti
162164
return creds, nil
163165
}
164166

167+
// parseOTelHeaders parses the OTEL_EXPORTER_OTLP_HEADERS format (key1=value1,key2=value2)
168+
// into a map[string]string
169+
func parseOTelHeaders(headersStr string) map[string]string {
170+
headers := make(map[string]string)
171+
if headersStr == "" {
172+
return headers
173+
}
174+
175+
// Split by comma to get individual key=value pairs
176+
pairs := strings.Split(headersStr, ",")
177+
for _, pair := range pairs {
178+
// Split by = to separate key and value
179+
kv := strings.SplitN(strings.TrimSpace(pair), "=", 2)
180+
if len(kv) == 2 {
181+
headers[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
182+
}
183+
}
184+
return headers
185+
}
186+
165187
// buildMetricReader builds a metric reader based on provided configurations
166188
func buildMetricReader(ctx context.Context, cfg Config) (metric.Reader, error) {
167189
if cfg.MetricsExporter == "" {
@@ -191,8 +213,24 @@ func buildMetricReader(ctx context.Context, cfg Config) (metric.Reader, error) {
191213
return nil, fmt.Errorf("error creating client connection: %w", err)
192214
}
193215

216+
// Build OTLP exporter options
217+
exporterOpts := []otlpmetricgrpc.Option{
218+
otlpmetricgrpc.WithGRPCConn(conn),
219+
}
220+
221+
// Add headers if provided
222+
if cfg.CollectorConfig.Headers != "" {
223+
headers := parseOTelHeaders(cfg.CollectorConfig.Headers)
224+
exporterOpts = append(exporterOpts, otlpmetricgrpc.WithHeaders(headers))
225+
}
226+
227+
// Add timeout if provided
228+
if cfg.CollectorConfig.Timeout > 0 {
229+
exporterOpts = append(exporterOpts, otlpmetricgrpc.WithTimeout(cfg.CollectorConfig.Timeout))
230+
}
231+
194232
// Otel metric exporter
195-
otelExporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithGRPCConn(conn))
233+
otelExporter, err := otlpmetricgrpc.New(ctx, exporterOpts...)
196234
if err != nil {
197235
return nil, fmt.Errorf("error creating otel metric exporter: %w", err)
198236
}
@@ -213,7 +251,23 @@ func buildOtlpExporter(ctx context.Context, cfg CollectorConfig) (*otlptrace.Exp
213251
return nil, fmt.Errorf("error creating client connection: %w", err)
214252
}
215253

216-
traceClient := otlptracegrpc.NewClient(otlptracegrpc.WithGRPCConn(conn))
254+
// Build OTLP trace exporter options
255+
traceOpts := []otlptracegrpc.Option{
256+
otlptracegrpc.WithGRPCConn(conn),
257+
}
258+
259+
// Add headers if provided
260+
if cfg.Headers != "" {
261+
headers := parseOTelHeaders(cfg.Headers)
262+
traceOpts = append(traceOpts, otlptracegrpc.WithHeaders(headers))
263+
}
264+
265+
// Add timeout if provided
266+
if cfg.Timeout > 0 {
267+
traceOpts = append(traceOpts, otlptracegrpc.WithTimeout(cfg.Timeout))
268+
}
269+
270+
traceClient := otlptracegrpc.NewClient(traceOpts...)
217271
exporter, err := otlptrace.New(ctx, traceClient)
218272
if err != nil {
219273
return nil, fmt.Errorf("error starting otel exporter: %w", err)

flagd/cmd/start.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,21 @@ func getOtelProtocol() string {
154154
return os.Getenv("OTEL_EXPORTER_OTLP_PROTOCOL")
155155
}
156156

157+
func getOtelTimeout() time.Duration {
158+
timeoutStr := os.Getenv("OTEL_EXPORTER_OTLP_TIMEOUT")
159+
if timeoutStr == "" {
160+
return 0 // No timeout set
161+
}
162+
163+
// OTEL_EXPORTER_OTLP_TIMEOUT is in milliseconds
164+
timeout, err := time.ParseDuration(timeoutStr + "ms")
165+
if err != nil {
166+
// If parsing fails, return 0
167+
return 0
168+
}
169+
return timeout
170+
}
171+
157172
// startCmd represents the start command
158173
var startCmd = &cobra.Command{
159174
Use: "start",
@@ -210,6 +225,7 @@ var startCmd = &cobra.Command{
210225
var collectorUri = overrideOtelUri()
211226
var otelHeaders = getOtelHeaders()
212227
var otelProtocol = getOtelProtocol()
228+
var otelTimeout = getOtelTimeout()
213229

214230
// Build Runtime -----------------------------------------------------------
215231
rt, err := runtime.FromConfig(logger, Version, runtime.Config{
@@ -223,6 +239,7 @@ var startCmd = &cobra.Command{
223239
OtelReloadInterval: viper.GetDuration(otelReloadIntervalFlagName),
224240
OtelHeaders: otelHeaders,
225241
OtelProtocol: otelProtocol,
242+
OtelTimeout: otelTimeout,
226243
OtelCAPath: viper.GetString(otelCAPathFlagName),
227244
ServiceCertPath: viper.GetString(serverCertPathFlagName),
228245
ServiceKeyPath: viper.GetString(serverKeyPathFlagName),

flagd/pkg/runtime/from_config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Config struct {
3333
OtelCAPath string
3434
OtelHeaders string
3535
OtelProtocol string
36+
OtelTimeout time.Duration
3637
OtelReloadInterval time.Duration
3738
ServiceCertPath string
3839
ServiceKeyPath string
@@ -63,6 +64,7 @@ func FromConfig(logger *logger.Logger, version string, config Config) (*Runtime,
6364
ReloadInterval: config.OtelReloadInterval,
6465
Headers: config.OtelHeaders,
6566
Protocol: config.OtelProtocol,
67+
Timeout: config.OtelTimeout,
6668
},
6769
}
6870

0 commit comments

Comments
 (0)