teg/backend/log.go
2026-05-22 14:11:18 +02:00

159 lines
2.9 KiB
Go

// backend/log.go
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"os"
"time"
)
type LogLevel string
const (
LogLevelInfo LogLevel = "info"
LogLevelWarning LogLevel = "warning"
LogLevelError LogLevel = "error"
)
type LogFields map[string]any
type AppError struct {
Message string
Err error
Fields LogFields
}
var appLogger = log.New(os.Stdout, "", 0)
func (e *AppError) Error() string {
if e == nil {
return ""
}
if e.Err == nil {
return e.Message
}
if e.Message == "" {
return e.Err.Error()
}
return e.Message + ": " + e.Err.Error()
}
func (e *AppError) Unwrap() error {
if e == nil {
return nil
}
return e.Err
}
func newErrorf(format string, args ...any) error {
return fmt.Errorf(format, args...)
}
func newAppError(message string, err error, fields LogFields) error {
return &AppError{
Message: message,
Err: err,
Fields: fields,
}
}
func newAppErrorf(message string, fields LogFields, format string, args ...any) error {
return newAppError(
message,
fmt.Errorf(format, args...),
fields,
)
}
func formatText(format string, args ...any) string {
return fmt.Sprintf(format, args...)
}
func logInfo(message string, fields LogFields) {
writeAppLog(LogLevelInfo, message, nil, fields)
}
func logInfof(format string, args ...any) {
writeAppLog(LogLevelInfo, fmt.Sprintf(format, args...), nil, nil)
}
func logWarning(message string, fields LogFields) {
writeAppLog(LogLevelWarning, message, nil, fields)
}
func logWarningf(format string, args ...any) {
writeAppLog(LogLevelWarning, fmt.Sprintf(format, args...), nil, nil)
}
func logError(message string, err error, fields LogFields) {
writeAppLog(LogLevelError, message, err, fields)
}
func logErrorf(err error, format string, args ...any) {
writeAppLog(LogLevelError, fmt.Sprintf(format, args...), err, nil)
}
func appErrorFields(err error) LogFields {
var appErr *AppError
if errors.As(err, &appErr) && appErr.Fields != nil {
return appErr.Fields
}
return nil
}
func mergeLogFields(base LogFields, extra LogFields) LogFields {
if len(base) == 0 && len(extra) == 0 {
return nil
}
merged := LogFields{}
for key, value := range base {
merged[key] = value
}
for key, value := range extra {
merged[key] = value
}
return merged
}
func writeAppLog(level LogLevel, message string, err error, fields LogFields) {
payload := map[string]any{
"timestamp": time.Now().UTC().Format(time.RFC3339Nano),
"level": level,
"message": message,
}
if err != nil {
payload["error"] = err.Error()
}
if len(fields) > 0 {
payload["fields"] = fields
}
data, marshalErr := json.Marshal(payload)
if marshalErr != nil {
appLogger.Printf(
`{"timestamp":"%s","level":"error","message":"could not marshal log payload","error":%q}`,
time.Now().UTC().Format(time.RFC3339Nano),
marshalErr.Error(),
)
return
}
appLogger.Println(string(data))
}