move config entities to client for docs

This commit is contained in:
mbecker20
2024-04-28 15:16:57 -07:00
parent ba52ce79fc
commit b8afd43d07
14 changed files with 408 additions and 388 deletions

2
Cargo.lock generated
View File

@@ -2010,10 +2010,10 @@ name = "logger"
version = "1.0.1"
dependencies = [
"anyhow",
"monitor_client",
"opentelemetry",
"opentelemetry-otlp",
"opentelemetry_sdk",
"serde",
"tracing",
"tracing-opentelemetry",
"tracing-subscriber",

View File

@@ -1,13 +1,16 @@
use std::sync::OnceLock;
use anyhow::{anyhow, Context};
use monitor_client::entities::config::core::{
CoreConfig, OauthCredentials,
};
use reqwest::StatusCode;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use tokio::sync::Mutex;
use crate::{
auth::{random_string, STATE_PREFIX_LENGTH},
config::{core_config, CoreConfig, OauthCredentials},
config::core_config,
};
pub fn github_oauth_client() -> &'static Option<GithubOauthClient> {

View File

@@ -2,6 +2,7 @@ use std::sync::OnceLock;
use anyhow::{anyhow, Context};
use jwt::Token;
use monitor_client::entities::config::core::{CoreConfig, OauthCredentials};
use reqwest::StatusCode;
use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value;
@@ -9,7 +10,7 @@ use tokio::sync::Mutex;
use crate::{
auth::{random_string, STATE_PREFIX_LENGTH},
config::{core_config, CoreConfig, OauthCredentials},
config::core_config,
};
pub fn google_oauth_client() -> &'static Option<GoogleOauthClient> {

View File

@@ -6,13 +6,12 @@ use async_timing_util::{
};
use hmac::{Hmac, Mac};
use jwt::SignWithKey;
use monitor_client::entities::config::core::CoreConfig;
use mungos::mongodb::bson::doc;
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use tokio::sync::Mutex;
use crate::config::CoreConfig;
use super::random_string;
type ExchangeTokenMap = Mutex<HashMap<String, (String, u128)>>;

View File

@@ -1,9 +1,8 @@
use std::sync::OnceLock;
use anyhow::Context;
use logger::LogConfig;
use merge_config_files::parse_config_file;
use monitor_client::entities::Timelength;
use monitor_client::entities::config::core::{CoreConfig, Env};
use serde::Deserialize;
pub fn frontend_path() -> &'static String {
@@ -29,61 +28,10 @@ pub fn frontend_path() -> &'static String {
}
pub fn core_config() -> &'static CoreConfig {
#[derive(Deserialize)]
struct OverrideEnv {
#[serde(default = "default_config_path")]
monitor_config_path: String,
monitor_title: Option<String>,
monitor_host: Option<String>,
monitor_port: Option<u16>,
monitor_passkey: Option<String>,
monitor_jwt_valid_for: Option<Timelength>,
monitor_monitoring_interval: Option<Timelength>,
monitor_keep_stats_for_days: Option<u64>,
monitor_keep_alerts_for_days: Option<u64>,
monitor_github_webhook_secret: Option<String>,
monitor_github_webhook_base_url: Option<String>,
monitor_docker_organizations: Option<Vec<String>>,
// logging
monitor_logging_level: Option<logger::LogLevel>,
monitor_logging_stdio: Option<logger::StdioLogMode>,
monitor_logging_otlp_endpoint: Option<String>,
monitor_local_auth: Option<bool>,
// github
monitor_github_oauth_enabled: Option<bool>,
monitor_github_oauth_id: Option<String>,
monitor_github_oauth_secret: Option<String>,
// google
monitor_google_oauth_enabled: Option<bool>,
monitor_google_oauth_id: Option<String>,
monitor_google_oauth_secret: Option<String>,
// mongo
monitor_mongo_uri: Option<String>,
monitor_mongo_address: Option<String>,
monitor_mongo_username: Option<String>,
monitor_mongo_password: Option<String>,
monitor_mongo_app_name: Option<String>,
monitor_mongo_db_name: Option<String>,
// aws
monitor_aws_access_key_id: Option<String>,
monitor_aws_secret_access_key: Option<String>,
}
fn default_config_path() -> String {
"/config/config.toml".to_string()
}
static CORE_CONFIG: OnceLock<CoreConfig> = OnceLock::new();
CORE_CONFIG.get_or_init(|| {
let env: OverrideEnv = envy::from_env()
.context("failed to parse OverrideEnv")
let env: Env = envy::from_env()
.context("failed to parse core Env")
.unwrap();
let config_path = &env.monitor_config_path;
let mut config =
@@ -171,176 +119,3 @@ pub fn core_config() -> &'static CoreConfig {
config
})
}
#[derive(Deserialize, Debug, Clone)]
pub struct CoreConfig {
#[serde(default = "default_title")]
pub title: String,
/// The host to use with oauth redirect url, whatever host
/// the user hits to access monitor. eg `https://monitor.mogh.tech`
#[serde(default)]
pub host: String,
/// Port the core web server runs on
#[serde(default = "default_core_port")]
pub port: u16,
/// Sent in auth header with req to periphery
pub passkey: String,
/// Control how long distributed JWT remain valid for. Default is 1-day
#[serde(default = "default_jwt_valid_for")]
pub jwt_valid_for: Timelength,
/// interval at which to collect server stats and send any alerts
#[serde(default = "default_monitoring_interval")]
pub monitoring_interval: Timelength,
/// Number of days to keep stats, or 0 to disable pruning. stats older than this number of days are deleted on a daily cycle
/// Default is 0 (no pruning)
#[serde(default)]
pub keep_stats_for_days: u64,
/// Number of days to keep alerts, or 0 to disable pruning. alerts older than this number of days are deleted on a daily cycle
/// Default is 0 (no pruning)
#[serde(default)]
pub keep_alerts_for_days: u64,
/// used to verify validity from github webhooks
#[serde(default)]
pub github_webhook_secret: String,
/// used to form the frontend listener url, if None will use 'host'.
pub github_webhook_base_url: Option<String>,
/// allowed docker orgs used with monitor. first in this list will be default for build
#[serde(default)]
pub docker_organizations: Vec<String>,
/// Configure logging
#[serde(default)]
pub logging: LogConfig,
/// enable login with local auth
#[serde(default)]
pub local_auth: bool,
#[serde(default)]
pub github_oauth: OauthCredentials,
#[serde(default)]
pub google_oauth: OauthCredentials,
pub mongo: MongoConfig,
#[serde(default)]
pub aws: AwsCredentials,
}
fn default_title() -> String {
String::from("Monitor")
}
fn default_core_port() -> u16 {
9000
}
fn default_jwt_valid_for() -> Timelength {
Timelength::OneDay
}
fn default_monitoring_interval() -> Timelength {
Timelength::FifteenSeconds
}
impl CoreConfig {
pub fn sanitized(&self) -> CoreConfig {
let mut config = self.clone();
config.passkey = empty_or_redacted(&config.passkey);
config.github_webhook_secret =
empty_or_redacted(&config.github_webhook_secret);
config.github_oauth.id =
empty_or_redacted(&config.github_oauth.id);
config.github_oauth.secret =
empty_or_redacted(&config.github_oauth.secret);
config.google_oauth.id =
empty_or_redacted(&config.google_oauth.id);
config.google_oauth.secret =
empty_or_redacted(&config.google_oauth.secret);
config.mongo.uri =
config.mongo.uri.map(|cur| empty_or_redacted(&cur));
config.mongo.username =
config.mongo.username.map(|cur| empty_or_redacted(&cur));
config.mongo.password =
config.mongo.password.map(|cur| empty_or_redacted(&cur));
config.aws.access_key_id =
empty_or_redacted(&config.aws.access_key_id);
config.aws.secret_access_key =
empty_or_redacted(&config.aws.secret_access_key);
config
}
}
fn empty_or_redacted(src: &str) -> String {
if src.is_empty() {
String::new()
} else {
String::from("##############")
}
}
#[derive(Deserialize, Debug, Clone, Default)]
pub struct OauthCredentials {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub id: String,
#[serde(default)]
pub secret: String,
}
#[derive(Deserialize, Debug, Clone)]
pub struct MongoConfig {
pub uri: Option<String>,
pub address: Option<String>,
pub username: Option<String>,
pub password: Option<String>,
#[serde(default = "default_core_mongo_app_name")]
pub app_name: String,
#[serde(default = "default_core_mongo_db_name")]
pub db_name: String,
}
fn default_core_mongo_app_name() -> String {
"monitor_core".to_string()
}
fn default_core_mongo_db_name() -> String {
"monitor".to_string()
}
impl Default for MongoConfig {
fn default() -> Self {
Self {
uri: None,
address: Some("localhost:27017".to_string()),
username: None,
password: None,
app_name: default_core_mongo_app_name(),
db_name: default_core_mongo_db_name(),
}
}
}
#[derive(Deserialize, Debug, Clone, Default)]
pub struct AwsCredentials {
pub access_key_id: String,
pub secret_access_key: String,
}

View File

@@ -5,6 +5,7 @@ use monitor_client::entities::{
api_key::ApiKey,
build::Build,
builder::Builder,
config::core::MongoConfig,
deployment::Deployment,
permission::Permission,
procedure::Procedure,
@@ -20,8 +21,6 @@ use mungos::{
mongodb::{Collection, Database},
};
use crate::config::MongoConfig;
pub struct DbClient {
pub users: Collection<User>,
pub user_groups: Collection<UserGroup>,

View File

@@ -1,31 +1,12 @@
use std::{
collections::HashMap, net::IpAddr, path::PathBuf, sync::OnceLock,
};
use std::sync::OnceLock;
use clap::Parser;
use logger::LogConfig;
use merge_config_files::parse_config_paths;
use monitor_client::entities::Timelength;
use serde::Deserialize;
use monitor_client::entities::config::periphery::{
Env, PeripheryConfig,
};
use serde_json::json;
#[derive(Deserialize)]
struct Env {
#[serde(default = "default_config_paths")]
config_paths: Vec<String>,
#[serde(default)]
config_keywords: Vec<String>,
// Overrides
port: Option<u16>,
log_level: Option<logger::LogLevel>,
stdio_log_mode: Option<logger::StdioLogMode>,
}
fn default_config_paths() -> Vec<String> {
vec!["~/.config/monitor/periphery.config.toml".to_string()]
}
#[derive(Parser)]
#[command(author, about, version)]
struct CliArgs {
@@ -97,58 +78,3 @@ pub fn secrets_response() -> &'static String {
.unwrap()
})
}
#[derive(Deserialize, Debug, Clone)]
pub struct PeripheryConfig {
/// The port periphery will run on
#[serde(default = "default_periphery_port")]
pub port: u16,
/// Configure the logging level: error, warn, info, debug, trace
#[serde(default)]
pub log_level: logger::LogLevel,
/// The system directory where monitor managed repos will be cloned
#[serde(default = "default_repo_dir")]
pub repo_dir: PathBuf,
/// The rate at which the system stats will be polled to update the cache
#[serde(default = "default_stats_refresh_interval")]
pub stats_polling_rate: Timelength,
/// Logging configuration
#[serde(default)]
pub logging: LogConfig,
/// Limits which IPv4 addresses are allowed to call the api
#[serde(default)]
pub allowed_ips: Vec<IpAddr>,
/// Limits the accepted passkeys
#[serde(default)]
pub passkeys: Vec<String>,
/// Mapping on local periphery secrets. These can be interpolated into eg. Deployment environment variables.
#[serde(default)]
pub secrets: HashMap<String, String>,
/// Mapping of github usernames to access tokens
#[serde(default)]
pub github_accounts: HashMap<String, String>,
/// Mapping of docker usernames to access tokens
#[serde(default)]
pub docker_accounts: HashMap<String, String>,
}
fn default_periphery_port() -> u16 {
8120
}
fn default_repo_dir() -> PathBuf {
"/repos".parse().unwrap()
}
fn default_stats_refresh_interval() -> Timelength {
Timelength::FiveSeconds
}

View File

@@ -0,0 +1,230 @@
use serde::{Deserialize, Serialize};
use crate::entities::{
logger::{LogConfig, LogLevel, StdioLogMode},
Timelength,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Env {
#[serde(default = "default_config_path")]
pub monitor_config_path: String,
pub monitor_title: Option<String>,
pub monitor_host: Option<String>,
pub monitor_port: Option<u16>,
pub monitor_passkey: Option<String>,
pub monitor_jwt_valid_for: Option<Timelength>,
pub monitor_monitoring_interval: Option<Timelength>,
pub monitor_keep_stats_for_days: Option<u64>,
pub monitor_keep_alerts_for_days: Option<u64>,
pub monitor_github_webhook_secret: Option<String>,
pub monitor_github_webhook_base_url: Option<String>,
pub monitor_docker_organizations: Option<Vec<String>>,
// logging
pub monitor_logging_level: Option<LogLevel>,
pub monitor_logging_stdio: Option<StdioLogMode>,
pub monitor_logging_otlp_endpoint: Option<String>,
pub monitor_local_auth: Option<bool>,
// github
pub monitor_github_oauth_enabled: Option<bool>,
pub monitor_github_oauth_id: Option<String>,
pub monitor_github_oauth_secret: Option<String>,
// google
pub monitor_google_oauth_enabled: Option<bool>,
pub monitor_google_oauth_id: Option<String>,
pub monitor_google_oauth_secret: Option<String>,
// mongo
pub monitor_mongo_uri: Option<String>,
pub monitor_mongo_address: Option<String>,
pub monitor_mongo_username: Option<String>,
pub monitor_mongo_password: Option<String>,
pub monitor_mongo_app_name: Option<String>,
pub monitor_mongo_db_name: Option<String>,
// aws
pub monitor_aws_access_key_id: Option<String>,
pub monitor_aws_secret_access_key: Option<String>,
}
fn default_config_path() -> String {
"/config/config.toml".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoreConfig {
#[serde(default = "default_title")]
pub title: String,
/// The host to use with oauth redirect url, whatever host
/// the user hits to access monitor. eg `https://monitor.mogh.tech`
#[serde(default)]
pub host: String,
/// Port the core web server runs on
#[serde(default = "default_core_port")]
pub port: u16,
/// Sent in auth header with req to periphery
pub passkey: String,
/// Control how long distributed JWT remain valid for. Default is 1-day
#[serde(default = "default_jwt_valid_for")]
pub jwt_valid_for: Timelength,
/// interval at which to collect server stats and send any alerts
#[serde(default = "default_monitoring_interval")]
pub monitoring_interval: Timelength,
/// Number of days to keep stats, or 0 to disable pruning. stats older than this number of days are deleted on a daily cycle
/// Default is 0 (no pruning)
#[serde(default)]
pub keep_stats_for_days: u64,
/// Number of days to keep alerts, or 0 to disable pruning. alerts older than this number of days are deleted on a daily cycle
/// Default is 0 (no pruning)
#[serde(default)]
pub keep_alerts_for_days: u64,
/// used to verify validity from github webhooks
#[serde(default)]
pub github_webhook_secret: String,
/// used to form the frontend listener url, if None will use 'host'.
pub github_webhook_base_url: Option<String>,
/// allowed docker orgs used with monitor. first in this list will be default for build
#[serde(default)]
pub docker_organizations: Vec<String>,
/// Configure logging
#[serde(default)]
pub logging: LogConfig,
/// enable login with local auth
#[serde(default)]
pub local_auth: bool,
#[serde(default)]
pub github_oauth: OauthCredentials,
#[serde(default)]
pub google_oauth: OauthCredentials,
pub mongo: MongoConfig,
#[serde(default)]
pub aws: AwsCredentials,
}
fn default_title() -> String {
String::from("Monitor")
}
fn default_core_port() -> u16 {
9000
}
fn default_jwt_valid_for() -> Timelength {
Timelength::OneDay
}
fn default_monitoring_interval() -> Timelength {
Timelength::FifteenSeconds
}
impl CoreConfig {
pub fn sanitized(&self) -> CoreConfig {
let mut config = self.clone();
config.passkey = empty_or_redacted(&config.passkey);
config.github_webhook_secret =
empty_or_redacted(&config.github_webhook_secret);
config.github_oauth.id =
empty_or_redacted(&config.github_oauth.id);
config.github_oauth.secret =
empty_or_redacted(&config.github_oauth.secret);
config.google_oauth.id =
empty_or_redacted(&config.google_oauth.id);
config.google_oauth.secret =
empty_or_redacted(&config.google_oauth.secret);
config.mongo.uri =
config.mongo.uri.map(|cur| empty_or_redacted(&cur));
config.mongo.username =
config.mongo.username.map(|cur| empty_or_redacted(&cur));
config.mongo.password =
config.mongo.password.map(|cur| empty_or_redacted(&cur));
config.aws.access_key_id =
empty_or_redacted(&config.aws.access_key_id);
config.aws.secret_access_key =
empty_or_redacted(&config.aws.secret_access_key);
config
}
}
fn empty_or_redacted(src: &str) -> String {
if src.is_empty() {
String::new()
} else {
String::from("##############")
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OauthCredentials {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub id: String,
#[serde(default)]
pub secret: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MongoConfig {
pub uri: Option<String>,
pub address: Option<String>,
pub username: Option<String>,
pub password: Option<String>,
#[serde(default = "default_core_mongo_app_name")]
pub app_name: String,
#[serde(default = "default_core_mongo_db_name")]
pub db_name: String,
}
fn default_core_mongo_app_name() -> String {
"monitor_core".to_string()
}
fn default_core_mongo_db_name() -> String {
"monitor".to_string()
}
impl Default for MongoConfig {
fn default() -> Self {
Self {
uri: None,
address: Some("localhost:27017".to_string()),
username: None,
password: None,
app_name: default_core_mongo_app_name(),
db_name: default_core_mongo_db_name(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AwsCredentials {
pub access_key_id: String,
pub secret_access_key: String,
}

View File

@@ -0,0 +1,2 @@
pub mod core;
pub mod periphery;

View File

@@ -0,0 +1,80 @@
use std::{collections::HashMap, net::IpAddr, path::PathBuf};
use serde::Deserialize;
use crate::entities::{
logger::{LogConfig, LogLevel, StdioLogMode},
Timelength,
};
#[derive(Deserialize)]
pub struct Env {
#[serde(default = "default_config_paths")]
pub config_paths: Vec<String>,
#[serde(default)]
pub config_keywords: Vec<String>,
// Overrides
pub port: Option<u16>,
pub log_level: Option<LogLevel>,
pub stdio_log_mode: Option<StdioLogMode>,
}
fn default_config_paths() -> Vec<String> {
vec!["~/.config/monitor/periphery.config.toml".to_string()]
}
#[derive(Deserialize, Debug, Clone)]
pub struct PeripheryConfig {
/// The port periphery will run on
#[serde(default = "default_periphery_port")]
pub port: u16,
/// Configure the logging level: error, warn, info, debug, trace
#[serde(default)]
pub log_level: LogLevel,
/// The system directory where monitor managed repos will be cloned
#[serde(default = "default_repo_dir")]
pub repo_dir: PathBuf,
/// The rate at which the system stats will be polled to update the cache
#[serde(default = "default_stats_refresh_interval")]
pub stats_polling_rate: Timelength,
/// Logging configuration
#[serde(default)]
pub logging: LogConfig,
/// Limits which IPv4 addresses are allowed to call the api
#[serde(default)]
pub allowed_ips: Vec<IpAddr>,
/// Limits the accepted passkeys
#[serde(default)]
pub passkeys: Vec<String>,
/// Mapping on local periphery secrets. These can be interpolated into eg. Deployment environment variables.
#[serde(default)]
pub secrets: HashMap<String, String>,
/// Mapping of github usernames to access tokens
#[serde(default)]
pub github_accounts: HashMap<String, String>,
/// Mapping of docker usernames to access tokens
#[serde(default)]
pub docker_accounts: HashMap<String, String>,
}
fn default_periphery_port() -> u16 {
8120
}
fn default_repo_dir() -> PathBuf {
"/repos".parse().unwrap()
}
fn default_stats_refresh_interval() -> Timelength {
Timelength::FiveSeconds
}

View File

@@ -0,0 +1,74 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LogConfig {
/// The logging level. default: info
#[serde(default)]
pub level: LogLevel,
/// Controls logging to stdout / stderr
#[serde(default)]
pub stdio: StdioLogMode,
/// Enable opentelemetry exporting
pub otlp_endpoint: Option<String>,
#[serde(default = "default_opentelemetry_service_name")]
pub opentelemetry_service_name: String,
}
fn default_opentelemetry_service_name() -> String {
String::from("Monitor")
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Trace,
Debug,
#[default]
Info,
Warn,
Error,
}
impl From<LogLevel> for tracing::Level {
fn from(value: LogLevel) -> Self {
match value {
LogLevel::Trace => tracing::Level::TRACE,
LogLevel::Debug => tracing::Level::DEBUG,
LogLevel::Info => tracing::Level::INFO,
LogLevel::Warn => tracing::Level::WARN,
LogLevel::Error => tracing::Level::ERROR,
}
}
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
)]
#[serde(rename_all = "lowercase")]
pub enum StdioLogMode {
#[default]
Standard,
Json,
None,
}

View File

@@ -10,6 +10,7 @@ pub mod alerter;
pub mod api_key;
pub mod build;
pub mod builder;
pub mod config;
pub mod deployment;
pub mod permission;
pub mod procedure;
@@ -21,6 +22,7 @@ pub mod toml;
pub mod update;
pub mod user;
pub mod user_group;
pub mod logger;
#[typeshare(serialized_as = "number")]
pub type I64 = i64;

View File

@@ -8,7 +8,9 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde.workspace = true
# local client
monitor_client.workspace = true
# external
anyhow.workspace = true
tracing.workspace = true
opentelemetry.workspace = true

View File

@@ -1,5 +1,5 @@
use anyhow::Context;
use serde::{Deserialize, Serialize};
use monitor_client::entities::logger::{LogConfig, StdioLogMode};
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{
layer::SubscriberExt, util::SubscriberInitExt,
@@ -7,27 +7,6 @@ use tracing_subscriber::{
mod opentelemetry;
#[derive(Debug, Clone, Default, Deserialize)]
pub struct LogConfig {
/// The logging level. default: info
#[serde(default)]
pub level: LogLevel,
/// Controls logging to stdout / stderr
#[serde(default)]
pub stdio: StdioLogMode,
/// Enable opentelemetry exporting
pub otlp_endpoint: Option<String>,
#[serde(default = "default_opentelemetry_service_name")]
pub opentelemetry_service_name: String,
}
fn default_opentelemetry_service_name() -> String {
String::from("Monitor")
}
pub fn init(config: &LogConfig) -> anyhow::Result<()> {
let log_level: tracing::Level = config.level.into();
@@ -66,55 +45,3 @@ pub fn init(config: &LogConfig) -> anyhow::Result<()> {
}
.context("failed to init logger")
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Trace,
Debug,
#[default]
Info,
Warn,
Error,
}
impl From<LogLevel> for tracing::Level {
fn from(value: LogLevel) -> Self {
match value {
LogLevel::Trace => tracing::Level::TRACE,
LogLevel::Debug => tracing::Level::DEBUG,
LogLevel::Info => tracing::Level::INFO,
LogLevel::Warn => tracing::Level::WARN,
LogLevel::Error => tracing::Level::ERROR,
}
}
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
)]
#[serde(rename_all = "lowercase")]
pub enum StdioLogMode {
#[default]
Standard,
Json,
None,
}