refactor(log): unified schema — mandatory type/class/method, request_id removed
Every log record now carries type ('http' for the request middleware
line, 'wrapper' for garmin execute() calls and forwarded wrapper.py
stderr, 'app' for everything else), class (Go type with package, or the
package/module for free functions), and method (the emitting Go/Python
function -- wrapper.py stamps it automatically, so its msg prefixes are
gone). msg is optional and omitted when empty; the redundant messages
('HTTP request', 'wrapper call', 'garmin wrapper stderr') are dropped,
the HTTP verb moves to http_method, source=garmin-wrapper is replaced by
type=wrapper, and the request_id middleware plumbing (applog
WithLogger/FromContext) is removed. applog.App(class, method) is the
tagging helper for app records.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -101,7 +101,7 @@ func TestSubprocessClient_RoundTrip_DetectsIDMismatch(t *testing.T) {
|
||||
scanner.Buffer(make([]byte, 0, 64*1024), maxWrapperLineBytes)
|
||||
c := &subprocessClient{started: true, enc: json.NewEncoder(reqW), scanner: scanner}
|
||||
|
||||
_, err := c.roundTrip(context.Background(), "authenticate", nil)
|
||||
_, err := c.execute(context.Background(), "authenticate", nil)
|
||||
if err == nil || !strings.Contains(err.Error(), "id mismatch") {
|
||||
t.Fatalf("roundTrip error = %v, want an id mismatch error", err)
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func TestSubprocessClient_RoundTrip_SubprocessClosedIsError(t *testing.T) {
|
||||
scanner.Buffer(make([]byte, 0, 64*1024), maxWrapperLineBytes)
|
||||
c := &subprocessClient{started: true, enc: json.NewEncoder(reqW), scanner: scanner}
|
||||
|
||||
_, err := c.roundTrip(context.Background(), "authenticate", nil)
|
||||
_, err := c.execute(context.Background(), "authenticate", nil)
|
||||
if err == nil || !strings.Contains(err.Error(), "closed") {
|
||||
t.Fatalf("roundTrip error = %v, want a subprocess-closed error", err)
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func TestSubprocessClient_RoundTrip_WrapperErrorPropagates(t *testing.T) {
|
||||
return fakeError("boom")
|
||||
})
|
||||
|
||||
_, err := c.roundTrip(context.Background(), "authenticate", nil)
|
||||
_, err := c.execute(context.Background(), "authenticate", nil)
|
||||
if err == nil || !strings.Contains(err.Error(), "boom") {
|
||||
t.Fatalf("roundTrip error = %v, want it to mention %q", err, "boom")
|
||||
}
|
||||
@@ -145,7 +145,7 @@ func TestSubprocessClient_RoundTrip_NotFoundWrapsErrNotFound(t *testing.T) {
|
||||
return fakeNotFoundError("API Error 404")
|
||||
})
|
||||
|
||||
_, err := c.roundTrip(context.Background(), "call", nil)
|
||||
_, err := c.execute(context.Background(), "call", nil)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("roundTrip error = %v, want errors.Is(err, ErrNotFound)", err)
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func TestSubprocessClient_RoundTrip_OrdinaryErrorDoesNotWrapErrNotFound(t *testi
|
||||
return fakeError("boom")
|
||||
})
|
||||
|
||||
_, err := c.roundTrip(context.Background(), "call", nil)
|
||||
_, err := c.execute(context.Background(), "call", nil)
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("roundTrip error = %v, want errors.Is(err, ErrNotFound) to be false", err)
|
||||
}
|
||||
@@ -436,10 +436,11 @@ func TestSubprocessClient_RoundTrip_LogsCallWithResultPreview(t *testing.T) {
|
||||
})
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := slog.New(slog.NewJSONHandler(&buf, nil))
|
||||
ctx := applog.WithLogger(context.Background(), logger)
|
||||
prev := slog.Default()
|
||||
slog.SetDefault(applog.NewLogger("info", &buf))
|
||||
defer slog.SetDefault(prev)
|
||||
|
||||
if _, err := c.Authenticate(ctx); err != nil {
|
||||
if _, err := c.Authenticate(context.Background()); err != nil {
|
||||
t.Fatalf("Authenticate: %v", err)
|
||||
}
|
||||
|
||||
@@ -447,8 +448,11 @@ func TestSubprocessClient_RoundTrip_LogsCallWithResultPreview(t *testing.T) {
|
||||
if err := json.Unmarshal(buf.Bytes(), &entry); err != nil {
|
||||
t.Fatalf("log output is not valid JSON: %v (%q)", err, buf.String())
|
||||
}
|
||||
if entry["msg"] != "Garmin wrapper call" {
|
||||
t.Errorf("msg = %v, want \"Garmin wrapper call\"", entry["msg"])
|
||||
if entry["type"] != "wrapper" || entry["class"] != "garmin.subprocessClient" || entry["method"] != "execute" {
|
||||
t.Errorf("schema fields = %v, want type=wrapper class=garmin.subprocessClient method=execute", entry)
|
||||
}
|
||||
if _, hasMsg := entry["msg"]; hasMsg {
|
||||
t.Errorf("expected no msg on the wrapper-call record, got %v", entry["msg"])
|
||||
}
|
||||
if entry["cmd"] != "authenticate" {
|
||||
t.Errorf("cmd = %v, want authenticate", entry["cmd"])
|
||||
@@ -468,10 +472,11 @@ func TestSubprocessClient_RoundTrip_LogsFailedCallAtErrorLevel(t *testing.T) {
|
||||
})
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := slog.New(slog.NewJSONHandler(&buf, nil))
|
||||
ctx := applog.WithLogger(context.Background(), logger)
|
||||
prev := slog.Default()
|
||||
slog.SetDefault(applog.NewLogger("info", &buf))
|
||||
defer slog.SetDefault(prev)
|
||||
|
||||
if _, err := c.Authenticate(ctx); err == nil {
|
||||
if _, err := c.Authenticate(context.Background()); err == nil {
|
||||
t.Fatal("expected Authenticate to return an error")
|
||||
}
|
||||
|
||||
@@ -490,7 +495,7 @@ func TestSubprocessClient_RoundTrip_LogsFailedCallAtErrorLevel(t *testing.T) {
|
||||
func TestForwardWrapperStderr_ReemitsJSONAndWrapsFreeText(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
prev := slog.Default()
|
||||
slog.SetDefault(slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})))
|
||||
slog.SetDefault(applog.NewLogger("debug", &buf))
|
||||
defer slog.SetDefault(prev)
|
||||
|
||||
stderr := strings.NewReader(
|
||||
@@ -511,16 +516,19 @@ func TestForwardWrapperStderr_ReemitsJSONAndWrapsFreeText(t *testing.T) {
|
||||
if first["level"] != "WARN" || first["msg"] != "startup tokenstore login failed" {
|
||||
t.Errorf("first = %v, want the wrapper's level and msg preserved", first)
|
||||
}
|
||||
if first["error"] != "boom" || first["source"] != "garmin-wrapper" {
|
||||
t.Errorf("first = %v, want error attr preserved and source=garmin-wrapper", first)
|
||||
if first["error"] != "boom" || first["type"] != "wrapper" || first["class"] != "wrapper" {
|
||||
t.Errorf("first = %v, want error attr preserved and type=wrapper class=wrapper", first)
|
||||
}
|
||||
|
||||
var second map[string]any
|
||||
if err := json.Unmarshal([]byte(lines[1]), &second); err != nil {
|
||||
t.Fatalf("second line not JSON: %v", err)
|
||||
}
|
||||
if second["msg"] != "garmin wrapper stderr" || second["level"] != "WARN" {
|
||||
t.Errorf("second = %v, want free text wrapped as a warn record", second)
|
||||
if _, hasMsg := second["msg"]; hasMsg {
|
||||
t.Errorf("second = %v, want no msg on a wrapped free-text record", second)
|
||||
}
|
||||
if second["type"] != "wrapper" || second["level"] != "WARN" {
|
||||
t.Errorf("second = %v, want free text wrapped as a warn type=wrapper record", second)
|
||||
}
|
||||
if line, _ := second["line"].(string); !strings.Contains(line, "Traceback") {
|
||||
t.Errorf("second line attr = %q, want the raw text preserved", line)
|
||||
|
||||
Reference in New Issue
Block a user