graphql_starter/tracing/
common.rs1use std::{
2 mem,
3 sync::{Arc, Mutex},
4};
5
6use anyhow::Context;
7use tracing::Subscriber;
8use tracing_error::ErrorLayer;
9use tracing_subscriber::{filter::Targets, prelude::*, registry::LookupSpan, reload, Layer, Registry};
10
11use super::MakeWriterInterceptor;
12use crate::error::{GenericErrorCode, MapToErr, Result};
13
14trait FnUpdateTracing: FnMut(&str) -> Result<()> + Send + Sync {}
15impl<T> FnUpdateTracing for T where T: FnMut(&str) -> Result<()> + Send + Sync {}
16
17#[derive(Clone)]
18pub struct TracingContext {
22 inner: Arc<Mutex<InnerCtx>>,
23}
24
25struct InnerCtx {
27 default_filter: String,
28 active_filter: String,
29 fn_update_filter: Box<dyn FnUpdateTracing>,
30}
31
32impl TracingContext {
33 fn new(ctx: InnerCtx) -> Self {
34 Self {
35 inner: Arc::new(Mutex::new(ctx)),
36 }
37 }
38
39 pub fn default_filter(&self) -> String {
41 let ctx = self.inner.lock().expect("poisoned lock");
42 ctx.default_filter.clone()
43 }
44
45 pub fn active_filter(&self) -> String {
47 let ctx = self.inner.lock().expect("poisoned lock");
48 ctx.active_filter.clone()
49 }
50
51 pub fn update_active_filter(&mut self, new_filter: impl Into<String>) -> Result<String> {
53 let new_filter = new_filter.into();
54 let mut ctx = self.inner.lock().expect("poisoned lock");
55
56 ctx.fn_update_filter.as_mut()(&new_filter)?;
57 Ok(mem::replace(&mut ctx.active_filter, new_filter))
58 }
59
60 pub fn update_default_filter(&mut self, new_default_filter: impl Into<String>) -> String {
62 let new_default_filter = new_default_filter.into();
63 let mut ctx = self.inner.lock().expect("poisoned lock");
64
65 mem::replace(&mut ctx.default_filter, new_default_filter)
66 }
67}
68
69pub fn initialize_tracing<L>(layer: L)
73where
74 L: Layer<Registry> + Send + Sync,
75{
76 tracing_subscriber::registry().with(layer).init();
78}
79
80pub fn tracing_layer<T>(filter: impl AsRef<str>) -> anyhow::Result<impl Layer<T>>
82where
83 T: Subscriber,
84 for<'span> T: LookupSpan<'span>,
85{
86 let layer = tracing_subscriber::fmt::layer().compact().with_filter(
88 filter
89 .as_ref()
90 .parse::<Targets>()
91 .context("Couldn't parse tracing filter")?,
92 );
93
94 Ok(ErrorLayer::default().and_then(layer))
96}
97
98pub fn dynamic_tracing_layer<T>(filter: impl Into<String>) -> anyhow::Result<(impl Layer<T>, TracingContext)>
100where
101 T: Subscriber,
102 for<'span> T: LookupSpan<'span>,
103{
104 let filter = filter.into();
105
106 let (layer, handler) = reload::Layer::new(
108 tracing_subscriber::fmt::layer()
109 .compact()
110 .with_filter(filter.parse::<Targets>().context("Couldn't parse tracing filter")?),
111 );
112
113 let context = TracingContext::new(InnerCtx {
115 default_filter: filter.clone(),
116 active_filter: filter,
117 fn_update_filter: Box::new(move |new_filter| {
118 let new_filter = new_filter
119 .parse::<Targets>()
120 .map_to_err_with(GenericErrorCode::BadRequest, "Couldn't parse the filter")?;
121 handler
122 .modify(|layer| *layer.filter_mut() = new_filter)
123 .map_to_internal_err("Couldn't update tracing filter")
124 }),
125 });
126
127 Ok((ErrorLayer::default().and_then(layer), context))
129}
130
131pub fn intercepted_tracing_layer<T>(
134 filter: impl AsRef<str>,
135 accumulate: usize,
136 stream_buffer: usize,
137) -> anyhow::Result<(impl Layer<T>, MakeWriterInterceptor)>
138where
139 T: Subscriber,
140 for<'span> T: LookupSpan<'span>,
141{
142 let make_writer = MakeWriterInterceptor::new(accumulate, stream_buffer);
144
145 let layer = tracing_subscriber::fmt::layer()
147 .compact()
148 .with_writer(make_writer.clone())
149 .with_filter(
150 filter
151 .as_ref()
152 .parse::<Targets>()
153 .context("Couldn't parse tracing filter")?,
154 );
155
156 Ok((ErrorLayer::default().and_then(layer), make_writer))
158}
159
160pub fn dynamic_intercepted_tracing_layer<T>(
163 filter: impl Into<String>,
164 accumulate: usize,
165 stream_buffer: usize,
166) -> anyhow::Result<(impl Layer<T>, TracingContext, MakeWriterInterceptor)>
167where
168 T: Subscriber,
169 for<'span> T: LookupSpan<'span>,
170{
171 let filter = filter.into();
172
173 let make_writer = MakeWriterInterceptor::new(accumulate, stream_buffer);
175
176 let (layer, handler) = reload::Layer::new(
178 tracing_subscriber::fmt::layer()
179 .compact()
180 .with_writer(make_writer.clone())
181 .with_filter(filter.parse::<Targets>().context("Couldn't parse tracing filter")?),
182 );
183
184 let context = TracingContext::new(InnerCtx {
186 default_filter: filter.clone(),
187 active_filter: filter,
188 fn_update_filter: Box::new(move |new_filter| {
189 let new_filter = new_filter
190 .parse::<Targets>()
191 .map_to_err_with(GenericErrorCode::BadRequest, "Couldn't parse the filter")?;
192 handler
193 .modify(|layer| *layer.filter_mut() = new_filter)
194 .map_to_internal_err("Couldn't update tracing filter")
195 }),
196 });
197
198 Ok((ErrorLayer::default().and_then(layer), context, make_writer))
200}