package config import ( "errors" "fmt" "strings" "go.uber.org/zap/zapcore" ) type ObservabilityConfig struct { Logging LoggingConfig `yaml:"logging"` Metrics MetricsConfig `yaml:"metrics"` } func (oc *ObservabilityConfig) Validate() error { var err error err = addValidationError(err, "logging section validation error", oc.Logging.Validate()) err = addValidationError(err, "metrics section validation error", oc.Metrics.Validate()) return err } type LoggingConfig struct { Stderr LoggingLevel `yaml:"stderr"` Files map[LoggingLevel]string `yaml:"files"` Exporter LogsExporterConfig `yaml:"exporter"` } func (lc *LoggingConfig) Validate() error { var err error _, loggingErr := lc.Stderr.ToZapLevel() if !errors.Is(loggingErr, ErrLoggingDisabled) { err = addValidationError(err, "stderr logging level validation error", loggingErr) } for level, filename := range lc.Files { _, loggingErr = level.ToZapLevel() err = addValidationError(err, fmt.Sprintf("logging level validation error for file '%s'", filename), loggingErr) } return err } type LogsExporterConfig struct { Level LoggingLevel `yaml:"level"` Endpoint string `yaml:"endpoint"` Transport OTLPTransportType `yaml:"transport"` } func (lec *LogsExporterConfig) Validate() error { var err error _, loggingErr := lec.Level.ToZapLevel() if errors.Is(loggingErr, ErrLoggingDisabled) { return nil } err = addValidationError(err, "logging level validation error", loggingErr) if !strings.HasPrefix(lec.Endpoint, "http") { return fmt.Errorf("endpoint must has http(s) prefix") } err = addValidationError(err, "transport type validation error", lec.Transport.PreprocessAndValidate()) return err } type OTLPTransportType string var ( GRPCTransportType OTLPTransportType = "grpc" HTTPTransportType OTLPTransportType = "http" ) func (otp *OTLPTransportType) PreprocessAndValidate() error { if *otp == "" { *otp = GRPCTransportType return nil } preprocessed := OTLPTransportType(strings.ToLower(string(*otp))) switch preprocessed { case GRPCTransportType, HTTPTransportType: *otp = preprocessed return nil default: return fmt.Errorf("invalid OTLPTransportType: '%v'", *otp) } } type MetricsConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` } func (mc *MetricsConfig) Validate() error { if mc.Host == "" { return nil } if mc.Port < 1 || mc.Port > 65535 { return fmt.Errorf("port value is invalid") } return nil } type LoggingLevel string var ( DebugLoggingLevel LoggingLevel = "DEBUG" InfoLoggingLevel LoggingLevel = "INFO" WarningLoggingLevel LoggingLevel = "WARNING" ErrorLoggingLevel LoggingLevel = "ERROR" PanicLoggingLevel LoggingLevel = "PANIC" FatalLoggingLevel LoggingLevel = "FATAL" ) func (l LoggingLevel) ToZapLevel() (zapcore.Level, error) { if l == "" { return zapcore.InvalidLevel, ErrLoggingDisabled } levelString := strings.ToLower(string(l)) mapping := map[string]zapcore.Level{ "debug": zapcore.DebugLevel, "info": zapcore.InfoLevel, "warn": zapcore.WarnLevel, "warning": zapcore.WarnLevel, "error": zapcore.ErrorLevel, "panic": zapcore.PanicLevel, "fatal": zapcore.FatalLevel, } if level, ok := mapping[levelString]; ok { return level, nil } return zapcore.InvalidLevel, fmt.Errorf("invalid logging level value: %v", l) }