2026-07-26 14:28:22 +02:00
|
|
|
package applog
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"log/slog"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestNewLogger_FiltersBelowConfiguredLevel(t *testing.T) {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
logger := NewLogger("warn", &buf)
|
|
|
|
|
|
|
|
|
|
logger.Info("should be dropped")
|
|
|
|
|
if buf.Len() != 0 {
|
|
|
|
|
t.Fatalf("expected no output for an Info message under a warn-level logger, got %q", buf.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Warn("should appear")
|
|
|
|
|
if !strings.Contains(buf.String(), "should appear") {
|
|
|
|
|
t.Fatalf("expected the Warn message in output, got %q", buf.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNewLogger_WritesValidJSON(t *testing.T) {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
logger := NewLogger("info", &buf)
|
|
|
|
|
logger.Info("hello", "key", "value")
|
|
|
|
|
|
|
|
|
|
var decoded map[string]any
|
|
|
|
|
if err := json.Unmarshal(buf.Bytes(), &decoded); err != nil {
|
|
|
|
|
t.Fatalf("output is not valid JSON: %v (%q)", err, buf.String())
|
|
|
|
|
}
|
|
|
|
|
if decoded["msg"] != "hello" || decoded["key"] != "value" {
|
|
|
|
|
t.Errorf("decoded = %+v, want msg=hello key=value", decoded)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-08-04 19:45:30 +02:00
|
|
|
func TestNewLogger_OmitsEmptyMsg(t *testing.T) {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
logger := NewLogger("info", &buf)
|
|
|
|
|
logger.Info("", "type", "http", "status", 200)
|
2026-07-26 14:28:22 +02:00
|
|
|
|
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>
2026-08-04 19:45:30 +02:00
|
|
|
var decoded map[string]any
|
|
|
|
|
if err := json.Unmarshal(buf.Bytes(), &decoded); err != nil {
|
|
|
|
|
t.Fatalf("output not JSON: %v (%q)", err, buf.String())
|
|
|
|
|
}
|
|
|
|
|
if _, present := decoded["msg"]; present {
|
|
|
|
|
t.Errorf("empty msg should be omitted, got %+v", decoded)
|
|
|
|
|
}
|
|
|
|
|
if decoded["type"] != "http" {
|
|
|
|
|
t.Errorf("attrs lost alongside dropped msg: %+v", decoded)
|
2026-07-26 14:28:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-08-04 19:45:30 +02:00
|
|
|
func TestApp_TagsMandatoryFields(t *testing.T) {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
prev := slog.Default()
|
|
|
|
|
slog.SetDefault(NewLogger("info", &buf))
|
|
|
|
|
defer slog.SetDefault(prev)
|
|
|
|
|
|
|
|
|
|
App("api.Server", "handleThing").Info("did it", "extra", 1)
|
|
|
|
|
|
|
|
|
|
var decoded map[string]any
|
|
|
|
|
if err := json.Unmarshal(buf.Bytes(), &decoded); err != nil {
|
|
|
|
|
t.Fatalf("output not JSON: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if decoded["type"] != "app" || decoded["class"] != "api.Server" || decoded["method"] != "handleThing" {
|
|
|
|
|
t.Errorf("mandatory fields = %+v, want type=app class=api.Server method=handleThing", decoded)
|
2026-07-26 14:28:22 +02:00
|
|
|
}
|
|
|
|
|
}
|