Rust OpenTelemetry

Материал из Документация Ключ-АСТРОМ
Версия от 15:51, 16 октября 2025; IKuznetsov (обсуждение | вклад) (Новая страница: «В этом пошаговом руководстве показано, как добавить возможность наблюдения в ваше прило...»)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)

В этом пошаговом руководстве показано, как добавить возможность наблюдения в ваше приложение Rust с помощью библиотек и инструментов OpenTelemetry Rust.

Особенность Поддержка
Автоматические инструменты Нет
Трассировки Да
Метрики Да
Логи Да

Предустановка

  • Ключ-АСТРОМ версии 1.222+
  • Для трассировки включен контекст трассировки W3C.
    1. Перейдите в Настройки > Предпочтения > Функции ЕдиногоАгента.
    2. Включите опцию Отправлять HTTP-заголовки контекста трассировки W3C.

Получение данных для доступа к Ключ-АСТРОМ

Определение базового URL API

Подробную информацию о сборке базового URL-адреса конечной точки OTLP см. в разделе Экспорт с помощью OTLP. URL-адрес должен заканчиваться на /api/v2/otlp.

Получение токена доступа API

Токен доступа для сбора трассировок, логов и метрик можно создать в разделе Токены доступа.

Экспорт с помощью OTLP содержит более подробную информацию о формате и необходимых областях доступа.

Настройка OpenTelemetry

1. Добавьте следующее в ваш файл Cargo.toml.

opentelemetry = { version = "~0", features = ["trace", "metrics"] }

opentelemetry_sdk = { version = "~0", features = ["rt-tokio", "metrics", "logs", "spec_unstable_metrics_views"] }

opentelemetry-otlp = { version = "~0", features = ["http-proto", "http-json", "logs", "reqwest-client", "reqwest-rustls"] }

opentelemetry-http = { version = "~0" }

opentelemetry-appender-log = { version = "~0" }

opentelemetry-semantic-conventions = { version = "~0" }

2. Добавьте в свой код следующие объявления use.

use std::{env, convert::Infallible, net::SocketAddr, collections::HashMap, io::{BufRead, BufReader, Read}};

use opentelemetry_sdk::trace::SdkTracerProvider;

use opentelemetry_sdk::{logs::SdkLoggerProvider, metrics::{PeriodicReader, SdkMeterProvider}, propagation::TraceContextPropagator, Resource};

use opentelemetry_otlp::{LogExporter, MetricExporter, Protocol, SpanExporter, WithExportConfig, WithHttpConfig};

use opentelemetry_semantic_conventions::trace;

use opentelemetry_http::{Bytes, HeaderExtractor, HeaderInjector};

use opentelemetry_appender_log::OpenTelemetryLogBridge;

use opentelemetry::{global, trace::{FutureExt, Span, SpanKind, TraceContextExt, Tracer}, Context, KeyValue};

3. Добавьте следующую функцию в ваш стартовый файл.

fn init_opentelemetry() {

    // Helper function to read potentially available OneAgent data

    fn read_dt_metadata() ->  Vec<KeyValue> {

        fn read_single(path: &str, metadata: &mut Vec<KeyValue>) -> std::io::Result<()> {

            let mut file = std::fs::File::open(path)?;

            if path.starts_with("dt_metadata") {

                let mut name = String::new();

                file.read_to_string(&mut name)?;

                file = std::fs::File::open(name)?;

            }

            for line in BufReader::new(file).lines() {

                if let Some((k, v)) = line?.split_once('=') {

                    metadata.push(KeyValue::new(k.to_string(), v.to_string()))

                }

            }

            Ok(())

        }

        let mut metadata = Vec::new();

        for name in [

            "dt_metadata_e617c525669e072eebe3d0f08212e8f2.properties",

            "/var/lib/astromkey/enrichment/dt_metadata.properties",

            "/var/lib/astromkey/enrichment/dt_host_metadata.properties"

        ] {

            let _ = read_single(name, &mut metadata);

        }

        return metadata;

    }

    // ===== GENERAL SETUP =====

    let dt_api_token = env::var("DT_API_TOKEN").unwrap(); // TODO: change

    let dt_api_url = env::var("DT_API_URL").unwrap();

   

    let mut map = HashMap::new();

    map.insert("Authorization".to_string(), format!("Api-Token {}", dt_api_token));

    let resource = Resource::builder()

    .with_service_name("rust-manual-quickstart")

    .with_attributes(read_dt_metadata())

    .build();

    use reqwest::blocking::Client; // Workaround for currently not supported reqwest library

    let client = Client::new();

    // ===== TRACING SETUP =====

    global::set_text_map_propagator(TraceContextPropagator::new());

    let tracer_exporter = SpanExporter::builder()

        .with_http()

        .with_http_client(client.clone())

        .with_headers(map.clone())

        .with_protocol(Protocol::HttpBinary)

        .with_endpoint(dt_api_url.clone() + "/v1/traces")

        .build()

        .unwrap();

    let tracer_provider = SdkTracerProvider::builder()

        .with_resource(resource.clone())

        .with_batch_exporter(tracer_exporter)

        .build();

    global::set_tracer_provider(tracer_provider.clone());

    // ===== METRICS SETUP ======

    let metrics_exporter = MetricExporter::builder()

        .with_http()

        .with_http_client(client.clone())

        .with_headers(map.clone())

        .with_endpoint(dt_api_url.clone() + "/v1/metrics")

        .with_protocol(opentelemetry_otlp::Protocol::HttpBinary)

        .build()

        .unwrap();

    let meter_provider = SdkMeterProvider::builder()

        .with_reader(PeriodicReader::builder(metrics_exporter).build())

        .with_resource(resource.clone())

        .build();

    global::set_meter_provider(meter_provider);

    // ===== LOGS SETUP ======

    let logger_exporter = LogExporter::builder()

        .with_http()

        .with_http_client(client.clone())

        .with_headers(map.clone())

        .with_endpoint(dt_api_url.clone() + "/v1/logs")

        .with_protocol(opentelemetry_otlp::Protocol::HttpBinary)

        .build()

        .unwrap();

    let logger_provider = SdkLoggerProvider::builder()

        .with_batch_exporter(logger_exporter)

        .with_resource(resource.clone())

        .build();     

    let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);

    log::set_boxed_logger(Box::new(otel_log_appender)).unwrap();

    log::set_max_level(Level::Debug.to_level_filter());

}

Расширение данных Ключ-АСТРОМ

Операции чтения файлов, анализирующие файлы dt_metadata в примере кода, пытаются прочитать файлы данных ЕдиногоАгента, чтобы расширить запрос OTLP и гарантировать, что вся соответствующая информация о топологии доступна в Ключ-АСТРОМ.

4. Убедитесь, что переменные среды DT_API_URL и DT_API_TOKEN правильно настроены для URL-адреса Ключ-АСТРОМ и токена доступа.

5. Вызовите функцию init_opentelemetry() как можно раньше в стартовом коде.

Инструментирование своего приложения

Добавление трассировки

1. Для начала нам нужно получить объект трассировки.

let tracer = global::tracer("my-tracer");

2. С помощью tracer теперь мы можем начинать новые интервалы.

let mut _span = tracer

    .span_builder("Call to /myendpoint")

    .with_kind(SpanKind::Internal)

    .start(&tracer);

_span.set_attribute(KeyValue::new("http.method", "GET"));

_span.set_attribute(KeyValue::new("net.protocol.version", "1.1"));

// TODO: Your code goes here

_span.end();

В приведенном выше коде мы:

  • Создали новый диапазон и назвали его «Call to /myendpoint».
  • Добавили два атрибута, следуя семантическому соглашению об именовании, специфичные для действия этого диапазона: информацию о методе HTTP и версии.
  • Добавили TODO вместо конечной бизнес-логики
  • Вызовали метод span end() для завершения span.

Сбор метрик

1. Для начала нам нужно получить объект метрики.

let meter = global::meter("request_counter");

2. С помощью meter мы теперь можем создавать отдельные инструменты, например, метрику.

let updown_counter = meter.i64_up_down_counter("request_counter").build();

3. Теперь мы можем вызвать метод add() для записи новых значений updown_counter с помощью счетчика.

updown_counter.add(1,&[],);

Подключение логов

В init_opentelemetry(), мы ранее инициализировали контейнер логов с его мостом логов OpenTelemetry и теперь можем вызывать любой из его макросов логов для ведения логов непосредственно в Ключ-АСТРОМ.

error!("logging an error");

debug!("logging a debug message");

Обеспечение распространения контекста (необязательно)

Распространение контекста особенно важно, когда задействованы сетевые вызовы (например, REST).

Извлечение контекста при получении запроса

Чтобы продолжить существующую трассировку HTTP-запроса, необходимо сначала извлечь контекст. Для этого мы объявляем функцию extract_context_from_request(), которая принимает объект входящего запроса, извлекает переданный контекст с помощью метода пропагатора extract() и возвращает соответствующий объект контекста.

// Utility function to extract the context from the incoming request headers

fn extract_context_from_request(req: &Request<Incoming>) -> Context {

    global::get_text_map_propagator(|propagator| {

        propagator.extract(&HeaderExtractor(req.headers()))

    })

}

Затем мы можем использовать extract_context_from_request() в нашем обработчике запросов для получения этого контекста и передать его как родительский в наш собственный, новый серверный диапазон с помощью start_with_context().

async fn router(req: Request<Incoming>) -> Result<Response<BoxBody<Bytes, hyper::Error>>, Infallible> {

    // Extract the context from the incoming request headers

    let parent_cx = extract_context_from_request(&req);

    let response = {

        // Create a span parenting the remote client span.

        let tracer = global::tracer("example/server");

        let mut span = tracer

            .span_builder("router")

            .with_kind(SpanKind::Server)

            .start_with_context(&tracer, &parent_cx);

        // Adding custom attributes

        span.set_attribute(KeyValue::new("my-server-key-1", "my-server-value-1"));

    };

    // TODO Handle the HTTP request

}

Внедрение контекста при отправке запросов

Чтобы передать текущий контекст другому HTTP-сервису, мы добавляем информацию о контексте в заголовки HTTP-запроса. В следующем примере объявляется функция send_request(), которая принимает URL-адрес запроса, его содержимое и отправляет запрос с помощью hyper.

После инициализации объекта гиперзапроса мы вызываем метод get_text_map_propagator() для получения глобального объекта propagator, а затем используем функцию inject_context() для добавления текущей контекстной информации к запросу.

async fn send_request(url: &str, body_content: &str, span_name: &str) -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {

    let client = Client::builder(TokioExecutor::new()).build_http();

    let tracer = global::tracer("example/client");

    let span = tracer

        .span_builder(String::from(span_name))

        .with_kind(SpanKind::Client)

        .start(&tracer);

    let cx = Context::current_with_span(span);

    let mut req = hyper::Request::builder().uri(url);

    global::get_text_map_propagator(|propagator| {

        propagator.inject_context(&cx, &mut HeaderInjector(req.headers_mut().unwrap()))

    });

    let res = client

        .request(req.body(Full::new(Bytes::from(body_content.to_string())))?)

        .await?;

    cx.span().add_event(

        "Got response!",

        vec![KeyValue::new("status", res.status().to_string())],

    );

    Ok(())

}

Настройте сбор данных в соответствии с требованиями конфиденциальности (необязательно)

Хотя Ключ-АСТРОМ автоматически собирает все атрибуты OpenTelemetry, в веб-интерфейсе Ключ-АСТРОМ сохраняются и отображаются только значения атрибутов, указанные в списке разрешенных. Это предотвращает случайное сохранение персональных данных, позволяя вам соблюдать требования к конфиденциальности и контролировать объем хранимых данных мониторинга.

Чтобы просматривать пользовательские атрибуты, необходимо сначала разрешить их использование в веб-интерфейсе Ключ-АСТРОМ.

Проверка загрузки данных в Ключ-АСТРОМ

После завершения инструментирования вашего приложения выполните несколько тестовых действий для создания и отправки демонстрационных трассировок, метрик и логов, а также проверьте, что они были правильно загружены в Ключ-АСТРОМ.

Чтобы сделать это для трассировок, перейдите в раздел Трассировки и выберите вкладку Распределенные трассировки. Если вы используете ЕдиныйАгент, выберите PurePaths .

Для просмотра метрик и логов перейдите в раздел Метрики или Логов или Логи и события.