tiny_web/sys/
log.rs

1use std::{fs::OpenOptions, io::Write, process};
2
3use chrono::Local;
4
5/// Responsible for log level
6///
7/// # Values
8///
9/// * `Info` - Informational message only;
10/// * `Warning` - Warning, the program may continue to run;
11/// * `Stop` - Error, the program must soft stop;
12/// * `Error` - Abnormal behavior, the program stops immediately;
13/// * `Critical` - Critical error, the program stops immediately, for internal use only.
14#[derive(Debug)]
15enum LogView {
16    /// Informational message only.
17    Info,
18    /// Warning, the program may continue to run.
19    Warning,
20    /// Error, the program must soft stop.
21    Stop,
22    /// Abnormal behavior, the program stops immediately.
23    Error,
24    /// Critical error, the program stops immediately, for internal use only.
25    Critical,
26}
27
28/// Responsible for description log message
29///
30/// # Values
31///
32/// * `view: LogView` - Log level;
33/// * `number: u16` - Number of log message;
34/// * `text: Option<String>` - Optional additional text;
35#[derive(Debug)]
36struct LogText {
37    /// Log level.
38    view: LogView,
39    /// Number of log message.
40    number: u16,
41    /// Optional additional text;
42    text: Option<String>,
43}
44
45/// Path to log file
46static mut LOG_FILE: String = String::new();
47
48/// Responsible for event log messages.
49pub struct Log;
50
51impl Log {
52    /// Save informational message only to log file.
53    ///
54    /// # Parameters
55    ///
56    /// * `number: u16` - Log number;
57    /// * `text: Option<String>` - Additional log description.
58    pub fn info(number: u16, text: Option<String>) -> String {
59        Log::save(LogText { view: LogView::Info, number, text })
60    }
61
62    /// Save warning message to log file, the program may continue to run.
63    ///
64    /// # Parameters
65    ///
66    /// * `number: u16` - Log number;
67    /// * `text: Option<String>` - Additional log description.
68    pub fn warning(number: u16, text: Option<String>) -> String {
69        Log::save(LogText { view: LogView::Warning, number, text })
70    }
71
72    /// Save stop message to log file, the program must soft stop.
73    ///
74    /// # Parameters
75    ///
76    /// * `number: u16` - Log number;
77    /// * `text: Option<String>` - Additional log description.
78    pub fn stop(number: u16, text: Option<String>) -> String {
79        Log::save(LogText { view: LogView::Stop, number, text })
80    }
81
82    /// Save error message to log file, this is abnormal behavior, the program stops immediately.
83    ///
84    /// # Parameters
85    ///
86    /// * `number: u16` - Log number;
87    /// * `text: Option<String>` - Additional log description.
88    ///
89    /// # Return
90    ///
91    /// Program call process::exit(1).
92    pub fn error(number: u16, text: Option<String>) -> ! {
93        Log::save(LogText { view: LogView::Error, number, text });
94        process::exit(number as i32);
95    }
96
97    /// Set new path to log file.
98    ///
99    /// # Parameters
100    ///
101    /// * `path: String` - New path to log file.
102    pub fn set_path(path: String) {
103        unsafe { LOG_FILE = path };
104    }
105
106    /// Simple save message to file.
107    ///
108    /// # Parameters
109    ///
110    /// * `log: LogText` - Description log message.
111    fn save(log: LogText) -> String {
112        let time = Local::now().format("%Y.%m.%d %H:%M:%S%.9f").to_string();
113        let text = match log.text {
114            Some(s) => format!("Text: {} -> {}", Log::number_to_text(log.number), s),
115            None => format!("Text: {}", Log::number_to_text(log.number)),
116        };
117        let str = format!("ID: {} Time: {} Type: {:?}. Number: {} {}\n", process::id(), time, log.view, log.number, text);
118
119        #[cfg(debug_assertions)]
120        eprintln!("{}", str.trim_end());
121
122        let mut file = unsafe { LOG_FILE.as_str() };
123        if file.is_empty() {
124            file = "tiny.log";
125        }
126        match OpenOptions::new().create(true).append(true).open(file) {
127            Ok(mut file) => match file.write_all(str.as_bytes()) {
128                Ok(f) => f,
129                Err(e) => Log::panic(&e.to_string()),
130            },
131            Err(e) => Log::panic(&format!("Can't save data to log file {} -> {}", file, e)),
132        };
133        text
134    }
135
136    /// Save or show panic message:
137    ///
138    /// # Parameters
139    ///
140    /// * `text: &str` - Panic message.
141    fn panic(text: &str) -> ! {
142        let time = Local::now().format("%Y.%m.%d %H:%M:%S%.9f").to_string();
143        let str = format!("ID: {} Time: {} Type: {:?}. Number: Text: Panic error -> {}\n", process::id(), time, LogView::Critical, text);
144        let file = unsafe { LOG_FILE.as_str() };
145        match OpenOptions::new().create(true).append(true).open(file) {
146            Ok(mut f) => {
147                if let Err(e) = f.write_all(str.as_bytes()) {
148                    let str = format!(
149                        r#"ID: {} Time: {} Type: {:?}. Number111: Text: Can't write log file "{}" - {}"#,
150                        process::id(),
151                        time,
152                        LogView::Critical,
153                        file,
154                        e
155                    );
156                    eprintln!("{}", &str);
157                }
158            }
159            Err(e) => {
160                let str = format!(
161                    r#"ID: {} Time: {} Type: {:?}. Number: Text: Can't open log file "{}" - {}"#,
162                    process::id(),
163                    time,
164                    LogView::Critical,
165                    file,
166                    e
167                );
168                eprintln!("{}", &str);
169            }
170        };
171        process::exit(1);
172    }
173
174    /// Get static text by log number:
175    ///
176    /// # Parameters
177    ///
178    /// * `number: u16` - Log number.
179    ///
180    /// # Return
181    ///
182    /// * `&'static str` - Static text with log number description
183    fn number_to_text(number: u16) -> &'static str {
184        match number {
185            1 => "Can't create runtime for async server",
186            2 => "Mutex can't lock cache",
187            3 => "Implementation of this protocol later",
188
189            10 => "Unable to get the app path",
190            11 => "The app path contains invalid characters",
191            12 => "The app must be on a local computer",
192            13 => "There is no path to the config file specified after the -r option",
193            14 => "Can't read the config file",
194            15 => "The config file is not found",
195            16 => "Can't detect the app path",
196            17 => "Init mode",
197            18 => "The config file 'tine.toml' is not a TOML https://toml.io/ format",
198            19 => "The lang file is not a TOML https://toml.io/ format",
199
200            50 => "The 'salt' parameter in the configuration file must be set",
201            51 => "The 'lang' parameter in the configuration file must be a string and consist of two characters according to ISO 639-1",
202            52 => "The 'max' parameter in the configuration file must be usize and greater than 0 or \"auto\"",
203            53 => "The 'bind_from' parameter in the configuration file must be IP address in format xxx.xxx.xxx.xxx or \"any\" or empty for Unix domain socket",
204            54 => "The 'bind' parameter in the configuration file must be IP:PORT address in format xxx.xxx.xxx.xxx:yyyy or Unix domain socket",
205            55 => "The 'rpc_from' parameter in the configuration file must be IP address in format xxx.xxx.xxx.xxx or \"any\" or empty for Unix domain socket",
206            56 => "The 'rpc' parameter in the configuration file must be IP:PORT address in format xxx.xxx.xxx.xxx:yyyy or Unix domain socket",
207            57 => "The 'db_port' parameter in the configuration file must be bool. If true sslmode is requeres",
208            58 => "The 'db_max' parameter in the configuration file must be usize and greater than 0 or \"auto\"",
209            59 => "The 'db_host' parameter in the configuration file can't be empty",
210            61 => "The 'log' parameter in the configuration file must be a string",
211            62 => "The 'salt' parameter in the configuration file must be a string",
212            63 => "The 'db_host' parameter in the configuration file must be a string",
213            64 => "The 'db_name' parameter in the configuration file must be a string",
214            65 => "The 'db_user' parameter in the configuration file must be a string",
215            66 => "The 'db_pwd' parameter in the configuration file must be a string",
216            67 => "The 'sslmode' parameter in the configuration file can be a bool",
217            71 => "The 'session' parameter in the configuration file must be a string",
218            74 => "The 'action_index' parameter in the configuration file must be a string",
219            75 => "The 'action_index' parameter in the configuration file must start with the character '/' and consist of 3 or 4 non-empty parts separated by the character '/'",
220            76 => "The 'action_not_found' parameter in the configuration file must be a string",
221            77 => "The 'action_not_found' parameter in the configuration file must start with the character '/' and consist of 3 or 4 non-empty parts separated by the character '/'",
222            78 => "The 'action_err' parameter in the configuration file must be a string",
223            79 => "The 'action_err' parameter in the configuration file must start with the character '/' and consist of 3 or 4 non-empty parts separated by the character '/'",
224            81 => "The 'lang' parameter in the command line must consist of two characters according to ISO 639-1",
225
226            100 => "Error sending message",
227            101 => "Error write message to the tcp stream",
228            102 => "Error wait join handle",
229
230            200 => "Start",
231            201 => "Stop",
232            202 => "Unable to open rpc port",
233            203 => "IP address for rpc control is not allowed",
234            204 => "Reading from the rpc stream timed out",
235            205 => "Error reading from the rpc",
236            206 => "Received signal is not the stop or status signals",
237            207 => "Stop signal received successfully",
238            211 => "The app start successfully",
239            212 => "Can't start the app",
240            213 => "Can't connect to the server",
241            214 => "Can't send 'stop' signal to the server",
242            215 => "Can't write 'stop' signal to the stream",
243            216 => "Can't set read_timeout",
244            217 => "Can't read signal from stream",
245            218 => "Stop signal sent successfully", 
246            219 => "Can't set TCP_NODELAY", 
247            220 => "An error occurred while waiting for the main thread to stop",
248            221 => "Unable to connect to server due to timeout",
249            222 => "Can't connect to server",
250            223 => "Can't set write_timeout",
251            224 => "Can't send 'status' signal to the server",
252            225 => "Can't read answer from stream",
253            226 => "Can't recognize answer from stream",
254            227 => "Status signal received successfully",
255            #[cfg(debug_assertions)]
256            228 => "Get request",
257            #[cfg(debug_assertions)]
258            229 => "Load lang file",
259            #[cfg(debug_assertions)]
260            230 => "Load html template file",
261            231 => "Couldn't get client",
262
263            400 => "Unsupported HTTP protocol",
264
265            500 => "Unable to open server port",
266            501 => "IP address from which to accept connections is not allowed",
267            502 => "Failed to send completion signal",
268            503 => "An error occurred while waiting for the main thread to stop",
269            504 => "Critical socket operation error",
270            505 => "An error occurred while waiting for the threads to abort",
271            506 => "Can't set TCP_NODELAY", 
272            507 => "Can't load SSL certicertificate or private key files", 
273
274            600 => "Can't create tlsconnector to database",
275            601 => "Can't connect to database",
276            602 => "Can't execute query",
277            603 => "Connection to database is lost",
278            604 => "Database is not initialized",
279            605 => "Database is closed. Reconnect is disabled.",
280            606 => "Can't receive free permit from pool database.",
281            607 => "Can't find free database, but semaphore says that can free.",
282            609 => "Can't parse connection string",
283            610 => "Can't create pool of connections",
284            611 => "Error close connection task in connection with database",
285            612 => "Error close connection with database",
286            613 => "Can't prepare statement",
287            614 => "Unknown database type",
288            615 => "Key not found",
289            616 => "Connection with database is wrong",
290
291            700 => "Error parse html template",
292
293            1100 => "Can't open root_dir/app",
294            1101 => "Can't get dir entry",
295            1102 => "Can't open dir",
296            1103 => "Can't delete input file",
297
298            1200 => "Unable to specify node type",
299            1201 => r#"Unable to specify "if" node type"#,
300            1202 => r#"Unable to specify "loop" node type"#,
301
302            1150 => "Can't load languages from database",
303            1151 => "Language list is empty",
304
305            2000 => "Unable to read from stream",
306            2001 => "It is not possible to read the first time from the stream, due to a timeout",
307            2002 => "Can't create temp file",
308            2003 => "Can't write temp file",
309            2004 => "The temporary file is partially written",
310            2005 => "Clock may have gone backwards",
311            2006 => "System error with buffer",
312            #[cfg(feature = "https")]
313            2007 => "A wrapper around a rustls::ServerConfig, providing an async accept method cannot accept an https stream",
314
315            // Ation engine
316            3000 => "Wrong cache type key of Redirect",
317            3001 => "Wrong cache type key of Route",
318            3002 => "Cannot serialize Mail Message",
319            3003 => "Cannot get Message-ID",
320            3004 => r#"Unable to read "from" mail"#,
321            3005 => r#"Unable to read "reply-to" mail"#,
322            3006 => r#"Unable to read "to" mail"#,
323            3007 => r#"Unable to read "cc" mail"#,
324            3008 => r#"Unable to read "bcc" mail"#,
325            3009 => "Unable to create mail message",
326            3010 => "Unable to get content type from filename",
327            3011 => "Cannot read parameter for Mail config",
328            3012 => "Cannot send email via sendmail transport",
329            3013 => "Cannot send email via file transport",
330            3014 => "Cannot send email via smtp transport",
331            3015 => "Cannot create dir for file transport",
332
333            _ => "Unknown error"
334        }
335    }
336}