1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::LoggerError;
use std::str::FromStr;
use structopt::StructOpt;
#[derive(Debug, Clone, StructOpt)]
#[structopt(rename_all = "kebab-case")]
pub struct LoggerConfig {
#[structopt(long, env = "LS_LOGGER_LEVEL", default_value = "debug")]
pub env_filter: String,
#[structopt(flatten)]
pub stdout_output: StandardOutputConfig,
#[structopt(flatten)]
pub file_output: FileOutputConfig,
}
impl Default for LoggerConfig {
fn default() -> Self {
Self {
env_filter: "debug".to_owned(),
stdout_output: StandardOutputConfig::default(),
file_output: FileOutputConfig::default(),
}
}
}
#[derive(Debug, Clone, StructOpt)]
#[structopt(rename_all = "kebab-case")]
pub struct StandardOutputConfig {
#[structopt(long, env = "LS_LOGGER_STDOUT_OUTPUT_ENABLED", parse(try_from_str), default_value = "true")]
pub stdout_enabled: bool,
#[structopt(long, env = "LS_LOGGER_STDOUT_OUTPUT_USE_ANSI_COLORS", parse(try_from_str), default_value = "true")]
pub stdout_use_ansi_colors: bool,
}
impl Default for StandardOutputConfig {
fn default() -> Self {
Self { stdout_use_ansi_colors: true, stdout_enabled: true }
}
}
#[derive(Debug, Clone, StructOpt)]
#[structopt(rename_all = "kebab-case")]
pub struct FileOutputConfig {
#[structopt(long, env = "LS_LOGGER_FILE_OUTPUT_ENABLED", parse(try_from_str), default_value = "false")]
pub file_output_enabled: bool,
#[structopt(long, env = "LS_LOGGER_FILE_OUTPUT_DIR", default_value = "/tmp")]
pub file_output_directory: String,
#[structopt(long, env = "LS_LOGGER_FILE_OUTPUT_NAME_PREFIX", default_value = "output.log")]
pub file_output_name_prefix: String,
#[structopt(long, env = "LS_LOGGER_FILE_OUTPUT_ROTATION", default_value = "daily")]
pub file_output_rotation: Rotation,
}
impl Default for FileOutputConfig {
fn default() -> Self {
Self {
file_output_enabled: false,
file_output_directory: "".to_owned(),
file_output_name_prefix: "".to_owned(),
file_output_rotation: Rotation::Daily,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Rotation {
Minutely,
Hourly,
Daily,
Never,
}
impl FromStr for Rotation {
type Err = LoggerError;
fn from_str(val: &str) -> Result<Self, Self::Err> {
match val.to_lowercase().as_ref() {
"minutely" => Ok(Rotation::Minutely),
"hourly" => Ok(Rotation::Hourly),
"daily" => Ok(Rotation::Daily),
"never" => Ok(Rotation::Never),
_ => Err(LoggerError::LoggerConfigurationError { message: format!("Could not parse rotation [{}]", val) }),
}
}
}
impl Rotation {
pub fn to_tracing_appender_rotation(&self) -> tracing_appender::rolling::Rotation {
match self {
Rotation::Minutely => tracing_appender::rolling::Rotation::MINUTELY,
Rotation::Hourly => tracing_appender::rolling::Rotation::HOURLY,
Rotation::Daily => tracing_appender::rolling::Rotation::DAILY,
Rotation::Never => tracing_appender::rolling::Rotation::NEVER,
}
}
}
impl LoggerConfig {
pub fn build() -> Self {
let app = Self::clap().setting(structopt::clap::AppSettings::AllowExternalSubcommands);
Self::from_clap(&app.get_matches())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn should_build_config() {
let config = LoggerConfig::build();
assert!(config.stdout_output.stdout_enabled);
}
}