Fyra steg till första händelsen
Installera paketet, anropa initTelemetry med er nyckel, kör install för de globala felhanterarna, och fånga det ni själva vill fånga med captureError. Maskningen är redan på, och taket är redan satt.

Ett paket, en nyckel, och första felet är inne
Inget agentpaket att installera på servern och ingen sidovagn att driva. Klienten skickar direkt, och buffrar när nätet är nere.

Ett. Installera paketet och sätt nyckeln i miljön. 
Två. Fånga felet där det uppstår, med källkodskarta. 
Tre. Se det i panelen, grupperat och med spår.

Klienten skickar direkt och buffrar när nätet är nere. 
Ingen agent på maskinen och ingen sidovagn bredvid tjänsten. 
Första felet ligger i vyn med spår och källkodsrad.
Installera klienten
Ett paket i er kodbas. Ingen agent på maskinen, ingen sidovagn bredvid tjänsten, och ingenting som ska startas om när ni ändrar en inställning.
- Paketet fungerar i webbläsaren, i Node, i Deno och i Bun.
- Inmatningsnyckeln är publik och får ligga i klientbygget. Den skriver bara, den läser aldrig.
- Kör ni redan OpenTelemetry behöver ni inte det här paketet alls. Se vägen in via OTLP.
npm install @gaddr/telemetry
# eller
pnpm add @gaddr/telemetry
# eller
bun add @gaddr/telemetry
Nyckeln hämtar ni i projektets inställningar. Den börjar på gdr_pk_ och är publik, alltså behöver den ingen hemlighetshantering.

Anropa initTelemetry
initTelemetry tar emot inställningarna, skapar klienten och sparar den som en singleton. Anropa den en gång, tidigt, i webbläsarens ingång.
1import { initTelemetry } from "@gaddr/telemetry";
2
3const log = initTelemetry({
4 ingestKey: "gdr_pk_din_nyckel",
5 environment: "production",
6 release: "web@2026.8.15",
7});
8
9log.install();
Tre rader räcker. Fältet release kopplar felet till versionen som körde, och därmed till commiten som byggde den. Utan release fungerar allt, men stackspåret pekar inte tillbaka på någon utgåva.
Vill ni nå klienten någon annanstans i koden gör ni det med telemetry(), som returnerar samma instans, eller null om initTelemetry inte har körts än.
1import { telemetry } from "@gaddr/telemetry";
2
3export function rapportera(fel: unknown) {
4 telemetry()?.captureError(fel);
5}
Frågetecknet är med flit. Körs koden på servern innan initTelemetry har anropats är telemetry() null, och raden gör då ingenting i stället för att kasta.
Varje inställning, med sitt standardvärde
| Fält | Standard | Vad det gör |
|---|---|---|
| ingestKey | krävs | Projektets inmatningsnyckel. Publik, får ligga i klientbygget. |
| environment | krävs | development, staging eller production. Är den development skickas ingenting alls. |
| endpoint | https://log.gaddr.com/api/ingest/v1 | Var Gaddr Log tar emot. Byts bara vid drift i egen miljö. |
| release | saknas | Versionen som körs. Kopplar felet till en commit. |
| maxEventsPerSession | 100 | Taket per session. En loop som kastar tiotusen fel producerar hundra händelser. |
| sampleRate | 1 | Andel som skickas alls, mellan 0 och 1. Resten räknas i dropped.sampled. |
| scrubKeys | tom lista | Egna fältnamn som alltid ska maskas. Läggs till standardlistan, ersätter den aldrig. |
Fälten och standardvärdena är hämtade ur TelemetryConfig och konstruktorn i packages/telemetry/src/index.ts.

install kopplar in de globala felhanterarna
Ett anrop kopplar in tre lyssnare i webbläsaren och returnerar en funktion som kopplar loss dem igen. Körs koden på servern gör install ingenting och returnerar en tom funktion, så raden är säker att låta ligga.
- error på fönstret fångar fel som inte tas om hand någon annanstans, med filnamnet och radnumret som kontext.
- unhandledrejection fångar avvisade löften, med flaggan unhandledRejection i kontexten.
- visibilitychange tömmer kön när fliken göms, så att det sista som hände inte försvinner med fliken.
Den returnerade funktionen tar bort alla tre lyssnarna och tömmer kön en sista gång. Det gör att en komponent som satte upp telemetrin kan städa efter sig, och att en omladdning under utveckling inte staplar lyssnare på varandra.
1const kopplaLoss = log.install();
2
3// Vid nedmontering, till exempel i en effekt eller i ett test:
4kopplaLoss();
install returnerar en funktion. Anropas den tas fönstrets och dokumentets lyssnare bort, och kön töms en sista gång innan allt släpps.

Fånga det ni själva vill fånga
captureError tar felet och ett fritt objekt med sammanhang. Den plockar ut meddelande, stackspår, kod och spår-id ur felet, och maskar allt innan det lämnar enheten.
1try {
2 await slutforKop(order);
3} catch (fel) {
4 log.captureError(fel, {
5 ordernummer: order.id,
6 epost: "anna@example.se",
7 steg: aktivtSteg,
8 });
9 visaFelruta();
10}
Fältet epost träffar standardlistan och ersätts med [maskerad]. Hade adressen legat inuti meddelandet i stället hade mönstret för e-post ersatt den med [epost]. Båda vägarna är stängda.
Är det som kastades inte ett Error görs det om till ett, så att raden fungerar även när något kastar en sträng. Bär felet fälten code eller traceId plockas de upp och följer med händelsen.
1log.capture({
2 kind: "log",
3 level: "warning",
4 message: "Kön växer snabbare än den töms",
5 traceId: aktivtSpar,
6 context: { koLangd, arbetare },
7});
8
9await log.flush();
10log.stats(); // { sent, queued, dropped: { quota, sampled, transport } }
capture är den generella vägen in, med kind satt till error, log, span eller metric. flush skickar kön direkt i stället för att vänta ut timern på två sekunder, vilket är det ni vill i ett kortlivat jobb som avslutas.
- dropped.quota räknas upp när taket per session är nått. Kvoten gjorde sitt jobb.
- dropped.sampled räknas upp när ni själva har sänkt sampleRate.
- dropped.transport räknas upp när överföringen misslyckades. Det är den siffran som ska vara noll.

Det här maskas innan något lämnar enheten
Det finns ingen inställning som stänger av maskningen. Ni kan lägga till egna fältnamn, aldrig ta bort de som redan står i listan.
21 fältnamn, ordagrant ur DEFAULT_SCRUB
- password
- losenord
- lösenord
- token
- secret
- authorization
- cookie
- apikey
- api_key
- personnummer
- ssn
- card
- kortnummer
- cvc
- cvv
- iban
- epost
- e-post
- phone
- telefon
Jämförelsen görs gemen och på delsträng. Ett fält som heter kundEpost träffar alltså epost, och ett fält som heter API_KEY_V2 träffar api_key. Värdet ersätts med [maskerad].
Listan och jämförelsen ligger i DEFAULT_SCRUB och i funktionen scrub i packages/telemetry/src/index.ts.
5 mönster, oavsett vilket fält de står i
| Vad | Formen | Blir |
|---|---|---|
| Personnummer | Sex siffror, valfritt bindestreck eller plus, sedan fyra siffror | [personnummer] |
| Kortnummer | Tretton till nitton siffror, med eller utan mellanslag och bindestreck | [kortnummer] |
| E-postadress | Namn, snabel-a, domän och toppdomän | [epost] |
| Nycklar | sk_, pk_ eller rk_ följt av live eller test och minst tio tecken | [nyckel] |
| Bärartoken | Ordet Bearer följt av ett värde | Bearer [maskerad] |
1const log = initTelemetry({
2 ingestKey: "gdr_pk_din_nyckel",
3 environment: "production",
4 scrubKeys: ["kundnummer", "leveransadress", "fodelsedatum"],
5});
scrubKeys läggs till standardlistan. Mönstren gäller alltid, också i meddelandet och i stackspåret, eftersom scrubString körs på båda.
Mönstren ligger i PATTERNS och körs av scrubString i packages/telemetry/src/index.ts. Maskningen går sex nivåer ned i ett nästlat objekt, och en lista kortas till femtio poster innan den skickas.

Maskningen är på från första händelsen. 
Ingen inställning stänger av den.
Det som brukar dyka upp under installationen

Vanliga frågor
Får inmatningsnyckeln ligga i klientbygget?
Ja. Den är publik och skriver bara. Den läser ingenting och den ger ingen åtkomst till era data. Läcker den ändå återkallar ni den och skapar en ny i projektets inställningar.
Varför skickas ingenting när jag kör lokalt?
För att environment är satt till development. capture returnerar direkt i det läget, alltså kostar era lokala fel ingenting av kvoten. Sätt staging eller production när ni vill se händelserna.
Kan ett fel i telemetrin krascha vår app?
Nej. Misslyckas överföringen fångas det och räknas som transport i stats. Inget kastas vidare in i er kod, och den som besöker sajten märker ingenting.
Hur ofta skickas kön?
En timer på 2000 millisekunder samlar kön och skickar den i en bunt. Kön töms också när fliken göms, och ni kan tömma den själva med flush när ni vill.
Vad används för att skicka?
navigator.sendBeacon i första hand, eftersom den överlever att fliken stängs. Finns den inte, eller returnerar den falskt, används fetch med keepalive.
Hur vet jag att något faktiskt kommer fram?
log.stats() returnerar sent, queued och dropped. Är sent noll och dropped.transport högt når ni inte fram. Är dropped.quota högt fungerar taket som det ska.
Kostar det något att börja?
Planen Fri kostar 0 kr och tar emot 500 000 händelser i månaden. Behöver ni mer kostar Team det som står på prissidan, och taket är hårt i båda.
Vi kör inte JavaScript. Vad gör vi då?
Skickar OTLP från er egen instrumentering. Det ingår i planen Fri och kräver ingen agent på maskinen. Se språken.
Nästa steg
Delarna i produkten har varsin sida med vad de gör och var i koden påståendet går att kontrollera.
