576 lines
19 KiB
Go
576 lines
19 KiB
Go
// Package config loads the user-service process configuration from environment
|
|
// variables.
|
|
package config
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
shutdownTimeoutEnvVar = "USERSERVICE_SHUTDOWN_TIMEOUT"
|
|
logLevelEnvVar = "USERSERVICE_LOG_LEVEL"
|
|
|
|
internalHTTPAddrEnvVar = "USERSERVICE_INTERNAL_HTTP_ADDR"
|
|
internalHTTPReadHeaderTimeoutEnvVar = "USERSERVICE_INTERNAL_HTTP_READ_HEADER_TIMEOUT"
|
|
internalHTTPReadTimeoutEnvVar = "USERSERVICE_INTERNAL_HTTP_READ_TIMEOUT"
|
|
internalHTTPIdleTimeoutEnvVar = "USERSERVICE_INTERNAL_HTTP_IDLE_TIMEOUT"
|
|
internalHTTPRequestTimeoutEnvVar = "USERSERVICE_INTERNAL_HTTP_REQUEST_TIMEOUT"
|
|
|
|
adminHTTPAddrEnvVar = "USERSERVICE_ADMIN_HTTP_ADDR"
|
|
adminHTTPReadHeaderTimeoutEnvVar = "USERSERVICE_ADMIN_HTTP_READ_HEADER_TIMEOUT"
|
|
adminHTTPReadTimeoutEnvVar = "USERSERVICE_ADMIN_HTTP_READ_TIMEOUT"
|
|
adminHTTPIdleTimeoutEnvVar = "USERSERVICE_ADMIN_HTTP_IDLE_TIMEOUT"
|
|
|
|
redisAddrEnvVar = "USERSERVICE_REDIS_ADDR"
|
|
redisUsernameEnvVar = "USERSERVICE_REDIS_USERNAME"
|
|
redisPasswordEnvVar = "USERSERVICE_REDIS_PASSWORD"
|
|
redisDBEnvVar = "USERSERVICE_REDIS_DB"
|
|
redisTLSEnabledEnvVar = "USERSERVICE_REDIS_TLS_ENABLED"
|
|
redisOperationTimeoutEnvVar = "USERSERVICE_REDIS_OPERATION_TIMEOUT"
|
|
redisKeyspacePrefixEnvVar = "USERSERVICE_REDIS_KEYSPACE_PREFIX"
|
|
redisDomainEventsStreamEnvVar = "USERSERVICE_REDIS_DOMAIN_EVENTS_STREAM"
|
|
redisDomainEventsStreamMaxLenEnvVar = "USERSERVICE_REDIS_DOMAIN_EVENTS_STREAM_MAX_LEN"
|
|
redisLifecycleEventsStreamEnvVar = "USERSERVICE_REDIS_LIFECYCLE_EVENTS_STREAM"
|
|
redisLifecycleEventsStreamMaxLenEnvVar = "USERSERVICE_REDIS_LIFECYCLE_EVENTS_STREAM_MAX_LEN"
|
|
|
|
otelServiceNameEnvVar = "OTEL_SERVICE_NAME"
|
|
otelTracesExporterEnvVar = "OTEL_TRACES_EXPORTER"
|
|
otelMetricsExporterEnvVar = "OTEL_METRICS_EXPORTER"
|
|
otelExporterOTLPProtocolEnvVar = "OTEL_EXPORTER_OTLP_PROTOCOL"
|
|
otelExporterOTLPTracesProtocolEnvVar = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
|
|
otelExporterOTLPMetricsProtocolEnvVar = "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL"
|
|
otelStdoutTracesEnabledEnvVar = "USERSERVICE_OTEL_STDOUT_TRACES_ENABLED"
|
|
otelStdoutMetricsEnabledEnvVar = "USERSERVICE_OTEL_STDOUT_METRICS_ENABLED"
|
|
|
|
defaultShutdownTimeout = 5 * time.Second
|
|
defaultLogLevel = "info"
|
|
defaultInternalHTTPAddr = ":8091"
|
|
defaultAdminHTTPAddr = ""
|
|
defaultReadHeaderTimeout = 2 * time.Second
|
|
defaultReadTimeout = 10 * time.Second
|
|
defaultIdleTimeout = time.Minute
|
|
defaultRequestTimeout = 3 * time.Second
|
|
defaultRedisDB = 0
|
|
defaultRedisOperationTimeout = 250 * time.Millisecond
|
|
defaultRedisKeyspacePrefix = "user:"
|
|
defaultDomainEventsStream = "user:domain_events"
|
|
defaultDomainEventsStreamMaxLen = 1024
|
|
defaultLifecycleEventsStream = "user:lifecycle_events"
|
|
defaultLifecycleEventsStreamMaxLen = 1024
|
|
defaultOTelServiceName = "galaxy-user"
|
|
otelExporterNone = "none"
|
|
otelExporterOTLP = "otlp"
|
|
otelProtocolHTTPProtobuf = "http/protobuf"
|
|
otelProtocolGRPC = "grpc"
|
|
)
|
|
|
|
// Config stores the full user-service process configuration.
|
|
type Config struct {
|
|
// ShutdownTimeout bounds graceful shutdown of the long-lived listeners and
|
|
// runtime resources.
|
|
ShutdownTimeout time.Duration
|
|
|
|
// Logging configures the process-wide logger.
|
|
Logging LoggingConfig
|
|
|
|
// InternalHTTP configures the trusted internal HTTP listener.
|
|
InternalHTTP InternalHTTPConfig
|
|
|
|
// AdminHTTP configures the optional private admin HTTP listener.
|
|
AdminHTTP AdminHTTPConfig
|
|
|
|
// Redis configures the Redis-backed user store and domain-event publisher.
|
|
Redis RedisConfig
|
|
|
|
// Telemetry configures the process-wide OpenTelemetry runtime.
|
|
Telemetry TelemetryConfig
|
|
}
|
|
|
|
// LoggingConfig configures the process-wide logger.
|
|
type LoggingConfig struct {
|
|
// Level stores the process log level.
|
|
Level string
|
|
}
|
|
|
|
// InternalHTTPConfig configures the internal HTTP listener.
|
|
type InternalHTTPConfig struct {
|
|
// Addr stores the TCP listen address.
|
|
Addr string
|
|
|
|
// ReadHeaderTimeout bounds request-header reading.
|
|
ReadHeaderTimeout time.Duration
|
|
|
|
// ReadTimeout bounds reading one request.
|
|
ReadTimeout time.Duration
|
|
|
|
// IdleTimeout bounds how long keep-alive connections stay open.
|
|
IdleTimeout time.Duration
|
|
|
|
// RequestTimeout bounds one application-layer request execution.
|
|
RequestTimeout time.Duration
|
|
}
|
|
|
|
// Validate reports whether cfg stores a usable internal HTTP listener
|
|
// configuration.
|
|
func (cfg InternalHTTPConfig) Validate() error {
|
|
switch {
|
|
case strings.TrimSpace(cfg.Addr) == "":
|
|
return fmt.Errorf("internal HTTP addr must not be empty")
|
|
case cfg.ReadHeaderTimeout <= 0:
|
|
return fmt.Errorf("internal HTTP read header timeout must be positive")
|
|
case cfg.ReadTimeout <= 0:
|
|
return fmt.Errorf("internal HTTP read timeout must be positive")
|
|
case cfg.IdleTimeout <= 0:
|
|
return fmt.Errorf("internal HTTP idle timeout must be positive")
|
|
case cfg.RequestTimeout <= 0:
|
|
return fmt.Errorf("internal HTTP request timeout must be positive")
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// AdminHTTPConfig describes the private operational HTTP listener used for
|
|
// Prometheus metrics exposure. The listener remains disabled when Addr is
|
|
// empty.
|
|
type AdminHTTPConfig struct {
|
|
// Addr stores the TCP listen address used by the admin HTTP server.
|
|
Addr string
|
|
|
|
// ReadHeaderTimeout bounds request-header reading.
|
|
ReadHeaderTimeout time.Duration
|
|
|
|
// ReadTimeout bounds reading one request.
|
|
ReadTimeout time.Duration
|
|
|
|
// IdleTimeout bounds how long keep-alive connections stay open.
|
|
IdleTimeout time.Duration
|
|
}
|
|
|
|
// Validate reports whether cfg stores a usable optional admin HTTP listener
|
|
// configuration.
|
|
func (cfg AdminHTTPConfig) Validate() error {
|
|
if strings.TrimSpace(cfg.Addr) == "" {
|
|
return nil
|
|
}
|
|
|
|
switch {
|
|
case cfg.ReadHeaderTimeout <= 0:
|
|
return fmt.Errorf("admin HTTP read header timeout must be positive")
|
|
case cfg.ReadTimeout <= 0:
|
|
return fmt.Errorf("admin HTTP read timeout must be positive")
|
|
case cfg.IdleTimeout <= 0:
|
|
return fmt.Errorf("admin HTTP idle timeout must be positive")
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// RedisConfig configures the Redis-backed store and domain-event publisher.
|
|
type RedisConfig struct {
|
|
// Addr stores the Redis network address.
|
|
Addr string
|
|
|
|
// Username stores the optional Redis ACL username.
|
|
Username string
|
|
|
|
// Password stores the optional Redis ACL password.
|
|
Password string
|
|
|
|
// DB stores the Redis logical database index.
|
|
DB int
|
|
|
|
// TLSEnabled reports whether TLS must be used for Redis connections.
|
|
TLSEnabled bool
|
|
|
|
// OperationTimeout bounds one Redis round trip.
|
|
OperationTimeout time.Duration
|
|
|
|
// KeyspacePrefix stores the root prefix of the service-owned Redis keyspace.
|
|
KeyspacePrefix string
|
|
|
|
// DomainEventsStream stores the Redis Stream key used for auxiliary
|
|
// post-commit domain events.
|
|
DomainEventsStream string
|
|
|
|
// DomainEventsStreamMaxLen bounds the domain-events Redis Stream with
|
|
// approximate trimming.
|
|
DomainEventsStreamMaxLen int64
|
|
|
|
// LifecycleEventsStream stores the Redis Stream key used for trusted
|
|
// user-lifecycle events (permanent_block, delete) consumed by
|
|
// `Game Lobby` for Race Name Directory cascade release.
|
|
LifecycleEventsStream string
|
|
|
|
// LifecycleEventsStreamMaxLen bounds the lifecycle-events Redis Stream
|
|
// with approximate trimming.
|
|
LifecycleEventsStreamMaxLen int64
|
|
}
|
|
|
|
// TLSConfig returns the conservative TLS configuration used by Redis adapters
|
|
// when TLSEnabled is true.
|
|
func (cfg RedisConfig) TLSConfig() *tls.Config {
|
|
if !cfg.TLSEnabled {
|
|
return nil
|
|
}
|
|
|
|
return &tls.Config{MinVersion: tls.VersionTLS12}
|
|
}
|
|
|
|
// Validate reports whether cfg stores a usable Redis configuration.
|
|
func (cfg RedisConfig) Validate() error {
|
|
switch {
|
|
case strings.TrimSpace(cfg.Addr) == "":
|
|
return fmt.Errorf("redis addr must not be empty")
|
|
case cfg.DB < 0:
|
|
return fmt.Errorf("redis db must not be negative")
|
|
case cfg.OperationTimeout <= 0:
|
|
return fmt.Errorf("redis operation timeout must be positive")
|
|
case strings.TrimSpace(cfg.KeyspacePrefix) == "":
|
|
return fmt.Errorf("redis keyspace prefix must not be empty")
|
|
case strings.TrimSpace(cfg.DomainEventsStream) == "":
|
|
return fmt.Errorf("redis domain events stream must not be empty")
|
|
case cfg.DomainEventsStreamMaxLen <= 0:
|
|
return fmt.Errorf("redis domain events stream max len must be positive")
|
|
case strings.TrimSpace(cfg.LifecycleEventsStream) == "":
|
|
return fmt.Errorf("redis lifecycle events stream must not be empty")
|
|
case cfg.LifecycleEventsStreamMaxLen <= 0:
|
|
return fmt.Errorf("redis lifecycle events stream max len must be positive")
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// TelemetryConfig configures the user-service OpenTelemetry runtime.
|
|
type TelemetryConfig struct {
|
|
// ServiceName overrides the default OpenTelemetry service name.
|
|
ServiceName string
|
|
|
|
// TracesExporter selects the external traces exporter. Supported values are
|
|
// `none` and `otlp`.
|
|
TracesExporter string
|
|
|
|
// MetricsExporter selects the external metrics exporter. Supported values
|
|
// are `none` and `otlp`.
|
|
MetricsExporter string
|
|
|
|
// TracesProtocol selects the OTLP traces protocol when TracesExporter is
|
|
// `otlp`.
|
|
TracesProtocol string
|
|
|
|
// MetricsProtocol selects the OTLP metrics protocol when MetricsExporter is
|
|
// `otlp`.
|
|
MetricsProtocol string
|
|
|
|
// StdoutTracesEnabled enables the additional stdout trace exporter used for
|
|
// local development and debugging.
|
|
StdoutTracesEnabled bool
|
|
|
|
// StdoutMetricsEnabled enables the additional stdout metric exporter used
|
|
// for local development and debugging.
|
|
StdoutMetricsEnabled bool
|
|
}
|
|
|
|
// Validate reports whether cfg contains a supported OpenTelemetry exporter
|
|
// configuration.
|
|
func (cfg TelemetryConfig) Validate() error {
|
|
switch cfg.TracesExporter {
|
|
case otelExporterNone, otelExporterOTLP:
|
|
default:
|
|
return fmt.Errorf("%s %q is unsupported", otelTracesExporterEnvVar, cfg.TracesExporter)
|
|
}
|
|
|
|
switch cfg.MetricsExporter {
|
|
case otelExporterNone, otelExporterOTLP:
|
|
default:
|
|
return fmt.Errorf("%s %q is unsupported", otelMetricsExporterEnvVar, cfg.MetricsExporter)
|
|
}
|
|
|
|
if cfg.TracesProtocol != "" && cfg.TracesProtocol != otelProtocolHTTPProtobuf && cfg.TracesProtocol != otelProtocolGRPC {
|
|
return fmt.Errorf("%s %q is unsupported", otelExporterOTLPTracesProtocolEnvVar, cfg.TracesProtocol)
|
|
}
|
|
if cfg.MetricsProtocol != "" && cfg.MetricsProtocol != otelProtocolHTTPProtobuf && cfg.MetricsProtocol != otelProtocolGRPC {
|
|
return fmt.Errorf("%s %q is unsupported", otelExporterOTLPMetricsProtocolEnvVar, cfg.MetricsProtocol)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DefaultAdminHTTPConfig returns the default settings for the optional private
|
|
// admin HTTP listener.
|
|
func DefaultAdminHTTPConfig() AdminHTTPConfig {
|
|
return AdminHTTPConfig{
|
|
Addr: defaultAdminHTTPAddr,
|
|
ReadHeaderTimeout: defaultReadHeaderTimeout,
|
|
ReadTimeout: defaultReadTimeout,
|
|
IdleTimeout: defaultIdleTimeout,
|
|
}
|
|
}
|
|
|
|
// DefaultConfig returns the default process configuration with all optional
|
|
// values filled.
|
|
func DefaultConfig() Config {
|
|
return Config{
|
|
ShutdownTimeout: defaultShutdownTimeout,
|
|
Logging: LoggingConfig{
|
|
Level: defaultLogLevel,
|
|
},
|
|
InternalHTTP: InternalHTTPConfig{
|
|
Addr: defaultInternalHTTPAddr,
|
|
ReadHeaderTimeout: defaultReadHeaderTimeout,
|
|
ReadTimeout: defaultReadTimeout,
|
|
IdleTimeout: defaultIdleTimeout,
|
|
RequestTimeout: defaultRequestTimeout,
|
|
},
|
|
AdminHTTP: DefaultAdminHTTPConfig(),
|
|
Redis: RedisConfig{
|
|
DB: defaultRedisDB,
|
|
OperationTimeout: defaultRedisOperationTimeout,
|
|
KeyspacePrefix: defaultRedisKeyspacePrefix,
|
|
DomainEventsStream: defaultDomainEventsStream,
|
|
DomainEventsStreamMaxLen: defaultDomainEventsStreamMaxLen,
|
|
LifecycleEventsStream: defaultLifecycleEventsStream,
|
|
LifecycleEventsStreamMaxLen: defaultLifecycleEventsStreamMaxLen,
|
|
},
|
|
Telemetry: TelemetryConfig{
|
|
ServiceName: defaultOTelServiceName,
|
|
TracesExporter: otelExporterNone,
|
|
MetricsExporter: otelExporterNone,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Validate reports whether cfg is process-ready.
|
|
func (cfg Config) Validate() error {
|
|
switch {
|
|
case cfg.ShutdownTimeout <= 0:
|
|
return fmt.Errorf("shutdown timeout must be positive")
|
|
}
|
|
if err := cfg.InternalHTTP.Validate(); err != nil {
|
|
return fmt.Errorf("internal HTTP config: %w", err)
|
|
}
|
|
if err := cfg.AdminHTTP.Validate(); err != nil {
|
|
return fmt.Errorf("admin HTTP config: %w", err)
|
|
}
|
|
if err := cfg.Redis.Validate(); err != nil {
|
|
return fmt.Errorf("redis config: %w", err)
|
|
}
|
|
if _, err := parseLogLevel(cfg.Logging.Level); err != nil {
|
|
return fmt.Errorf("logging config: %w", err)
|
|
}
|
|
if err := cfg.Telemetry.Validate(); err != nil {
|
|
return fmt.Errorf("telemetry config: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadFromEnv loads Config from the process environment.
|
|
func LoadFromEnv() (Config, error) {
|
|
cfg := DefaultConfig()
|
|
|
|
var err error
|
|
cfg.ShutdownTimeout, err = loadDuration(shutdownTimeoutEnvVar, cfg.ShutdownTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.Logging.Level = loadString(logLevelEnvVar, cfg.Logging.Level)
|
|
|
|
cfg.InternalHTTP.Addr = loadString(internalHTTPAddrEnvVar, cfg.InternalHTTP.Addr)
|
|
cfg.InternalHTTP.ReadHeaderTimeout, err = loadDuration(internalHTTPReadHeaderTimeoutEnvVar, cfg.InternalHTTP.ReadHeaderTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.InternalHTTP.ReadTimeout, err = loadDuration(internalHTTPReadTimeoutEnvVar, cfg.InternalHTTP.ReadTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.InternalHTTP.IdleTimeout, err = loadDuration(internalHTTPIdleTimeoutEnvVar, cfg.InternalHTTP.IdleTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.InternalHTTP.RequestTimeout, err = loadDuration(internalHTTPRequestTimeoutEnvVar, cfg.InternalHTTP.RequestTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
|
|
cfg.AdminHTTP.Addr = loadString(adminHTTPAddrEnvVar, cfg.AdminHTTP.Addr)
|
|
cfg.AdminHTTP.ReadHeaderTimeout, err = loadDuration(adminHTTPReadHeaderTimeoutEnvVar, cfg.AdminHTTP.ReadHeaderTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.AdminHTTP.ReadTimeout, err = loadDuration(adminHTTPReadTimeoutEnvVar, cfg.AdminHTTP.ReadTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.AdminHTTP.IdleTimeout, err = loadDuration(adminHTTPIdleTimeoutEnvVar, cfg.AdminHTTP.IdleTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
|
|
cfg.Redis.Addr = loadString(redisAddrEnvVar, cfg.Redis.Addr)
|
|
cfg.Redis.Username = loadString(redisUsernameEnvVar, cfg.Redis.Username)
|
|
cfg.Redis.Password = loadString(redisPasswordEnvVar, cfg.Redis.Password)
|
|
cfg.Redis.DB, err = loadInt(redisDBEnvVar, cfg.Redis.DB)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.Redis.TLSEnabled, err = loadBool(redisTLSEnabledEnvVar, cfg.Redis.TLSEnabled)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.Redis.OperationTimeout, err = loadDuration(redisOperationTimeoutEnvVar, cfg.Redis.OperationTimeout)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.Redis.KeyspacePrefix = loadString(redisKeyspacePrefixEnvVar, cfg.Redis.KeyspacePrefix)
|
|
cfg.Redis.DomainEventsStream = loadString(redisDomainEventsStreamEnvVar, cfg.Redis.DomainEventsStream)
|
|
cfg.Redis.DomainEventsStreamMaxLen, err = loadInt64(redisDomainEventsStreamMaxLenEnvVar, cfg.Redis.DomainEventsStreamMaxLen)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.Redis.LifecycleEventsStream = loadString(redisLifecycleEventsStreamEnvVar, cfg.Redis.LifecycleEventsStream)
|
|
cfg.Redis.LifecycleEventsStreamMaxLen, err = loadInt64(redisLifecycleEventsStreamMaxLenEnvVar, cfg.Redis.LifecycleEventsStreamMaxLen)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
|
|
cfg.Telemetry.ServiceName = loadString(otelServiceNameEnvVar, cfg.Telemetry.ServiceName)
|
|
cfg.Telemetry.TracesExporter = normalizeExporterValue(loadString(otelTracesExporterEnvVar, cfg.Telemetry.TracesExporter))
|
|
cfg.Telemetry.MetricsExporter = normalizeExporterValue(loadString(otelMetricsExporterEnvVar, cfg.Telemetry.MetricsExporter))
|
|
cfg.Telemetry.TracesProtocol = loadOTLPProtocol(
|
|
os.Getenv(otelExporterOTLPTracesProtocolEnvVar),
|
|
os.Getenv(otelExporterOTLPProtocolEnvVar),
|
|
cfg.Telemetry.TracesExporter,
|
|
)
|
|
cfg.Telemetry.MetricsProtocol = loadOTLPProtocol(
|
|
os.Getenv(otelExporterOTLPMetricsProtocolEnvVar),
|
|
os.Getenv(otelExporterOTLPProtocolEnvVar),
|
|
cfg.Telemetry.MetricsExporter,
|
|
)
|
|
cfg.Telemetry.StdoutTracesEnabled, err = loadBool(otelStdoutTracesEnabledEnvVar, cfg.Telemetry.StdoutTracesEnabled)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.Telemetry.StdoutMetricsEnabled, err = loadBool(otelStdoutMetricsEnabledEnvVar, cfg.Telemetry.StdoutMetricsEnabled)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
|
|
if err := cfg.Validate(); err != nil {
|
|
return Config{}, err
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func loadString(envName string, defaultValue string) string {
|
|
value, ok := os.LookupEnv(envName)
|
|
if !ok {
|
|
return defaultValue
|
|
}
|
|
|
|
return strings.TrimSpace(value)
|
|
}
|
|
|
|
func loadDuration(envName string, defaultValue time.Duration) (time.Duration, error) {
|
|
value, ok := os.LookupEnv(envName)
|
|
if !ok {
|
|
return defaultValue, nil
|
|
}
|
|
|
|
duration, err := time.ParseDuration(strings.TrimSpace(value))
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s: parse duration: %w", envName, err)
|
|
}
|
|
|
|
return duration, nil
|
|
}
|
|
|
|
func loadInt(envName string, defaultValue int) (int, error) {
|
|
value, ok := os.LookupEnv(envName)
|
|
if !ok {
|
|
return defaultValue, nil
|
|
}
|
|
|
|
parsedValue, err := strconv.Atoi(strings.TrimSpace(value))
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s: parse int: %w", envName, err)
|
|
}
|
|
|
|
return parsedValue, nil
|
|
}
|
|
|
|
func loadInt64(envName string, defaultValue int64) (int64, error) {
|
|
value, ok := os.LookupEnv(envName)
|
|
if !ok {
|
|
return defaultValue, nil
|
|
}
|
|
|
|
parsedValue, err := strconv.ParseInt(strings.TrimSpace(value), 10, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s: parse int64: %w", envName, err)
|
|
}
|
|
|
|
return parsedValue, nil
|
|
}
|
|
|
|
func loadBool(envName string, defaultValue bool) (bool, error) {
|
|
value, ok := os.LookupEnv(envName)
|
|
if !ok {
|
|
return defaultValue, nil
|
|
}
|
|
|
|
parsedValue, err := strconv.ParseBool(strings.TrimSpace(value))
|
|
if err != nil {
|
|
return false, fmt.Errorf("%s: parse bool: %w", envName, err)
|
|
}
|
|
|
|
return parsedValue, nil
|
|
}
|
|
|
|
func parseLogLevel(value string) (string, error) {
|
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
|
case "debug", "info", "warn", "error":
|
|
return value, nil
|
|
default:
|
|
return "", fmt.Errorf("unsupported log level %q", value)
|
|
}
|
|
}
|
|
|
|
func normalizeExporterValue(value string) string {
|
|
switch strings.TrimSpace(value) {
|
|
case "", otelExporterNone:
|
|
return otelExporterNone
|
|
default:
|
|
return strings.TrimSpace(value)
|
|
}
|
|
}
|
|
|
|
func loadOTLPProtocol(primary string, fallback string, exporter string) string {
|
|
protocol := strings.TrimSpace(primary)
|
|
if protocol == "" {
|
|
protocol = strings.TrimSpace(fallback)
|
|
}
|
|
if protocol == "" && exporter == otelExporterOTLP {
|
|
return otelProtocolHTTPProtobuf
|
|
}
|
|
|
|
return protocol
|
|
}
|
|
|
|
// ListenAddress returns the resolved listen address used by tests and process
|
|
// startup.
|
|
func (cfg InternalHTTPConfig) ListenAddress() string {
|
|
if strings.HasPrefix(cfg.Addr, ":") {
|
|
return net.JoinHostPort("", strings.TrimPrefix(cfg.Addr, ":"))
|
|
}
|
|
|
|
return cfg.Addr
|
|
}
|