Files
Arjun Patel 563c91e7d5 infra: add logging wrapping
Resolves issues with gcp cloud logging quirks such as field names
2026-05-27 15:49:27 -07:00

59 lines
1.7 KiB
Go

// Package flog is a structured logger formatted for GCP Cloud Logging.
//
// The API mirrors log/slog: a message string followed by alternating key/value
// pairs. Each kv pair becomes a JSON field in the emitted record, which Cloud
// Logging promotes to a queryable jsonPayload field.
//
// flog.Info("particle processed", "particleID", id, "elapsed", dt)
package flog
import (
"os"
"github.com/sirupsen/logrus"
)
// Prepares global logger for GCP Cloud Logging's quirks
var log = func() *logrus.Logger {
logger := logrus.New()
logger.SetFormatter(&logrus.JSONFormatter{
FieldMap: logrus.FieldMap{
logrus.FieldKeyLevel: "severity",
logrus.FieldKeyMsg: "message",
logrus.FieldKeyTime: "timestamp",
},
})
logger.SetOutput(os.Stderr)
logger.SetLevel(logrus.DebugLevel)
return logger
}()
// fields converts alternating key/value args into a logrus.Fields map. A
// trailing odd arg or non-string key is stored under "!BADKEY" to match
// slog's behavior.
func fields(args []any) logrus.Fields {
if len(args) == 0 {
return nil
}
f := make(logrus.Fields, len(args)/2+1)
for i := 0; i < len(args); i += 2 {
if i+1 >= len(args) {
f["!BADKEY"] = args[i]
break
}
key, ok := args[i].(string)
if !ok {
f["!BADKEY"] = args[i]
continue
}
f[key] = args[i+1]
}
return f
}
func Debug(msg string, args ...any) { log.WithFields(fields(args)).Debug(msg) }
func Info(msg string, args ...any) { log.WithFields(fields(args)).Info(msg) }
func Warn(msg string, args ...any) { log.WithFields(fields(args)).Warn(msg) }
func Error(msg string, args ...any) { log.WithFields(fields(args)).Error(msg) }
func Fatal(msg string, args ...any) { log.WithFields(fields(args)).Fatal(msg) }