diff --git a/cmd/thv/app/otel.go b/cmd/thv/app/otel.go index cc955befc..92298a712 100644 --- a/cmd/thv/app/otel.go +++ b/cmd/thv/app/otel.go @@ -2,8 +2,6 @@ package app import ( "fmt" - "strconv" - "strings" "github.com/spf13/cobra" @@ -17,561 +15,113 @@ var OtelCmd = &cobra.Command{ Long: "Configure OpenTelemetry settings for observability and monitoring of MCP servers.", } -var setOtelEndpointCmd = &cobra.Command{ - Use: "set-endpoint ", - Short: "Set the OpenTelemetry endpoint URL", - Long: `Set the OpenTelemetry OTLP endpoint URL for tracing and metrics. - -This endpoint will be used by default when running MCP servers unless overridden by the --otel-endpoint flag. - -Example: - - thv config otel set-endpoint https://api.honeycomb.io`, - Args: cobra.ExactArgs(1), - RunE: setOtelEndpointCmdFunc, -} - -var getOtelEndpointCmd = &cobra.Command{ - Use: "get-endpoint", - Short: "Get the currently configured OpenTelemetry endpoint", - Long: "Display the OpenTelemetry endpoint URL that is currently configured.", - RunE: getOtelEndpointCmdFunc, -} - -var unsetOtelEndpointCmd = &cobra.Command{ - Use: "unset-endpoint", - Short: "Remove the configured OpenTelemetry endpoint", - Long: "Remove the OpenTelemetry endpoint configuration.", - RunE: unsetOtelEndpointCmdFunc, -} - -var setOtelMetricsEnabledCmd = &cobra.Command{ - Use: "set-metrics-enabled ", - Short: "Set the OpenTelemetry metrics export to enabled", - Long: `Set the OpenTelemetry metrics flag to enable to export metrics to an OTel collector. - - thv config otel set-metrics-enabled true`, - Args: cobra.ExactArgs(1), - RunE: setOtelMetricsEnabledCmdFunc, -} - -var getOtelMetricsEnabledCmd = &cobra.Command{ - Use: "get-metrics-enabled", - Short: "Get the currently configured OpenTelemetry metrics export flag", - Long: "Display the OpenTelemetry metrics export flag that is currently configured.", - RunE: getOtelMetricsEnabledCmdFunc, -} - -var unsetOtelMetricsEnabledCmd = &cobra.Command{ - Use: "unset-metrics-enabled", - Short: "Remove the configured OpenTelemetry metrics export flag", - Long: "Remove the OpenTelemetry metrics export flag configuration.", - RunE: unsetOtelMetricsEnabledCmdFunc, -} - -var setOtelTracingEnabledCmd = &cobra.Command{ - Use: "set-tracing-enabled ", - Short: "Set the OpenTelemetry tracing export to enabled", - Long: `Set the OpenTelemetry tracing flag to enable to export traces to an OTel collector. - - thv config otel set-tracing-enabled true`, - Args: cobra.ExactArgs(1), - RunE: setOtelTracingEnabledCmdFunc, -} - -var getOtelTracingEnabledCmd = &cobra.Command{ - Use: "get-tracing-enabled", - Short: "Get the currently configured OpenTelemetry tracing export flag", - Long: "Display the OpenTelemetry tracing export flag that is currently configured.", - RunE: getOtelTracingEnabledCmdFunc, -} - -var unsetOtelTracingEnabledCmd = &cobra.Command{ - Use: "unset-tracing-enabled", - Short: "Remove the configured OpenTelemetry tracing export flag", - Long: "Remove the OpenTelemetry tracing export flag configuration.", - RunE: unsetOtelTracingEnabledCmdFunc, -} - -var setOtelSamplingRateCmd = &cobra.Command{ - Use: "set-sampling-rate ", - Short: "Set the OpenTelemetry sampling rate", - Long: `Set the OpenTelemetry trace sampling rate (between 0.0 and 1.0). - -This sampling rate will be used by default when running MCP servers unless overridden by the --otel-sampling-rate flag. - -Example: - - thv config otel set-sampling-rate 0.1`, - Args: cobra.ExactArgs(1), - RunE: setOtelSamplingRateCmdFunc, -} - -var getOtelSamplingRateCmd = &cobra.Command{ - Use: "get-sampling-rate", - Short: "Get the currently configured OpenTelemetry sampling rate", - Long: "Display the OpenTelemetry sampling rate that is currently configured.", - RunE: getOtelSamplingRateCmdFunc, -} - -var unsetOtelSamplingRateCmd = &cobra.Command{ - Use: "unset-sampling-rate", - Short: "Remove the configured OpenTelemetry sampling rate", - Long: "Remove the OpenTelemetry sampling rate configuration.", - RunE: unsetOtelSamplingRateCmdFunc, -} - -var setOtelEnvVarsCmd = &cobra.Command{ - Use: "set-env-vars ", - Short: "Set the OpenTelemetry environment variables", - Long: `Set the list of environment variable names to include in OpenTelemetry spans. - -These environment variables will be used by default when running MCP servers unless overridden by the --otel-env-vars flag. - -Example: - - thv config otel set-env-vars USER,HOME,PATH`, - Args: cobra.ExactArgs(1), - RunE: setOtelEnvVarsCmdFunc, -} - -var getOtelEnvVarsCmd = &cobra.Command{ - Use: "get-env-vars", - Short: "Get the currently configured OpenTelemetry environment variables", - Long: "Display the OpenTelemetry environment variables that are currently configured.", - RunE: getOtelEnvVarsCmdFunc, -} - -var unsetOtelEnvVarsCmd = &cobra.Command{ - Use: "unset-env-vars", - Short: "Remove the configured OpenTelemetry environment variables", - Long: "Remove the OpenTelemetry environment variables configuration.", - RunE: unsetOtelEnvVarsCmdFunc, -} - -var setOtelInsecureCmd = &cobra.Command{ - Use: "set-insecure ", - Short: "Set the OpenTelemetry insecure transport flag", - Long: `Set the OpenTelemetry insecure flag to enable HTTP instead of HTTPS for OTLP endpoints. - - thv config otel set-insecure true`, - Args: cobra.ExactArgs(1), - RunE: setOtelInsecureCmdFunc, -} - -var getOtelInsecureCmd = &cobra.Command{ - Use: "get-insecure", - Short: "Get the currently configured OpenTelemetry insecure transport flag", - Long: "Display the OpenTelemetry insecure transport flag that is currently configured.", - RunE: getOtelInsecureCmdFunc, -} - -var unsetOtelInsecureCmd = &cobra.Command{ - Use: "unset-insecure", - Short: "Remove the configured OpenTelemetry insecure transport flag", - Long: "Remove the OpenTelemetry insecure transport flag configuration.", - RunE: unsetOtelInsecureCmdFunc, -} - -var setOtelEnablePrometheusMetricsPathCmd = &cobra.Command{ - Use: "set-enable-prometheus-metrics-path ", - Short: "Set the OpenTelemetry Prometheus metrics path flag", - Long: `Set the OpenTelemetry Prometheus metrics path flag to enable /metrics endpoint. - - thv config otel set-enable-prometheus-metrics-path true`, - Args: cobra.ExactArgs(1), - RunE: setOtelEnablePrometheusMetricsPathCmdFunc, -} - -var getOtelEnablePrometheusMetricsPathCmd = &cobra.Command{ - Use: "get-enable-prometheus-metrics-path", - Short: "Get the currently configured OpenTelemetry Prometheus metrics path flag", - Long: "Display the OpenTelemetry Prometheus metrics path flag that is currently configured.", - RunE: getOtelEnablePrometheusMetricsPathCmdFunc, -} - -var unsetOtelEnablePrometheusMetricsPathCmd = &cobra.Command{ - Use: "unset-enable-prometheus-metrics-path", - Short: "Remove the configured OpenTelemetry Prometheus metrics path flag", - Long: "Remove the OpenTelemetry Prometheus metrics path flag configuration.", - RunE: unsetOtelEnablePrometheusMetricsPathCmdFunc, -} - -// init sets up the OTEL command hierarchy -func init() { - // Add OTEL subcommands to otel command - OtelCmd.AddCommand(setOtelEndpointCmd) - OtelCmd.AddCommand(getOtelEndpointCmd) - OtelCmd.AddCommand(unsetOtelEndpointCmd) - OtelCmd.AddCommand(setOtelMetricsEnabledCmd) - OtelCmd.AddCommand(getOtelMetricsEnabledCmd) - OtelCmd.AddCommand(unsetOtelMetricsEnabledCmd) - OtelCmd.AddCommand(setOtelTracingEnabledCmd) - OtelCmd.AddCommand(getOtelTracingEnabledCmd) - OtelCmd.AddCommand(unsetOtelTracingEnabledCmd) - OtelCmd.AddCommand(setOtelSamplingRateCmd) - OtelCmd.AddCommand(getOtelSamplingRateCmd) - OtelCmd.AddCommand(unsetOtelSamplingRateCmd) - OtelCmd.AddCommand(setOtelEnvVarsCmd) - OtelCmd.AddCommand(getOtelEnvVarsCmd) - OtelCmd.AddCommand(unsetOtelEnvVarsCmd) - OtelCmd.AddCommand(setOtelInsecureCmd) - OtelCmd.AddCommand(getOtelInsecureCmd) - OtelCmd.AddCommand(unsetOtelInsecureCmd) - OtelCmd.AddCommand(setOtelEnablePrometheusMetricsPathCmd) - OtelCmd.AddCommand(getOtelEnablePrometheusMetricsPathCmd) - OtelCmd.AddCommand(unsetOtelEnablePrometheusMetricsPathCmd) -} - -func setOtelEndpointCmdFunc(_ *cobra.Command, args []string) error { - endpoint := args[0] - - // The endpoint should not start with http:// or https:// - if endpoint != "" && (strings.HasPrefix(endpoint, "http://") || strings.HasPrefix(endpoint, "https://")) { - return fmt.Errorf("endpoint URL should not start with http:// or https://") - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.Endpoint = endpoint - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Printf("Successfully set OpenTelemetry endpoint: %s\n", endpoint) - return nil -} - -func getOtelEndpointCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if cfg.OTEL.Endpoint == "" { - fmt.Println("No OpenTelemetry endpoint is currently configured.") - return nil - } - - fmt.Printf("Current OpenTelemetry endpoint: %s\n", cfg.OTEL.Endpoint) - return nil -} - -func unsetOtelEndpointCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if cfg.OTEL.Endpoint == "" { - fmt.Println("No OpenTelemetry endpoint is currently configured.") - return nil - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.Endpoint = "" - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Println("Successfully removed OpenTelemetry endpoint configuration.") - return nil -} - -func setOtelSamplingRateCmdFunc(_ *cobra.Command, args []string) error { - rate, err := strconv.ParseFloat(args[0], 64) - if err != nil { - return fmt.Errorf("invalid sampling rate format: %w", err) - } - - // Validate the rate - if rate < 0.0 || rate > 1.0 { - return fmt.Errorf("sampling rate must be between 0.0 and 1.0") - } - - // Update the configuration - err = config.UpdateConfig(func(c *config.Config) { - c.OTEL.SamplingRate = rate - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Printf("Successfully set OpenTelemetry sampling rate: %f\n", rate) - return nil -} - -func getOtelSamplingRateCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if cfg.OTEL.SamplingRate == 0.0 { - fmt.Println("No OpenTelemetry sampling rate is currently configured.") - return nil - } - - fmt.Printf("Current OpenTelemetry sampling rate: %f\n", cfg.OTEL.SamplingRate) - return nil -} - -func unsetOtelSamplingRateCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if cfg.OTEL.SamplingRate == 0.0 { - fmt.Println("No OpenTelemetry sampling rate is currently configured.") - return nil - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.SamplingRate = 0.0 - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Println("Successfully removed OpenTelemetry sampling rate configuration.") - return nil -} - -func setOtelEnvVarsCmdFunc(_ *cobra.Command, args []string) error { - vars := strings.Split(args[0], ",") - - // Trim whitespace from each variable name - for i, varName := range vars { - vars[i] = strings.TrimSpace(varName) - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.EnvVars = vars - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Printf("Successfully set OpenTelemetry environment variables: %v\n", vars) - return nil -} - -func getOtelEnvVarsCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if len(cfg.OTEL.EnvVars) == 0 { - fmt.Println("No OpenTelemetry environment variables are currently configured.") - return nil - } - - fmt.Printf("Current OpenTelemetry environment variables: %v\n", cfg.OTEL.EnvVars) - return nil -} - -func unsetOtelEnvVarsCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if len(cfg.OTEL.EnvVars) == 0 { - fmt.Println("No OpenTelemetry environment variables are currently configured.") - return nil - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.EnvVars = []string{} - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Println("Successfully removed OpenTelemetry environment variables configuration.") - return nil -} - -func setOtelMetricsEnabledCmdFunc(_ *cobra.Command, args []string) error { - enabled, err := strconv.ParseBool(args[0]) - if err != nil { - return fmt.Errorf("invalid boolean value for metrics enabled flag: %w", err) - } - - // Update the configuration - err = config.UpdateConfig(func(c *config.Config) { - c.OTEL.MetricsEnabled = enabled - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Printf("Successfully set OpenTelemetry metrics enabled: %t\n", enabled) - return nil -} - -func getOtelMetricsEnabledCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - fmt.Printf("Current OpenTelemetry metrics enabled: %t\n", cfg.OTEL.MetricsEnabled) - return nil -} - -func unsetOtelMetricsEnabledCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if !cfg.OTEL.MetricsEnabled { - fmt.Println("OpenTelemetry metrics enabled is already disabled.") - return nil - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.MetricsEnabled = false - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Println("Successfully disabled OpenTelemetry metrics enabled configuration.") - return nil -} - -func setOtelTracingEnabledCmdFunc(_ *cobra.Command, args []string) error { - enabled, err := strconv.ParseBool(args[0]) - if err != nil { - return fmt.Errorf("invalid boolean value for tracing enabled flag: %w", err) +// createOTELSetCommand creates a generic set command for an OTEL field +func createOTELSetCommand(fieldName, commandName, description, example string) *cobra.Command { + return &cobra.Command{ + Use: fmt.Sprintf("set-%s <%s>", commandName, commandName), + Short: fmt.Sprintf("Set the OpenTelemetry %s", description), + Long: fmt.Sprintf("Set the OpenTelemetry %s.\n\nExample:\n\n\tthv config otel set-%s %s", description, commandName, example), + Args: cobra.ExactArgs(1), + RunE: func(_ *cobra.Command, args []string) error { + provider := config.NewDefaultProvider() + err := config.SetConfigField(provider, fieldName, args[0]) + if err != nil { + return err + } + fmt.Printf("Successfully set OpenTelemetry %s: %s\n", description, args[0]) + return nil + }, + } +} + +// createOTELGetCommand creates a generic get command for an OTEL field +func createOTELGetCommand(fieldName, commandName, description string) *cobra.Command { + return &cobra.Command{ + Use: fmt.Sprintf("get-%s", commandName), + Short: fmt.Sprintf("Get the currently configured OpenTelemetry %s", description), + Long: fmt.Sprintf("Display the OpenTelemetry %s that is currently configured.", description), + RunE: func(_ *cobra.Command, _ []string) error { + provider := config.NewDefaultProvider() + value, isSet, err := config.GetConfigField(provider, fieldName) + if err != nil { + return err + } + + if !isSet { + fmt.Printf("No OpenTelemetry %s is currently configured.\n", description) + return nil + } + + fmt.Printf("Current OpenTelemetry %s: %s\n", description, value) + return nil + }, + } +} + +// createOTELUnsetCommand creates a generic unset command for an OTEL field +func createOTELUnsetCommand(fieldName, commandName, description string) *cobra.Command { + return &cobra.Command{ + Use: fmt.Sprintf("unset-%s", commandName), + Short: fmt.Sprintf("Remove the configured OpenTelemetry %s", description), + Long: fmt.Sprintf("Remove the OpenTelemetry %s configuration.", description), + RunE: func(_ *cobra.Command, _ []string) error { + provider := config.NewDefaultProvider() + + // Check if it's set before unsetting + _, isSet, err := config.GetConfigField(provider, fieldName) + if err != nil { + return err + } + + if !isSet { + fmt.Printf("No OpenTelemetry %s is currently configured.\n", description) + return nil + } + + err = config.UnsetConfigField(provider, fieldName) + if err != nil { + return err + } + + fmt.Printf("Successfully removed OpenTelemetry %s configuration.\n", description) + return nil + }, } - - // Update the configuration - err = config.UpdateConfig(func(c *config.Config) { - c.OTEL.TracingEnabled = enabled - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Printf("Successfully set OpenTelemetry tracing enabled: %t\n", enabled) - return nil -} - -func getOtelTracingEnabledCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - fmt.Printf("Current OpenTelemetry tracing enabled: %t\n", cfg.OTEL.TracingEnabled) - return nil -} - -func unsetOtelTracingEnabledCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if !cfg.OTEL.TracingEnabled { - fmt.Println("OpenTelemetry tracing enabled is already disabled.") - return nil - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.TracingEnabled = false - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Println("Successfully disabled OpenTelemetry tracing enabled configuration.") - return nil -} - -func setOtelInsecureCmdFunc(_ *cobra.Command, args []string) error { - enabled, err := strconv.ParseBool(args[0]) - if err != nil { - return fmt.Errorf("invalid boolean value for insecure flag: %w", err) - } - - // Update the configuration - err = config.UpdateConfig(func(c *config.Config) { - c.OTEL.Insecure = enabled - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Printf("Successfully set OpenTelemetry insecure transport: %t\n", enabled) - return nil -} - -func getOtelInsecureCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - fmt.Printf("Current OpenTelemetry insecure transport: %t\n", cfg.OTEL.Insecure) - return nil } -func unsetOtelInsecureCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if !cfg.OTEL.Insecure { - fmt.Println("OpenTelemetry insecure transport is already disabled.") - return nil - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.Insecure = false - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Println("Successfully disabled OpenTelemetry insecure transport configuration.") - return nil -} - -func setOtelEnablePrometheusMetricsPathCmdFunc(_ *cobra.Command, args []string) error { - enabled, err := strconv.ParseBool(args[0]) - if err != nil { - return fmt.Errorf("invalid boolean value for Prometheus metrics path flag: %w", err) - } - - // Update the configuration - err = config.UpdateConfig(func(c *config.Config) { - c.OTEL.EnablePrometheusMetricsPath = enabled - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Printf("Successfully set Prometheus metrics path: %t\n", enabled) - return nil -} - -func getOtelEnablePrometheusMetricsPathCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - fmt.Printf("Current Prometheus metrics path flag: %t\n", cfg.OTEL.EnablePrometheusMetricsPath) - return nil -} - -func unsetOtelEnablePrometheusMetricsPathCmdFunc(_ *cobra.Command, _ []string) error { - configProvider := config.NewDefaultProvider() - cfg := configProvider.GetConfig() - - if !cfg.OTEL.EnablePrometheusMetricsPath { - fmt.Println("Prometheus metrics path is already disabled.") - return nil - } - - // Update the configuration - err := config.UpdateConfig(func(c *config.Config) { - c.OTEL.EnablePrometheusMetricsPath = false - }) - if err != nil { - return fmt.Errorf("failed to update configuration: %w", err) - } - - fmt.Println("Successfully disabled the Prometheus metrics path configuration.") - return nil +func init() { + // Endpoint commands + OtelCmd.AddCommand(createOTELSetCommand("otel-endpoint", "endpoint", "endpoint URL", "https://api.honeycomb.io")) + OtelCmd.AddCommand(createOTELGetCommand("otel-endpoint", "endpoint", "endpoint")) + OtelCmd.AddCommand(createOTELUnsetCommand("otel-endpoint", "endpoint", "endpoint")) + + // Sampling rate commands + OtelCmd.AddCommand(createOTELSetCommand("otel-sampling-rate", "sampling-rate", "sampling rate", "0.5")) + OtelCmd.AddCommand(createOTELGetCommand("otel-sampling-rate", "sampling-rate", "sampling rate")) + OtelCmd.AddCommand(createOTELUnsetCommand("otel-sampling-rate", "sampling-rate", "sampling rate")) + + // Environment variables commands + OtelCmd.AddCommand(createOTELSetCommand("otel-env-vars", "env-vars", "environment variables", "VAR1,VAR2,VAR3")) + OtelCmd.AddCommand(createOTELGetCommand("otel-env-vars", "env-vars", "environment variables")) + OtelCmd.AddCommand(createOTELUnsetCommand("otel-env-vars", "env-vars", "environment variables")) + + // Metrics enabled commands + OtelCmd.AddCommand(createOTELSetCommand("otel-metrics-enabled", "metrics-enabled", "metrics export flag", "true")) + OtelCmd.AddCommand(createOTELGetCommand("otel-metrics-enabled", "metrics-enabled", "metrics export flag")) + OtelCmd.AddCommand(createOTELUnsetCommand("otel-metrics-enabled", "metrics-enabled", "metrics export flag")) + + // Tracing enabled commands + OtelCmd.AddCommand(createOTELSetCommand("otel-tracing-enabled", "tracing-enabled", "tracing export flag", "true")) + OtelCmd.AddCommand(createOTELGetCommand("otel-tracing-enabled", "tracing-enabled", "tracing export flag")) + OtelCmd.AddCommand(createOTELUnsetCommand("otel-tracing-enabled", "tracing-enabled", "tracing export flag")) + + // Insecure commands + OtelCmd.AddCommand(createOTELSetCommand("otel-insecure", "insecure", "insecure connection flag", "true")) + OtelCmd.AddCommand(createOTELGetCommand("otel-insecure", "insecure", "insecure connection flag")) + OtelCmd.AddCommand(createOTELUnsetCommand("otel-insecure", "insecure", "insecure connection flag")) + + // Enable Prometheus metrics path commands + OtelCmd.AddCommand(createOTELSetCommand("otel-enable-prometheus-metrics-path", "enable-prometheus-metrics-path", "Prometheus metrics path flag", "true")) + OtelCmd.AddCommand(createOTELGetCommand("otel-enable-prometheus-metrics-path", "enable-prometheus-metrics-path", "Prometheus metrics path flag")) + OtelCmd.AddCommand(createOTELUnsetCommand("otel-enable-prometheus-metrics-path", "enable-prometheus-metrics-path", "Prometheus metrics path flag")) } diff --git a/pkg/config/fields_otel.go b/pkg/config/fields_otel.go new file mode 100644 index 000000000..e0ad3d7d0 --- /dev/null +++ b/pkg/config/fields_otel.go @@ -0,0 +1,234 @@ +package config + +import ( + "fmt" + "strconv" + "strings" +) + +// init registers all OTEL config fields +func init() { + registerOTELEndpoint() + registerOTELSamplingRate() + registerOTELEnvVars() + registerOTELMetricsEnabled() + registerOTELTracingEnabled() + registerOTELInsecure() + registerOTELEnablePrometheusMetricsPath() +} + +// registerOTELEndpoint registers the OTEL endpoint config field +func registerOTELEndpoint() { + RegisterConfigField(ConfigFieldSpec{ + Name: "otel-endpoint", + SetValidator: func(_ Provider, value string) error { + // The endpoint should not start with http:// or https:// + if value != "" && (strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://")) { + return fmt.Errorf("endpoint URL should not start with http:// or https://") + } + return nil + }, + Setter: func(cfg *Config, value string) { + cfg.OTEL.Endpoint = value + }, + Getter: func(cfg *Config) string { + return cfg.OTEL.Endpoint + }, + Unsetter: func(cfg *Config) { + cfg.OTEL.Endpoint = "" + }, + IsSet: func(cfg *Config) bool { + return cfg.OTEL.Endpoint != "" + }, + DisplayName: "OTEL Endpoint", + HelpText: "OpenTelemetry OTLP endpoint URL for tracing and metrics", + }) +} + +// registerOTELSamplingRate registers the OTEL sampling rate config field +func registerOTELSamplingRate() { + RegisterConfigField(ConfigFieldSpec{ + Name: "otel-sampling-rate", + SetValidator: func(_ Provider, value string) error { + rate, err := strconv.ParseFloat(value, 64) + if err != nil { + return fmt.Errorf("invalid sampling rate format: %w", err) + } + if rate < 0.0 || rate > 1.0 { + return fmt.Errorf("sampling rate must be between 0.0 and 1.0") + } + return nil + }, + Setter: func(cfg *Config, value string) { + rate, _ := strconv.ParseFloat(value, 64) // Already validated + cfg.OTEL.SamplingRate = rate + }, + Getter: func(cfg *Config) string { + if cfg.OTEL.SamplingRate == 0.0 { + return "" + } + return strconv.FormatFloat(cfg.OTEL.SamplingRate, 'f', -1, 64) + }, + Unsetter: func(cfg *Config) { + cfg.OTEL.SamplingRate = 0.0 + }, + IsSet: func(cfg *Config) bool { + return cfg.OTEL.SamplingRate != 0.0 + }, + DisplayName: "OTEL Sampling Rate", + HelpText: "OpenTelemetry trace sampling rate (between 0.0 and 1.0)", + }) +} + +// registerOTELEnvVars registers the OTEL environment variables config field +func registerOTELEnvVars() { + RegisterConfigField(ConfigFieldSpec{ + Name: "otel-env-vars", + SetValidator: func(_ Provider, value string) error { + // No validation needed - any comma-separated string is valid + return nil + }, + Setter: func(cfg *Config, value string) { + vars := strings.Split(value, ",") + // Trim whitespace from each variable name + for i, varName := range vars { + vars[i] = strings.TrimSpace(varName) + } + cfg.OTEL.EnvVars = vars + }, + Getter: func(cfg *Config) string { + if len(cfg.OTEL.EnvVars) == 0 { + return "" + } + return strings.Join(cfg.OTEL.EnvVars, ",") + }, + Unsetter: func(cfg *Config) { + cfg.OTEL.EnvVars = nil + }, + IsSet: func(cfg *Config) bool { + return len(cfg.OTEL.EnvVars) > 0 + }, + DisplayName: "OTEL Environment Variables", + HelpText: "Comma-separated list of environment variable names to include in telemetry", + }) +} + +// registerOTELMetricsEnabled registers the OTEL metrics enabled config field +func registerOTELMetricsEnabled() { + RegisterConfigField(ConfigFieldSpec{ + Name: "otel-metrics-enabled", + SetValidator: func(_ Provider, value string) error { + _, err := strconv.ParseBool(value) + if err != nil { + return fmt.Errorf("invalid boolean value: %w (expected: true, false, 1, 0)", err) + } + return nil + }, + Setter: func(cfg *Config, value string) { + enabled, _ := strconv.ParseBool(value) // Already validated + cfg.OTEL.MetricsEnabled = enabled + }, + Getter: func(cfg *Config) string { + return strconv.FormatBool(cfg.OTEL.MetricsEnabled) + }, + Unsetter: func(cfg *Config) { + cfg.OTEL.MetricsEnabled = false + }, + IsSet: func(cfg *Config) bool { + // Consider it set if it's explicitly set to true + return cfg.OTEL.MetricsEnabled + }, + DisplayName: "OTEL Metrics Enabled", + HelpText: "Enable OpenTelemetry metrics export", + }) +} + +// registerOTELTracingEnabled registers the OTEL tracing enabled config field +func registerOTELTracingEnabled() { + RegisterConfigField(ConfigFieldSpec{ + Name: "otel-tracing-enabled", + SetValidator: func(_ Provider, value string) error { + _, err := strconv.ParseBool(value) + if err != nil { + return fmt.Errorf("invalid boolean value: %w (expected: true, false, 1, 0)", err) + } + return nil + }, + Setter: func(cfg *Config, value string) { + enabled, _ := strconv.ParseBool(value) // Already validated + cfg.OTEL.TracingEnabled = enabled + }, + Getter: func(cfg *Config) string { + return strconv.FormatBool(cfg.OTEL.TracingEnabled) + }, + Unsetter: func(cfg *Config) { + cfg.OTEL.TracingEnabled = false + }, + IsSet: func(cfg *Config) bool { + // Consider it set if it's explicitly set to true + return cfg.OTEL.TracingEnabled + }, + DisplayName: "OTEL Tracing Enabled", + HelpText: "Enable OpenTelemetry tracing export", + }) +} + +// registerOTELInsecure registers the OTEL insecure config field +func registerOTELInsecure() { + RegisterConfigField(ConfigFieldSpec{ + Name: "otel-insecure", + SetValidator: func(_ Provider, value string) error { + _, err := strconv.ParseBool(value) + if err != nil { + return fmt.Errorf("invalid boolean value: %w (expected: true, false, 1, 0)", err) + } + return nil + }, + Setter: func(cfg *Config, value string) { + insecure, _ := strconv.ParseBool(value) // Already validated + cfg.OTEL.Insecure = insecure + }, + Getter: func(cfg *Config) string { + return strconv.FormatBool(cfg.OTEL.Insecure) + }, + Unsetter: func(cfg *Config) { + cfg.OTEL.Insecure = false + }, + IsSet: func(cfg *Config) bool { + // Consider it set if it's explicitly set to true + return cfg.OTEL.Insecure + }, + DisplayName: "OTEL Insecure", + HelpText: "Use insecure connection to OpenTelemetry endpoint", + }) +} + +// registerOTELEnablePrometheusMetricsPath registers the OTEL enable Prometheus metrics path config field +func registerOTELEnablePrometheusMetricsPath() { + RegisterConfigField(ConfigFieldSpec{ + Name: "otel-enable-prometheus-metrics-path", + SetValidator: func(_ Provider, value string) error { + _, err := strconv.ParseBool(value) + if err != nil { + return fmt.Errorf("invalid boolean value: %w (expected: true, false, 1, 0)", err) + } + return nil + }, + Setter: func(cfg *Config, value string) { + enabled, _ := strconv.ParseBool(value) // Already validated + cfg.OTEL.EnablePrometheusMetricsPath = enabled + }, + Getter: func(cfg *Config) string { + return strconv.FormatBool(cfg.OTEL.EnablePrometheusMetricsPath) + }, + Unsetter: func(cfg *Config) { + cfg.OTEL.EnablePrometheusMetricsPath = false + }, + IsSet: func(cfg *Config) bool { + // Consider it set if it's explicitly set to true + return cfg.OTEL.EnablePrometheusMetricsPath + }, + DisplayName: "OTEL Enable Prometheus Metrics Path", + HelpText: "Enable Prometheus metrics endpoint path", + }) +}