-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(daemon): support dynamically setting the logging level at runtime #105
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look pretty good, thanks for working on this!
I'm rather confused about what valid log levels are, and where log levels vs filters should be used. In some places (e.g. set_filter
and log_filter
), it seems like these terms are used interchangeably, which confuses me a bit more :)
What I'd really like would be to have an enum of valid log levels, ideally passed as a set of options displayed via clap. That, with a bit more clarity about level
vs filter
, and it looks good to me.
It seems that EnvFilter
and reload
, among others, are defined in external crates, so some comments or documentation about what they require would be nice too, I think. Perhaps where the filter is constructed based on level, since it seems arbitrary if one doesn't know that that's just the format which is expected by the other crate.
/// Set the logging level for a running instance of the prompting client daemon | ||
#[derive(Debug, Parser)] | ||
#[clap(about, long_about = None)] | ||
struct Args { | ||
/// The filter to use for setting the logging level | ||
#[clap(short, long, value_name = "FILTER")] | ||
level: String, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there a list of supported options for the log level? Or how does the filtering work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's not a single list, no. The API docs here cover what is possible which really is a lot, which is why I've not restricted this to a fixed list of levels like trace, debug, info etc as an enum. That would prevent us using more granular targeting of logs in situations where we really want it.
async fn set_logging_level( | ||
&self, | ||
level: Request<String>, | ||
) -> Result<Response<SetLoggingLevelResponse>, Status> { | ||
let current = log_filter(&level.into_inner()); | ||
|
||
match self.reload_handle.set_filter(¤t) { | ||
Ok(_) => Ok(Response::new(SetLoggingLevelResponse { current })), | ||
Err(e) => Err(Status::new( | ||
Code::InvalidArgument, | ||
format!("unable to set logging level: {e}"), | ||
)), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here level
is passed into log_filter
as filter
, but I don't think of levels and filters as really the same thing. What can level
be set to, and is it validated anywhere?
pub const SOCKET_ENV_VAR: &str = "PROMPTING_CLIENT_SOCKET"; | ||
pub const DEFAULT_LOG_LEVEL: &str = "info"; | ||
|
||
pub fn log_filter(filter: &str) -> String { | ||
format!("{filter},hyper=error,h2=error") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See here.
I would prefer if there were an enum of support log levels, and if the clap arg were defined so the default is explicit.
@@ -417,6 +423,36 @@ pub mod app_armor_prompting_client { | |||
); | |||
self.inner.unary(req, path, codec).await | |||
} | |||
pub async fn set_logging_level( | |||
&mut self, | |||
request: impl tonic::IntoRequest<::prost::alloc::string::String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the logging level is contained in the request?
let listener = UnixListener::bind(&socket_path).expect("to be able to bind to our socket"); | ||
|
||
(AppArmorPromptingServer::new(service), listener) | ||
} | ||
|
||
pub struct Service<R> | ||
pub trait SetLogLevel: Send + Sync + 'static { | ||
fn set_filter(&self, level: &str) -> crate::Result<()>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here as well, we define set_filter
to take a level
, but
L: From<EnvFilter> + Send + Sync + 'static, | ||
S: 'static, | ||
{ | ||
fn set_filter(&self, filter: &str) -> crate::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then set_filter
is implemented to take a filter
.
let f = filter | ||
.parse::<EnvFilter>() | ||
.map_err(|_| Error::UnableToUpdateLogLevel { | ||
reason: format!("{filter:?} is not a valid logging filter"), | ||
})?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like EnvFilter
is defined in another crate (not in this repo) so I definitely think there should be some documentation (both in the code and in the CLI args) about what valid log levels are.
reason: format!("{filter:?} is not a valid logging filter"), | ||
})?; | ||
|
||
self.reload(f).map_err(|e| Error::UnableToUpdateLogLevel { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reload also isn't defined in this repo either? Seems like some tonic
magic or something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a feature of tracing_subscriber, yeah. All of this is just a way of plumbing a valid logging filter through to this call to reload
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
@olivercalder I see what you mean about being inconsistent around levels vs filters. I'll update things to consistently use the term |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, thanks for the changes! Some filenames still use "log level" terminology, and could be updated to use "filter" instead, but not a huge deal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the filename could be updated to match as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
//! This is a helper command for setting our logging filter within the daemon while it is running. | ||
//! Doing this involves having to send a GRPC message to the daemon which inolves more plumbing | ||
//! than we'd ideally like but it allows us to update the filter without having to restart the | ||
//! daemon. | ||
//! | ||
//! The docs on the command itself cover how to set simple logging levels but the tracing framework | ||
//! we are using actually supports a rich syntax for how these filters are written. As and when we | ||
//! need to use this in anger, the docs can be found here: | ||
//! https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is great!
/// Set the logging level for a running instance of the prompting client daemon. | ||
/// | ||
/// Simple usage of this command involves specifying a level based filter for what logs are written | ||
/// to the system journal by the prompting-client daemon. The supported levels are (in order of | ||
/// least to most verbose): | ||
/// | ||
/// - error | ||
/// - warn | ||
/// - info | ||
/// - debug | ||
/// - trace | ||
/// | ||
/// The default level is "info". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect!
This lets us do the following:
and have the daemon update its logging filter without needing to restart the service. By default we will now log at info level.