## 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>
5.7 KiB
5.7 KiB
dance-lessons-coach — Agent Documentation
AI agent reference for developing, testing, and operating the dance-lessons-coach service.
Tech Stack
| Component | Technology | Version |
|---|---|---|
| Language | Go | 1.26.1 |
| Router | Chi | v5.2.5 |
| Logging | Zerolog | v1.35.0 |
| Configuration | Viper | v1.21.0 |
| Telemetry | OpenTelemetry | v1.43.0 |
Project Structure
dance-lessons-coach/
├── adr/ # Architecture Decision Records
├── cmd/
│ ├── greet/ # CLI application
│ └── server/ # Web server entry point
├── pkg/
│ ├── config/ # Viper-based configuration
│ ├── greet/ # Core domain logic + API handlers
│ ├── server/ # HTTP server, routing, graceful shutdown
│ ├── telemetry/ # OpenTelemetry instrumentation
│ ├── user/ # User domain (auth, JWT, repository)
│ └── validation/ # Request validation
├── scripts/ # Server lifecycle, build, test scripts
├── config.yaml # Configuration file
└── config.example.yaml # Configuration template
Server Management
# Start / stop / restart
./scripts/start-server.sh start
./scripts/start-server.sh stop
./scripts/start-server.sh restart
# Status and logs
./scripts/start-server.sh status
./scripts/start-server.sh logs
# Test all API endpoints
./scripts/start-server.sh test
Configuration
All settings can be provided via config.yaml or environment variables (DLC_ prefix).
| Option | Env var | Default | Description |
|---|---|---|---|
| Host | DLC_SERVER_HOST |
0.0.0.0 |
Bind address |
| Port | DLC_SERVER_PORT |
8080 |
Listening port |
| Shutdown timeout | DLC_SHUTDOWN_TIMEOUT |
30s |
Graceful shutdown window |
| JSON logging | DLC_LOGGING_JSON |
false |
Structured JSON output |
| Log output | DLC_LOGGING_OUTPUT |
"" |
File path (empty = stderr) |
| API v2 | DLC_API_V2_ENABLED |
false |
Enable /api/v2 routes |
| Config file | DLC_CONFIG_FILE |
./config.yaml |
Override config path |
Minimal config.yaml:
server:
host: "0.0.0.0"
port: 8080
shutdown:
timeout: 30s
logging:
json: false
Priority: env var > config file > default.
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/health |
Liveness — always {"status":"healthy"} |
| GET | /api/ready |
Readiness — 200 when ready, 503 during shutdown |
| GET | /api/version |
Version info (?format=plain|full|json) |
| GET | /api/v1/greet/ |
Default greeting |
| GET | /api/v1/greet/{name} |
Personalized greeting |
| POST | /api/v2/greet |
V2 greeting with validation (feature-flagged) |
| GET | /swagger/ |
Swagger UI |
| GET | /swagger/doc.json |
OpenAPI spec |
curl http://localhost:8080/api/health
curl http://localhost:8080/api/ready
curl http://localhost:8080/api/v1/greet/Alice
curl -X POST http://localhost:8080/api/v2/greet \
-H "Content-Type: application/json" -d '{"name":"Alice"}'
Testing
# Unit + integration tests
go test ./...
go test -v ./...
# Graceful shutdown + JSON logging validation
./scripts/test-graceful-shutdown.sh
# OpenTelemetry end-to-end
./scripts/test-opentelemetry.sh
Note: Do not call go generate unless editing API endpoint annotations.
When needed: go generate ./pkg/server/
Build
./scripts/build.sh
# Produces: ./bin/server ./bin/greet
./bin/server --version
Build injects version, commit, and date via -ldflags.
Graceful Shutdown
On SIGTERM / SIGINT:
- Readiness context is cancelled →
/api/readyreturns 503. - 1-second propagation window (load balancer drains).
srv.Shutdown()waits up toshutdown.timeoutfor active requests.- Process exits cleanly.
Health endpoint stays 200 throughout; readiness endpoint goes 503 immediately on signal.
OpenTelemetry / Jaeger
Enable in config or via env:
export DLC_TELEMETRY_ENABLED=true
export DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317"
Quick Jaeger setup:
docker run -d --name jaeger \
-e COLLECTOR_OTLP_ENABLED=true \
-p 16686:16686 -p 4317:4317 \
jaegertracing/all-in-one:latest
Architecture Decision Records
| ADR | Decision |
|---|---|
| 0001 | Go 1.26.1 |
| 0002 | Chi router |
| 0003 | Zerolog |
| 0004 | Interface-based design |
| 0005 | Graceful shutdown |
| 0006 | Viper configuration |
| 0007 | OpenTelemetry |
| 0008 | BDD with Godog |
| 0009 | Hybrid testing strategy |
Add a new ADR: copy an existing file, edit it, update adr/README.md.
Commit Conventions
Conventional Commits with optional gitmoji:
| Emoji | Type | When |
|---|---|---|
| ✨ | feat |
New feature |
| 🐛 | fix |
Bug fix |
| 📝 | docs |
Documentation |
| 🎨 | style |
Formatting only |
| ♻️ | refactor |
Structural change |
| 🚀 | perf |
Performance |
| 🔒 | security |
Security fix |
| 📦 | chore |
Dependencies / build |
| 🧪 | test |
Tests |
| 🤖 | ci |
CI/CD |
| 🔥 | remove |
Delete code/files |
Examples:
feat: add JWT authentication middleware
fix: ensure first log line is JSON when json logging is enabled
docs: rewrite AGENTS.md for clarity