설치

공식 Rust OpenTelemetry 크레이트로 트레이스와 로그를 OTLP로 전송하도록 연결하고, 환경 변수로 설정하여 Sophonz로 텔레메트리를 보내세요.

이 문서는 Sophonz 패키지가 아니라 업스트림 opentelemetry / opentelemetry-otlp 크레이트를 연결합니다. Rust에는 자동 계측 에이전트가 없으므로 한 번만 작성하면 되는 짧은 설정 함수를 사용합니다. 여기서 만드는 OTLP 익스포터는 프로그래밍 방식으로 설정하지 않는 한 이미 OTEL_EXPORTER_OTLP_* 환경 변수를 읽습니다.

빠른 시작

아래에서 수집기 URL, 프로젝트, 앱 이름, 버전, 앱 키를 입력하면 그 값으로 초기화 코드가 생성됩니다. 고급 옵션은 "고급 옵션" 토글 뒤에 있습니다.

// otel.rs — 트래픽을 받기 전에 시작 시 init_telemetry()를 한 번 호출하세요
use opentelemetry::global;
use opentelemetry::trace::TracerProvider as _;
use opentelemetry::KeyValue;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_otlp::{LogExporter, SpanExporter, WithExportConfig, WithHttpConfig};
use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;
use std::collections::HashMap;
use tracing_subscriber::prelude::*;

fn init_telemetry() -> (SdkTracerProvider, SdkLoggerProvider) {
    let headers = HashMap::from([("authorization".to_string(), "sk_tYtJUa4WweXXedj6lxjSqoPW5O0DnbXz".to_string())]);

    let resource = Resource::builder()
        .with_service_name("next-sample")
        .with_attribute(KeyValue::new("service.version", "1.0.0"))
        .with_attribute(KeyValue::new("service.namespace", "sophonz"))
        .with_attribute(KeyValue::new("service.key", "sk_tYtJUa4WweXXedj6lxjSqoPW5O0DnbXz"))
        .build();

    let span_exporter = SpanExporter::builder()
        .with_http()
        .with_endpoint("https://in.sophonz.ai")
        .with_headers(headers.clone())
        .build()
        .expect("failed to build span exporter");

    let tracer_provider = SdkTracerProvider::builder()
        .with_batch_exporter(span_exporter)
        .with_resource(resource.clone())
        .build();
    global::set_tracer_provider(tracer_provider.clone());
    let tracer = tracer_provider.tracer("next-sample");

    let log_exporter = LogExporter::builder()
        .with_http()
        .with_endpoint("https://in.sophonz.ai")
        .with_headers(headers)
        .build()
        .expect("failed to build log exporter");

    let logger_provider = SdkLoggerProvider::builder()
        .with_batch_exporter(log_exporter)
        .with_resource(resource)
        .build();

    let otel_log_layer = OpenTelemetryTracingBridge::new(&logger_provider);
    let otel_trace_layer = tracing_opentelemetry::layer().with_tracer(tracer);

    tracing_subscriber::registry()
        .with(otel_trace_layer)
        .with(otel_log_layer)
        .init();

    (tracer_provider, logger_provider)
}

fn main() {
    let (tracer_provider, logger_provider) = init_telemetry();

    tracing::info!("service starting");
    // ... run your server ...

    let _ = tracer_provider.shutdown();
    let _ = logger_provider.shutdown();
}

// 위처럼 직접 넘기는 대신 환경 변수로 지정할 수도 있습니다:
//   OTEL_SERVICE_NAME            service.name (appName)
//   OTEL_EXPORTER_OTLP_ENDPOINT  콜렉터 base URL (collectorUrl)
//   OTEL_EXPORTER_OTLP_HEADERS   authorization=<appKey>
//   OTEL_RESOURCE_ATTRIBUTES     service.key=<appKey>,service.namespace=<project>

사전 요구 사항

  • 최신 안정 버전의 Rust 툴체인 (edition 2021 이상).
  • Sophonz 콘솔에 등록한 서비스와 그 서비스에 발급된 sk_... API 키.

의존성 추가

# Cargo.toml
[dependencies]
opentelemetry = "0.32"
opentelemetry_sdk = { version = "0.32", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "0.32", features = ["http-proto", "reqwest-blocking-client", "logs", "trace"] }
opentelemetry-appender-tracing = "0.32"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
tracing-opentelemetry = "0.33"

CAUTION — opentelemetry와 tracing-opentelemetry 버전을 신중하게 맞추세요

tracing-opentelemetry의 버전 번호는 opentelemetry의 버전과 그대로 일치하지 않습니다 — 사용 중인 opentelemetry 버전에 의존하는 릴리스를 체인지로그에서 확인하세요. 이 글을 쓰는 시점에는 opentelemetry 0.32가 tracing-opentelemetry 0.32가 아니라 0.33과 짝을 이룹니다. 버전이 맞지 않으면 컴파일 시점에 트레이트 불일치 오류가 발생하므로, cargo build만 실행해도 조합이 잘못됐다는 것을 바로 알 수 있습니다.

TIP — API 키 발급

Sophonz 콘솔에 로그인해 서비스를 등록하고 발급된 sk_... 키를 복사하세요. 이 키는 테넌트 신원을 나타냅니다 — 아래 service.key 참고.

SDK 설정

트래픽을 처리하기 전, 시작 시점에 한 번 호출합니다.

use opentelemetry::global;
use opentelemetry::trace::TracerProvider as _;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_otlp::{LogExporter, SpanExporter};
use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;
use tracing_subscriber::prelude::*;
 
fn init_telemetry() -> (SdkTracerProvider, SdkLoggerProvider) {
    let resource = Resource::builder()
        .with_service_name(
            std::env::var("OTEL_SERVICE_NAME").unwrap_or_else(|_| "my-rust-service".into()),
        )
        .build();
 
    // OTEL_EXPORTER_OTLP_ENDPOINT / _PROTOCOL / _HEADERS를 자동으로 읽습니다.
    let span_exporter = SpanExporter::builder()
        .with_http()
        .build()
        .expect("failed to build span exporter");
 
    let tracer_provider = SdkTracerProvider::builder()
        .with_batch_exporter(span_exporter)
        .with_resource(resource.clone())
        .build();
    global::set_tracer_provider(tracer_provider.clone());
    let tracer = tracer_provider.tracer("my-rust-service");
 
    let log_exporter = LogExporter::builder()
        .with_http()
        .build()
        .expect("failed to build log exporter");
 
    let logger_provider = SdkLoggerProvider::builder()
        .with_batch_exporter(log_exporter)
        .with_resource(resource)
        .build();
 
    let otel_log_layer = OpenTelemetryTracingBridge::new(&logger_provider);
    let otel_trace_layer = tracing_opentelemetry::layer().with_tracer(tracer);
 
    tracing_subscriber::registry()
        .with(otel_trace_layer)
        .with(otel_log_layer)
        .init();
 
    (tracer_provider, logger_provider)
}
 
fn main() {
    let (tracer_provider, logger_provider) = init_telemetry();
 
    tracing::info!("service starting");
    // ... 서버 실행 ...
 
    let _ = tracer_provider.shutdown();
    let _ = logger_provider.shutdown();
}

tracing_subscriber::registry()가 전역 tracing 서브스크라이버가 되므로, 애플리케이션 어디에서든 — 그리고 계측된 의존성 안에서도 — 호출되는 tracing::info!, tracing::error!, #[tracing::instrument]가 이제 OTel 스팬과 로그 레코드를 만들어 냅니다. 프로세스 종료 전에 두 프로바이더 모두에서 shutdown()을 호출해 배치 익스포터가 플러시되도록 하세요.

환경 변수 구성

OTEL_SERVICE_NAME=my-rust-service
OTEL_EXPORTER_OTLP_ENDPOINT=https://in.sophonz.ai
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS=authorization=sk_your_api_key
OTEL_RESOURCE_ATTRIBUTES=service.key=sk_your_api_key,service.namespace=my-project,deployment.environment.name=production
변수값설명
OTEL_SERVICE_NAME서비스 이름위 설정 코드에서 std::env::var로 직접 읽습니다. 콘솔에 등록한 서비스 이름과 일치해야 합니다 — 포털은 (project, service.name) 조합으로 텔레메트리를 등록된 서비스에 연결합니다.
OTEL_EXPORTER_OTLP_ENDPOINThttps://in.sophonz.ai기본 OTLP 엔드포인트. .with_http()를 사용한 SpanExporter/LogExporter가 여기서 /v1/traces와 /v1/logs를 파생시킵니다.
OTEL_EXPORTER_OTLP_PROTOCOLhttp/protobuf.with_http() 빌더가 자동으로 읽습니다.
OTEL_EXPORTER_OTLP_HEADERSauthorization=sk_your_api_key모든 전송 요청에 HTTP 헤더로 포함됩니다.
OTEL_RESOURCE_ATTRIBUTESservice.key=sk_your_api_key,service.namespace=...,deployment.environment.name=...service.key는 컬렉터가 검사하는 테넌트 신원으로 필수입니다. service.namespace는 서비스들을 프로젝트로 묶고, deployment.environment.name은 환경을 표시합니다. 환경 변수 자동 인식에 의존하고 싶지 않다면 Resource::builder().with_attributes(...)로 직접 추가해도 됩니다.

CAUTION — service.key는 필수입니다

컬렉터는 강제 모드로 동작합니다. 해석 가능한 service.key 리소스 속성이 없는 텔레메트리는 조용히 폐기됩니다. 프로세스는 계속 실행되고 .build()도 오류를 내지 않지만, Sophonz에는 아무것도 나타나지 않습니다. 항상 OTEL_RESOURCE_ATTRIBUTES에 service.key를 포함하고, OTEL_EXPORTER_OTLP_HEADERS를 통해 같은 키를 authorization 헤더로도 전송하세요.

프레임워크 참고 사항

위 설정은 프레임워크에 독립적입니다 — 대부분의 Rust 웹 프레임워크가 이미 사용하거나 쉽게 연결할 수 있는 tracing에 연결됩니다.

프레임워크tracing에 연결하는 방법
Axumtower-http의 TraceLayer(.layer(TraceLayer::new_for_http())), 또는 들어오는 요청에서 트레이스 컨텍스트를 자동 추출하는 axum-tracing-opentelemetry.
Actix Webtracing-actix-web의 TracingLogger 미들웨어, 또는 더 직접적인 OTel 통합을 제공하는 actix-web-opentelemetry.
tower 기반 서비스 전반tower-http의 TraceLayer는 모든 tower::Service에서 동작합니다.

이들 각각은 들어오는 요청에 대해 tracing 스팬을 만들어 내며, 위에서 등록한 tracing-opentelemetry 레이어를 거쳐 Sophonz 전용 코드 없이 OTel 스팬이 됩니다.

확인

환경 변수를 설정한 뒤 서비스를 실행하세요.

OTEL_SERVICE_NAME=my-rust-service \
OTEL_EXPORTER_OTLP_ENDPOINT=https://in.sophonz.ai \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_HEADERS=authorization=sk_your_api_key \
OTEL_RESOURCE_ATTRIBUTES=service.key=sk_your_api_key,service.namespace=my-project,deployment.environment.name=production \
cargo run

전송 실패는 패닉이나 반환 오류가 아니라 배치 프로세서 내부의 에러 채널로 처리되므로, 정상적으로 실행됐다고 해서 텔레메트리가 도착했다는 보장은 없습니다. service.key가 잘못되었거나 없어도 정상 동작할 때와 똑같이 실행되며, 다만 아무것도 보이지 않을 뿐입니다. 트래픽을 조금 발생시킨 뒤 Sophonz 콘솔에서 등록한 서비스를 확인하세요.

TIP — 이제 시작입니다

이제 앱을 배포하세요. Sophonz가 실사용자가 겪는 문제를 포착하고, 당신의 앱은 스스로 진화합니다.