관측성 및 트레이싱
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 파이프라인
섹션 제목: “Observer 파이프라인”모든 백엔드는 두 개의 핫 패스 메서드와 종료 드레인을 포함한 하나의 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 / CacheMiss는 hot(인메모리)과 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에 출력 (대화형 전용) | 없음 | — |
prometheus | GET /metrics에서 메트릭을 노출 | Prometheus 서버 | observability-prometheus |
otel | OTLP HTTP를 통해 트레이스 및 메트릭을 전송 | OTel 콜렉터 | observability-otel |
none / noop (기본값)
섹션 제목: “none / noop (기본값)”안전한 기본값입니다. 모든 옵저버 메서드가 no-op으로 컴파일되어 오버헤드와 의존성이 없습니다. 기능 게이팅된 백엔드가 요청되었지만 해당 Cargo 기능이 없거나 backend가 인식할 수 없는 문자열인 경우, 팩토리도 이 옵션으로 폴백합니다(warn! 출력과 함께).
log
섹션 제목: “log”모든 이벤트와 메트릭을 이름 있는 필드(agent.start, tool.call, cache.hit, metric.tokens_used, …)를 포함한 구조화된 tracing::info! 줄로 내보냅니다. 외부 의존성이 없고 어떤 tracing 구독자와도 동작하므로 기존 로그 전송 시스템과 쉽게 연동됩니다:
RUST_LOG=info revka daemonJSON 포매팅 tracing-subscriber 아래서 데몬을 실행하면 출력이 수집에 바로 사용할 수 있는 구조화된 JSON 형식이 됩니다. Prometheus나 OTel을 추가하기 전 첫 단계로 권장합니다.
verbose
섹션 제목: “verbose”대화형 CLI 세션에서 LLM 처리, 툴 시작/종료, 턴 완료 등 간결하고 사람이 읽기 쉬운 진행 상황을 stderr에 출력합니다. 메트릭은 기록하지 않으며, 프롬프트 내용이 아닌 진행 표시만 나타납니다:
> Thinking> Send (provider=openrouter, model=claude-sonnet, messages=3)< Receive (success=true, duration_ms=412)> Tool shell< CompletePrometheus 메트릭
섹션 제목: “Prometheus 메트릭”백엔드를 설정하고, 해당 기능으로 빌드한 후 /metrics를 스크레이프합니다:
-
Prometheus 기능으로 빌드합니다.
Terminal window cargo build --release --features observability-prometheus -
백엔드를 선택합니다.
[observability]backend = "prometheus" -
엔드포인트를 스크레이프합니다. 게이트웨이의
/metrics에서 인증 없이 Prometheus 텍스트 형식으로 제공됩니다.Terminal window curl http://127.0.0.1:42617/metrics
GET /metricsAuth: 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 | 서머리 | — |
스크레이프 설정 및 Grafana
섹션 제목: “스크레이프 설정 및 Grafana”Prometheus 서버가 게이트웨이를 바라보도록 설정합니다:
scrape_configs: - job_name: "revka" static_configs: - targets: ["127.0.0.1:42617"]이후 위의 메트릭을 기반으로 Grafana 패널을 구성하세요 — 예: provider와 model별 revka_tokens_input_total / revka_tokens_output_total을 이용한 토큰 소비 그래프, revka_tool_duration_seconds를 이용한 툴 지연 시간 히트맵, 배포 시리즈를 이용한 DORA 대시보드 등. 달러 비용 추적이 주목적이라면, 호출별 USD를 기록하는 전용 비용 추적 및 예산 원장을 사용하세요.
OpenTelemetry (OTLP)
섹션 제목: “OpenTelemetry (OTLP)”OTel 백엔드는 트레이스와 메트릭 모두를 OTLP HTTP/protobuf를 통해 OpenTelemetry 호환 콜렉터(Jaeger, Tempo, Honeycomb, Datadog 등)로 내보냅니다.
-
OTel 기능으로 빌드합니다.
Terminal window cargo build --release --features observability-otel -
백엔드와 콜렉터 엔드포인트를 설정합니다.
[observability]backend = "otel" # aliases: "opentelemetry", "otlp"otel_endpoint = "http://localhost:4318" # defaultotel_service_name = "revka" # default; sets the service.name resource attribute -
데몬을 시작합니다. 스팬과 메트릭이 콜렉터로 전달되기 시작합니다.
Terminal window revka daemon
| 키 | 타입 | 기본값 | 의미 |
|---|---|---|---|
otel_endpoint | string | http://localhost:4318 | OTLP HTTP 기본 URL. 트레이스는 <endpoint>/v1/traces로, 메트릭은 <endpoint>/v1/metrics로 전송됩니다. |
otel_service_name | string | "revka" | 모든 스팬과 메트릭의 service.name 리소스 속성. |
백엔드는 이벤트 페이로드에서 추출한 속성과 함께 agent.invocation, llm.call, tool.call, hand.run, error에 대한 스팬을 생성합니다:
| 속성 | 나타나는 스팬 |
|---|---|
provider, model, success, duration_s | llm.call, agent.invocation |
tokens_used, cost_usd | llm.call |
tool.name | tool.call |
hand.name | hand.run |
error.message | error |
메트릭 인스트루먼트는 Prometheus 세트와 동일하며, revka.* 접두사를 사용하고 스크레이프 대신 OTLP를 통해 전송됩니다.
런타임 트레이스 로거
섹션 제목: “런타임 트레이스 로거”메트릭 백엔드와 독립적으로, 런타임 트레이스 로거는 툴 호출, 모델 응답, 오류 등 구조화된 JSONL 이벤트를 사후 진단을 위해 디스크에 영구 저장합니다. 기본적으로 비활성화되어 있습니다.
[observability]runtime_trace_mode = "rolling" # "none" | "rolling" | "full"runtime_trace_path = "state/runtime-trace.jsonl" # relative to workspace unless absoluteruntime_trace_max_entries = 200 # rolling mode only| 키 | 타입 | 기본값 | 의미 |
|---|---|---|---|
runtime_trace_mode | string | "none" | none(비활성화), rolling(최근 N개 유지), full(무제한) |
runtime_trace_path | string | state/runtime-trace.jsonl | 트레이스 파일; 상대 경로는 워크스페이스 기준으로 해석됩니다 |
runtime_trace_max_entries | integer | 200 | rolling 모드에서 유지할 최대 줄 수 |
각 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자로 잘립니다.
revka doctor traces # 20 most recent eventsrevka doctor traces --limit 50 # show 50 eventsrevka doctor traces --event tool_call_result # exact event-type filterrevka doctor traces --contains "timeout" # full-text substring searchrevka 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 traces는 rolling 모드를 먼저 활성화하라는 안내 메시지를 출력합니다.
멀티 옵저버 팬아웃
섹션 제목: “멀티 옵저버 팬아웃”내부적으로 Revka는 MultiObserver를 통해 옵저버를 조합할 수 있습니다. MultiObserver는 모든 이벤트와 메트릭을 자식 옵저버 목록으로 팬아웃하고(flush()도 모두에 전파) — 예를 들어 log와 prometheus를 동시에 내보낼 수 있습니다.
MultiObserver::new(vec![Box::new(obs1), Box::new(obs2)])[observability] 설정 참조
섹션 제목: “[observability] 설정 참조”기본값이 포함된 전체 섹션:
[observability]backend = "none" # "none" | "noop" | "log" | "verbose" | "prometheus" | "otel"otel_endpoint = "http://localhost:4318" # OTel onlyotel_service_name = "revka" # OTel onlyruntime_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_entries | 200 | 런타임 트레이스 (rolling) |
backend를 제외한 모든 필드는 선택 사항이며 기본값은 안전합니다(no-op 옵저버, 트레이스 파일 없음). opentelemetry와 otlp는 otel의 별칭입니다.
게이트웨이 엔드포인트
섹션 제목: “게이트웨이 엔드포인트”두 개의 인증 없는 게이트웨이 엔드포인트가 관측성 데이터를 제공합니다. 둘 다 실행 중인 게이트웨이(기본값 http://127.0.0.1:42617)에서 제공됩니다:
| 엔드포인트 | 메서드 | 인증 | 반환값 |
|---|---|---|---|
/metrics | GET | 없음 | Prometheus 메트릭 (text/plain; version=0.0.4), Prometheus가 활성 백엔드가 아닌 경우 안내 메시지 |
/health | GET | 없음 | 컴포넌트 상태 스냅샷 JSON (항상 200; 상태는 본문을 확인하세요) |
curl http://127.0.0.1:42617/metricscurl http://127.0.0.1:42617/health/health 엔드포인트는 Docker HEALTHCHECK, 로드 밸런서, revka status --format exit-code에서 사용하는 주요 활성 상태 신호입니다. 전체 응답 형식과 컴포넌트 상태 레지스트리에 대해서는 상태, 헬스, 설정 및 툴 엔드포인트와 업데이트, 런북 및 문제 해결 페이지를 참조하세요.