У меня есть отслеживаемый подписчик, например:
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 }
Я надеюсь, что диапазоны будут автоматически генерировать идентификаторы, автоматически ссылаться на идентификаторы родительских диапазонов, если они есть, а журналы будут включать идентификатор текущего диапазона.
Просто убедитесь, что вы знаете, о чем спрашиваете. Журналы и диапазоны уже скрыто связаны, но не отображаются вашим подписчиком. Я могу написать ответ, который сделает то, что вы хотите, но, скорее всего, потребует специального средства форматирования событий.
@kmdreko извините за задержку, да, я осознаю полезность, удобочитаемую для человека. Я попробовал использовать .json()
, как вы можете видеть, закомментировав, но они там не появились. Таким образом, я не верил, что идентификаторы где-либо генерируются, но, возможно, .json() не печатает их, и если бы я добавил, скажем, подписчика отеля, как вы упомянули, он будет использовать их по мере необходимости при доставке, как вы упомянули. Я просто хотел увидеть их, чтобы убедиться, что это нечто большее, чем просто бревна!
«Поэтому я не верил, что идентификаторы где-либо генерируются» — вы можете видеть, что Id
требуются при реализации Подписчика. «если бы я добавил, скажем, такого абонента отеля, как вы упомянули, он бы использовал их по мере необходимости» — да.
@kmdreko можно ли хотя бы легко вывести идентификатор родительского диапазона? Тогда легко перейти от журнала -> трассировка при переходе, например, от локи к темпу.
Идентификаторы спанов и их родителей генерируются, но ваш подписчик их просто не показывает.
Идентификаторы интервалов не особенно полезны в удобочитаемых журналах. Любые промежутки естественным образом ограничены для вашего варианта использования, и в этом случае они вам не нужны; или они часто чередуются, и в этом случае связывать их на глаз утомительно и обременительно. Чаще всего у вас есть определенный инструмент для организованного просмотра трассировок, и, следовательно, ваш выходной формат привязан к нему (например, профилировщик, такой как 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 Subscriber
отвечает за генерацию идентификаторов. В этом контексте да, реализация fmt отслеживающего подписчика является чисто локальной для приложения, и поэтому диапазоны имеют смысл только в рамках текущей запущенной программы (конфликты с другими программами или другие вызовы вообще не учитываются). Но опять же, если вы используете подписчика, предназначенного для работы с несколькими программами/вызовами, например opentelemetry, этот идентификатор может быть более случайным (а может и нет, поскольку локальные идентификаторы могут быть сопоставлены с глобальными идентификаторами при довольно тривиальном экспорте).
Ах, ок, круто, спасибо за помощь!!
Идентификаторы интервалов не особенно полезны в удобочитаемых журналах. Либо промежутки естественно разделены, и в этом случае они вам не нужны; или они часто чередуются, и в этом случае связывать их на глаз утомительно и обременительно. Чаще всего у вас есть определенный инструмент для организованного просмотра трассировок, и, следовательно, ваш выходной формат привязан к нему (например, профилировщик, такой как Tracy или Coz, или какой-либо инструмент открытой телеметрии).