対象
注意
tracingを初めて触る方は以下の記事を先に参照されるとわかりやすいかと思います。
動機
info!等ログ出力を、それが含まれるspanの情報をもとにフィルタしたい。
以下に例を挙げます。
#[instrument(name = "hoge")]
fn hoge_fn() {
info!("say hoge!");
}
#[instrument(name = "fuga")]
fn fuga_fn() {
info!("say fuga!");
}
fn main() {
hoge_fn();
fuga_fn();
// filter span.name == "hoge"
// output: INFO: say hoge!
}
tracingはドキュメントが比較的豊富ではあるのですが、命名がややこしかったり概念が複雑で少しハマってしまいました。
方法
用途によって複数の方法が考えられるのですが、構造は似ているのでまずは一番シンプルなケースを示します。用途別に何を変更すべきかは後述する情報からわかると思います。
dynamic_filter_fn関数を使用してフィルタを作成します。
use tracing_subscriber::{
Registry,
filter::{DynFilterFn, dynamic_filter_fn},
layer::*,
prelude::*,
util::SubscriberInitExt,
};
fn main() {
tracing_subscriber::fmt()
.finish()
.with(
tracing_subscriber::fmt::layer().with_filter(dynamic_filter_fn(|meta, cx| {
if meta.is_span() {
return true;
}
cx.current_span()
.metadata()
.map_or(true, |t| t.name() == "hoge")
})),
)
.init();
hoge_fn();
fuga_fn();
}
INFO: say hoge!
Subscriber, Layer, Filterの違い
Subscriber
Subscriberはtracingが求めるすべての機能を持ちます。具体的には、生成されたspanの管理やinfo!等によるevent発生時のデータの収集, etc..です(詳細)。そのため、Subscriberを定義することで、フィルタリングや出力など、tracingで可能なことはすべて行うことができます。
しかし、Subscriberは多機能すぎるがゆえに、別々の機能を持つSubscriber同士を合成することが難しく、汎用性や再利用性の観点から望ましくありません。(こちらで、Subscriberは生成されたSpanに対して一意に識別可能なIDを振る必要があるため、合成することは難しいと説明されています。)
そのため、Subscriberの機能を部分的に抜粋したLayerが用意されています。
Layer
前述の経緯よりLayerは合成可能であるため、機能を実装する際は基本的にLayerを使用することになります。個々のLayerの環境は独立しており、Layerで行った操作が後続のLayerに影響することはありません。
Filter
FilterはLayerと違い、後続のLayerに流れるトレースデータをフィルタリングすることができます。これが有用なケースの例はこちらに示されています。複数のレイヤーで同じようなフィルタリングを行う場合に、Filterならまとめてフィルタリングできるということですね。
tracing_subscriber::registry()とtracing_subscriber::fmt()
tracingの使用例を見ると、似たような見た目の上記の関数が使用されており混乱するかと思います。どちらも生成されるのはSubscriberで、registry()のほうはspanを管理するだけでログ出力は行いません。fmt()のほうはspanも管理しつつログ出力も行います。そのためロギングしたいだけなら基本的にはfmt()を使用していれば問題ないと思われます。
registry()を使用しても、ログ出力を行うレイヤー tracing_subscriber::fmt::layer() をアタッチすることでロギングすることができます
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer())
.init()
参考