Skip to content
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

Add support for changing tracing directives at runtime #3565

Merged
merged 14 commits into from
Sep 12, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- Add support for changing `tracing` directives at runtime.
Please see the [corresponding page in the Hermes guide][tracing-guide] for more information.
([\#3564](https://github.com/informalsystems/hermes/issues/3564))

[tracing-guide]: https://hermes.informal.systems/advanced/troubleshooting/log-level.html
9 changes: 9 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ port = 3001
# [1000, 3900, 6800, 9700, 12600, 15500, 18400, 21300, 24200, 27100, 30000]
# latency_confirmed = { start = 1000, end = 30000, buckets = 10 }

# The tracing server section defines parameters for Hermes' server allowing updates to the tracing directives.
#
# https://hermes.informal.systems/advanced/troubleshooting/log-level.html#overriding-the-tracing-filter-during-runtime
[tracing_server]
# Whether or not to enable the tracing server. Default: false
enabled = false

# Specify the port over which the built-in TCP server will serve the directives. Default: 5555
port = 5555

# A chains section includes parameters related to a chain and the full node to which
# the relayer can send transactions and queries.
Expand Down
1 change: 0 additions & 1 deletion crates/relayer-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ tokio = { version = "1.0", features = ["full"] }
tracing = "0.1.36"
tracing-subscriber = { version = "0.3.14", features = ["fmt", "env-filter", "json"]}
time = "0.3"

[dependencies.tendermint]
version = "0.33.0"
features = ["secp256k1"]
Expand Down
40 changes: 37 additions & 3 deletions crates/relayer-cli/src/application.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Definition of the application, based on the Abscissa framework

use std::path::PathBuf;
use std::{path::PathBuf, thread};

use abscissa_core::{
application::{self, AppCell},
Expand All @@ -10,12 +10,17 @@ use abscissa_core::{
terminal::ColorChoice,
Application, Configurable, FrameworkError, FrameworkErrorKind, StandardPaths,
};
use ibc_relayer::{config::Config, util::debug_section::DebugSection};
use ibc_relayer::{
config::{Config, TracingServerConfig},
util::debug_section::DebugSection,
};

use crate::{
commands::CliCmd,
components::{JsonTracing, PrettyTracing},
config::validate_config,
entry::EntryPoint,
tracing_handle::{spawn_reload_handler, ReloadHandle},
};

/// Application state
Expand Down Expand Up @@ -195,13 +200,24 @@ impl Application for CliApp {
let enable_json = self.debug_enabled(DebugSection::ProfilingJson);
ibc_relayer::util::profiling::enable(enable_console, enable_json);

let is_start_cmd = command
.command
.as_ref()
.map_or(false, |cmd| matches!(cmd, CliCmd::Start(_)));

if command.json {
// Enable JSON by using the crate-level `Tracing`
let tracing = JsonTracing::new(config.global, &self.debug_sections)?;
Ok(vec![Box::new(terminal), Box::new(tracing)])
} else {
// Use abscissa's tracing, which pretty-prints to the terminal obeying log levels
let tracing = PrettyTracing::new(config.global, &self.debug_sections)?;
let (tracing, reload_handle) =
PrettyTracing::new_with_reload_handle(config.global, &self.debug_sections)?;

if is_start_cmd {
spawn_tracing_reload_server(reload_handle, config.tracing_server.clone());
}

Ok(vec![Box::new(terminal), Box::new(tracing)])
}
}
Expand All @@ -212,3 +228,21 @@ impl Application for CliApp {
ColorChoice::Never
}
}

fn spawn_tracing_reload_server<S: 'static>(
reload_handle: ReloadHandle<S>,
config: TracingServerConfig,
) {
thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();

let result = rt.block_on(spawn_reload_handler(reload_handle, config));

if let Err(e) = result {
eprintln!("ERROR: failed to spawn tracing reload handler: {e}");
}
});
}
7 changes: 6 additions & 1 deletion crates/relayer-cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod fee;
mod health;
mod keys;
mod listen;
mod logs;
mod misbehaviour;
mod query;
mod start;
Expand All @@ -18,7 +19,7 @@ mod version;

use self::{
clear::ClearCmds, completions::CompletionsCmd, config::ConfigCmd, create::CreateCmds,
fee::FeeCmd, health::HealthCheckCmd, keys::KeysCmd, listen::ListenCmd,
fee::FeeCmd, health::HealthCheckCmd, keys::KeysCmd, listen::ListenCmd, logs::LogsCmd,
misbehaviour::MisbehaviourCmd, query::QueryCmd, start::StartCmd, tx::TxCmd, update::UpdateCmds,
upgrade::UpgradeCmds, version::VersionCmd,
};
Expand Down Expand Up @@ -85,6 +86,10 @@ pub enum CliCmd {
/// Listen to and display IBC events emitted by a chain
Listen(ListenCmd),

/// Update tracing log directives
#[clap(subcommand)]
Logs(LogsCmd),

/// Listen to client update IBC events and handles misbehaviour
Misbehaviour(MisbehaviourCmd),

Expand Down
20 changes: 20 additions & 0 deletions crates/relayer-cli/src/commands/logs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use abscissa_core::clap::Parser;
use abscissa_core::Command;
use abscissa_core::Runnable;

pub mod log_level;
pub mod raw;
pub mod reset;

/// `logs` subcommand
#[derive(Command, Debug, Parser, Runnable)]
pub enum LogsCmd {
/// Subcommand used to easily update the lowest log level displayed
SetLogLevel(log_level::SetLogLevelCmd),

/// Subcommand used to send a raw filter directive for logs displayed
SetRawFilter(raw::SetRawFilterCmd),

/// Subcommand to restore the log level by using the configuration defined in the config.toml file
Reset(reset::ResetCmd),
}
60 changes: 60 additions & 0 deletions crates/relayer-cli/src/commands/logs/log_level.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use abscissa_core::clap::Parser;
use abscissa_core::Command;
use abscissa_core::Runnable;
use tracing::info;
use tracing::Level;

use crate::prelude::app_config;
use crate::tracing_handle::send_command;

#[derive(Clone, Command, Debug, Parser, PartialEq, Eq)]
pub struct SetLogLevelCmd {
#[clap(
long = "log-level",
required = true,
value_name = "LOG_LEVEL",
help = "The new lowest log level which will be displayed. Possible values are `trace`, `debug`, `info`, `warn` or `error`"
)]
log_level: Level,

#[clap(
long = "log-filter",
help_heading = "FLAGS",
value_name = "LOG_FILTER",
help = "The target of the log level to update, if left empty all the targets will be updated. Example `ibc` or `tendermint_rpc`"
)]
log_filter: Option<String>,
}

impl Runnable for SetLogLevelCmd {
fn run(&self) {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();

rt.block_on(run(&self.log_level, self.log_filter.as_ref()));
}
}

async fn run(log_level: &Level, log_filter: Option<&String>) {
let config = app_config();

let port = config.tracing_server.port;

let str_log = log_level.to_string();

let result = if let Some(log_filter) = log_filter {
let log_cmd = format!("{log_filter}={str_log}");
info!("Setting log level to: {log_cmd}");
send_command(&log_cmd, port).await
} else {
info!("Setting log level to: {str_log}");
send_command(&str_log, port).await
};

match result {
Ok(msg) => info!("{msg}"),
Err(e) => info!("Failed to set log level: {e}"),
}
}
45 changes: 45 additions & 0 deletions crates/relayer-cli/src/commands/logs/raw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use abscissa_core::clap::Parser;
use abscissa_core::Command;
use abscissa_core::Runnable;
use tracing::error;
use tracing::info;

use crate::prelude::app_config;
use crate::tracing_handle::send_command;

// TODO `hermes set-raw-filter`
#[derive(Clone, Command, Debug, Parser, PartialEq, Eq)]
pub struct SetRawFilterCmd {
#[clap(
long = "raw-filter",
required = true,
value_name = "RAW_FILTER",
help = "Raw filter used as new tracing directive. Use with caution"
)]
raw_filter: String,
}

impl Runnable for SetRawFilterCmd {
fn run(&self) {
let config = app_config();

let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();

let port = config.tracing_server.port;

rt.block_on(run(&self.raw_filter, port));
}
}

async fn run(raw_filter: &str, port: u16) {
info!("Setting raw log filter to: {raw_filter}");
let result = send_command(raw_filter, port).await;

match result {
Ok(_) => info!("Successfully set raw filter"),
Err(e) => error!("Failed to set raw filter: {e}"),
}
}
36 changes: 36 additions & 0 deletions crates/relayer-cli/src/commands/logs/reset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use abscissa_core::clap::Parser;
use abscissa_core::Command;
use abscissa_core::Runnable;

use crate::components::default_directive;
use crate::prelude::*;
use crate::tracing_handle::send_command;

#[derive(Clone, Command, Debug, Parser, PartialEq, Eq)]
pub struct ResetCmd {}

impl Runnable for ResetCmd {
fn run(&self) {
let config = app_config();

let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();

let port = config.tracing_server.port;
let directive = default_directive(config.global.log_level);

rt.block_on(run(&directive, port));
}
}

async fn run(directive: &str, port: u16) {
info!("Resetting log filter to: {directive}");
let result = send_command(directive, port).await;

match result {
Ok(_) => info!("Successfully reset log filter"),
Err(e) => error!("Failed to reset log filter: {e}"),
}
}
26 changes: 25 additions & 1 deletion crates/relayer-cli/src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use ibc_relayer::{
};

use crate::config::Error;
use crate::tracing_handle::ReloadHandle;

/// The name of the environment variable through which one can override
/// the tracing filter built in [`build_tracing_filter`].
Expand Down Expand Up @@ -74,6 +75,29 @@ impl PrettyTracing {

Ok(Self)
}

pub fn new_with_reload_handle(
cfg: GlobalConfig,
debug_sections: &[DebugSection],
) -> Result<(Self, ReloadHandle<impl tracing::Subscriber + 'static>), FrameworkError> {
let filter = build_tracing_filter(cfg.log_level, debug_sections)?;

// Construct a tracing subscriber with the supplied filter and enable reloading.
let builder = FmtSubscriber::builder()
.with_target(false)
.with_env_filter(filter)
.with_writer(std::io::stderr)
.with_ansi(enable_ansi())
.with_thread_ids(true)
.with_filter_reloading();

let reload_handle = builder.reload_handle();

let subscriber = builder.finish();
subscriber.init();

Ok((Self, reload_handle))
}
}

/// Check if both stdout and stderr are proper terminal (tty),
Expand All @@ -90,7 +114,7 @@ const TARGET_CRATES: [&str; 2] = ["ibc_relayer", "ibc_relayer_cli"];

/// Build a tracing directive setting the log level for the relayer crates to the
/// given `log_level`.
fn default_directive(log_level: LogLevel) -> String {
pub fn default_directive(log_level: LogLevel) -> String {
use itertools::Itertools;

TARGET_CRATES
Expand Down
1 change: 1 addition & 0 deletions crates/relayer-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub mod config;
pub mod entry;
pub mod error;
pub mod prelude;
pub mod tracing_handle;

/// The path to the default configuration file, relative to the home directory.
pub const DEFAULT_CONFIG_PATH: &str = ".hermes/config.toml";
Loading
Loading