SDK 수동 계측: 사용자 정의 범위, 측정항목 및 이벤트 생성
자동 계측은 인프라 호출(HTTP, 데이터베이스, 메시지),SDK를 사용한 수동 계측 컨텍스트를 추가할 수 있습니다. 비즈니스를 추적하고, 사용자 정의 지표를 생성하고, 중요한 이벤트를 기록합니다. 응용 프로그램 도메인. 관찰가능성을 기술적 도구에서 도구로 전환하는 단계입니다. 비즈니스 분석 도구.
수동 SDK를 사용하면 다음과 같은 질문에 답할 수 있습니다. "검증에 소요되는 시간은 얼마나 됩니까?" 프리미엄 주문에 대한 재고는 무엇입니까?", "카드 유형별로 결제 거부 횟수는 얼마나 됩니까?", “지역별 주문규모 분포는 어떻게 되나요?” 이 정보 그들은 자기 계측에 보이지 않습니다.
이 기사에서 배울 내용
- 비즈니스 속성으로 커스텀 스팬 만들기
- 컨텍스트 및 범위 전파 관리
- 예외 기록 및 스팬 상태 설정
- 사용자 정의 지표 정의: 카운터, 히스토그램 및 게이지
- 범위에 포인트 이벤트 추가
- 고급 패턴: 범위 강화 및 컨텍스트 주입
커스텀 스팬 만들기
사용자 정의 범위는 발생하지 않는 중요한 비즈니스 운영을 나타냅니다. 자가 계측으로 포착했습니다. 각 범위에는 이름, 속성, 기간 및 상태가 있습니다. 핵심은 선택이다. 올바른 세분성: 스팬이 너무 많으면 소음이 발생합니다. 너무 적은 수가 가시성을 제공하지 않습니다.
from opentelemetry import trace
from opentelemetry.trace import StatusCode
tracer = trace.get_tracer("order-service", "2.0.0")
class OrderProcessor:
def process_order(self, order_request):
"""Processa un ordine con span personalizzati per ogni fase"""
with tracer.start_as_current_span(
"process-order",
attributes={
"order.type": order_request.type,
"order.items_count": len(order_request.items),
"order.total_amount": order_request.total,
"order.currency": order_request.currency,
"customer.id": order_request.customer_id,
"customer.tier": order_request.customer_tier,
}
) as root_span:
# Fase 1: Validazione
with tracer.start_as_current_span(
"validate-order",
attributes={
"validation.rules_count": 8,
"validation.type": "comprehensive"
}
) as validation_span:
errors = self.validate(order_request)
validation_span.set_attribute("validation.errors_count", len(errors))
if errors:
validation_span.set_status(StatusCode.ERROR, "Validation failed")
validation_span.set_attribute("validation.error_types",
",".join([e.type for e in errors]))
raise ValidationError(errors)
# Fase 2: Verifica inventario
with tracer.start_as_current_span(
"check-inventory",
attributes={
"inventory.items_to_check": len(order_request.items),
"inventory.warehouse": order_request.preferred_warehouse
}
) as inv_span:
availability = self.check_stock(order_request.items)
inv_span.set_attribute("inventory.all_available",
all(a.available for a in availability))
inv_span.set_attribute("inventory.backorder_count",
sum(1 for a in availability if not a.available))
# Fase 3: Calcolo prezzo finale
with tracer.start_as_current_span("calculate-pricing") as pricing_span:
pricing = self.calculate_price(order_request)
pricing_span.set_attribute("pricing.subtotal", pricing.subtotal)
pricing_span.set_attribute("pricing.discount_applied", pricing.discount > 0)
pricing_span.set_attribute("pricing.discount_amount", pricing.discount)
pricing_span.set_attribute("pricing.tax", pricing.tax)
pricing_span.set_attribute("pricing.final_total", pricing.total)
# Impostare il risultato sullo span root
root_span.set_attribute("order.id", order.id)
root_span.set_attribute("order.status", "created")
root_span.set_status(StatusCode.OK)
return order
예외 관리
디버깅을 위해서는 범위 내 예외를 올바르게 로깅하는 것이 중요합니다. 오텔이 제공하는
방법 record_exception 유형, 메시지 및 스택 추적을 자동으로 캡처합니다.
오류를 범위 이벤트로 저장합니다.
def process_payment(self, order, payment_info):
with tracer.start_as_current_span(
"process-payment",
attributes={
"payment.method": payment_info.method,
"payment.amount": order.total,
"payment.currency": order.currency
}
) as span:
try:
# Tentativo di addebito
result = self.payment_gateway.charge(
amount=order.total,
currency=order.currency,
method=payment_info
)
span.set_attribute("payment.transaction_id", result.tx_id)
span.set_attribute("payment.processor_response", result.response_code)
span.set_status(StatusCode.OK)
return result
except PaymentDeclinedException as e:
# Registrare eccezione con attributi aggiuntivi
span.record_exception(e, attributes={
"payment.decline_code": e.decline_code,
"payment.decline_reason": e.reason,
"payment.retry_eligible": e.retry_eligible
})
span.set_status(StatusCode.ERROR, f"Payment declined: {e.decline_code}")
# Aggiungere evento di retry se applicabile
if e.retry_eligible:
span.add_event("payment.retry.scheduled", {
"retry.delay_seconds": 5,
"retry.max_attempts": 3
})
raise
except GatewayTimeoutError as e:
span.record_exception(e)
span.set_status(StatusCode.ERROR, "Payment gateway timeout")
span.set_attribute("payment.timeout_ms", e.timeout_ms)
raise
except Exception as e:
span.record_exception(e)
span.set_status(StatusCode.ERROR, f"Unexpected error: {type(e).__name__}")
raise
Metrics API를 사용한 커스텀 측정항목
사용자 정의 지표를 사용하면 사용할 수 없는 비즈니스 지표를 추적할 수 있습니다. 자가 계측을 통해 OTel은 세 가지 유형의 미터법 기기를 지원합니다. 다양한 시나리오에 적합합니다.
from opentelemetry import metrics
meter = metrics.get_meter("order-service", "2.0.0")
# --- COUNTER: valori che solo aumentano ---
orders_created = meter.create_counter(
name="orders.created.total",
description="Numero totale di ordini creati",
unit="1"
)
revenue_total = meter.create_counter(
name="orders.revenue.total",
description="Ricavo totale degli ordini",
unit="EUR"
)
# --- HISTOGRAM: distribuzione di valori ---
order_processing_duration = meter.create_histogram(
name="orders.processing.duration",
description="Durata del processing degli ordini",
unit="ms"
)
order_items_count = meter.create_histogram(
name="orders.items.count",
description="Numero di articoli per ordine",
unit="1"
)
# --- UP_DOWN_COUNTER: valori che possono aumentare o diminuire ---
pending_orders = meter.create_up_down_counter(
name="orders.pending.count",
description="Ordini attualmente in fase di elaborazione",
unit="1"
)
# --- Utilizzo delle metriche ---
def on_order_created(order):
# Counter: incrementare con attributi
orders_created.add(1, {
"order.type": order.type,
"customer.tier": order.customer_tier,
"payment.method": order.payment_method,
"region": order.shipping_region
})
# Counter: registrare il ricavo
revenue_total.add(order.total, {
"order.type": order.type,
"currency": order.currency
})
# Histogram: registrare la durata
order_processing_duration.record(order.processing_time_ms, {
"order.type": order.type,
"customer.tier": order.customer_tier
})
# Histogram: registrare il numero di articoli
order_items_count.record(len(order.items), {
"order.type": order.type
})
def on_order_processing_started(order):
pending_orders.add(1, {"order.type": order.type})
def on_order_processing_completed(order):
pending_orders.add(-1, {"order.type": order.type})
Span 이벤트: 컨텍스트에 맞는 정확한 로그
그만큼 스팬 이벤트 타임스탬프가 포함된 특정 범위와 관련된 시기적절한 로그입니다. 그리고 속성. 작업 중 중요한 순간을 기록하지 않고 기록하는 데 이상적입니다. 각 단계마다 별도의 범위를 만듭니다.
// Java: utilizzo avanzato di span events
import io.opentelemetry.api.trace.Span;
public class FraudDetectionService {
public FraudCheckResult checkFraud(Order order) {
Span span = Span.current();
// Evento: inizio analisi
span.addEvent("fraud.analysis.started", Attributes.of(
AttributeKey.stringKey("fraud.model_version"), "v3.2.1",
AttributeKey.longKey("fraud.rules_count"), 42L
));
// Fase 1: controllo velocità
double velocityScore = checkVelocity(order);
span.addEvent("fraud.velocity_check.completed", Attributes.of(
AttributeKey.doubleKey("fraud.velocity_score"), velocityScore,
AttributeKey.booleanKey("fraud.velocity_passed"), velocityScore < 0.7
));
// Fase 2: controllo geo
double geoScore = checkGeolocation(order);
span.addEvent("fraud.geo_check.completed", Attributes.of(
AttributeKey.doubleKey("fraud.geo_score"), geoScore,
AttributeKey.stringKey("fraud.geo_country"), order.getShippingCountry()
));
// Fase 3: ML model
double mlScore = runMLModel(order);
span.addEvent("fraud.ml_model.completed", Attributes.of(
AttributeKey.doubleKey("fraud.ml_score"), mlScore,
AttributeKey.stringKey("fraud.ml_decision"),
mlScore > 0.8 ? "block" : mlScore > 0.5 ? "review" : "pass"
));
// Risultato finale
double finalScore = (velocityScore + geoScore + mlScore) / 3;
span.setAttribute("fraud.final_score", finalScore);
span.setAttribute("fraud.decision",
finalScore > 0.7 ? "blocked" : finalScore > 0.4 ? "review" : "approved");
return new FraudCheckResult(finalScore);
}
}
컨텍스트 관리: 수동 전파
스레드 풀, 비동기/대기 또는 내부 큐와 같은 복잡한 시나리오에서는 범위 컨텍스트 자동으로 전파되지 않을 수 있습니다. 이러한 경우 컨텍스트를 수동으로 관리해야 합니다.
from opentelemetry import trace, context
from opentelemetry.context import attach, detach
import concurrent.futures
tracer = trace.get_tracer("order-service")
def process_orders_in_parallel(orders):
with tracer.start_as_current_span("batch-process-orders") as parent_span:
parent_span.set_attribute("batch.size", len(orders))
# Catturare il contesto corrente per propagarlo ai thread
ctx = context.get_current()
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
futures = []
for order in orders:
# Passare il contesto al thread worker
future = executor.submit(
process_single_order_with_context,
order,
ctx
)
futures.append(future)
# Attendere tutti i risultati
results = [f.result() for f in futures]
parent_span.set_attribute("batch.completed", len(results))
return results
def process_single_order_with_context(order, parent_ctx):
# Attaccare il contesto del genitore in questo thread
token = attach(parent_ctx)
try:
with tracer.start_as_current_span(
"process-single-order",
attributes={"order.id": order.id}
) as span:
result = do_processing(order)
span.set_attribute("order.status", result.status)
return result
finally:
# Ripristinare il contesto originale del thread
detach(token)
수동 계측 모범 사례
의미있는 이름을 선택하세요: 사업 운영을 설명하는 동사를 사용하세요
(validate-order, check-inventory, process-payment), 아니
일반 기술 이름(step1, process).
시작과 끝에 속성을 추가하세요.: 즉시 알려진 속성(주문 유형,
고객 계층) 처음에는 결과(주문 ID, 상태)가 끝에 표시됩니다.
모든 코드 줄에 대해 범위를 만들지 마세요.: 좋은 범위는 작업을 나타냅니다.
단일 if/else가 아닌 완전한 논리(검증, 결제, 알림)입니다.
항상 예외 처리: 모든 try/catch에는 record_exception
e set_status(ERROR) 추적에 오류가 표시되도록 합니다.
결론 및 다음 단계
OpenTelemetry SDK를 사용한 수동 계측으로 기술 타임라인의 트랙을 변환합니다. 비즈니스 분석 도구에. 커스텀 스팬, 도메인별 속성, 측정항목 맞춤형 및 시기적절한 이벤트는 다음과 같은 질문에 답하는 데 필요한 컨텍스트를 제공합니다. 자체 계측으로는 대처할 수 없습니다.
자동 계측(인프라 적용 범위용)과 계측의 조합 수동(비즈니스 컨텍스트용) 및 완전한 관찰 가능성을 위한 최적의 접근 방식입니다. 핵심은 세분성: 악기 중요한 작업 없이 과도한 스팬으로 소음을 발생시킵니다.
다음 기사에서는오텔 콜렉터, 의 핵심 구성 요소 데이터를 수신, 처리하고 스토리지 백엔드로 내보내는 원격 측정 파이프라인 그리고 시각화.







