אינסטרומנטציה של יישומים עם OpenTelemetry SDK
ללא תקן טלמטריה אחיד, אתה מאבד עד 40% מהזמן באיתור שורש הבעיה: טרייסים במערכת אחת, מטריקות באחרת, לוגים בשלישית. OpenTelemetry מאחד הכל לממשק אחד, וחוסך עד 30% בעלויות תפעול. עבדנו עם מחסנית טכנולוגית זו למעלה מ-5 שנים ויישמנו אותה ביותר מ-50 פרויקטים—החיסכון האופייני מהיישום מסתכם ב-$5,000–$10,000 לחודש בשל הפחתת זמני השבתה.
אינסטרומנטציה פירושה הוספת נקודות תצפית לקוד: היכן פעולה מתחילה, היכן היא מסתיימת, אילו תכונות היא נושאת, והאם היא הושלמה בהצלחה. OpenTelemetry SDK מתקן תהליך זה: API אחד לטרייסים, מטריקות ולוגים, ללא קשר לאן הנתונים מגיעים בסופו של דבר.
קיימות שתי רמות של אינסטרומנטציה לכל שפה. אוטומטית—סוכן/תיקון (patch) מבצע אינסטרומנטציה לספריות פופולריות ללא שינויי קוד. ידנית—המפתח יוצר במפורש ספנים (spans) ללוגיקה עסקית: "ביצוע הזמנה," "חישוב הנחה," "קריאה ל-API של שותף חיצוני." אוטומטית אינה מחליפה ידנית—היא מכסה רק את שכבת התשתית.
למה לבחור ב-OpenTelemetry SDK לאינסטרומנטציה?
בניגוד לפתרונות קנייניים (Datadog, New Relic), OpenTelemetry אינו נועל אותך לספק מסוים: ניתן להחליף מערכות קצה (Prometheus, Jaeger, Grafana) ללא שכתוב קוד. זהו גם תקן פתוח הנתמך על ידי כל העננים והכלים המרכזיים. המהנדסים שלנו מוסמכים ב-Kubernetes ו-OpenTelemetry—אנו מבטיחים תצורה נכונה מההתחלה. OpenTelemetry חסכוני פי 3–5 מכלי APM קנייניים לפריסות בקנה מידה גדול.
אינסטרומנטציה ידנית: דפוסים נכונים
ב-Python באמצעות מנהל הקשר (הגישה הנכונה):
from opentelemetry import trace
from opentelemetry.trace import SpanKind
tracer = trace.get_tracer("order-service", "1.4.2")
def process_order(order_id: str, user_id: str) -> Order:
with tracer.start_as_current_span(
"order.process",
kind=SpanKind.INTERNAL,
attributes={
"order.id": order_id,
"user.id": user_id,
}
) as span:
try:
order = fetch_order(order_id)
span.set_attribute("order.total_amount", order.total)
span.set_attribute("order.items_count", len(order.items))
result = apply_discounts(order)
span.add_event("discounts_applied", {"discount_percent": result.discount_percent})
return result
except OrderNotFoundException as e:
span.record_exception(e)
span.set_status(trace.StatusCode.ERROR, str(e))
raise
from opentelemetry import trace from opentelemetry.trace import SpanKind tracer = trace.get_tracer("order-service", "1.4.2") def process_order(order_id: str, user_id: str) -> Order: with tracer.start_as_current_span( "order.process", kind=SpanKind.INTERNAL, attributes={ "order.id": order_id, "user.id": user_id, } ) as span: try: order = fetch_order(order_id) span.set_attribute("order.total_amount", order.total) span.set_attribute("order.items_count", len(order.items)) result = apply_discounts(order) span.add_event("discounts_applied", {"discount_percent": result.discount_percent}) return result except OrderNotFoundException as e: span.record_exception(e) span.set_status(trace.StatusCode.ERROR, str(e)) raise מוסיף אוטומטית מחסנית קריאות (stack trace) לאירועי ספן. record_exception() מאותת למערכות קצה ולמדיניות דגימה. ללא סטטוס ERROR מפורש, דוגם מבוסס-זנב (tail-based sampler) עשוי שלא לשמר את ספן החריגה.
מטריקות: Counter, Histogram, Gauge
from opentelemetry import metrics
meter = metrics.get_meter("order-service", "1.4.2")
# Счётчик запросов
order_counter = meter.create_counter(
"orders.created.total",
unit="1",
description="Total number of orders created"
)
# Гистограмма времени обработки
processing_duration = meter.create_histogram(
"orders.processing.duration",
unit="ms",
description="Order processing duration"
)
def create_order(order_data):
start = time.monotonic()
order = _create_order_internal(order_data)
duration_ms = (time.monotonic() - start) * 1000
order_counter.add(1, {"order.type": order.type, "region": order.region})
processing_duration.record(duration_ms, {"order.type": order.type})נקודה מרכזית לגבי תכונות מטריקה: כל שילוב ייחודי של תכונות הופך לסדרת זמן נפרדת ב-Prometheus. שימוש ב-set_status(ERROR) כתכונת מטריקה הוא אסון קרדינליות. from opentelemetry import metrics meter = metrics.get_meter("order-service", "1.4.2") # Счётчик запросов order_counter = meter.create_counter( "orders.created.total", unit="1", description="Total number of orders created" ) # Гистограмма времени обработки processing_duration = meter.create_histogram( "orders.processing.duration", unit="ms", description="Order processing duration" ) def create_order(order_data): start = time.monotonic() order = _create_order_internal(order_data) duration_ms = (time.monotonic() - start) * 1000 order_counter.add(1, {"order.type": order.type, "region": order.region}) processing_duration.record(duration_ms, {"order.type": order.type}) עם 5-10 ערכים הוא בסדר.
כיצד להימנע ממטריקות בקרדינליות גבוהה?
השתמש רק בתכונות בקרדינליות נמוכה: סוג בקשה, אזור, גרסת שירות. עבור מזהים ייחודיים (user_id, order_id) השאר אותם רק בספנים, לא במטריקות. זה שומר על ביצועי Prometheus ומונע עומס אחסון.
כיצד לבצע אינסטרומנטציה ליישומי Java?
עבור Spring Boot, קיימות שתי דרכים: אינסטרומנטציה אוטומטית דרך סוכן (מומלץ להתחלה):
-javaagent:opentelemetry-javaagent-2.x.jar
-Dotel.service.name=order-service
-Dotel.exporter.otlp.endpoint=http://otel-collector:4317
-Dotel.logs.exporter=otlpSpring Boot Starter לאינסטרומנטציה ידנית—הוסף את התלות ל-pom.xml וחבר אוטומטית את Tracer באמצעות @Autowired. דוגמה:
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
<version>2.3.0-alpha</version>
</dependency>לאחר מכן, ניתן להזריק OpenTelemetry וליצור ספנים ללוגיקה עסקית כפי שמוצג בתיעוד.
קורלציית לוגים עם טרייסים
כדי לנווט מ-Grafana Loki לטרייס ספציפי, שורות הלוג חייבות להכיל user.id ו-order.type. OTel מספק עזרים לספריות לוג פופולריות.
Python + structlog:
import structlog
from opentelemetry import trace
def add_trace_context(logger, method, event_dict):
span = trace.get_current_span()
if span.is_recording():
ctx = span.get_span_context()
event_dict["trace_id"] = format(ctx.trace_id, "032x")
event_dict["span_id"] = format(ctx.span_id, "016x")
return event_dict
structlog.configure(
processors=[add_trace_context, structlog.processors.JSONRenderer()]
) השוואה בין אינסטרומנטציה אוטומטית לידנית
| פרמטר | אוטומטית | ידנית |
|---|---|---|
| שינויי קוד | אין (סוכן מחובר) | כן (נוספים ספנים) |
| כיסוי לוגיקה עסקית | רק קריאות תשתית | שליטה מלאה |
| זמן יישום לכל שירות | 30 דקות | 1–2 ימים |
| תלות בגרסת ספרייה | כן (סוכן לכל גרסה) | לא |
אינסטרומנטציה ידנית מספקת פי 3 יותר נתונים שימושיים לניפוי באגים אך דורשת משמעת. אופטימלי: אוטומטית להתחלה מהירה + ידנית לפעולות מפתח.
ספריות מומלצות לשפות שונות
| שפה | אינסטרומנטציה אוטומטית | SDK ידני |
|---|---|---|
| Java | OpenTelemetry Java Agent | opentelemetry-java |
| Python | opentelemetry-distro | opentelemetry-api / SDK |
| Go | אין מקבילה מובנית | opentelemetry-go |
| .NET | OpenTelemetry .NET AutoInstrumentation | OpenTelemetry .NET |
הבחירה בין אוטומטי לידני תלויה בבשלות השירות וברמת הפירוט הנדרשת.
תוצרים מסחריים
- ביקורת קוד ותשתית נוכחיים – יום אחד
- תצורת OTel Collector עם סינון ודגימה – יומיים
- אינטגרציה עם מערכות קצה (Prometheus, Loki, Jaeger) – יום אחד
- יצירת חוזי טלמטריה (תיעוד תכונות ומטריקות) – יום אחד
- הכשרת צוות (סדנה על אינסטרומנטציה וקורלציה) – יום אחד
- שבועיים של תמיכה חינמית לאחר היישום – כלול
- תיעוד שירותים שעברו אינסטרומנטציה – כלול
עבור פריסה של 10 מיקרוסרוויסים, מעבר מ-APM קנייני ל-OpenTelemetry חוסך כ-$4,000–$8,000 לחודש בעלויות רישוי.
רשימת בדיקה להשלמת אינסטרומנטציה
- לכל קריאות ה-HTTP החיצוניות יש ספנים עם
-javaagent:opentelemetry-javaagent-2.x.jar -Dotel.service.name=order-service -Dotel.exporter.otlp.endpoint=http://otel-collector:4317 -Dotel.logs.exporter=otlp,<dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-spring-boot-starter</artifactId> <version>2.3.0-alpha</version> </dependency>,trace_id - פעולות DB:
span_id,import structlog from opentelemetry import trace def add_trace_context(logger, method, event_dict): span = trace.get_current_span() if span.is_recording(): ctx = span.get_span_context() event_dict["trace_id"] = format(ctx.trace_id, "032x") event_dict["span_id"] = format(ctx.span_id, "016x") return event_dict structlog.configure( processors=[add_trace_context, structlog.processors.JSONRenderer()] ),http.request.method(ללא פרטי התחברות או PII) - שגיאות נרשמות דרך
server.addressו-http.response.status_code -
db.systemזורם ללוגים - למטריקות אין תכונות בקרדינליות גבוהה (user_id, order_id)
- שמות ספנים הם מחרוזות יציבות, שאינן מכילות מזהים דינמיים
דגימה מבוססת-ראש (למשל, ProbabilisticSampler) מחליטה האם לשמר טרייס בתחילתו. דגימה מבוססת-זנב (IdGenerator) מקבלת החלטה לאחר השלמתו על סמך שגיאות ומשך זמן. כדי לחסוך במשאבים, השתמש בדגימה מבוססת-ראש עם הסתברות של 10% ובדגימה מבוססת-זנב רק לאחסון טרייסים בעייתיים.
ציר הזמן של אינסטרומנטציה תלוי במספר השירותים ובמורכבות הלוגיקה העסקית: מיומיים-שלושה עבור 3-5 שירותים עד 1-2 שבועות עבור מחסנית רב-שפתית גדולה. צור קשר—נעריך את הפרויקט שלך ביום אחד ונציע תוכנית יישום. הזמן יישום וקבל שבועיים של תמיכה חינמית.
תיעוד OpenTelemetry







