Some checks failed
Report errors, panics and optional performance traces to Sentry. Sentry stays disabled unless SENTRY_DSN is set. - slog ERROR records become Sentry issues, with the error attribute promoted to an exception so issues group by root cause - queue worker, reminder worker, cache cleanup and HTTP handler panics are captured with a stack trace - events carry platform/plugin/component tags for filtering - HTTP requests are traced when SENTRY_TRACES_SAMPLE_RATE is above 0; /healthz is never traced Configured credentials are redacted from every outgoing payload. This is required rather than defensive: the Telegram webhook embeds the bot token in its URL path, and failed Telegram API calls quote that URL in their error text, so events would otherwise carry the token in the clear. A new platform must register its credential in Config.Secrets(). Also moves the module to Go 1.27, refreshes every dependency and pins golangci-lint v2.13.2. sentry-go 0.48 removed issue creation from its slog integration, so the ERROR-to-issue conversion lives in internal/observability/handler.go instead of relying on the SDK; leaving it to the SDK would have silently downgraded issues to log lines. Claude-Session: https://claude.ai/code/session_01W7tcpMTEyk9RrHvT7Be5zZ
88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Config holds all application configuration
|
|
type Config struct {
|
|
Debug bool
|
|
Hostname string
|
|
Port string
|
|
LogLevel string
|
|
SecretKey string
|
|
DatabasePath string
|
|
TelegramConfig TelegramConfig
|
|
SentryConfig SentryConfig
|
|
}
|
|
|
|
// TelegramConfig holds Telegram platform configuration
|
|
type TelegramConfig struct {
|
|
Token string
|
|
}
|
|
|
|
// SentryConfig holds Sentry observability configuration
|
|
type SentryConfig struct {
|
|
DSN string
|
|
Environment string
|
|
Release string
|
|
TracesSampleRate float64
|
|
EnableLogs bool
|
|
}
|
|
|
|
// Load loads configuration from environment variables
|
|
func Load() (*Config, error) {
|
|
config := &Config{
|
|
Debug: getEnv("DEBUG", "n") == "y",
|
|
Hostname: getEnv("BUTTERROBOT_HOSTNAME", "butterrobot-dev.int.fmartingr.network"),
|
|
Port: getEnv("PORT", "8080"),
|
|
LogLevel: getEnv("LOG_LEVEL", "ERROR"),
|
|
SecretKey: getEnv("SECRET_KEY", "1234"),
|
|
DatabasePath: getEnv("DATABASE_PATH", "butterrobot.db"),
|
|
TelegramConfig: TelegramConfig{
|
|
Token: getEnv("TELEGRAM_TOKEN", ""),
|
|
},
|
|
SentryConfig: SentryConfig{
|
|
DSN: getEnv("SENTRY_DSN", ""),
|
|
Environment: getEnv("SENTRY_ENVIRONMENT", "production"),
|
|
Release: getEnv("SENTRY_RELEASE", ""),
|
|
TracesSampleRate: getEnvFloat("SENTRY_TRACES_SAMPLE_RATE", 0),
|
|
EnableLogs: getEnv("SENTRY_ENABLE_LOGS", "n") == "y",
|
|
},
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// Secrets returns configured credentials that must never appear in logs or
|
|
// error reports. Platform credentials travel inside webhook paths and API URLs,
|
|
// so error reporting has to know which values to redact.
|
|
func (c *Config) Secrets() []string {
|
|
var secrets []string
|
|
for _, secret := range []string{c.TelegramConfig.Token, c.SecretKey} {
|
|
if secret != "" {
|
|
secrets = append(secrets, secret)
|
|
}
|
|
}
|
|
return secrets
|
|
}
|
|
|
|
// getEnv retrieves an environment variable value or returns a default value
|
|
func getEnv(key, defaultValue string) string {
|
|
value := os.Getenv(key)
|
|
if strings.TrimSpace(value) == "" {
|
|
return defaultValue
|
|
}
|
|
return value
|
|
}
|
|
|
|
// getEnvFloat retrieves an environment variable as a float or returns a default value
|
|
func getEnvFloat(key string, defaultValue float64) float64 {
|
|
value, err := strconv.ParseFloat(getEnv(key, ""), 64)
|
|
if err != nil {
|
|
return defaultValue
|
|
}
|
|
return value
|
|
}
|