🐛 fix: emit all config-loading logs in correct JSON format from the start (#16)
## Summary Closes #15 When `logging.json: true` (or `DLC_LOGGING_JSON=true`), the logger was unconditionally initialised to console/text format at the top of `LoadConfig()`, so early log lines — most visibly **"Config file loaded"** — were always written as human-readable text regardless of configuration. ## Root cause Classic chicken-and-egg: the format flag lives inside the config that is being loaded. The format-switch block only ran *after* `v.Unmarshal()`, too late for the config-file log. ## Changes ### `pkg/config/config.go` - Add `peekJSONLogging()`: resolves the JSON flag **before** any log is emitted by (1) checking `DLC_LOGGING_JSON` directly via `os.Getenv`, then (2) doing a minimal throwaway Viper pre-read of the config file for the `logging.json` key. This mirrors Viper's own priority order without parsing the full config twice. - Apply the resolved format immediately and emit **"Logging configured"** as the very first log line. - Remove the now-redundant format-switch block that ran after `Unmarshal()`. ### `scripts/start-server.sh`, `test-graceful-shutdown.sh`, `test-opentelemetry.sh` - Replace hardcoded `PROJECT_DIR` path with a dynamic `SCRIPTS_DIR=$(dirname $(realpath ${BASH_SOURCE[0]}))` derivation so scripts work from any worktree or clone location. ## Test plan - [x] `go test ./pkg/...` — all pass - [x] `scripts/test-graceful-shutdown.sh` — all JSON valid, all startup logs present - [x] Manual smoke test: first line is `{"level":"info",...,"message":"Logging configured"}`, every line is valid JSON Reviewed-on: #16 Co-authored-by: Gabriel Radureau <arcodange@gmail.com> Co-committed-by: Gabriel Radureau <arcodange@gmail.com>
This commit was merged in pull request #16.
This commit is contained in:
@@ -33,6 +33,28 @@ import (
|
||||
//go:embed docs/swagger.json
|
||||
var swaggerJSON embed.FS
|
||||
|
||||
// CancelableContext wraps a context.Context and exposes a Cancel() method so
|
||||
// that Server.Run() can cancel readiness during graceful shutdown via the type
|
||||
// assertion it already performs. Callers that don't need controlled cancellation
|
||||
// (tests, CLI) can pass a plain context.Background() — the assertion silently
|
||||
// fails and readiness is never explicitly cancelled, which is harmless.
|
||||
type CancelableContext struct {
|
||||
context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// NewCancelableContext creates a CancelableContext whose Cancel() method will
|
||||
// be invoked by Server.Run() at the start of graceful shutdown, before the
|
||||
// 1-second readiness propagation window. The returned CancelFunc is a no-op
|
||||
// after Cancel() has been called, so it is safe to defer in main.
|
||||
func NewCancelableContext(parent context.Context) (*CancelableContext, context.CancelFunc) {
|
||||
ctx, cancel := context.WithCancel(parent)
|
||||
return &CancelableContext{Context: ctx, cancel: cancel}, cancel
|
||||
}
|
||||
|
||||
// Cancel satisfies the interface checked in Run() and cancels the context.
|
||||
func (c *CancelableContext) Cancel() { c.cancel() }
|
||||
|
||||
type Server struct {
|
||||
router *chi.Mux
|
||||
readyCtx context.Context
|
||||
|
||||
Reference in New Issue
Block a user