콘텐츠로 이동

관측성 및 트레이싱

Observer 백엔드(log/verbose/prometheus/otel), Prometheus 메트릭, OTLP 내보내기, 런타임 트레이스 등을 설명합니다.

Revka는 플러그인 방식의 Observer 파이프라인을 통해 텔레메트리를 내보냅니다. 단일 설정 키 — [observability] backend — 로 런타임이 에이전트 생명주기를 기록하는 방식을 선택합니다: 버리기(none), 로그 남기기(log), 터미널에 출력하기(verbose), Prometheus 메트릭으로 노출하기(prometheus), 또는 OTLP로 트레이스와 메트릭을 내보내기(otel). 별도의 런타임 트레이스 로거는 구조화된 JSONL 이벤트를 디스크에 영구 저장하여 사후 디버깅에 사용할 수 있으며, revka doctor traces로 조회할 수 있습니다.

Revka를 Grafana, Jaeger/Tempo/Honeycomb, 또는 기존 로그 파이프라인에 연결하거나, 어떤 툴 호출과 모델 응답이 실행되었는지 정확히 확인하고 싶을 때 이 페이지를 참조하세요. 여기의 모든 설정은 ~/.revka/config.toml[observability] 섹션 아래에 있습니다. 해당 파일의 경로 해석 방식은 설정 개요를 참조하세요.

모든 백엔드는 두 개의 핫 패스 메서드와 종료 드레인을 포함한 하나의 Observer 트레이트를 구현합니다:

메서드실행 시점비고
record_event(&ObserverEvent)각 생명주기 이벤트 발생 시핫 패스에서 동기적으로 호출 — 백엔드는 절대 블로킹해서는 안 됩니다.
record_metric(&ObserverMetric)각 측정값 기록 시동일한 동기 계약을 따릅니다.
flush()정상 종료 시버퍼링된 스팬/메트릭을 드레인합니다 (OTel 백엔드에서 사용).
name()언제든지로그에 사용할 백엔드 식별자.

옵저버는 에이전트 실행과 인라인으로 동작하기 때문에, 내장 백엔드는 가볍게 설계되어 있습니다 (로그 한 줄, 카운터 증가, 또는 버퍼 추가). OTLP 네트워크 내보내기와 같은 무거운 작업은 비동기로 버퍼링되어 전송되며, 종료 시 flush()에서 강제로 플러시됩니다.

런타임의 팩토리는 시작 시 backend를 읽고 해당 옵저버를 한 번 생성합니다. 요청별 전환은 지원하지 않습니다.

record_event는 에이전트 루프 전체, 채널, 하트비트, 응답 캐시, “핸드”(에이전트 실행), DORA 배포 시그널을 아우르는 ObserverEvent 변형 중 하나를 수신합니다:

그룹이벤트
에이전트 루프AgentStart, AgentEnd, LlmRequest, LlmResponse, ToolCallStart, ToolCall, TurnComplete
채널 및 하트비트ChannelMessage, HeartbeatTick
응답 캐시CacheHit, CacheMiss
오류Error
핸드(에이전트 실행)HandStarted, HandCompleted, HandFailed
배포 (DORA)DeploymentStarted, DeploymentCompleted, DeploymentFailed, RecoveryCompleted

CacheHit / CacheMisshot(인메모리)과 warm(SQLite) 캐시 레이어를 구분하며, tokens_saved를 포함하므로 응답 캐시가 절약한 토큰을 수치로 확인할 수 있습니다.

record_metric은 다음 ObserverMetric 변형 중 하나를 수신합니다:

메트릭타입의미
RequestLatency(Duration)타이밍LLM 요청의 벽시계 지연 시간
TokensUsed(u64)카운트마지막 요청에서 소비한 토큰 수
ActiveSessions(u64)게이지현재 활성 세션 수
QueueDepth(u64)게이지대기 중인 작업 큐 깊이
HandRunDuration타이밍핸드(에이전트) 실행 시간
HandFindingsCount카운트핸드가 생성한 결과 수
HandSuccessRate비율롤링 핸드 성공률
DeploymentLeadTime(Duration)타이밍DORA 변경 리드 타임
RecoveryTime(Duration)타이밍DORA 서비스 복구 시간
[observability]
backend = "none" # "none" | "noop" | "log" | "verbose" | "prometheus" | "otel"
backend동작외부 의존성빌드 기능
none / noop오버헤드 없는 no-op. 기본값.없음
log모든 이벤트와 메트릭에 대해 구조화된 tracing::info! 줄을 출력없음
verbose사람이 읽기 쉬운 > / < 진행 상황을 stderr에 출력 (대화형 전용)없음
prometheusGET /metrics에서 메트릭을 노출Prometheus 서버observability-prometheus
otelOTLP HTTP를 통해 트레이스 및 메트릭을 전송OTel 콜렉터observability-otel

안전한 기본값입니다. 모든 옵저버 메서드가 no-op으로 컴파일되어 오버헤드와 의존성이 없습니다. 기능 게이팅된 백엔드가 요청되었지만 해당 Cargo 기능이 없거나 backend가 인식할 수 없는 문자열인 경우, 팩토리도 이 옵션으로 폴백합니다(warn! 출력과 함께).

모든 이벤트와 메트릭을 이름 있는 필드(agent.start, tool.call, cache.hit, metric.tokens_used, …)를 포함한 구조화된 tracing::info! 줄로 내보냅니다. 외부 의존성이 없고 어떤 tracing 구독자와도 동작하므로 기존 로그 전송 시스템과 쉽게 연동됩니다:

Terminal window
RUST_LOG=info revka daemon

JSON 포매팅 tracing-subscriber 아래서 데몬을 실행하면 출력이 수집에 바로 사용할 수 있는 구조화된 JSON 형식이 됩니다. Prometheus나 OTel을 추가하기 전 첫 단계로 권장합니다.

대화형 CLI 세션에서 LLM 처리, 툴 시작/종료, 턴 완료 등 간결하고 사람이 읽기 쉬운 진행 상황을 stderr에 출력합니다. 메트릭은 기록하지 않으며, 프롬프트 내용이 아닌 진행 표시만 나타납니다:

> Thinking
> Send (provider=openrouter, model=claude-sonnet, messages=3)
< Receive (success=true, duration_ms=412)
> Tool shell
< Complete

백엔드를 설정하고, 해당 기능으로 빌드한 후 /metrics를 스크레이프합니다:

  1. Prometheus 기능으로 빌드합니다.

    Terminal window
    cargo build --release --features observability-prometheus
  2. 백엔드를 선택합니다.

    [observability]
    backend = "prometheus"
  3. 엔드포인트를 스크레이프합니다. 게이트웨이/metrics에서 인증 없이 Prometheus 텍스트 형식으로 제공됩니다.

    Terminal window
    curl http://127.0.0.1:42617/metrics
GET /metrics
Auth: none (read-only)
Returns: text/plain; version=0.0.4

백엔드가 prometheus아니거나 바이너리가 observability-prometheus 없이 빌드된 경우, /metrics 엔드포인트는 메트릭 대신 안내 메시지를 반환합니다 — curl로 빈 응답처럼 보인다면 백엔드나 기능이 활성화되지 않은 것입니다.

메트릭타입레이블
revka_agent_starts_total카운터provider, model
revka_llm_requests_total카운터provider, model, success
revka_tokens_input_total카운터provider, model
revka_tokens_output_total카운터provider, model
revka_agent_duration_seconds히스토그램 (0.1–60s)provider, model
revka_tool_calls_total카운터tool, success
revka_tool_duration_seconds히스토그램 (0.01–10s)tool
revka_channel_messages_total카운터channel, direction
revka_heartbeat_ticks_total카운터
revka_errors_total카운터component
revka_cache_hits_total카운터cache_type
revka_cache_misses_total카운터cache_type
revka_cache_tokens_saved_total카운터cache_type
revka_request_latency_seconds히스토그램 (0.01–10s)
revka_tokens_used_last게이지
revka_active_sessions게이지
revka_queue_depth게이지
revka_hand_runs_total카운터hand, success
revka_hand_duration_seconds히스토그램hand
revka_hand_findings_total카운터hand
revka_deployments_total카운터status
revka_deployment_lead_time_seconds서머리
revka_deployment_failure_rate게이지
revka_recovery_time_seconds서머리
revka_mttr_seconds서머리

Prometheus 서버가 게이트웨이를 바라보도록 설정합니다:

scrape_configs:
- job_name: "revka"
static_configs:
- targets: ["127.0.0.1:42617"]

이후 위의 메트릭을 기반으로 Grafana 패널을 구성하세요 — 예: providermodelrevka_tokens_input_total / revka_tokens_output_total을 이용한 토큰 소비 그래프, revka_tool_duration_seconds를 이용한 툴 지연 시간 히트맵, 배포 시리즈를 이용한 DORA 대시보드 등. 달러 비용 추적이 주목적이라면, 호출별 USD를 기록하는 전용 비용 추적 및 예산 원장을 사용하세요.

OTel 백엔드는 트레이스메트릭 모두를 OTLP HTTP/protobuf를 통해 OpenTelemetry 호환 콜렉터(Jaeger, Tempo, Honeycomb, Datadog 등)로 내보냅니다.

  1. OTel 기능으로 빌드합니다.

    Terminal window
    cargo build --release --features observability-otel
  2. 백엔드와 콜렉터 엔드포인트를 설정합니다.

    [observability]
    backend = "otel" # aliases: "opentelemetry", "otlp"
    otel_endpoint = "http://localhost:4318" # default
    otel_service_name = "revka" # default; sets the service.name resource attribute
  3. 데몬을 시작합니다. 스팬과 메트릭이 콜렉터로 전달되기 시작합니다.

    Terminal window
    revka daemon
타입기본값의미
otel_endpointstringhttp://localhost:4318OTLP HTTP 기본 URL. 트레이스는 <endpoint>/v1/traces로, 메트릭은 <endpoint>/v1/metrics로 전송됩니다.
otel_service_namestring"revka"모든 스팬과 메트릭의 service.name 리소스 속성.

백엔드는 이벤트 페이로드에서 추출한 속성과 함께 agent.invocation, llm.call, tool.call, hand.run, error에 대한 스팬을 생성합니다:

속성나타나는 스팬
provider, model, success, duration_sllm.call, agent.invocation
tokens_used, cost_usdllm.call
tool.nametool.call
hand.namehand.run
error.messageerror

메트릭 인스트루먼트는 Prometheus 세트와 동일하며, revka.* 접두사를 사용하고 스크레이프 대신 OTLP를 통해 전송됩니다.

메트릭 백엔드와 독립적으로, 런타임 트레이스 로거는 툴 호출, 모델 응답, 오류 등 구조화된 JSONL 이벤트를 사후 진단을 위해 디스크에 영구 저장합니다. 기본적으로 비활성화되어 있습니다.

[observability]
runtime_trace_mode = "rolling" # "none" | "rolling" | "full"
runtime_trace_path = "state/runtime-trace.jsonl" # relative to workspace unless absolute
runtime_trace_max_entries = 200 # rolling mode only
타입기본값의미
runtime_trace_modestring"none"none(비활성화), rolling(최근 N개 유지), full(무제한)
runtime_trace_pathstringstate/runtime-trace.jsonl트레이스 파일; 상대 경로는 워크스페이스 기준으로 해석됩니다
runtime_trace_max_entriesinteger200rolling 모드에서 유지할 최대 줄 수

RuntimeTraceEvent 줄에는 id(UUID), timestamp(RFC 3339), event_type, 선택적 channel / provider / model / turn_id / success / message, 그리고 payload JSON 객체가 포함됩니다.

모드별 트레이드오프:

  • rolling추가할 때마다 원자적 임시 파일 이름 변경을 통해 트리밍하므로, 파일이 runtime_trace_max_entries 줄을 초과하지 않습니다 — 항상 켜두어도 안전합니다.
  • full무제한으로 증가합니다 — 단기 디버깅 목적으로만 사용하세요.

revka doctor traces로 트레이스 파일을 검사합니다 (별도의 revka trace 커맨드가 아닙니다). 이벤트는 최신순으로 반환되며, 목록 뷰에서 메시지 미리보기는 80자로 잘립니다.

Terminal window
revka doctor traces # 20 most recent events
revka doctor traces --limit 50 # show 50 events
revka doctor traces --event tool_call_result # exact event-type filter
revka doctor traces --contains "timeout" # full-text substring search
revka doctor traces --id <uuid> # one event, full JSON payload
플래그기본값의미
--limit <n>20나열할 최대 이벤트 수
--event <type>event_type의 대소문자 구분 없는 정확한 일치
--contains <text>event_type, message, payload, channel, provider, model 전체 텍스트 부분 문자열 검색
--id <uuid>UUID로 단일 이벤트를 예쁘게 포매팅된 JSON으로 가져옴 (다른 필터 무시)

runtime_trace_mode = "none"인 경우 파일이 존재하지 않으며, revka doctor tracesrolling 모드를 먼저 활성화하라는 안내 메시지를 출력합니다.

내부적으로 Revka는 MultiObserver를 통해 옵저버를 조합할 수 있습니다. MultiObserver는 모든 이벤트와 메트릭을 자식 옵저버 목록으로 팬아웃하고(flush()도 모두에 전파) — 예를 들어 logprometheus를 동시에 내보낼 수 있습니다.

MultiObserver::new(vec![Box::new(obs1), Box::new(obs2)])

기본값이 포함된 전체 섹션:

[observability]
backend = "none" # "none" | "noop" | "log" | "verbose" | "prometheus" | "otel"
otel_endpoint = "http://localhost:4318" # OTel only
otel_service_name = "revka" # OTel only
runtime_trace_mode = "none" # "none" | "rolling" | "full"
runtime_trace_path = "state/runtime-trace.jsonl"
runtime_trace_max_entries = 200
기본값적용 대상
backend"none"전체
otel_endpoint"http://localhost:4318"otel
otel_service_name"revka"otel
runtime_trace_mode"none"런타임 트레이스
runtime_trace_path"state/runtime-trace.jsonl"런타임 트레이스
runtime_trace_max_entries200런타임 트레이스 (rolling)

backend를 제외한 모든 필드는 선택 사항이며 기본값은 안전합니다(no-op 옵저버, 트레이스 파일 없음). opentelemetryotlpotel의 별칭입니다.

두 개의 인증 없는 게이트웨이 엔드포인트가 관측성 데이터를 제공합니다. 둘 다 실행 중인 게이트웨이(기본값 http://127.0.0.1:42617)에서 제공됩니다:

엔드포인트메서드인증반환값
/metricsGET없음Prometheus 메트릭 (text/plain; version=0.0.4), Prometheus가 활성 백엔드가 아닌 경우 안내 메시지
/healthGET없음컴포넌트 상태 스냅샷 JSON (항상 200; 상태는 본문을 확인하세요)
Terminal window
curl http://127.0.0.1:42617/metrics
curl http://127.0.0.1:42617/health

/health 엔드포인트는 Docker HEALTHCHECK, 로드 밸런서, revka status --format exit-code에서 사용하는 주요 활성 상태 신호입니다. 전체 응답 형식과 컴포넌트 상태 레지스트리에 대해서는 상태, 헬스, 설정 및 툴 엔드포인트업데이트, 런북 및 문제 해결 페이지를 참조하세요.