Трассировка ржавчины не генерирует идентификаторы диапазона и не связывается с родительскими элементами

У меня есть отслеживаемый подписчик, например:

let subscriber = tracing_subscriber::registry().with(
        tracing_subscriber::fmt::layer()
            .compact()
            .with_file(true)
            .with_line_number(true)
            .with_span_events(FmtSpan::CLOSE)
            .with_target(false)
            // .json()
            .with_filter(level_filters::LevelFilter::from_level(Level::DEBUG))
    );

    tracing::subscriber::set_global_default(subscriber).unwrap();

Но журналы и диапазоны не генерируют идентификаторы (и, следовательно, не связаны с родительским/текущим диапазоном). Что мне нужно исправить, чтобы диапазоны имели сгенерированные идентификаторы, были связаны с родительскими элементами, а в журналах содержался текущий идентификатор диапазона? Я отслеживаю как #[tracing::instrument(level = "debug", skip(state))] и регистрирую как

debug!(
                "Getting subslice of value for key {} with start = {} end = {}",
                key, start, end
            );

Сгенерированные журналы выглядят так:

2024-06-23T12:37:08.371471Z DEBUG get_or_list_prefix:get_item:root:outer_child: src/routes/get.rs:74: close time.busy=5.38µs time.idle=5.71µs key_prefix=Some("a") params=GetOrListParams { list: None, limit: None, with_vals: None, reverse: None, start: None, end: None } params=GetOrListParams { list: None, limit: None, with_vals: None, reverse: None, start: None, end: None } key = "a"
2024-06-23T12:37:08.371534Z  INFO get_or_list_prefix:get_item:root: src/routes/get.rs:71: close time.busy=86.0µs time.idle=3.67µs key_prefix=Some("a") params=GetOrListParams { list: None, limit: None, with_vals: None, reverse: None, start: None, end: None } params=GetOrListParams { list: None, limit: None, with_vals: None, reverse: None, start: None, end: None } key = "a"
2024-06-23T12:37:08.371606Z DEBUG get_or_list_prefix:get_item: src/routes/get.rs:65: close time.busy=163µs time.idle=7.37µs key_prefix=Some("a") params=GetOrListParams { list: None, limit: None, with_vals: None, reverse: None, start: None, end: None } params=GetOrListParams { list: None, limit: None, with_vals: None, reverse: None, start: None, end: None } key = "a"
2024-06-23T12:37:08.371637Z DEBUG get_or_list_prefix: src/routes/get.rs:41: close time.busy=216µs time.idle=16.9µs key_prefix=Some("a") params=GetOrListParams { list: None, limit: None, with_vals: None, reverse: None, start: None, end: None }

Я надеюсь, что диапазоны будут автоматически генерировать идентификаторы, автоматически ссылаться на идентификаторы родительских диапазонов, если они есть, а журналы будут включать идентификатор текущего диапазона.

Идентификаторы интервалов не особенно полезны в удобочитаемых журналах. Либо промежутки естественно разделены, и в этом случае они вам не нужны; или они часто чередуются, и в этом случае связывать их на глаз утомительно и обременительно. Чаще всего у вас есть определенный инструмент для организованного просмотра трассировок, и, следовательно, ваш выходной формат привязан к нему (например, профилировщик, такой как Tracy или Coz, или какой-либо инструмент открытой телеметрии).

kmdreko 23.06.2024 16:57

Просто убедитесь, что вы знаете, о чем спрашиваете. Журналы и диапазоны уже скрыто связаны, но не отображаются вашим подписчиком. Я могу написать ответ, который сделает то, что вы хотите, но, скорее всего, потребует специального средства форматирования событий.

kmdreko 23.06.2024 16:57

@kmdreko извините за задержку, да, я осознаю полезность, удобочитаемую для человека. Я попробовал использовать .json(), как вы можете видеть, закомментировав, но они там не появились. Таким образом, я не верил, что идентификаторы где-либо генерируются, но, возможно, .json() не печатает их, и если бы я добавил, скажем, подписчика отеля, как вы упомянули, он будет использовать их по мере необходимости при доставке, как вы упомянули. Я просто хотел увидеть их, чтобы убедиться, что это нечто большее, чем просто бревна!

danthegoodman 23.06.2024 20:03

«Поэтому я не верил, что идентификаторы где-либо генерируются» — вы можете видеть, что Id требуются при реализации Подписчика. «если бы я добавил, скажем, такого абонента отеля, как вы упомянули, он бы использовал их по мере необходимости» — да.

kmdreko 23.06.2024 20:23

@kmdreko можно ли хотя бы легко вывести идентификатор родительского диапазона? Тогда легко перейти от журнала -> трассировка при переходе, например, от локи к темпу.

danthegoodman 23.06.2024 21:26
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
5
102
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Идентификаторы спанов и их родителей генерируются, но ваш подписчик их просто не показывает.

Идентификаторы интервалов не особенно полезны в удобочитаемых журналах. Любые промежутки естественным образом ограничены для вашего варианта использования, и в этом случае они вам не нужны; или они часто чередуются, и в этом случае связывать их на глаз утомительно и обременительно. Чаще всего у вас есть определенный инструмент для организованного просмотра трассировок, и, следовательно, ваш выходной формат привязан к нему (например, профилировщик, такой как Tracy или Coz, или какой-либо инструмент открытой телеметрии).

Но вот демонстрация того, как вы можете записать идентификатор диапазона в свое сообщение, создав собственную реализацию FormatEvent для использования с существующим подписчиком трассировки. Это адаптировано из примера здесь, так что вы можете посмотреть там или в источнике других реализаций для получения дополнительных идей.

use tracing::{Event, Subscriber};
use tracing_subscriber::fmt::format::{self, FormatEvent, FormatFields};
use tracing_subscriber::fmt::time::{FormatTime, SystemTime};
use tracing_subscriber::fmt::FmtContext;
use tracing_subscriber::registry::LookupSpan;

struct MyFormatter;

impl<S, N> FormatEvent<S, N> for MyFormatter
where
    S: Subscriber + for<'a> LookupSpan<'a>,
    N: for<'a> FormatFields<'a> + 'static,
{
    fn format_event(
        &self,
        ctx: &FmtContext<'_, S, N>,
        mut writer: format::Writer<'_>,
        event: &Event<'_>,
    ) -> std::fmt::Result {
        let metadata = event.metadata();

        write!(&mut writer, "[")?;
        SystemTime.format_time(&mut writer)?;
        write!(&mut writer, "]")?;

        write!(&mut writer, " {} {}", metadata.level(), metadata.target())?;

        if let Some(span) = ctx.parent_span() {
            write!(&mut writer, " span = {:?}", span.id())?;
        }

        write!(&mut writer, ": ")?;
        ctx.field_format().format_fields(writer.by_ref(), event)?;

        writeln!(writer)
    }
}

fn main() {
    tracing_subscriber::fmt().event_format(MyFormatter).init();

    tracing::info!("no span");

    let _span = tracing::info_span!("my_span", answer = 42).entered();
    tracing::info!(
        question = "life, the universe, and everything",
        "hello world"
    );
}
[2024-06-26T16:54:21.851457Z] INFO mycrate: no span
[2024-06-26T16:54:21.853566Z] INFO mycrate span=Id(1): hello world question = "life, the universe, and everything"

Спасибо, а действительно ли идентификатор диапазона будет равен «1»? Кажется, это легко могло бы привести к конфликту.

danthegoodman 26.06.2024 22:40

@danthegoodman Subscriber отвечает за генерацию идентификаторов. В этом контексте да, реализация fmt отслеживающего подписчика является чисто локальной для приложения, и поэтому диапазоны имеют смысл только в рамках текущей запущенной программы (конфликты с другими программами или другие вызовы вообще не учитываются). Но опять же, если вы используете подписчика, предназначенного для работы с несколькими программами/вызовами, например opentelemetry, этот идентификатор может быть более случайным (а может и нет, поскольку локальные идентификаторы могут быть сопоставлены с глобальными идентификаторами при довольно тривиальном экспорте).

kmdreko 26.06.2024 22:48

Ах, ок, круто, спасибо за помощь!!

danthegoodman 26.06.2024 23:29

Другие вопросы по теме