mirror of
https://github.com/holos-run/holos.git
synced 2026-03-20 17:25:01 +00:00
Remove the server.Config struct, not needed. Remove the app struct and move the configuration to the main holos.Config.ServerConfig. Add flags specific to server configuration. With this patch logging is simplified. Subcommands have a handle on the top level holos.Config and can get a fully configured logger from cfg.Logger() after flag parsing happens.
115 lines
2.5 KiB
Go
115 lines
2.5 KiB
Go
// Package errors provides error wrapping with location information
|
|
package errors
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
// ErrUnsupported is errors.ErrUnsupported
|
|
var ErrUnsupported = errors.ErrUnsupported
|
|
|
|
// As calls errors.As
|
|
func As(err error, target any) bool {
|
|
return errors.As(err, target)
|
|
}
|
|
|
|
// Is calls errors.Is
|
|
func Is(err error, target error) bool {
|
|
return errors.Is(err, target)
|
|
}
|
|
|
|
// Join calls errors.Join
|
|
func Join(errs ...error) error {
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
// New calls errors.New
|
|
func New(text string) error {
|
|
return errors.New(text)
|
|
}
|
|
|
|
// Unwrap calls errors.Unwrap
|
|
func Unwrap(err error) error {
|
|
return errors.Unwrap(err)
|
|
}
|
|
|
|
// Source represents the Source file and line where an error was encountered
|
|
type Source struct {
|
|
File string `json:"file"`
|
|
Line int `json:"line"`
|
|
}
|
|
|
|
func (s *Source) Loc() string {
|
|
return fmt.Sprintf("%s:%d", filepath.Base(s.File), s.Line)
|
|
}
|
|
|
|
// ErrorAt wraps an error with the Source location the error was encountered at
|
|
// for tracing from a top level error handler.
|
|
type ErrorAt struct {
|
|
Err error
|
|
Source Source
|
|
}
|
|
|
|
// Unwrap implements error wrapping.
|
|
func (e *ErrorAt) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// Error returns the error string with Source location.
|
|
func (e *ErrorAt) Error() string {
|
|
return e.Source.Loc() + ": " + e.Err.Error()
|
|
}
|
|
|
|
// Wrap wraps err in a ErrorAt or returns err if err is nil, already a
|
|
// ErrorAt, or caller info is not available.
|
|
//
|
|
// XXX: Refactor to Err(error, ...slog.Attr). Often want to add attributes for the top level logger.
|
|
func Wrap(err error) error {
|
|
// Nothing to do
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
// Already a holos error no need to do anything.
|
|
var errAt *ErrorAt
|
|
if errors.As(err, &errAt) {
|
|
return err
|
|
}
|
|
|
|
// Try to wrap err with caller info
|
|
if _, file, line, ok := runtime.Caller(1); ok {
|
|
return &ErrorAt{
|
|
Err: err,
|
|
Source: Source{
|
|
File: file,
|
|
Line: line,
|
|
},
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// Log logs err with Source location if Err is a ErrorAt
|
|
func Log(log *slog.Logger, ctx context.Context, level slog.Level, err error, msg string, args ...any) {
|
|
var errAt *ErrorAt
|
|
if ok := errors.As(err, &errAt); ok {
|
|
args = append(args,
|
|
slog.String("err", errAt.Unwrap().Error()),
|
|
slog.String("loc", errAt.Source.Loc()),
|
|
)
|
|
} else {
|
|
if _, file, line, ok := runtime.Caller(1); ok {
|
|
source := Source{file, line}
|
|
args = append(args, slog.String("loc", source.Loc()))
|
|
}
|
|
args = append(args, slog.String("err", err.Error()))
|
|
}
|
|
log.Log(ctx, level, msg, args...)
|
|
}
|