Observation: se var tiden går, utan ett enda nytt beroende

CloudWatch gav loggar och mätvärden men svarade inte på frågan man
faktiskt har när något är långsamt: var tog tiden vägen.

Tjänsterna har medvetet nästan inga beroenden — plattformen har
pg-drivrutinen, orkestern har Claude-klienten. Att dra in ett
OpenTelemetry-SDK med trettio paket för att mäta fyra saker vore fel
avvägning. I stället två standarder som båda bara är text på stdout:

W3C Trace Context. Klienten startar spåret och traceparent följer med
genom plattformen till orkestern, så en teknikers handling går att följa
hela vägen till modellsvaret i stället för att bli två orelaterade spår.

CloudWatch EMF. Strukturerad JSON som CloudWatch själv extraherar
mätvärden ur — ingen agent, ingen SDK, inget som kan sluta fungera tyst.

Varje anrop ger en loggrad med nedbrytning av tiden per del: databasen,
modellanropet, objektlagringen, kundens leverantör. Det svarar direkt på
om ett långsamt ärende beror på S3 eller på Opus-granskningen, i stället
för att någon ska korrelera fem loggrader.

Vägen normaliseras innan den blir dimension, och organisation, ärende-id
och spår-id blir aldrig dimensioner — varje unik kombination är en egen
tidsserie som kostar. De ligger som vanliga fält, sökbara i Logs
Insights. Ett test låser det, eftersom det är precis den sortens sak som
smyger in senare.

Tre larm på det teknikern märker: svarstid p95 över tre sekunder
(medelvärdet döljer att var tjugonde tekniker väntar orimligt länge),
serverfel med spår-id i loggraden, och att modellen avböjer — det senare
tyder på att underlaget innehåller något oväntat, inte på ett driftfel.

Modulen är delad mellan tjänsterna i stället för duplicerad.
Byggkontexten flyttas därför till felsokning/services, och en symlänk gör
att testerna och integrationstestet kör mot samma fil som bilderna.

Verifierat: 106 vitest-tester (10 nya för spårning, EMF-format och att
dimensionerna hålls få), typkontroll, eslint på klient och tjänster,
rotens CI, terraform fmt och referenskontroll på båda lagren, samt
integrationstest mot riktig Postgres där spårraderna syns live.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
Claude
2026-08-04 13:36:47 +00:00
parent e94a97715a
commit 69a519be75
15 changed files with 591 additions and 16 deletions
+54
View File
@@ -182,6 +182,60 @@ rätt. Andra konton påverkas inte. Inget lösenord lagras, bara att ett
försök skedde och om det lyckades; rader äldre än ett dygn städas bort i
skrivvägen.
## Observation
Tjänsterna har medvetet nästan inga beroenden. Att dra in ett
OpenTelemetry-SDK med trettio paket för att mäta fyra saker vore fel
avvägning, så observationen bygger på två standarder som båda bara är
text på stdout:
**W3C Trace Context.** Klienten startar spåret och `traceparent` följer
med genom plattformen till orkestern. En teknikers handling går därför
att följa hela vägen till modellsvaret i stället för att bli två
orelaterade spår.
**CloudWatch EMF.** Strukturerad JSON som CloudWatch själv extraherar
mätvärden ur — ingen agent, ingen SDK, inget som kan sluta fungera tyst.
Varje anrop ger en loggrad med **nedbrytning av tiden**:
```json
{"nivå":"info","meddelande":"plattform","spårId":"fd5dec…","väg":"/api/arenden/:id/handelser",
"status":200,"ms":842.1,"delar":{"databas":{"antal":3,"ms":31.2},"bilaga_skriv":{"antal":1,"ms":780.4}}}
```
Det svarar på frågan man faktiskt har klockan tre på natten: *var tog
tiden vägen*. Här i objektlagringen, inte i databasen.
### Vägen ut är alltid samma
Vägen normaliseras (`/api/arenden/:id/handelser`) innan den blir en
dimension. Organisation, ärende-id och spår-id blir **aldrig**
dimensioner — varje unik kombination är en egen tidsserie som kostar.
De ligger som vanliga fält, sökbara i Logs Insights. Ett test låser det.
### Frågor som brukar behövas
```
# Var tog tiden vägen för ett långsamt anrop?
fields tid, väg, ms, delar.databas.ms, delar.modell_handledning.ms, spårId
| filter ms > 1000 | sort ms desc | limit 20
# Hela kedjan för ett spår — plattform och orkester i samma vy
fields tid, meddelande, väg, ms, status | filter spårId = "fd5dec…" | sort tid
# Vilka modellanrop kostar mest?
stats sum(ut) as ut_tokens, avg(ms) as snitt by Uppgift, Modell
```
### Larm
Utöver infrastrukturlarmen finns tre på applikationens egna mätvärden:
svarstid **p95** över tre sekunder (medelvärdet döljer att var tjugonde
tekniker väntar orimligt länge), serverfel, och att modellen avböjer —
det senare tyder på att underlaget innehåller något oväntat, inte på ett
driftfel.
## Multi-tenant och roller
Enligt Master Prompt: varje kund är en egen tenant, ingen data blandas mellan kunder.