diff --git a/Cargo.lock b/Cargo.lock index c68d8bf259..3243264f92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1822,6 +1822,7 @@ dependencies = [ "opentelemetry", "opentelemetry-jaeger", "parking_lot", + "serde", "tracing", "tracing-appender", "tracing-bunyan-formatter", diff --git a/config/datanode.example.toml b/config/datanode.example.toml index 2583448cb5..2e12ee9320 100644 --- a/config/datanode.example.toml +++ b/config/datanode.example.toml @@ -58,3 +58,8 @@ type = "File" data_dir = "/tmp/greptimedb/procedure/" max_retry_times = 3 retry_delay = "500ms" + +# Log options, see `standalone.example.toml` +[logging] +dir = "/tmp/greptimedb/logs" +level = "info" diff --git a/config/frontend.example.toml b/config/frontend.example.toml index 2247ebf5df..f0296426bf 100644 --- a/config/frontend.example.toml +++ b/config/frontend.example.toml @@ -56,3 +56,8 @@ metasrv_addrs = ["127.0.0.1:3002"] timeout_millis = 3000 connect_timeout_millis = 5000 tcp_nodelay = true + +# Log options, see `standalone.example.toml` +[logging] +dir = "/tmp/greptimedb/logs" +level = "info" diff --git a/config/metasrv.example.toml b/config/metasrv.example.toml index 395d68d6fd..47bd8addea 100644 --- a/config/metasrv.example.toml +++ b/config/metasrv.example.toml @@ -13,3 +13,8 @@ datanode_lease_secs = 15 selector = "LeaseBased" # Store data in memory, false by default. use_memory_store = false + +# Log options, see `standalone.example.toml` +[logging] +dir = "/tmp/greptimedb/logs" +level = "info" diff --git a/config/standalone.example.toml b/config/standalone.example.toml index 3ba676a288..6397b0d253 100644 --- a/config/standalone.example.toml +++ b/config/standalone.example.toml @@ -127,3 +127,10 @@ data_dir = "/tmp/greptimedb/procedure/" max_retry_times = 3 # Initial retry delay of procedures, increases exponentially retry_delay = "500ms" + +# Log options +[logging] +# Specify logs directory. +dir = "/tmp/greptimedb/logs" +# Specify the log level [info | debug | error | warn] +level = "debug" diff --git a/src/cmd/src/bin/greptime.rs b/src/cmd/src/bin/greptime.rs index 1d5067c8cc..ada4ce21eb 100644 --- a/src/cmd/src/bin/greptime.rs +++ b/src/cmd/src/bin/greptime.rs @@ -18,16 +18,17 @@ use std::fmt; use clap::Parser; use cmd::error::Result; +use cmd::options::{Options, TopLevelOptions}; use cmd::{cli, datanode, frontend, metasrv, standalone}; use common_telemetry::logging::{error, info}; #[derive(Parser)] #[clap(name = "greptimedb", version = print_version())] struct Command { - #[clap(long, default_value = "/tmp/greptimedb/logs")] - log_dir: String, - #[clap(long, default_value = "info")] - log_level: String, + #[clap(long)] + log_dir: Option, + #[clap(long)] + log_level: Option, #[clap(subcommand)] subcmd: SubCommand, } @@ -63,8 +64,20 @@ impl Application { } impl Command { - async fn build(self) -> Result { - self.subcmd.build().await + async fn build(self, opts: Options) -> Result { + self.subcmd.build(opts).await + } + + fn load_options(&self) -> Result { + let top_level_opts = self.top_level_options(); + self.subcmd.load_options(top_level_opts) + } + + fn top_level_options(&self) -> TopLevelOptions { + TopLevelOptions { + log_dir: self.log_dir.clone(), + log_level: self.log_level.clone(), + } } } @@ -83,28 +96,40 @@ enum SubCommand { } impl SubCommand { - async fn build(self) -> Result { - match self { - SubCommand::Datanode(cmd) => { - let app = cmd.build().await?; + async fn build(self, opts: Options) -> Result { + match (self, opts) { + (SubCommand::Datanode(cmd), Options::Datanode(dn_opts)) => { + let app = cmd.build(*dn_opts).await?; Ok(Application::Datanode(app)) } - SubCommand::Frontend(cmd) => { - let app = cmd.build().await?; + (SubCommand::Frontend(cmd), Options::Frontend(fe_opts)) => { + let app = cmd.build(*fe_opts).await?; Ok(Application::Frontend(app)) } - SubCommand::Metasrv(cmd) => { - let app = cmd.build().await?; + (SubCommand::Metasrv(cmd), Options::Metasrv(meta_opts)) => { + let app = cmd.build(*meta_opts).await?; Ok(Application::Metasrv(app)) } - SubCommand::Standalone(cmd) => { - let app = cmd.build().await?; + (SubCommand::Standalone(cmd), Options::Standalone(opts)) => { + let app = cmd.build(opts.fe_opts, opts.dn_opts).await?; Ok(Application::Standalone(app)) } - SubCommand::Cli(cmd) => { + (SubCommand::Cli(cmd), Options::Cli(_)) => { let app = cmd.build().await?; Ok(Application::Cli(app)) } + + _ => unreachable!(), + } + } + + fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + match self { + SubCommand::Datanode(cmd) => cmd.load_options(top_level_opts), + SubCommand::Frontend(cmd) => cmd.load_options(top_level_opts), + SubCommand::Metasrv(cmd) => cmd.load_options(top_level_opts), + SubCommand::Standalone(cmd) => cmd.load_options(top_level_opts), + SubCommand::Cli(cmd) => cmd.load_options(top_level_opts), } } } @@ -144,14 +169,15 @@ async fn main() -> Result<()> { // TODO(dennis): // 1. adds ip/port to app let app_name = &cmd.subcmd.to_string(); - let log_dir = &cmd.log_dir; - let log_level = &cmd.log_level; + + let opts = cmd.load_options()?; + let logging_opts = opts.logging_options(); common_telemetry::set_panic_hook(); common_telemetry::init_default_metrics_recorder(); - let _guard = common_telemetry::init_global_logging(app_name, log_dir, log_level, false); + let _guard = common_telemetry::init_global_logging(app_name, logging_opts); - let mut app = cmd.build().await?; + let mut app = cmd.build(opts).await?; tokio::select! { result = app.run() => { diff --git a/src/cmd/src/cli.rs b/src/cmd/src/cli.rs index cc8e08f29d..348a550439 100644 --- a/src/cmd/src/cli.rs +++ b/src/cmd/src/cli.rs @@ -17,9 +17,11 @@ mod helper; mod repl; use clap::Parser; +use common_telemetry::logging::LoggingOptions; pub use repl::Repl; use crate::error::Result; +use crate::options::{Options, TopLevelOptions}; pub struct Instance { repl: Repl, @@ -45,6 +47,17 @@ impl Command { pub async fn build(self) -> Result { self.cmd.build().await } + + pub fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + let mut logging_opts = LoggingOptions::default(); + if let Some(dir) = top_level_opts.log_dir { + logging_opts.dir = dir; + } + if let Some(level) = top_level_opts.log_level { + logging_opts.level = level; + } + Ok(Options::Cli(Box::new(logging_opts))) + } } #[derive(Parser)] @@ -76,3 +89,46 @@ impl AttachCommand { Ok(Instance { repl }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_load_options() { + let cmd = Command { + cmd: SubCommand::Attach(AttachCommand { + grpc_addr: String::from(""), + meta_addr: None, + disable_helper: false, + }), + }; + + let opts = cmd.load_options(TopLevelOptions::default()).unwrap(); + let logging_opts = opts.logging_options(); + assert_eq!("/tmp/greptimedb/logs", logging_opts.dir); + assert_eq!("info", logging_opts.level); + assert!(!logging_opts.enable_jaeger_tracing); + } + + #[test] + fn test_top_level_options() { + let cmd = Command { + cmd: SubCommand::Attach(AttachCommand { + grpc_addr: String::from(""), + meta_addr: None, + disable_helper: false, + }), + }; + + let opts = cmd + .load_options(TopLevelOptions { + log_dir: Some("/tmp/greptimedb/test/logs".to_string()), + log_level: Some("debug".to_string()), + }) + .unwrap(); + let logging_opts = opts.logging_options(); + assert_eq!("/tmp/greptimedb/test/logs", logging_opts.dir); + assert_eq!("debug", logging_opts.level); + } +} diff --git a/src/cmd/src/datanode.rs b/src/cmd/src/datanode.rs index ced365c6bc..8050e5c012 100644 --- a/src/cmd/src/datanode.rs +++ b/src/cmd/src/datanode.rs @@ -23,7 +23,8 @@ use meta_client::MetaClientOptions; use servers::Mode; use snafu::ResultExt; -use crate::error::{Error, MissingConfigSnafu, Result, ShutdownDatanodeSnafu, StartDatanodeSnafu}; +use crate::error::{MissingConfigSnafu, Result, ShutdownDatanodeSnafu, StartDatanodeSnafu}; +use crate::options::{Options, TopLevelOptions}; use crate::toml_loader; pub struct Instance { @@ -50,8 +51,12 @@ pub struct Command { } impl Command { - pub async fn build(self) -> Result { - self.subcmd.build().await + pub async fn build(self, opts: DatanodeOptions) -> Result { + self.subcmd.build(opts).await + } + + pub fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + self.subcmd.load_options(top_level_opts) } } @@ -61,9 +66,15 @@ enum SubCommand { } impl SubCommand { - async fn build(self) -> Result { + async fn build(self, opts: DatanodeOptions) -> Result { match self { - SubCommand::Start(cmd) => cmd.build().await, + SubCommand::Start(cmd) => cmd.build(opts).await, + } + } + + fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + match self { + SubCommand::Start(cmd) => cmd.load_options(top_level_opts), } } } @@ -95,45 +106,37 @@ struct StartCommand { } impl StartCommand { - async fn build(self) -> Result { - logging::info!("Datanode start command: {:#?}", self); - - let opts: DatanodeOptions = self.try_into()?; - - logging::info!("Datanode options: {:#?}", opts); - - let datanode = Datanode::new(opts).await.context(StartDatanodeSnafu)?; - - Ok(Instance { datanode }) - } -} - -impl TryFrom for DatanodeOptions { - type Error = Error; - fn try_from(cmd: StartCommand) -> Result { - let mut opts: DatanodeOptions = if let Some(path) = cmd.config_file { - toml_loader::from_file!(&path)? + fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + let mut opts: DatanodeOptions = if let Some(path) = &self.config_file { + toml_loader::from_file!(path)? } else { DatanodeOptions::default() }; - if let Some(addr) = cmd.rpc_addr { + if let Some(dir) = top_level_opts.log_dir { + opts.logging.dir = dir; + } + if let Some(level) = top_level_opts.log_level { + opts.logging.level = level; + } + + if let Some(addr) = self.rpc_addr.clone() { opts.rpc_addr = addr; } - if cmd.rpc_hostname.is_some() { - opts.rpc_hostname = cmd.rpc_hostname; + if self.rpc_hostname.is_some() { + opts.rpc_hostname = self.rpc_hostname.clone(); } - if let Some(addr) = cmd.mysql_addr { + if let Some(addr) = self.mysql_addr.clone() { opts.mysql_addr = addr; } - if let Some(node_id) = cmd.node_id { + if let Some(node_id) = self.node_id { opts.node_id = Some(node_id); } - if let Some(meta_addr) = cmd.metasrv_addr { + if let Some(meta_addr) = self.metasrv_addr.clone() { opts.meta_client_options .get_or_insert_with(MetaClientOptions::default) .metasrv_addrs = meta_addr @@ -151,33 +154,41 @@ impl TryFrom for DatanodeOptions { .fail(); } - if let Some(data_dir) = cmd.data_dir { + if let Some(data_dir) = self.data_dir.clone() { opts.storage.store = ObjectStoreConfig::File(FileConfig { data_dir }); } - if let Some(wal_dir) = cmd.wal_dir { + if let Some(wal_dir) = self.wal_dir.clone() { opts.wal.dir = wal_dir; } - if let Some(procedure_dir) = cmd.procedure_dir { + if let Some(procedure_dir) = self.procedure_dir.clone() { opts.procedure = ProcedureConfig::from_file_path(procedure_dir); } - if let Some(http_addr) = cmd.http_addr { + if let Some(http_addr) = self.http_addr.clone() { opts.http_opts.addr = http_addr } - if let Some(http_timeout) = cmd.http_timeout { + if let Some(http_timeout) = self.http_timeout { opts.http_opts.timeout = Duration::from_secs(http_timeout) } // Disable dashboard in datanode. opts.http_opts.disable_dashboard = true; - Ok(opts) + Ok(Options::Datanode(Box::new(opts))) + } + + async fn build(self, opts: DatanodeOptions) -> Result { + logging::info!("Datanode start command: {:#?}", self); + logging::info!("Datanode options: {:#?}", opts); + + let datanode = Datanode::new(opts).await.context(StartDatanodeSnafu)?; + + Ok(Instance { datanode }) } } #[cfg(test)] mod tests { - use std::assert_matches::assert_matches; use std::io::Write; use std::time::Duration; @@ -228,6 +239,10 @@ mod tests { checkpoint_margin = 9 gc_duration = '7s' checkpoint_on_startup = true + + [logging] + level = "debug" + dir = "/tmp/greptimedb/test/logs" "#; write!(file, "{}", toml_str).unwrap(); @@ -235,7 +250,10 @@ mod tests { config_file: Some(file.path().to_str().unwrap().to_string()), ..Default::default() }; - let options: DatanodeOptions = cmd.try_into().unwrap(); + + let Options::Datanode(options) = + cmd.load_options(TopLevelOptions::default()).unwrap() else { unreachable!() }; + assert_eq!("127.0.0.1:3001".to_string(), options.rpc_addr); assert_eq!("127.0.0.1:4406".to_string(), options.mysql_addr); assert_eq!(2, options.mysql_runtime_size); @@ -283,37 +301,60 @@ mod tests { }, options.storage.manifest, ); + + assert_eq!("debug".to_string(), options.logging.level); + assert_eq!("/tmp/greptimedb/test/logs".to_string(), options.logging.dir); } #[test] fn test_try_from_cmd() { - assert_eq!( - Mode::Standalone, - DatanodeOptions::try_from(StartCommand::default()) - .unwrap() - .mode - ); + if let Options::Datanode(opt) = StartCommand::default() + .load_options(TopLevelOptions::default()) + .unwrap() + { + assert_eq!(Mode::Standalone, opt.mode) + } - let mode = DatanodeOptions::try_from(StartCommand { + if let Options::Datanode(opt) = (StartCommand { node_id: Some(42), metasrv_addr: Some("127.0.0.1:3002".to_string()), ..Default::default() }) + .load_options(TopLevelOptions::default()) .unwrap() - .mode; - assert_matches!(mode, Mode::Distributed); + { + assert_eq!(Mode::Distributed, opt.mode) + } - assert!(DatanodeOptions::try_from(StartCommand { + assert!((StartCommand { metasrv_addr: Some("127.0.0.1:3002".to_string()), ..Default::default() }) + .load_options(TopLevelOptions::default()) .is_err()); // Providing node_id but leave metasrv_addr absent is ok since metasrv_addr has default value - DatanodeOptions::try_from(StartCommand { + (StartCommand { node_id: Some(42), ..Default::default() }) + .load_options(TopLevelOptions::default()) .unwrap(); } + + #[test] + fn test_top_level_options() { + let cmd = StartCommand::default(); + + let options = cmd + .load_options(TopLevelOptions { + log_dir: Some("/tmp/greptimedb/test/logs".to_string()), + log_level: Some("debug".to_string()), + }) + .unwrap(); + + let logging_opt = options.logging_options(); + assert_eq!("/tmp/greptimedb/test/logs", logging_opt.dir); + assert_eq!("debug", logging_opt.level); + } } diff --git a/src/cmd/src/frontend.rs b/src/cmd/src/frontend.rs index 9413ab0cad..849d6bd55a 100644 --- a/src/cmd/src/frontend.rs +++ b/src/cmd/src/frontend.rs @@ -31,6 +31,7 @@ use servers::{auth, Mode}; use snafu::ResultExt; use crate::error::{self, IllegalAuthConfigSnafu, Result}; +use crate::options::{Options, TopLevelOptions}; use crate::toml_loader; pub struct Instance { @@ -60,8 +61,12 @@ pub struct Command { } impl Command { - pub async fn build(self) -> Result { - self.subcmd.build().await + pub async fn build(self, opts: FrontendOptions) -> Result { + self.subcmd.build(opts).await + } + + pub fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + self.subcmd.load_options(top_level_opts) } } @@ -71,9 +76,15 @@ enum SubCommand { } impl SubCommand { - async fn build(self) -> Result { + async fn build(self, opts: FrontendOptions) -> Result { match self { - SubCommand::Start(cmd) => cmd.build().await, + SubCommand::Start(cmd) => cmd.build(opts).await, + } + } + + fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + match self { + SubCommand::Start(cmd) => cmd.load_options(top_level_opts), } } } @@ -111,9 +122,85 @@ pub struct StartCommand { } impl StartCommand { - async fn build(self) -> Result { + fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + let mut opts: FrontendOptions = if let Some(path) = &self.config_file { + toml_loader::from_file!(path)? + } else { + FrontendOptions::default() + }; + + if let Some(dir) = top_level_opts.log_dir { + opts.logging.dir = dir; + } + if let Some(level) = top_level_opts.log_level { + opts.logging.level = level; + } + + let tls_option = TlsOption::new( + self.tls_mode.clone(), + self.tls_cert_path.clone(), + self.tls_key_path.clone(), + ); + + if let Some(addr) = self.http_addr.clone() { + opts.http_options.get_or_insert_with(Default::default).addr = addr; + } + + if let Some(disable_dashboard) = self.disable_dashboard { + opts.http_options + .get_or_insert_with(Default::default) + .disable_dashboard = disable_dashboard; + } + + if let Some(addr) = self.grpc_addr.clone() { + opts.grpc_options = Some(GrpcOptions { + addr, + ..Default::default() + }); + } + + if let Some(addr) = self.mysql_addr.clone() { + opts.mysql_options = Some(MysqlOptions { + addr, + tls: tls_option.clone(), + ..Default::default() + }); + } + if let Some(addr) = self.prom_addr.clone() { + opts.prom_options = Some(PromOptions { addr }); + } + if let Some(addr) = self.postgres_addr.clone() { + opts.postgres_options = Some(PostgresOptions { + addr, + tls: tls_option, + ..Default::default() + }); + } + if let Some(addr) = self.opentsdb_addr.clone() { + opts.opentsdb_options = Some(OpentsdbOptions { + addr, + ..Default::default() + }); + } + if let Some(enable) = self.influxdb_enable { + opts.influxdb_options = Some(InfluxdbOptions { enable }); + } + if let Some(metasrv_addr) = self.metasrv_addr.clone() { + opts.meta_client_options + .get_or_insert_with(MetaClientOptions::default) + .metasrv_addrs = metasrv_addr + .split(',') + .map(&str::trim) + .map(&str::to_string) + .collect::>(); + opts.mode = Mode::Distributed; + } + + Ok(Options::Frontend(Box::new(opts))) + } + + async fn build(self, opts: FrontendOptions) -> Result { let plugins = Arc::new(load_frontend_plugins(&self.user_provider)?); - let opts: FrontendOptions = self.try_into()?; let mut instance = FeInstance::try_new_distributed(&opts, plugins.clone()) .await @@ -138,75 +225,6 @@ pub fn load_frontend_plugins(user_provider: &Option) -> Result Ok(plugins) } -impl TryFrom for FrontendOptions { - type Error = error::Error; - - fn try_from(cmd: StartCommand) -> Result { - let mut opts: FrontendOptions = if let Some(path) = cmd.config_file { - toml_loader::from_file!(&path)? - } else { - FrontendOptions::default() - }; - - let tls_option = TlsOption::new(cmd.tls_mode, cmd.tls_cert_path, cmd.tls_key_path); - - if let Some(addr) = cmd.http_addr { - opts.http_options.get_or_insert_with(Default::default).addr = addr; - } - - if let Some(disable_dashboard) = cmd.disable_dashboard { - opts.http_options - .get_or_insert_with(Default::default) - .disable_dashboard = disable_dashboard; - } - - if let Some(addr) = cmd.grpc_addr { - opts.grpc_options = Some(GrpcOptions { - addr, - ..Default::default() - }); - } - - if let Some(addr) = cmd.mysql_addr { - opts.mysql_options = Some(MysqlOptions { - addr, - tls: tls_option.clone(), - ..Default::default() - }); - } - if let Some(addr) = cmd.prom_addr { - opts.prom_options = Some(PromOptions { addr }); - } - if let Some(addr) = cmd.postgres_addr { - opts.postgres_options = Some(PostgresOptions { - addr, - tls: tls_option, - ..Default::default() - }); - } - if let Some(addr) = cmd.opentsdb_addr { - opts.opentsdb_options = Some(OpentsdbOptions { - addr, - ..Default::default() - }); - } - if let Some(enable) = cmd.influxdb_enable { - opts.influxdb_options = Some(InfluxdbOptions { enable }); - } - if let Some(metasrv_addr) = cmd.metasrv_addr { - opts.meta_client_options - .get_or_insert_with(MetaClientOptions::default) - .metasrv_addrs = metasrv_addr - .split(',') - .map(&str::trim) - .map(&str::to_string) - .collect::>(); - opts.mode = Mode::Distributed; - } - Ok(opts) - } -} - #[cfg(test)] mod tests { use std::io::Write; @@ -236,7 +254,9 @@ mod tests { disable_dashboard: Some(false), }; - let opts: FrontendOptions = command.try_into().unwrap(); + let Options::Frontend(opts) = + command.load_options(TopLevelOptions::default()).unwrap() else { unreachable!() }; + assert_eq!(opts.http_options.as_ref().unwrap().addr, "127.0.0.1:1234"); assert_eq!(opts.mysql_options.as_ref().unwrap().addr, "127.0.0.1:5678"); assert_eq!( @@ -279,6 +299,10 @@ mod tests { [http_options] addr = "127.0.0.1:4000" timeout = "30s" + + [logging] + level = "debug" + dir = "/tmp/greptimedb/test/logs" "#; write!(file, "{}", toml_str).unwrap(); @@ -299,7 +323,8 @@ mod tests { disable_dashboard: Some(false), }; - let fe_opts = FrontendOptions::try_from(command).unwrap(); + let Options::Frontend(fe_opts) = + command.load_options(TopLevelOptions::default()).unwrap() else {unreachable!()}; assert_eq!(Mode::Distributed, fe_opts.mode); assert_eq!( "127.0.0.1:4000".to_string(), @@ -309,6 +334,9 @@ mod tests { Duration::from_secs(30), fe_opts.http_options.as_ref().unwrap().timeout ); + + assert_eq!("debug".to_string(), fe_opts.logging.level); + assert_eq!("/tmp/greptimedb/test/logs".to_string(), fe_opts.logging.dir); } #[tokio::test] @@ -342,4 +370,35 @@ mod tests { .await; assert!(result.is_ok()); } + + #[test] + fn test_top_level_options() { + let cmd = StartCommand { + http_addr: None, + grpc_addr: None, + mysql_addr: None, + prom_addr: None, + postgres_addr: None, + opentsdb_addr: None, + influxdb_enable: None, + config_file: None, + metasrv_addr: None, + tls_mode: None, + tls_cert_path: None, + tls_key_path: None, + user_provider: None, + disable_dashboard: Some(false), + }; + + let options = cmd + .load_options(TopLevelOptions { + log_dir: Some("/tmp/greptimedb/test/logs".to_string()), + log_level: Some("debug".to_string()), + }) + .unwrap(); + + let logging_opt = options.logging_options(); + assert_eq!("/tmp/greptimedb/test/logs", logging_opt.dir); + assert_eq!("debug", logging_opt.level); + } } diff --git a/src/cmd/src/lib.rs b/src/cmd/src/lib.rs index 157e4853f1..df2a46199b 100644 --- a/src/cmd/src/lib.rs +++ b/src/cmd/src/lib.rs @@ -19,5 +19,6 @@ pub mod datanode; pub mod error; pub mod frontend; pub mod metasrv; +pub mod options; pub mod standalone; mod toml_loader; diff --git a/src/cmd/src/metasrv.rs b/src/cmd/src/metasrv.rs index 6bc71fe7fa..bc60c8da64 100644 --- a/src/cmd/src/metasrv.rs +++ b/src/cmd/src/metasrv.rs @@ -15,12 +15,13 @@ use std::time::Duration; use clap::Parser; -use common_telemetry::{info, logging, warn}; +use common_telemetry::logging; use meta_srv::bootstrap::MetaSrvInstance; use meta_srv::metasrv::MetaSrvOptions; use snafu::ResultExt; -use crate::error::{Error, Result}; +use crate::error::Result; +use crate::options::{Options, TopLevelOptions}; use crate::{error, toml_loader}; pub struct Instance { @@ -50,8 +51,12 @@ pub struct Command { } impl Command { - pub async fn build(self) -> Result { - self.subcmd.build().await + pub async fn build(self, opts: MetaSrvOptions) -> Result { + self.subcmd.build(opts).await + } + + pub fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + self.subcmd.load_options(top_level_opts) } } @@ -61,9 +66,15 @@ enum SubCommand { } impl SubCommand { - async fn build(self) -> Result { + async fn build(self, opts: MetaSrvOptions) -> Result { match self { - SubCommand::Start(cmd) => cmd.build().await, + SubCommand::Start(cmd) => cmd.build(opts).await, + } + } + + fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + match self { + SubCommand::Start(cmd) => cmd.load_options(top_level_opts), } } } @@ -89,62 +100,62 @@ struct StartCommand { } impl StartCommand { - async fn build(self) -> Result { - logging::info!("MetaSrv start command: {:#?}", self); - - let opts: MetaSrvOptions = self.try_into()?; - - logging::info!("MetaSrv options: {:#?}", opts); - let instance = MetaSrvInstance::new(opts) - .await - .context(error::BuildMetaServerSnafu)?; - - Ok(Instance { instance }) - } -} - -impl TryFrom for MetaSrvOptions { - type Error = Error; - - fn try_from(cmd: StartCommand) -> Result { - let mut opts: MetaSrvOptions = if let Some(path) = cmd.config_file { - toml_loader::from_file!(&path)? + fn load_options(&self, top_level_opts: TopLevelOptions) -> Result { + let mut opts: MetaSrvOptions = if let Some(path) = &self.config_file { + toml_loader::from_file!(path)? } else { MetaSrvOptions::default() }; - if let Some(addr) = cmd.bind_addr { + if let Some(dir) = top_level_opts.log_dir { + opts.logging.dir = dir; + } + if let Some(level) = top_level_opts.log_level { + opts.logging.level = level; + } + + if let Some(addr) = self.bind_addr.clone() { opts.bind_addr = addr; } - if let Some(addr) = cmd.server_addr { + if let Some(addr) = self.server_addr.clone() { opts.server_addr = addr; } - if let Some(addr) = cmd.store_addr { + if let Some(addr) = self.store_addr.clone() { opts.store_addr = addr; } - if let Some(selector_type) = &cmd.selector { + if let Some(selector_type) = &self.selector { opts.selector = selector_type[..] .try_into() .context(error::UnsupportedSelectorTypeSnafu { selector_type })?; - info!("Using {} selector", selector_type); } - if cmd.use_memory_store { - warn!("Using memory store for Meta. Make sure you are in running tests."); + if self.use_memory_store { opts.use_memory_store = true; } - if let Some(http_addr) = cmd.http_addr { + if let Some(http_addr) = self.http_addr.clone() { opts.http_opts.addr = http_addr; } - if let Some(http_timeout) = cmd.http_timeout { + if let Some(http_timeout) = self.http_timeout { opts.http_opts.timeout = Duration::from_secs(http_timeout); } // Disable dashboard in metasrv. opts.http_opts.disable_dashboard = true; - Ok(opts) + Ok(Options::Metasrv(Box::new(opts))) + } + + async fn build(self, opts: MetaSrvOptions) -> Result { + logging::info!("MetaSrv start command: {:#?}", self); + + logging::info!("MetaSrv options: {:#?}", opts); + + let instance = MetaSrvInstance::new(opts) + .await + .context(error::BuildMetaServerSnafu)?; + + Ok(Instance { instance }) } } @@ -169,9 +180,10 @@ mod tests { http_addr: None, http_timeout: None, }; - let options: MetaSrvOptions = cmd.try_into().unwrap(); + + let Options::Metasrv(options) = + cmd.load_options(TopLevelOptions::default()).unwrap() else { unreachable!() }; assert_eq!("127.0.0.1:3002".to_string(), options.bind_addr); - assert_eq!("127.0.0.1:3002".to_string(), options.server_addr); assert_eq!("127.0.0.1:2380".to_string(), options.store_addr); assert_eq!(SelectorType::LoadBased, options.selector); } @@ -186,6 +198,10 @@ mod tests { datanode_lease_secs = 15 selector = "LeaseBased" use_memory_store = false + + [logging] + level = "debug" + dir = "/tmp/greptimedb/test/logs" "#; write!(file, "{}", toml_str).unwrap(); @@ -199,11 +215,40 @@ mod tests { http_addr: None, http_timeout: None, }; - let options: MetaSrvOptions = cmd.try_into().unwrap(); + + let Options::Metasrv(options) = + cmd.load_options(TopLevelOptions::default()).unwrap() else { unreachable!() }; assert_eq!("127.0.0.1:3002".to_string(), options.bind_addr); assert_eq!("127.0.0.1:3002".to_string(), options.server_addr); assert_eq!("127.0.0.1:2379".to_string(), options.store_addr); assert_eq!(15, options.datanode_lease_secs); assert_eq!(SelectorType::LeaseBased, options.selector); + assert_eq!("debug".to_string(), options.logging.level); + assert_eq!("/tmp/greptimedb/test/logs".to_string(), options.logging.dir); + } + + #[test] + fn test_top_level_options() { + let cmd = StartCommand { + bind_addr: Some("127.0.0.1:3002".to_string()), + server_addr: Some("127.0.0.1:3002".to_string()), + store_addr: Some("127.0.0.1:2380".to_string()), + config_file: None, + selector: Some("LoadBased".to_string()), + use_memory_store: false, + http_addr: None, + http_timeout: None, + }; + + let options = cmd + .load_options(TopLevelOptions { + log_dir: Some("/tmp/greptimedb/test/logs".to_string()), + log_level: Some("debug".to_string()), + }) + .unwrap(); + + let logging_opt = options.logging_options(); + assert_eq!("/tmp/greptimedb/test/logs", logging_opt.dir); + assert_eq!("debug", logging_opt.level); } } diff --git a/src/cmd/src/options.rs b/src/cmd/src/options.rs new file mode 100644 index 0000000000..eeec11f2aa --- /dev/null +++ b/src/cmd/src/options.rs @@ -0,0 +1,49 @@ +// Copyright 2023 Greptime Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use common_telemetry::logging::LoggingOptions; +use datanode::datanode::DatanodeOptions; +use frontend::frontend::FrontendOptions; +use meta_srv::metasrv::MetaSrvOptions; + +pub struct MixOptions { + pub fe_opts: FrontendOptions, + pub dn_opts: DatanodeOptions, + pub logging: LoggingOptions, +} + +pub enum Options { + Datanode(Box), + Frontend(Box), + Metasrv(Box), + Standalone(Box), + Cli(Box), +} + +impl Options { + pub fn logging_options(&self) -> &LoggingOptions { + match self { + Options::Datanode(opts) => &opts.logging, + Options::Frontend(opts) => &opts.logging, + Options::Metasrv(opts) => &opts.logging, + Options::Standalone(opts) => &opts.logging, + Options::Cli(opts) => opts, + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct TopLevelOptions { + pub log_dir: Option, + pub log_level: Option, +} diff --git a/src/cmd/src/standalone.rs b/src/cmd/src/standalone.rs index 2148b5a097..f17f617058 100644 --- a/src/cmd/src/standalone.rs +++ b/src/cmd/src/standalone.rs @@ -17,6 +17,7 @@ use std::sync::Arc; use clap::Parser; use common_base::Plugins; use common_telemetry::info; +use common_telemetry::logging::LoggingOptions; use datanode::datanode::{Datanode, DatanodeOptions, ProcedureConfig, StorageConfig, WalConfig}; use datanode::instance::InstanceRef; use frontend::frontend::FrontendOptions; @@ -35,10 +36,11 @@ use servers::Mode; use snafu::ResultExt; use crate::error::{ - Error, IllegalConfigSnafu, Result, ShutdownDatanodeSnafu, ShutdownFrontendSnafu, - StartDatanodeSnafu, StartFrontendSnafu, + IllegalConfigSnafu, Result, ShutdownDatanodeSnafu, ShutdownFrontendSnafu, StartDatanodeSnafu, + StartFrontendSnafu, }; use crate::frontend::load_frontend_plugins; +use crate::options::{MixOptions, Options, TopLevelOptions}; use crate::toml_loader; #[derive(Parser)] @@ -48,8 +50,16 @@ pub struct Command { } impl Command { - pub async fn build(self) -> Result { - self.subcmd.build().await + pub async fn build( + self, + fe_opts: FrontendOptions, + dn_opts: DatanodeOptions, + ) -> Result { + self.subcmd.build(fe_opts, dn_opts).await + } + + pub fn load_options(&self, top_level_options: TopLevelOptions) -> Result { + self.subcmd.load_options(top_level_options) } } @@ -59,9 +69,15 @@ enum SubCommand { } impl SubCommand { - async fn build(self) -> Result { + async fn build(self, fe_opts: FrontendOptions, dn_opts: DatanodeOptions) -> Result { match self { - SubCommand::Start(cmd) => cmd.build().await, + SubCommand::Start(cmd) => cmd.build(fe_opts, dn_opts).await, + } + } + + fn load_options(&self, top_level_options: TopLevelOptions) -> Result { + match self { + SubCommand::Start(cmd) => cmd.load_options(top_level_options), } } } @@ -82,6 +98,7 @@ pub struct StandaloneOptions { pub wal: WalConfig, pub storage: StorageConfig, pub procedure: ProcedureConfig, + pub logging: LoggingOptions, } impl Default for StandaloneOptions { @@ -100,6 +117,7 @@ impl Default for StandaloneOptions { wal: WalConfig::default(), storage: StorageConfig::default(), procedure: ProcedureConfig::default(), + logging: LoggingOptions::default(), } } } @@ -117,6 +135,7 @@ impl StandaloneOptions { prometheus_options: self.prometheus_options, prom_options: self.prom_options, meta_client_options: None, + logging: self.logging, } } @@ -196,21 +215,108 @@ struct StartCommand { } impl StartCommand { - async fn build(self) -> Result { + fn load_options(&self, top_level_options: TopLevelOptions) -> Result { let enable_memory_catalog = self.enable_memory_catalog; - let config_file = self.config_file.clone(); - let plugins = Arc::new(load_frontend_plugins(&self.user_provider)?); - let fe_opts = FrontendOptions::try_from(self)?; - let dn_opts: DatanodeOptions = { - let mut opts: StandaloneOptions = if let Some(path) = config_file { - toml_loader::from_file!(&path)? - } else { - StandaloneOptions::default() - }; - opts.enable_memory_catalog = enable_memory_catalog; - opts.datanode_options() + let config_file = &self.config_file; + let mut opts: StandaloneOptions = if let Some(path) = config_file { + toml_loader::from_file!(path)? + } else { + StandaloneOptions::default() }; + opts.enable_memory_catalog = enable_memory_catalog; + + let mut fe_opts = opts.clone().frontend_options(); + let mut logging = opts.logging.clone(); + let dn_opts = opts.datanode_options(); + + if let Some(dir) = top_level_options.log_dir { + logging.dir = dir; + } + if let Some(level) = top_level_options.log_level { + logging.level = level; + } + + fe_opts.mode = Mode::Standalone; + + if let Some(addr) = self.http_addr.clone() { + fe_opts.http_options = Some(HttpOptions { + addr, + ..Default::default() + }); + } + if let Some(addr) = self.rpc_addr.clone() { + // frontend grpc addr conflict with datanode default grpc addr + let datanode_grpc_addr = DatanodeOptions::default().rpc_addr; + if addr == datanode_grpc_addr { + return IllegalConfigSnafu { + msg: format!( + "gRPC listen address conflicts with datanode reserved gRPC addr: {datanode_grpc_addr}", + ), + } + .fail(); + } + fe_opts.grpc_options = Some(GrpcOptions { + addr, + ..Default::default() + }); + } + + if let Some(addr) = self.mysql_addr.clone() { + fe_opts.mysql_options = Some(MysqlOptions { + addr, + ..Default::default() + }) + } + + if let Some(addr) = self.prom_addr.clone() { + fe_opts.prom_options = Some(PromOptions { addr }) + } + + if let Some(addr) = self.postgres_addr.clone() { + fe_opts.postgres_options = Some(PostgresOptions { + addr, + ..Default::default() + }) + } + + if let Some(addr) = self.opentsdb_addr.clone() { + fe_opts.opentsdb_options = Some(OpentsdbOptions { + addr, + ..Default::default() + }); + } + + if self.influxdb_enable { + fe_opts.influxdb_options = Some(InfluxdbOptions { enable: true }); + } + + let tls_option = TlsOption::new( + self.tls_mode.clone(), + self.tls_cert_path.clone(), + self.tls_key_path.clone(), + ); + + if let Some(mut mysql_options) = fe_opts.mysql_options { + mysql_options.tls = tls_option.clone(); + fe_opts.mysql_options = Some(mysql_options); + } + + if let Some(mut postgres_options) = fe_opts.postgres_options { + postgres_options.tls = tls_option; + fe_opts.postgres_options = Some(postgres_options); + } + + Ok(Options::Standalone(Box::new(MixOptions { + fe_opts, + dn_opts, + logging, + }))) + } + + async fn build(self, fe_opts: FrontendOptions, dn_opts: DatanodeOptions) -> Result { + let plugins = Arc::new(load_frontend_plugins(&self.user_provider)?); + info!( "Standalone frontend options: {:#?}, datanode options: {:#?}", fe_opts, dn_opts @@ -243,143 +349,17 @@ async fn build_frontend( Ok(frontend_instance) } -impl TryFrom for FrontendOptions { - type Error = Error; - - fn try_from(cmd: StartCommand) -> std::result::Result { - let opts: StandaloneOptions = if let Some(path) = cmd.config_file { - toml_loader::from_file!(&path)? - } else { - StandaloneOptions::default() - }; - - let mut opts = opts.frontend_options(); - - opts.mode = Mode::Standalone; - - if let Some(addr) = cmd.http_addr { - opts.http_options = Some(HttpOptions { - addr, - ..Default::default() - }); - } - if let Some(addr) = cmd.rpc_addr { - // frontend grpc addr conflict with datanode default grpc addr - let datanode_grpc_addr = DatanodeOptions::default().rpc_addr; - if addr == datanode_grpc_addr { - return IllegalConfigSnafu { - msg: format!( - "gRPC listen address conflicts with datanode reserved gRPC addr: {datanode_grpc_addr}", - ), - } - .fail(); - } - opts.grpc_options = Some(GrpcOptions { - addr, - ..Default::default() - }); - } - - if let Some(addr) = cmd.mysql_addr { - opts.mysql_options = Some(MysqlOptions { - addr, - ..Default::default() - }) - } - - if let Some(addr) = cmd.prom_addr { - opts.prom_options = Some(PromOptions { addr }) - } - - if let Some(addr) = cmd.postgres_addr { - opts.postgres_options = Some(PostgresOptions { - addr, - ..Default::default() - }) - } - - if let Some(addr) = cmd.opentsdb_addr { - opts.opentsdb_options = Some(OpentsdbOptions { - addr, - ..Default::default() - }); - } - - if cmd.influxdb_enable { - opts.influxdb_options = Some(InfluxdbOptions { enable: true }); - } - - let tls_option = TlsOption::new(cmd.tls_mode, cmd.tls_cert_path, cmd.tls_key_path); - - if let Some(mut mysql_options) = opts.mysql_options { - mysql_options.tls = tls_option.clone(); - opts.mysql_options = Some(mysql_options); - } - - if let Some(mut postgres_options) = opts.postgres_options { - postgres_options.tls = tls_option; - opts.postgres_options = Some(postgres_options); - } - - Ok(opts) - } -} - #[cfg(test)] mod tests { + use std::io::Write; use std::time::Duration; + use common_test_util::temp_dir::create_named_temp_file; use servers::auth::{Identity, Password, UserProviderRef}; + use servers::Mode; use super::*; - #[test] - fn test_read_config_file() { - let cmd = StartCommand { - http_addr: None, - rpc_addr: None, - mysql_addr: None, - prom_addr: None, - postgres_addr: None, - opentsdb_addr: None, - config_file: Some(format!( - "{}/../../config/standalone.example.toml", - std::env::current_dir().unwrap().as_path().to_str().unwrap() - )), - influxdb_enable: false, - enable_memory_catalog: false, - tls_mode: None, - tls_cert_path: None, - tls_key_path: None, - user_provider: None, - }; - - let fe_opts = FrontendOptions::try_from(cmd).unwrap(); - assert_eq!(Mode::Standalone, fe_opts.mode); - assert_eq!( - "127.0.0.1:4000".to_string(), - fe_opts.http_options.as_ref().unwrap().addr - ); - assert_eq!( - Duration::from_secs(30), - fe_opts.http_options.as_ref().unwrap().timeout - ); - assert_eq!( - "127.0.0.1:4001".to_string(), - fe_opts.grpc_options.unwrap().addr - ); - assert_eq!( - "127.0.0.1:4002", - fe_opts.mysql_options.as_ref().unwrap().addr - ); - assert_eq!(2, fe_opts.mysql_options.as_ref().unwrap().runtime_size); - assert_eq!( - None, - fe_opts.mysql_options.as_ref().unwrap().reject_no_database - ); - assert!(fe_opts.influxdb_options.as_ref().unwrap().enable); - } - #[tokio::test] async fn test_try_from_start_command_to_anymap() { let command = StartCommand { @@ -416,4 +396,124 @@ mod tests { let toml_string = toml::to_string(&opts).unwrap(); let _parsed: StandaloneOptions = toml::from_str(&toml_string).unwrap(); } + + #[test] + fn test_read_from_config_file() { + let mut file = create_named_temp_file(); + let toml_str = r#" + mode = "distributed" + + enable_memory_catalog = true + + [wal] + dir = "/tmp/greptimedb/test/wal" + file_size = "1GB" + purge_threshold = "50GB" + purge_interval = "10m" + read_batch_size = 128 + sync_write = false + + [storage] + type = "File" + data_dir = "/tmp/greptimedb/data/" + + [storage.compaction] + max_inflight_tasks = 3 + max_files_in_level0 = 7 + max_purge_tasks = 32 + + [storage.manifest] + checkpoint_margin = 9 + gc_duration = '7s' + checkpoint_on_startup = true + + [http_options] + addr = "127.0.0.1:4000" + timeout = "30s" + + [logging] + level = "debug" + dir = "/tmp/greptimedb/test/logs" + "#; + write!(file, "{}", toml_str).unwrap(); + let cmd = StartCommand { + http_addr: None, + rpc_addr: None, + prom_addr: None, + mysql_addr: None, + postgres_addr: None, + opentsdb_addr: None, + config_file: Some(file.path().to_str().unwrap().to_string()), + influxdb_enable: false, + enable_memory_catalog: false, + tls_mode: None, + tls_cert_path: None, + tls_key_path: None, + user_provider: Some("static_user_provider:cmd:test=test".to_string()), + }; + + let Options::Standalone(options) = cmd.load_options(TopLevelOptions::default()).unwrap() else {unreachable!()}; + let fe_opts = options.fe_opts; + let dn_opts = options.dn_opts; + let logging_opts = options.logging; + assert_eq!(Mode::Standalone, fe_opts.mode); + assert_eq!( + "127.0.0.1:4000".to_string(), + fe_opts.http_options.as_ref().unwrap().addr + ); + assert_eq!( + Duration::from_secs(30), + fe_opts.http_options.as_ref().unwrap().timeout + ); + assert_eq!( + "127.0.0.1:4001".to_string(), + fe_opts.grpc_options.unwrap().addr + ); + assert_eq!( + "127.0.0.1:4002", + fe_opts.mysql_options.as_ref().unwrap().addr + ); + assert_eq!(2, fe_opts.mysql_options.as_ref().unwrap().runtime_size); + assert_eq!( + None, + fe_opts.mysql_options.as_ref().unwrap().reject_no_database + ); + assert!(fe_opts.influxdb_options.as_ref().unwrap().enable); + + assert_eq!("/tmp/greptimedb/test/wal", dn_opts.wal.dir); + + assert_eq!("debug".to_string(), logging_opts.level); + assert_eq!("/tmp/greptimedb/test/logs".to_string(), logging_opts.dir); + } + + #[test] + fn test_top_level_options() { + let cmd = StartCommand { + http_addr: None, + rpc_addr: None, + prom_addr: None, + mysql_addr: None, + postgres_addr: None, + opentsdb_addr: None, + config_file: None, + influxdb_enable: false, + enable_memory_catalog: false, + tls_mode: None, + tls_cert_path: None, + tls_key_path: None, + user_provider: Some("static_user_provider:cmd:test=test".to_string()), + }; + + let Options::Standalone(opts) = cmd + .load_options(TopLevelOptions { + log_dir: Some("/tmp/greptimedb/test/logs".to_string()), + log_level: Some("debug".to_string()), + }) + .unwrap() else { + unreachable!() + }; + + assert_eq!("/tmp/greptimedb/test/logs", opts.logging.dir); + assert_eq!("debug", opts.logging.level); + } } diff --git a/src/common/telemetry/Cargo.toml b/src/common/telemetry/Cargo.toml index 1d0c6b9690..e0dcf5ab11 100644 --- a/src/common/telemetry/Cargo.toml +++ b/src/common/telemetry/Cargo.toml @@ -23,6 +23,7 @@ opentelemetry-jaeger = { version = "0.16", features = ["rt-tokio"] } parking_lot = { version = "0.12", features = [ "deadlock_detection", ], optional = true } +serde = "1.0" tracing = "0.1" tracing-appender = "0.2" tracing-bunyan-formatter = "0.3" diff --git a/src/common/telemetry/src/logging.rs b/src/common/telemetry/src/logging.rs index 729e7676bc..5bc3a34a3e 100644 --- a/src/common/telemetry/src/logging.rs +++ b/src/common/telemetry/src/logging.rs @@ -19,6 +19,7 @@ use std::sync::{Arc, Mutex, Once}; use once_cell::sync::Lazy; use opentelemetry::global; use opentelemetry::sdk::propagation::TraceContextPropagator; +use serde::{Deserialize, Serialize}; pub use tracing::{event, span, Level}; use tracing_appender::non_blocking::WorkerGuard; use tracing_appender::rolling::{RollingFileAppender, Rotation}; @@ -31,6 +32,24 @@ use tracing_subscriber::{filter, EnvFilter, Registry}; pub use crate::{debug, error, info, log, trace, warn}; +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default)] +pub struct LoggingOptions { + pub dir: String, + pub level: String, + pub enable_jaeger_tracing: bool, +} + +impl Default for LoggingOptions { + fn default() -> Self { + Self { + dir: "/tmp/greptimedb/logs".to_string(), + level: "info".to_string(), + enable_jaeger_tracing: false, + } + } +} + /// Init tracing for unittest. /// Write logs to file `unittest`. pub fn init_default_ut_logging() { @@ -47,7 +66,12 @@ pub fn init_default_ut_logging() { env::var("UNITTEST_LOG_DIR").unwrap_or_else(|_| "/tmp/__unittest_logs".to_string()); let level = env::var("UNITTEST_LOG_LEVEL").unwrap_or_else(|_| "DEBUG".to_string()); - *g = Some(init_global_logging("unittest", &dir, &level, false)); + let opts = LoggingOptions { + dir: dir.clone(), + level, + ..Default::default() + }; + *g = Some(init_global_logging("unittest", &opts)); info!("logs dir = {}", dir); }); @@ -56,13 +80,11 @@ pub fn init_default_ut_logging() { static GLOBAL_UT_LOG_GUARD: Lazy>>>> = Lazy::new(|| Arc::new(Mutex::new(None))); -pub fn init_global_logging( - app_name: &str, - dir: &str, - level: &str, - enable_jaeger_tracing: bool, -) -> Vec { +pub fn init_global_logging(app_name: &str, opts: &LoggingOptions) -> Vec { let mut guards = vec![]; + let dir = &opts.dir; + let level = &opts.level; + let enable_jaeger_tracing = opts.enable_jaeger_tracing; // Enable log compatible layer to convert log record to tracing span. LogTracer::init().expect("log tracer must be valid"); diff --git a/src/datanode/src/datanode.rs b/src/datanode/src/datanode.rs index 578a14371d..650478b8d2 100644 --- a/src/datanode/src/datanode.rs +++ b/src/datanode/src/datanode.rs @@ -17,6 +17,7 @@ use std::time::Duration; use common_base::readable_size::ReadableSize; use common_telemetry::info; +use common_telemetry::logging::LoggingOptions; use meta_client::MetaClientOptions; use serde::{Deserialize, Serialize}; use servers::http::HttpOptions; @@ -238,6 +239,7 @@ pub struct DatanodeOptions { pub wal: WalConfig, pub storage: StorageConfig, pub procedure: ProcedureConfig, + pub logging: LoggingOptions, } impl Default for DatanodeOptions { @@ -256,6 +258,7 @@ impl Default for DatanodeOptions { wal: WalConfig::default(), storage: StorageConfig::default(), procedure: ProcedureConfig::default(), + logging: LoggingOptions::default(), } } } diff --git a/src/frontend/src/frontend.rs b/src/frontend/src/frontend.rs index b99092134f..574bf7727d 100644 --- a/src/frontend/src/frontend.rs +++ b/src/frontend/src/frontend.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use common_telemetry::logging::LoggingOptions; use meta_client::MetaClientOptions; use serde::{Deserialize, Serialize}; use servers::http::HttpOptions; @@ -38,6 +39,7 @@ pub struct FrontendOptions { pub prometheus_options: Option, pub prom_options: Option, pub meta_client_options: Option, + pub logging: LoggingOptions, } impl Default for FrontendOptions { @@ -53,6 +55,7 @@ impl Default for FrontendOptions { prometheus_options: Some(PrometheusOptions::default()), prom_options: Some(PromOptions::default()), meta_client_options: None, + logging: LoggingOptions::default(), } } } diff --git a/src/meta-srv/src/metasrv.rs b/src/meta-srv/src/metasrv.rs index 64217ad79c..10b95bee03 100644 --- a/src/meta-srv/src/metasrv.rs +++ b/src/meta-srv/src/metasrv.rs @@ -20,6 +20,7 @@ use std::sync::Arc; use api::v1::meta::Peer; use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME}; use common_procedure::ProcedureManagerRef; +use common_telemetry::logging::LoggingOptions; use common_telemetry::{error, info, warn}; use serde::{Deserialize, Serialize}; use servers::http::HttpOptions; @@ -48,6 +49,7 @@ pub struct MetaSrvOptions { pub selector: SelectorType, pub use_memory_store: bool, pub http_opts: HttpOptions, + pub logging: LoggingOptions, } impl Default for MetaSrvOptions { @@ -60,6 +62,7 @@ impl Default for MetaSrvOptions { selector: SelectorType::default(), use_memory_store: false, http_opts: HttpOptions::default(), + logging: LoggingOptions::default(), } } }