diff --git a/Cargo.lock b/Cargo.lock index fb399b27..95c8a8bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1941,6 +1941,20 @@ dependencies = [ "capnpc", ] +[[package]] +name = "g3statsd" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "g3-build-env", + "g3-daemon", + "g3-yaml", + "log", + "tokio", + "yaml-rust2", +] + [[package]] name = "g3tiles" version = "0.3.7" diff --git a/Cargo.toml b/Cargo.toml index fa7cd5e6..cc5f1b34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "g3proxy/utils/ctl", "g3proxy/utils/ftp", "g3proxy/utils/lua", + "g3statsd", "g3tiles", "g3tiles/proto", "g3tiles/utils/ctl", diff --git a/g3statsd/Cargo.toml b/g3statsd/Cargo.toml new file mode 100644 index 00000000..cffbac08 --- /dev/null +++ b/g3statsd/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "g3statsd" +version = "0.1.0" +license.workspace = true +edition.workspace = true +description = "G3 StatsD" + +[dependencies] +anyhow.workspace = true +clap.workspace = true +log = { workspace = true, features = ["max_level_trace", "release_max_level_debug"] } +yaml-rust.workspace = true +tokio = { workspace = true, features = ["time", "signal"] } +g3-daemon.workspace = true +g3-yaml.workspace = true + +[build-dependencies] +g3-build-env.workspace = true diff --git a/g3statsd/build.rs b/g3statsd/build.rs new file mode 100644 index 00000000..47ad47a5 --- /dev/null +++ b/g3statsd/build.rs @@ -0,0 +1,19 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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. + */ + +fn main() { + g3_build_env::check_basic(); +} diff --git a/g3statsd/src/build.rs b/g3statsd/src/build.rs new file mode 100644 index 00000000..089679ff --- /dev/null +++ b/g3statsd/src/build.rs @@ -0,0 +1,41 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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. + */ + +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const PKG_NAME: &str = env!("CARGO_PKG_NAME"); + +const RUSTC_VERSION: &str = env!("G3_BUILD_RUSTC_VERSION"); +const RUSTC_CHANNEL: &str = env!("G3_BUILD_RUSTC_CHANNEL"); + +const BUILD_HOST: &str = env!("G3_BUILD_HOST"); +const BUILD_TARGET: &str = env!("G3_BUILD_TARGET"); +const BUILD_PROFILE: &str = env!("G3_BUILD_PROFILE"); +const BUILD_OPT_LEVEL: &str = env!("G3_BUILD_OPT_LEVEL"); +const BUILD_DEBUG: &str = env!("G3_BUILD_DEBUG"); + +const PACKAGE_VERSION: Option<&str> = option_env!("G3_PACKAGE_VERSION"); + +pub fn print_version(verbose_level: u8) { + println!("{PKG_NAME} {VERSION}"); + if verbose_level > 1 { + println!("Compiler: {RUSTC_VERSION} ({RUSTC_CHANNEL})"); + println!("Host: {BUILD_HOST}, Target: {BUILD_TARGET}"); + println!("Profile: {BUILD_PROFILE}, Opt Level: {BUILD_OPT_LEVEL}, Debug: {BUILD_DEBUG}"); + if let Some(package_version) = PACKAGE_VERSION { + println!("Package Version: {package_version}"); + } + } +} diff --git a/g3statsd/src/config/input/mod.rs b/g3statsd/src/config/input/mod.rs new file mode 100644 index 00000000..6b652b97 --- /dev/null +++ b/g3statsd/src/config/input/mod.rs @@ -0,0 +1,15 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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. + */ diff --git a/g3statsd/src/config/mod.rs b/g3statsd/src/config/mod.rs new file mode 100644 index 00000000..c5f1a486 --- /dev/null +++ b/g3statsd/src/config/mod.rs @@ -0,0 +1,44 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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 std::path::Path; + +use anyhow::anyhow; +use yaml_rust::{yaml, Yaml}; + +mod input; +mod output; + +pub fn load() -> anyhow::Result<&'static Path> { + let config_file = + g3_daemon::opts::config_file().ok_or_else(|| anyhow!("no config file set"))?; + + // allow multiple docs, and treat them as the same + g3_yaml::foreach_doc(config_file, |_, doc| match doc { + Yaml::Hash(map) => load_doc(map), + _ => Err(anyhow!("yaml doc root should be hash")), + })?; + + Ok(config_file) +} + +fn load_doc(map: &yaml::Hash) -> anyhow::Result<()> { + g3_yaml::foreach_kv(map, |k, v| match g3_yaml::key::normalize(k).as_str() { + "runtime" => g3_daemon::runtime::config::load(v), + _ => Err(anyhow!("invalid key {k} in main conf")), + })?; + Ok(()) +} diff --git a/g3statsd/src/config/output/mod.rs b/g3statsd/src/config/output/mod.rs new file mode 100644 index 00000000..6b652b97 --- /dev/null +++ b/g3statsd/src/config/output/mod.rs @@ -0,0 +1,15 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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. + */ diff --git a/g3statsd/src/input/mod.rs b/g3statsd/src/input/mod.rs new file mode 100644 index 00000000..6b652b97 --- /dev/null +++ b/g3statsd/src/input/mod.rs @@ -0,0 +1,15 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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. + */ diff --git a/g3statsd/src/lib.rs b/g3statsd/src/lib.rs new file mode 100644 index 00000000..734eb602 --- /dev/null +++ b/g3statsd/src/lib.rs @@ -0,0 +1,22 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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. + */ + +pub mod config; +pub mod opts; + +mod build; +mod input; +mod output; diff --git a/g3statsd/src/main.rs b/g3statsd/src/main.rs new file mode 100644 index 00000000..1faf52ce --- /dev/null +++ b/g3statsd/src/main.rs @@ -0,0 +1,68 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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 anyhow::Context; +use log::{debug, error, info}; + +use g3statsd::opts::ProcArgs; + +fn main() -> anyhow::Result<()> { + let Some(proc_args) = + g3statsd::opts::parse_clap().context("failed to parse command line options")? + else { + return Ok(()); + }; + + // set up process logger early, only proc args is used inside + g3_daemon::log::process::setup(&proc_args.daemon_config); + + let config_file = g3statsd::config::load() + .context(format!("failed to load config, opts: {:?}", &proc_args))?; + debug!("loaded config from {}", config_file.display()); + + if proc_args.daemon_config.test_config { + info!("the format of the config file is ok"); + return Ok(()); + } + + // enter daemon mode after config loaded + #[cfg(unix)] + g3_daemon::daemonize::check_enter(&proc_args.daemon_config)?; + + let ret = tokio_run(&proc_args); + + match ret { + Ok(_) => Ok(()), + Err(e) => { + error!("{:?}", e); + Err(e) + } + } +} + +fn tokio_run(_args: &ProcArgs) -> anyhow::Result<()> { + let rt = g3_daemon::runtime::config::get_runtime_config() + .start() + .context("failed to start runtime")?; + rt.block_on(async { + g3_daemon::runtime::set_main_handle(); + + // TODO setup output + // TODO setup input + + Ok(()) + }) +} diff --git a/g3statsd/src/opts.rs b/g3statsd/src/opts.rs new file mode 100644 index 00000000..50f3e306 --- /dev/null +++ b/g3statsd/src/opts.rs @@ -0,0 +1,88 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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 std::path::PathBuf; + +use anyhow::{anyhow, Context}; +use clap::{value_parser, Arg, ArgAction, Command, ValueHint}; + +use g3_daemon::opts::{DaemonArgs, DaemonArgsExt}; + +const GLOBAL_ARG_VERSION: &str = "version"; +const GLOBAL_ARG_CONFIG_FILE: &str = "config-file"; + +#[derive(Debug)] +pub struct ProcArgs { + pub daemon_config: DaemonArgs, +} + +impl Default for ProcArgs { + fn default() -> Self { + ProcArgs { + daemon_config: DaemonArgs::new(crate::build::PKG_NAME), + } + } +} + +fn build_cli_args() -> Command { + Command::new(crate::build::PKG_NAME) + .disable_version_flag(true) + .append_daemon_args() + .arg( + Arg::new(GLOBAL_ARG_VERSION) + .help("Show version") + .num_args(0) + .action(ArgAction::SetTrue) + .short('V') + .long(GLOBAL_ARG_VERSION), + ) + .arg( + Arg::new(GLOBAL_ARG_CONFIG_FILE) + .help("Config file path") + .num_args(1) + .value_name("CONFIG FILE") + .value_hint(ValueHint::FilePath) + .value_parser(value_parser!(PathBuf)) + .required_unless_present_any([GLOBAL_ARG_VERSION]) + .short('c') + .long(GLOBAL_ARG_CONFIG_FILE), + ) +} + +pub fn parse_clap() -> anyhow::Result> { + let args_parser = build_cli_args(); + let args = args_parser.get_matches(); + + let mut proc_args = ProcArgs::default(); + proc_args.daemon_config.parse_clap(&args)?; + + if args.get_flag(GLOBAL_ARG_VERSION) { + crate::build::print_version(proc_args.daemon_config.verbose_level); + return Ok(None); + } + + if let Some(config_file) = args.get_one::(GLOBAL_ARG_CONFIG_FILE) { + g3_daemon::opts::validate_and_set_config_file(config_file, crate::build::PKG_NAME) + .context(format!( + "failed to load config file {}", + config_file.display() + ))?; + } else { + return Err(anyhow!("no config file given")); + } + + Ok(Some(proc_args)) +} diff --git a/g3statsd/src/output/mod.rs b/g3statsd/src/output/mod.rs new file mode 100644 index 00000000..6b652b97 --- /dev/null +++ b/g3statsd/src/output/mod.rs @@ -0,0 +1,15 @@ +/* + * Copyright 2025 ByteDance and/or its affiliates. + * + * 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. + */