Modernizes several `internal/*` packages under the Go proxy, replaces
third-party forks with standard-library primitives, and brings the
test suite from near-zero to high coverage across the touched packages.
Package changes
- **`internal/errors`** — Rewrites the `pkg/errors` fork as a thin
wrapper
over stdlib `errors`. A single `withStack` struct captures stack
traces via `runtime.Callers`; `fmt.Errorf("%w", ...)` handles all
message wrapping. Restores `errors.Is`/`As`/`Unwrap` chain traversal
(silently broken in the fork) and deletes ~190 lines of stack/frame
formatting. `Is`, `As`, `Unwrap`, and `Join` are re-exported so
callers need a single import.
- **`internal/logger`** — Swaps stdlib `log.Logger` for `log/slog` JSON
handlers with UTC timestamps and custom level labels (`verb`, `debug`,
`warn`, `error`). Hides `withContextID` (no external callers).
- **`internal/sync`** — Converts `Map[K, V]` from a concrete struct to
an interface with a `NewMap` constructor for testability.
- **`internal/signal`** — Adds `signalNotify` / `osExit` indirections so
`InstallSignals` and `InstallForceQuit` can be exercised without real
OS signals or process termination.
- **`internal/utils`** — Drops deprecated `io/ioutil` and the stdlib
`errors` alias (the internal `errors` package re-exports what's
needed).
- **`internal/version`** — No code changes; fully covered by new tests.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
123 lines
2.6 KiB
Go
123 lines
2.6 KiB
Go
// Copyright (c) 2026 Winlin
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
package logger
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
)
|
|
|
|
type logger interface {
|
|
Printf(ctx context.Context, format string, v ...any)
|
|
}
|
|
|
|
type loggerPlus struct {
|
|
logger *slog.Logger
|
|
level slog.Level
|
|
}
|
|
|
|
func newLoggerPlus(opts ...func(*loggerPlus)) *loggerPlus {
|
|
v := &loggerPlus{}
|
|
for _, opt := range opts {
|
|
opt(v)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func (v *loggerPlus) Printf(ctx context.Context, f string, a ...any) {
|
|
attrs := []slog.Attr{slog.Int("pid", os.Getpid())}
|
|
if cid := ContextID(ctx); cid != "" {
|
|
attrs = append(attrs, slog.String("cid", cid))
|
|
}
|
|
v.logger.LogAttrs(ctx, v.level, fmt.Sprintf(f, a...), attrs...)
|
|
}
|
|
|
|
var verboseLogger logger
|
|
|
|
func Vf(ctx context.Context, format string, a ...any) {
|
|
verboseLogger.Printf(ctx, format, a...)
|
|
}
|
|
|
|
var debugLogger logger
|
|
|
|
func Df(ctx context.Context, format string, a ...any) {
|
|
debugLogger.Printf(ctx, format, a...)
|
|
}
|
|
|
|
var warnLogger logger
|
|
|
|
func Wf(ctx context.Context, format string, a ...any) {
|
|
warnLogger.Printf(ctx, format, a...)
|
|
}
|
|
|
|
var errorLogger logger
|
|
|
|
func Ef(ctx context.Context, format string, a ...any) {
|
|
errorLogger.Printf(ctx, format, a...)
|
|
}
|
|
|
|
const (
|
|
levelVerb slog.Level = slog.LevelDebug - 4
|
|
levelDebug slog.Level = slog.LevelDebug
|
|
levelWarn slog.Level = slog.LevelWarn
|
|
levelError slog.Level = slog.LevelError
|
|
)
|
|
|
|
// newJSONLogger builds a slog.Logger that writes JSON records to w, renders the
|
|
// time in UTC, and maps our custom levels to short lowercase labels.
|
|
func newJSONLogger(w io.Writer) *slog.Logger {
|
|
h := slog.NewJSONHandler(w, &slog.HandlerOptions{
|
|
Level: levelVerb,
|
|
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
|
if len(groups) != 0 {
|
|
return a
|
|
}
|
|
switch a.Key {
|
|
case slog.TimeKey:
|
|
return slog.Time(slog.TimeKey, a.Value.Time().UTC())
|
|
case slog.LevelKey:
|
|
return slog.String(slog.LevelKey, levelLabel(a.Value.Any().(slog.Level)))
|
|
}
|
|
return a
|
|
},
|
|
})
|
|
return slog.New(h)
|
|
}
|
|
|
|
func levelLabel(l slog.Level) string {
|
|
switch l {
|
|
case levelVerb:
|
|
return "verb"
|
|
case levelDebug:
|
|
return "debug"
|
|
case levelWarn:
|
|
return "warn"
|
|
case levelError:
|
|
return "error"
|
|
}
|
|
return l.String()
|
|
}
|
|
|
|
func init() {
|
|
verboseLogger = newLoggerPlus(func(l *loggerPlus) {
|
|
l.logger = newJSONLogger(io.Discard)
|
|
l.level = levelVerb
|
|
})
|
|
debugLogger = newLoggerPlus(func(l *loggerPlus) {
|
|
l.logger = newJSONLogger(os.Stdout)
|
|
l.level = levelDebug
|
|
})
|
|
warnLogger = newLoggerPlus(func(l *loggerPlus) {
|
|
l.logger = newJSONLogger(os.Stderr)
|
|
l.level = levelWarn
|
|
})
|
|
errorLogger = newLoggerPlus(func(l *loggerPlus) {
|
|
l.logger = newJSONLogger(os.Stderr)
|
|
l.level = levelError
|
|
})
|
|
}
|