---
id: w3/m8/t002
title: App-log level labeling at the shipper — JSON logs, honest unknown fallback
worker: worker3
status: todo
estimate: 30m
depends_on: [w3/m8/t001]
---

## Objective

Give application logs a `level` label where one is honestly derivable: an Alloy processing stage that parses JSON log lines and promotes a bounded `level` label (`debug/info/warn/error` — normalize common spellings), with everything unparseable labeled `unknown` — never guessed from substring matching (a line containing the word "error" is not an error). This is what makes `level=error` filtering truthful for apps that log structured JSON, and honestly absent for those that don't.

## Context

- Alloy `loki.process` stages (json + label extraction) in `deploy/gitops/base/log-shipper.yaml`; keep the stage cheap — it runs on every app line.
- Level vocabulary: match what Render's filter accepts (check the captured filter values / docs from the m13 audit artifacts before hardcoding).

## Steps

1. Processing stage: JSON parse → `level` normalize → bounded label; non-JSON → `unknown`.
2. Verify with two test apps (one JSON-logging, one plaintext): labels land as designed.
3. ADR010 paragraph: what `level` means, its honesty boundary.

## Files

- `deploy/gitops/base/log-shipper.yaml`
- `docs/ADR010-observability.md`

## Acceptance criteria

- [ ] JSON app lines carry normalized `level`; plaintext lines carry `unknown`; no substring-derived levels anywhere.

## Out of scope

- Query-side (t003); asking tenants to log JSON (docs may recommend, never require).
