מתאם בין לוגים, מטריקות וטרייסים
אתה חוקר תקרית: המטריקות מראות קפיצת חביון חדה בשעה 03:47, אבל בגרפנה אין הערה, לוקי פולטת אלפי שורות לוג לאותה דקה, וליהגר יש טרייסים אבל אתה לא יכול לדעת איזה ספאן היה הראשון שנכשל. שלוש מערכות, שלושה ממשקים, אפס קישוריות — והצוות שלך מבלה שעה בהצלבה ידנית של חותמות זמן. עברנו את זה עשרות פעמים. הגישה שלנו: הקמת מתאם מקצה לקצה תוך 5–10 ימי עבודה, קיצוץ של MTTR ב-60%, ושחרור הצוות שלך מעבודה ידנית מייגעת.
מתאם בין לוגים, מטריקות וטרייסים נותן לך את היכולת ללחוץ מחריגה במטריקה ישירות לטרייס, ומהטרייס לשורת הלוג המדויקת עם הקשר השגיאה. בלעדיו, אבחון מערכות מבוזרות מרגיש כמו עבודת בלשות עם זכוכית מגדלת. צור קשר לייעוץ בנושא הקמת מתאם לפרויקט שלך.
כיצד OpenTelemetry מספקת שכבה אחידה
הערכה המודרנית למתאם היא OpenTelemetry Collector, שמקבל נתונים מכל שלושת האותות ומעביר אותם למערכות האחסון המתאימות. תצורה טיפוסית:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
prometheus:
config:
scrape_configs:
- job_name: 'app'
static_configs:
- targets: ['app:8080']
processors:
batch:
timeout: 10s
resource:
attributes:
- key: deployment.environment
value: production
action: upsert
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
loki:
endpoint: http://loki:3100/loki/api/v1/push
labels:
resource:
service.name: "service_name"
deployment.environment: "env"
prometheus:
endpoint: 0.0.0.0:8889
namespace: otel
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, resource]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp, prometheus]
processors: [batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [batch, resource]
exporters: [loki]הנקודה המרכזית: כל שלושת הצינורות עוברים דרך Collector יחיד שמחזיק משאב אחיד. לפי מפרט OpenTelemetry, # otel-collector-config.yaml receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 prometheus: config: scrape_configs: - job_name: 'app' static_configs: - targets: ['app:8080'] processors: batch: timeout: 10s resource: attributes: - key: deployment.environment value: production action: upsert exporters: otlp/jaeger: endpoint: jaeger:4317 tls: insecure: true loki: endpoint: http://loki:3100/loki/api/v1/push labels: resource: service.name: "service_name" deployment.environment: "env" prometheus: endpoint: 0.0.0.0:8889 namespace: otel service: pipelines: traces: receivers: [otlp] processors: [batch, resource] exporters: [otlp/jaeger] metrics: receivers: [otlp, prometheus] processors: [batch] exporters: [prometheus] logs: receivers: [otlp] processors: [batch, resource] exporters: [loki] חייב להיות מחרוזת הקס של 128 ביט.
דוגמאות (Exemplars): קישור מטריקות לטרייסים
פרומתאוס מגרסה 2.29 תומך ב-exemplars — נקודות נתונים על היסטוגרמות שנושאות trace_id. נדרשים שני תנאים:
# prometheus.yml global: scrape_interval: 15s storage: exemplars: max_exemplars: 100000 ובתוך האפליקציה עצמה (דוגמה ב-Go עם prometheus/client_golang):
histogram.With(prometheus.Labels{"handler": "/api/users"}).ObserveWithExemplar(
duration,
prometheus.Labels{"traceID": span.SpanContext().TraceID().String()},
)בגרפנה, עם exemplars מופעלים, מופיעים יהלומים קטנים על הגרף — לחיצה על אחד מובילה אותך ישירות לטרייס המתאים בליהגר/טמפו. הניסיון שלנו מראה ש-exemplars מקצרים את זמן איתור שורש הבעיה מ-40 דקות ל-5.
העברת trace_id ללוגים
כל בקשה צריכה לחלץ את trace_id מהספאן הפעיל ולהוסיף אותו לרשומת הלוג. ב-Node.js עם winston:
const { trace } = require('@opentelemetry/api');
const addTraceInfo = winston.format((info) => {
const span = trace.getActiveSpan();
if (span) {
const ctx = span.spanContext();
info.trace_id = ctx.traceId;
info.span_id = ctx.spanId;
}
return info;
});ברגע שלוקי מקבלת לוגים עם שדה # prometheus.yml global: scrape_interval: 15s storage: exemplars: max_exemplars: 100000 , גרפנה יכולה לבנות קישור משורת לוג ישירות לטמפו/ליהגר דרך histogram.With(prometheus.Labels{"handler": "/api/users"}). ObserveWithExemplar( duration, prometheus.Labels{"traceID": span.SpanContext().TraceID().String()}, ) במקור הנתונים.
למה correlation_id לא מספיק
צוותים רבים עוצרים ב-trace_id headers וב-correlation_id בלוגי JSON. זה הכרחי אבל לא מספיק. בעיות מתעוררות כששירות ב-Go כותב לוגים לא מובנים ל-stdout, Logstash מנתח עם תבניות grok שמפספסות את const { trace } = require('@opentelemetry/api'); const addTraceInfo = winston.format((info) => { const span = trace.getActiveSpan(); if (span) { const ctx = span.spanContext(); info.trace_id = ctx.traceId; info.span_id = ctx.spanId; } return info; }); , ופרומתאוס אוסף מטריקות בלי תווית trace_id — הוא לא יכול, זה סוג נתונים שונה מהותית. טרייסים של ליהגר מאחסנים derivedFields ו-X-Request-ID, אבל לוגי השירות לא מכילים אותם כי המפתח לא העביר קונטקסט דרך trace_id.
מתאם אמיתי נשען על שלושה עמודים: פורמט זמן אחיד (UTC, מילישניות), העברת קונטקסט טרייס ללוגים, ו-exemplars במטריקות לקישור לטרייסים. בלי exemplars, המתאם לא שלם.
כיצד גרפנה מאחדת את כל שלושת המקורות
גרפנה 10+ יכולה "להדביק" את כל שלושת המקורות יחד דרך Explore. שאילתה אחת בלוקי מציגה כפתור "View in Tempo"; מטמפו, כפתור "Logs for this span". זה עובד רק אם קורלציות בין מקורות נתונים מוגדרות. קורלציות שמוגדרות דרך ממשק המשתמש של גרפנה נוטות להיות שבירות — עדיף להגדיר אותן ב-trace_id.
השווה שלוש שיטות מתאם:
| שיטה | דרישות | יתרונות | חסרונות |
|---|---|---|---|
| correlation_id | העברת Header | יישום פשוט | אין קישור לטרייסים ומטריקות |
| OpenTelemetry + exemplars | OpenTelemetry SDK, exemplars במטריקות | מתאם מלא מקצה לקצה | דורש שינויי קוד |
| ניתוח לוגים ידני | לוגים בלבד | אין צורך בשינויים | איטי, לא אמין |
בעיות טיפוסיות במהלך היישום
-
פורמטים שונים של trace_id. ליהגר משתמש בהקס של 128 ביט, זיפיקין ב-64 ביט. אם שירות מדור קודם משתמש בזיפיקין, ל-trace_id בלוגים יהיו פורמטים שונים והמתאם ייכשל. פתרון: OpenTelemetry Collector עם
span_idוהמרה. -
משימות אסינכרוניות שמאבדות קונטקסט. עובד Celery שמעבד משימה מתור Redis לא יודע את ה-trace_id של בקשת ה-HTTP. חייבים להעביר במפורש את קונטקסט הטרייס ב-payload ולשחזר אותו דרך
trace_id. -
לוגים לא מובנים. אם אפליקציה כותבת
context.Contextל-stdout, אי אפשר לחלץ trace_id בלי ניתוח grok. לפעמים פשוט יותר להוסיף פורמטר JSON.
מה כלול בעבודה
- ביקורת על הערכה הנוכחית: מלאי של שירותים, שפות, פורמטי לוגים ואינסטרומנטציית טרייס קיימת.
- יישום OpenTelemetry SDK או אינסטרומנטציה אוטומטית דרך otel-operator עבור Kubernetes.
- פריסת OpenTelemetry Collector עם שלושה צינורות ו-exporters מוגדרים.
- הגדרת exemplars בפרומתאוס וקורלציות בגרפנה דרך provisioning.
- העברת trace_id ללוגים בכל השירותים.
- בדיקת מתאם מקצה לקצה: יצירת תעבורה, אימות trace_id מ-HTTP עד למסד הנתונים.
- תיעוד: תיאור ארכיטקטורה, הוראות למפתחים.
- הדרכת צוות (סמינר מקוון של שעתיים).
- שבועיים של תמיכה לאחר הפריסה.
איך אנחנו עושים את זה: שלב אחר שלב
- ביקורת על הערכה הנוכחית (1–2 ימים).
- יישום OpenTelemetry SDK בכל השירותים (3–10 ימים).
- הגדרת Collector וגרפנה (1–2 ימים).
- בדיקה ותיעוד (1–2 ימים).
למהנדסים שלנו יש ניסיון של 5+ שנים ב-observability, והם מוסמכים ב-OpenTelemetry וב-Kubernetes. במשך 5 שנים השלמנו 50+ פרויקטי observability. הפרויקט מחזיר את ההשקעה תוך 2–3 חודשים בזכות קיצור זמן אבחון התקריות. צור קשר להערכת פרויקט — הזמן יישום מפתח.







