Prima dată când aveți nevoie cu adevărat de observabilitate nu este atunci când vă uitați calm la un tablou de bord. Este atunci când un utilizator scrie „checkout is slow”, graficul de eroare arată normal și în jurnale găsești doar un rând de mesaje deconectate.
OpenTelemetry a fost creat pentru a evita acel moment: nu pentru a avea mai multă grafică, ci pentru a conecta piesele. O solicitare intră în API, apelează o bază de date, trece printr-un furnizor extern, postează un job în coadă și poate eșuează trei servicii mai târziu. Fără urmărire distribuită, reconstruiești acea poveste manual. Cu OpenTelemetry cel puțin aveți o hartă.
Ideea nu este trace, ci povestea
A trace este o secvență de span. Pune asa suna rece. În practică, fiecare span este o bucată din poveste: POST /checkout, SELECT inventory, call payment provider, publish order.created.
Valoarea vine atunci când începi să răspunzi la întrebări reale:
- care serviciu extern încetinește?
- erorile provin dintr-o anumită versiune?
- problema afecteaza pe toti sau doar pe un singur chirias?
- o reîncercare ascunde un timeout?
- lucrarea asincronă începe, dar apoi moare în altă parte?
Aceste întrebări nu pot fi rezolvate printr-un console.log aruncat în grabă. Într-adevăr, adesea jurnalul adăugat în caz de urgență te ajută astăzi și devine zgomot mâine.
Cum aș pune asta într-o aplicație Node.js
Cea mai sănătoasă configurație este simplă: aplicația produce telemetrie, Collector decide unde să o trimită.
Node.js app -> OpenTelemetry Collector -> backend di observability
De ce nu exportați direct către furnizor? Pentru că la început pare mai rapid, apoi îți dai seama că fiecare serviciu are configurații diferite, încercări diferite, filtre diferite și niciun punct central pentru a elimina datele sensibile sau a schimba destinația.
Collector este plictisitor în toate modurile corecte. Primește OTLP, face loturi, poate filtra, poate face eșantionare, poate adăuga atribute comune și poate exporta pe mai multe sisteme.
Auto-instrumentare: bună, dar nu suficientă
În Node.js aș începe cu auto-instrumentația. Vă oferă vizibilitate imediată în HTTP, cadrele acceptate, bazele de date și bibliotecile comune.
npm install @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/exporter-trace-otlp-http
Apoi inițializați SDK înainte de restul aplicației:
import { NodeSDK } from '@opentelemetry/sdk-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, }), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();
Cu toate acestea, acesta vede cadrul, nu produsul dvs. Știe că ați făcut o interogare, dar nu știe că interogarea a fost în „ordine de creare” sau „reînnoire abonament”. Pentru asta ai nevoie de span manuale în punctele în care contează dominația.
const span = tracer.startSpan('checkout.create_order'); try { span.setAttribute('cart.items_count', input.items.length); const order = await createOrder(input); span.setAttribute('order.id', order.id); return order; } catch (error) { span.recordException(error as Error); throw error; } finally { span.end(); }
N-aș pune span manuale nicăieri. Le-aș pune acolo unde, la trei dimineața, aș vrea să înțeleg ce s-a întâmplat fără să citesc jumătate din baza de cod.
Trei reguli care evită mult haos
Prima regulă: fiecare serviciu trebuie să aibă service.name, mediu și versiune. Pare banal, dar fără aceste atribute un trace este mult mai puțin util. Când o implementare întrerupe ceva, doriți să filtrați după versiune în două secunde.
A doua regulă: nu introduceți date sensibile în atribute. E-mailurile, jetoanele, încărcăturile utile cu numere întregi și adresele nu ar trebui să ajungă accidental într-un backend de observabilitate. Dacă trebuie să identificați un utilizator, luați în considerare ID-urile interne, hashingul sau câmpurile mai puțin sensibile.
A treia regulă: acordați atenție cardinalității. user.id ca atribut al lui trace poate avea sens. Ca etichetă de măsură, vă poate distruge costurile și performanța.
Valori: puține, dar bune
Aș începe cu valori foarte practice:
- tarife, erori și durata solicitărilor;
- latența dependențelor externe;
- numărul de timeout-uri și reîncercări;
- adâncimea cozilor;
- durata postului;
- procentul de erori pe versiune.
Restul se adauga la nevoie. Tablourile de bord pline cu grafice la care nimeni nu se uită sunt mobilier, nu observabilitate.
Jurnale: încă util, dar legat
Buștenii nu dispar. Pur și simplu devin mult mai utile atunci când poartă trace_id și span_id. Deci, puteți începe dintr-un jurnal de erori și deschideți trace, sau puteți începe de la un trace lent și citiți numai jurnalele produse în acea cale.
Fără corelare, cauți ace. Cu corelație, cel puțin știi în ce sertar să te uiți.
Lista de verificare pe care aș folosi-o înainte de a spune „suntem acoperiți”
- trace de fapt traversează mai multe servicii.
- Jurnalele includ
trace_idșispan_id. - Collector este configurat cu limite de loturi și memorie.
- Erorile sunt înregistrate în span.
- Există o politică de eșantionare.
- Metricile au cardinalitate controlată.
- Datele sensibile sunt filtrate.
- Alertele pornesc de la simptomele utilizatorului, nu de la grafice aleatorii.
Concluzie
OpenTelemetry nu rezolvă singur problemele de producție. Dar felul în care te descurci cu ei se schimbă. În loc să adăugați orbește jurnalele, începeți să urmați calea reală a unei cereri.
Pentru mine semnul că funcționează este simplu: când se întâmplă ceva, echipa încetează să mai întrebe „unde căutăm?” și începe să se întrebe „de ce piesa aceea este lentă?”. Acolo, observabilitatea devine un instrument, nu o colecție de tablouri de bord.