diff --git a/api/v1alpha1/envoygateway_metrics_types.go b/api/v1alpha1/envoygateway_metrics_types.go index 0b827a06afa..4847f4e7bff 100644 --- a/api/v1alpha1/envoygateway_metrics_types.go +++ b/api/v1alpha1/envoygateway_metrics_types.go @@ -24,8 +24,26 @@ type EnvoyGatewayMetricSink struct { // OpenTelemetry defines the configuration for OpenTelemetry sink. // It's required if the sink type is OpenTelemetry. OpenTelemetry *EnvoyGatewayOpenTelemetrySink `json:"openTelemetry,omitempty"` + // ExporterInterval configures the intervening time between exports for a + // Sink. This option overrides any value set for the + // OTEL_METRIC_EXPORT_INTERVAL environment variable. + // If ExporterInterval is less than or equal to zero, 60 seconds + // is used as the default. + ExporterInterval *ExporterDuration `json:"exporterInterval"` + // ExporterTimeout configures the time a Sink waits for an export to + // complete before canceling it. This option overrides any value set for the + // OTEL_METRIC_EXPORT_TIMEOUT environment variable. + // If ExporterTimeout is less than or equal to zero, 30 seconds + // is used as the default. + ExporterTimeout *ExporterDuration `json:"exporterTimeout"` } +// ExporterDuration is used to represent the duration of the exporter. +// It is used to represent duration as a string and parsed by time.ParseDuration function. +// This type uses the time format in Go. +// example: "1h30m" means a duration of 1 hour and 30 minutes, "10s" means a duration of 10 seconds. +type ExporterDuration string + type EnvoyGatewayOpenTelemetrySink struct { // Host define the sink service hostname. Host string `json:"host"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index e82cda7787f..e27397323aa 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -994,6 +994,16 @@ func (in *EnvoyGatewayMetricSink) DeepCopyInto(out *EnvoyGatewayMetricSink) { *out = new(EnvoyGatewayOpenTelemetrySink) **out = **in } + if in.ExporterInterval != nil { + in, out := &in.ExporterInterval, &out.ExporterInterval + *out = new(ExporterDuration) + **out = **in + } + if in.ExporterTimeout != nil { + in, out := &in.ExporterTimeout, &out.ExporterTimeout + *out = new(ExporterDuration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyGatewayMetricSink. diff --git a/internal/metrics/register.go b/internal/metrics/register.go index 9c8abdf479d..82234925d41 100644 --- a/internal/metrics/register.go +++ b/internal/metrics/register.go @@ -32,7 +32,11 @@ const ( // Init initializes and registers the global metrics server. func Init(cfg *config.Server) error { - options := newOptions(cfg) + options, err := newOptions(cfg) + if err != nil { + return err + } + handler, err := registerForHandler(options) if err != nil { return err @@ -72,7 +76,7 @@ func start(address string, handler http.Handler) error { return nil } -func newOptions(svr *config.Server) registerOptions { +func newOptions(svr *config.Server) (registerOptions, error) { newOpts := registerOptions{} newOpts.address = net.JoinHostPort(v1alpha1.GatewayMetricsHost, fmt.Sprint(v1alpha1.GatewayMetricsPort)) @@ -85,14 +89,37 @@ func newOptions(svr *config.Server) registerOptions { } for _, config := range svr.EnvoyGateway.GetEnvoyGatewayTelemetry().Metrics.Sinks { - newOpts.pushOptions.sinks = append(newOpts.pushOptions.sinks, metricsSink{ + sink := metricsSink{ host: config.OpenTelemetry.Host, port: config.OpenTelemetry.Port, protocol: config.OpenTelemetry.Protocol, - }) + } + + // we do not explicitly set default values for ExporterInterval and ExporterTimeout + // instead, let the upstream repository set default values for it + if config.ExporterInterval != nil && len(*config.ExporterInterval) != 0 { + interval, err := time.ParseDuration(string(*config.ExporterInterval)) + if err != nil { + metricsLogger.Error(err, "failed to parse exporter interval time format") + return newOpts, err + } + + sink.exporterInterval = interval + } + if config.ExporterTimeout != nil && len(*config.ExporterTimeout) != 0 { + timeout, err := time.ParseDuration(string(*config.ExporterTimeout)) + if err != nil { + metricsLogger.Error(err, "failed to parse exporter timeout time format") + return newOpts, err + } + + sink.exporterTimeout = timeout + } + + newOpts.pushOptions.sinks = append(newOpts.pushOptions.sinks, sink) } - return newOpts + return newOpts, nil } // registerForHandler sets the global metrics registry to the provided Prometheus registerer. @@ -156,7 +183,15 @@ func registerOTELHTTPexporter(otelOpts *[]metric.Option, opts registerOptions) e return err } - otelreader := metric.NewPeriodicReader(httpexporter) + periodOpts := []metric.PeriodicReaderOption{} + if sink.exporterInterval != 0 { + periodOpts = append(periodOpts, metric.WithInterval(sink.exporterInterval)) + } + if sink.exporterTimeout != 0 { + periodOpts = append(periodOpts, metric.WithTimeout(sink.exporterTimeout)) + } + + otelreader := metric.NewPeriodicReader(httpexporter, periodOpts...) *otelOpts = append(*otelOpts, metric.WithReader(otelreader)) metricsLogger.Info("initialized otel http metrics push endpoint", "address", address) } @@ -179,7 +214,15 @@ func registerOTELgRPCexporter(otelOpts *[]metric.Option, opts registerOptions) e return err } - otelreader := metric.NewPeriodicReader(httpexporter) + periodOpts := []metric.PeriodicReaderOption{} + if sink.exporterInterval != 0 { + periodOpts = append(periodOpts, metric.WithInterval(sink.exporterInterval)) + } + if sink.exporterTimeout != 0 { + periodOpts = append(periodOpts, metric.WithTimeout(sink.exporterTimeout)) + } + + otelreader := metric.NewPeriodicReader(httpexporter, periodOpts...) *otelOpts = append(*otelOpts, metric.WithReader(otelreader)) metricsLogger.Info("initialized otel grpc metrics push endpoint", "address", address) } @@ -201,7 +244,9 @@ type registerOptions struct { } type metricsSink struct { - protocol string - host string - port int32 + protocol string + host string + port int32 + exporterTimeout time.Duration + exporterInterval time.Duration } diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index c3316f67026..92506bc3a96 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -713,6 +713,8 @@ _Appears in:_ | --- | --- | --- | --- | | `type` | _[MetricSinkType](#metricsinktype)_ | true | Type defines the metric sink type.
EG control plane currently supports OpenTelemetry. | | `openTelemetry` | _[EnvoyGatewayOpenTelemetrySink](#envoygatewayopentelemetrysink)_ | true | OpenTelemetry defines the configuration for OpenTelemetry sink.
It's required if the sink type is OpenTelemetry. | +| `exporterInterval` | _[ExporterDuration](#exporterduration)_ | true | ExporterInterval configures the intervening time between exports for a
Sink. This option overrides any value set for the
OTEL_METRIC_EXPORT_INTERVAL environment variable.
If ExporterInterval is less than or equal to zero, 60 seconds
is used as the default. | +| `exporterTimeout` | _[ExporterDuration](#exporterduration)_ | true | ExporterTimeout configures the time a Sink waits for an export to
complete before canceling it. This option overrides any value set for the
OTEL_METRIC_EXPORT_TIMEOUT environment variable.
If ExporterTimeout is less than or equal to zero, 30 seconds
is used as the default. | #### EnvoyGatewayMetrics @@ -990,6 +992,20 @@ _Appears in:_ +#### ExporterDuration + +_Underlying type:_ _string_ + +ExporterDuration is used to represent the duration of the exporter. +It is used to represent duration as a string and parsed by time.ParseDuration function. +This type uses the time format in Go. +example: "1h30m" means a duration of 1 hour and 30 minutes, "10s" means a duration of 10 seconds. + +_Appears in:_ +- [EnvoyGatewayMetricSink](#envoygatewaymetricsink) + + + #### ExtAuth