mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-17 07:41:23 +01:00
Add admin commands to get and reset tracing filters
This commit is contained in:
parent
5eab758bd2
commit
99924e5779
4 changed files with 121 additions and 19 deletions
|
|
@ -261,9 +261,10 @@ This will be the first release of Grapevine since it was forked from Conduit
|
||||||
([!46](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/46))
|
([!46](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/46))
|
||||||
10. Recognize the `!admin` prefix to invoke admin commands.
|
10. Recognize the `!admin` prefix to invoke admin commands.
|
||||||
([!45](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/45))
|
([!45](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/45))
|
||||||
11. Add the `set-tracing-filter` admin command to change log/metrics/flame
|
11. Add the `tracing-filter` admin command to view and change log/metrics/flame
|
||||||
filters dynamically at runtime.
|
filters dynamically at runtime.
|
||||||
([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49))
|
([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49),
|
||||||
|
[!164](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/164))
|
||||||
12. Add more configuration options.
|
12. Add more configuration options.
|
||||||
([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49))
|
([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49))
|
||||||
* `observability.traces.filter`: The `tracing` filter to use for
|
* `observability.traces.filter`: The `tracing` filter to use for
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use tracing_subscriber::EnvFilter;
|
||||||
/// Use [`FromStr`] or [`Deserialize`] to construct this type, then [`From`] or
|
/// Use [`FromStr`] or [`Deserialize`] to construct this type, then [`From`] or
|
||||||
/// [`Into`] to convert it into an [`EnvFilter`] when needed.
|
/// [`Into`] to convert it into an [`EnvFilter`] when needed.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct EnvFilterClone(String);
|
pub(crate) struct EnvFilterClone(pub(crate) String);
|
||||||
|
|
||||||
impl FromStr for EnvFilterClone {
|
impl FromStr for EnvFilterClone {
|
||||||
type Err = <EnvFilter as FromStr>::Err;
|
type Err = <EnvFilter as FromStr>::Err;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use opentelemetry_sdk::{
|
||||||
Resource,
|
Resource,
|
||||||
};
|
};
|
||||||
use strum::{AsRefStr, IntoStaticStr};
|
use strum::{AsRefStr, IntoStaticStr};
|
||||||
|
use thiserror::Error;
|
||||||
use tokio::time::Instant;
|
use tokio::time::Instant;
|
||||||
use tracing::{subscriber::SetGlobalDefaultError, Span};
|
use tracing::{subscriber::SetGlobalDefaultError, Span};
|
||||||
use tracing_flame::{FlameLayer, FlushGuard};
|
use tracing_flame::{FlameLayer, FlushGuard};
|
||||||
|
|
@ -78,9 +79,67 @@ impl<L, S> ReloadHandle<L> for reload::Handle<L, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type-erased [reload handle][reload::Handle] for an [`EnvFilter`].
|
/// Error returned from [`FilterReloadHandle::set_filter()`]
|
||||||
pub(crate) type FilterReloadHandle =
|
#[allow(clippy::missing_docs_in_private_items)]
|
||||||
Box<dyn ReloadHandle<EnvFilter> + Send + Sync>;
|
#[derive(Debug, Error)]
|
||||||
|
pub(crate) enum SetFilterError {
|
||||||
|
#[error("invalid filter string")]
|
||||||
|
InvalidFilter(#[from] tracing_subscriber::filter::ParseError),
|
||||||
|
#[error("failed to reload filter layer")]
|
||||||
|
Reload(#[from] reload::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper around a tracing filter [reload handle][reload::Handle] that
|
||||||
|
/// remembers the filter string that was last set.
|
||||||
|
pub(crate) struct FilterReloadHandle {
|
||||||
|
/// The actual [`reload::Handle`] that can be used to modify the filter
|
||||||
|
/// [`Layer`]
|
||||||
|
inner: Box<dyn ReloadHandle<EnvFilter> + Send + Sync>,
|
||||||
|
/// Filter string that was last applied to `inner`
|
||||||
|
current_filter: String,
|
||||||
|
/// Filter string that was initially loaded from the configuration
|
||||||
|
initial_filter: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterReloadHandle {
|
||||||
|
/// Creates a new [`FilterReloadHandle`] from a filter string, returning the
|
||||||
|
/// filter layer itself and the handle that can be used to modify it.
|
||||||
|
pub(crate) fn new<S: tracing::Subscriber>(
|
||||||
|
filter: EnvFilterClone,
|
||||||
|
) -> (impl tracing_subscriber::layer::Filter<S>, Self) {
|
||||||
|
let (layer, handle) = reload::Layer::new(EnvFilter::from(&filter));
|
||||||
|
let handle = Self {
|
||||||
|
inner: Box::new(handle),
|
||||||
|
current_filter: filter.0.clone(),
|
||||||
|
initial_filter: filter.0,
|
||||||
|
};
|
||||||
|
(layer, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the filter string for the linked filter layer. Can fail if the
|
||||||
|
/// filter string is invalid or when the link to the layer has been
|
||||||
|
/// broken.
|
||||||
|
pub(crate) fn set_filter(
|
||||||
|
&mut self,
|
||||||
|
filter: String,
|
||||||
|
) -> Result<(), SetFilterError> {
|
||||||
|
self.inner.reload(filter.parse()?)?;
|
||||||
|
self.current_filter = filter;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the filter string that the underlying filter layer is currently
|
||||||
|
/// configured for.
|
||||||
|
pub(crate) fn get_filter(&self) -> &str {
|
||||||
|
&self.current_filter
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the filter string that the underlying filter layer was
|
||||||
|
/// initialized with.
|
||||||
|
pub(crate) fn get_initial_filter(&self) -> &str {
|
||||||
|
&self.initial_filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Collection of [`FilterReloadHandle`]s, allowing the filters for tracing
|
/// Collection of [`FilterReloadHandle`]s, allowing the filters for tracing
|
||||||
/// backends to be changed dynamically. Handles may be [`None`] if the backend
|
/// backends to be changed dynamically. Handles may be [`None`] if the backend
|
||||||
|
|
@ -153,9 +212,9 @@ where
|
||||||
return Ok((None, None, None));
|
return Ok((None, None, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (filter, handle) = reload::Layer::new(EnvFilter::from(filter));
|
let (filter, handle) = FilterReloadHandle::new(filter.clone());
|
||||||
let (layer, data) = init()?;
|
let (layer, data) = init()?;
|
||||||
Ok((Some(layer.with_filter(filter)), Some(Box::new(handle)), Some(data)))
|
Ok((Some(layer.with_filter(filter)), Some(handle), Some(data)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize observability
|
/// Initialize observability
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::BTreeMap, fmt::Write, sync::Arc, time::Instant};
|
use std::{collections::BTreeMap, fmt::Write, sync::Arc, time::Instant};
|
||||||
|
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, Subcommand, ValueEnum};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::appservice::Registration,
|
api::appservice::Registration,
|
||||||
|
|
@ -205,10 +205,41 @@ enum AdminCommand {
|
||||||
VerifyJson,
|
VerifyJson,
|
||||||
|
|
||||||
/// Dynamically change a tracing backend's filter string
|
/// Dynamically change a tracing backend's filter string
|
||||||
SetTracingFilter {
|
TracingFilter {
|
||||||
|
#[command(subcommand)]
|
||||||
|
cmd: TracingFilterCommand,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
enum TracingFilterCommand {
|
||||||
|
Get {
|
||||||
|
backend: TracingBackend,
|
||||||
|
},
|
||||||
|
Set {
|
||||||
backend: TracingBackend,
|
backend: TracingBackend,
|
||||||
filter: String,
|
filter: String,
|
||||||
},
|
},
|
||||||
|
Reset {
|
||||||
|
backend: TracingBackend,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TracingFilterCommand {
|
||||||
|
fn backend(&self) -> &TracingBackend {
|
||||||
|
match self {
|
||||||
|
TracingFilterCommand::Get {
|
||||||
|
backend,
|
||||||
|
}
|
||||||
|
| TracingFilterCommand::Set {
|
||||||
|
backend,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| TracingFilterCommand::Reset {
|
||||||
|
backend,
|
||||||
|
} => backend,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -1167,9 +1198,8 @@ impl Service {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::SetTracingFilter {
|
AdminCommand::TracingFilter {
|
||||||
backend,
|
cmd,
|
||||||
filter,
|
|
||||||
} => {
|
} => {
|
||||||
let Some(handles) = &services().globals.reload_handles else {
|
let Some(handles) = &services().globals.reload_handles else {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
|
@ -1177,7 +1207,7 @@ impl Service {
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let mut handles = handles.write().await;
|
let mut handles = handles.write().await;
|
||||||
let handle = match backend {
|
let handle = match cmd.backend() {
|
||||||
TracingBackend::Log => &mut handles.log,
|
TracingBackend::Log => &mut handles.log,
|
||||||
TracingBackend::Flame => &mut handles.flame,
|
TracingBackend::Flame => &mut handles.flame,
|
||||||
TracingBackend::Traces => &mut handles.traces,
|
TracingBackend::Traces => &mut handles.traces,
|
||||||
|
|
@ -1187,15 +1217,27 @@ impl Service {
|
||||||
"Backend is disabled",
|
"Backend is disabled",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let filter = match filter.parse() {
|
|
||||||
Ok(filter) => filter,
|
let filter = match cmd {
|
||||||
Err(e) => {
|
TracingFilterCommand::Set {
|
||||||
|
filter,
|
||||||
|
..
|
||||||
|
} => filter,
|
||||||
|
TracingFilterCommand::Reset {
|
||||||
|
..
|
||||||
|
} => handle.get_initial_filter().to_owned(),
|
||||||
|
TracingFilterCommand::Get {
|
||||||
|
..
|
||||||
|
} => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
format!("Invalid filter string: {e}"),
|
format!(
|
||||||
|
"Current filter string: {}",
|
||||||
|
handle.get_filter()
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Err(e) = handle.reload(filter) {
|
if let Err(e) = handle.set_filter(filter) {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to reload filter: {e}"
|
"Failed to reload filter: {e}"
|
||||||
)));
|
)));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue