1use crate::http_client::service;
2use crate::http_client::HttpClient;
3use crate::http_client::ReqwestClient;
4use crate::r#struct::env_config::{EnvConfig, EnvEnum};
5use crate::r#struct::log_level::LogLevel;
6pub use crate::r#struct::log_schema::LogSchema;
8pub mod http_client;
9mod r#struct;
10
11pub struct Logger<C: HttpClient = ReqwestClient> {
12 env_config: EnvConfig,
13 client: C,
14}
15
16impl Default for Logger<ReqwestClient> {
17 fn default() -> Self {
18 let env_config = EnvConfig::default();
19 Self {
20 env_config,
21 client: ReqwestClient,
22 }
23 }
24}
25
26impl Logger<ReqwestClient> {
27 pub fn new(app_version: String, verbose: bool) -> Self {
28 let env_config = EnvConfig::new(app_version, verbose);
29 Self {
30 env_config,
31 client: ReqwestClient,
32 }
33 }
34}
35
36impl<C: HttpClient> Logger<C> {
37 #[cfg(test)]
38 pub(crate) fn with_client(env_config: EnvConfig, client: C) -> Self {
39 Self { env_config, client }
40 }
41
42 pub async fn info(&self, log: LogSchema) {
43 let env_config = &self.env_config;
44 let better_log = log.to_betterstack(env_config, LogLevel::Info);
45 if better_log.env != EnvEnum::Local {
46 let _result = service::push_log(&self.client, env_config, &better_log).await;
47 }
48 if env_config.verbose {
49 println!("{}", better_log);
50 }
51 }
52
53 pub async fn warn(&self, log: LogSchema) {
54 let env_config = &self.env_config;
55 let better_log = log.to_betterstack(&self.env_config, LogLevel::Warn);
56 if better_log.env != EnvEnum::Local {
57 let _result = service::push_log(&self.client, env_config, &better_log).await;
58 }
59 if self.env_config.verbose {
60 println!("{}", better_log);
61 }
62 }
63
64 pub async fn error(&self, log: LogSchema) {
65 let env_config = &self.env_config;
66 let better_log = log.to_betterstack(&self.env_config, LogLevel::Error);
67 if better_log.env != EnvEnum::Local {
68 let _result = service::push_log(&self.client, env_config, &better_log).await;
69 }
70 if self.env_config.verbose {
71 eprintln!("{}", better_log);
72 }
73 }
74
75 pub async fn debug(&self, log: LogSchema) {
76 let better_log = log.to_betterstack(&self.env_config, LogLevel::Debug);
77 println!("{}", better_log);
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::http_client::mock::MockHttpClient;
85 use crate::r#struct::env_config::EnvConfig;
86 use std::sync::atomic::Ordering;
87
88 fn qa_config() -> EnvConfig {
89 EnvConfig::from_values("1.0.0".to_string(), EnvEnum::QA, "token".to_string(), false)
90 }
91
92 fn local_config() -> EnvConfig {
93 EnvConfig::from_values(
94 "1.0.0".to_string(),
95 EnvEnum::Local,
96 "token".to_string(),
97 false,
98 )
99 }
100
101 fn test_log() -> LogSchema {
102 LogSchema {
103 message: "test".to_string(),
104 context: "ctx".to_string(),
105 }
106 }
107
108 #[tokio::test]
109 async fn info_sends_info_level() {
110 let mock = MockHttpClient::with_success(None);
111 let logger = Logger::with_client(qa_config(), mock);
112
113 logger.info(test_log()).await;
114
115 let body = logger.client.captured_body.lock().unwrap().clone().unwrap();
116 assert_eq!(body["level"], "Info");
117 }
118
119 #[tokio::test]
120 async fn warn_sends_warn_level() {
121 let mock = MockHttpClient::with_success(None);
122 let logger = Logger::with_client(qa_config(), mock);
123
124 logger.warn(test_log()).await;
125
126 let body = logger.client.captured_body.lock().unwrap().clone().unwrap();
127 assert_eq!(body["level"], "Warn");
128 }
129
130 #[tokio::test]
131 async fn error_sends_error_level() {
132 let mock = MockHttpClient::with_success(None);
133 let logger = Logger::with_client(qa_config(), mock);
134
135 logger.error(test_log()).await;
136
137 let body = logger.client.captured_body.lock().unwrap().clone().unwrap();
138 assert_eq!(body["level"], "Error");
139 }
140
141 #[tokio::test]
142 async fn debug_skips_http() {
143 let mock = MockHttpClient::with_success(None);
144 let logger = Logger::with_client(qa_config(), mock);
145
146 logger.debug(test_log()).await;
147
148 assert_eq!(logger.client.call_count.load(Ordering::SeqCst), 0);
149 }
150
151 #[tokio::test]
152 async fn local_env_skips_http() {
153 let mock = MockHttpClient::with_success(None);
154 let logger = Logger::with_client(local_config(), mock);
155
156 logger.info(test_log()).await;
157 logger.warn(test_log()).await;
158 logger.error(test_log()).await;
159
160 assert_eq!(logger.client.call_count.load(Ordering::SeqCst), 0);
161 }
162
163 #[tokio::test]
164 async fn non_local_env_sends_http() {
165 let mock = MockHttpClient::with_success(None);
166 let logger = Logger::with_client(qa_config(), mock);
167
168 logger.info(test_log()).await;
169
170 assert_eq!(logger.client.call_count.load(Ordering::SeqCst), 1);
171 }
172}