Skip to main content

syslog_rs/
logger.rs

1/*-
2 * syslog-rs - a syslog client translated from libc to rust
3 * 
4 * Copyright 2025 Aleksandr Morozov
5 * 
6 * The syslog-rs crate can be redistributed and/or modified
7 * under the terms of either of the following licenses:
8 *
9 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
10 *
11 *   2. The MIT License (MIT)
12 *                     
13 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
14 */
15
16use std::{borrow::Cow, env::{self, VarError}, str::FromStr};
17
18#[cfg(feature = "build_sync")]
19use crate::{LogFacility, LogStat, Priority, };
20#[cfg(feature = "build_ext_file")]
21use crate::SyslogFile;
22#[cfg(feature = "build_ext_tls")]
23use crate::SyslogTls;
24#[cfg(feature = "build_ext_net")]
25use crate::{SyslogNetTcp, SyslogNetUdp};
26#[cfg(feature = "build_with_queue")]
27use crate::{SyQueued, sy_sync_queue::SyslogQueueChannel};
28#[cfg(feature = "build_with_thread_local")]
29use crate::{MutDynSyslog, SyThreadLocal};
30
31use crate::{
32    DynSyslog, LOG_UPTO, SyShared, Syslog, SyslogDestination, SyslogLocal, 
33    error::{SyRes}, 
34    formatters::
35    {
36        DEFAULT_SYSLOG_FORMATTER_TYPE, FormatFile, FormatRfc3146, FormatRfc5424, SyslogFormatter, 
37        SyslogFormatters
38    }, 
39    map_error, throw_error
40};
41
42
43/// * `local#` - for default
44/// * `local#/var/run/syslogcustom` - for custom path 
45pub const LOGGER_TYPE_LOCAL: &'static str = "local";
46
47/// * `udp#127.0.0.1:80` - for IPv4 + port
48/// * `udp#[fe00:::0]:80` - for IPv6 + port
49/// * `udp#www.mydomain.tdl:80` - for hostname + port
50pub const LOGGER_TYPE_UDP: &'static str = "udp";
51
52/// * `tcp#127.0.0.1:80` - for IPv4 + port
53/// * `tcp#[fe00:::0]:80` - for IPv6 + port
54/// * `tcp#www.mydomain.tdl:80` - for hostname + port
55pub const LOGGER_TYPE_TCP: &'static str = "tcp";
56
57/// * `tls#127.0.0.1:80#servername#root_cert_path` - for IPv4 + port
58/// * `tls#[fe00:::0]:80#servername#root_cert_path` - for IPv6 + port
59/// * `tls#www.mydomain.tdl:80#servername#root_cert_path` - for hostname + port
60pub const LOGGER_TYPE_TLS: &'static str = "tls";
61
62/// * `file#/tmp/file1.txt` - for file append mode
63pub const LOGGER_TYPE_FILE: &'static str = "file";
64
65/// A syslog server destinatio: type and location. If not set, then logging is disabled.
66pub const SYSLOG_LOGGER_DST_URI: &'static str = "SYSLOG_LOGGER_DST_URI";
67
68/// A log facility. Starts with "LOG_*" see [LogFacility]. If not set, then default.
69pub const SYSLOG_LOGGER_LOG_FACILITY: &'static str = "SYSLOG_LOGGER_LOG_FACILITY";
70
71/// A formatter. See `formatters` for available. It is defined as `FORMATTER_NAME`.
72/// 
73/// If not defined, then a default formatter will be used.
74/// 
75/// Available: RFC3146, RFC5424, FILE.
76pub const SYSLOG_LOGGER_LOG_FORMATTER: &'static str = "SYSLOG_LOGGER_LOG_FORMATTER";
77
78/// An identification of the program. If not set, will be determined automatically.
79/// 
80/// If used in crate, then don't set this.
81pub const SYSLOG_LOGGER_LOG_IDENTITY: &'static str = "SYSLOG_LOGGER_LOG_IDENTITY";
82
83/// Limits the message to upto `logmask`. Same as in libc's syslog.
84/// 
85/// LOG_UPTO!(Priority::LOG_ERROR) see [Priority].
86pub const SYSLOG_LOGGER_LOG_UPTO: &'static str = "SYSLOG_LOGGER_LOG_UPTO";
87
88#[cfg(not(feature = "logger_lazy"))]
89mod logger_cells
90{
91    #[cfg(feature = "build_with_thread_local")]
92    use std::cell::RefCell;
93    use std::sync::OnceLock;
94
95    use crate::DynSyslog;
96    #[cfg(feature = "build_with_thread_local")]
97    use crate::MutDynSyslog;
98
99    /// A logger instances. todo: if one instance fails, use backup.
100    pub(crate) static LOGGER: OnceLock<Box<dyn DynSyslog + Send + Sync>> = OnceLock::new();
101
102    #[cfg(feature = "build_with_thread_local")]
103    thread_local! 
104    {
105        // A thread local logger
106        pub(super) static LOGGER_LOCAL: RefCell<Option<Box<dyn MutDynSyslog>>> = RefCell::new(None);
107    }
108}
109
110/// In lazy init, it is not required to call an init_*. It will be initialized
111/// automatically on the first access.
112#[cfg(feature = "logger_lazy")]
113mod logger_lazy
114{
115    use std::sync::LazyLock;
116
117    use crate::{DynSyslog, SyShared, logger::internal_init_event_log};
118
119    pub(crate) static LOGGER: LazyLock<Option<Box<dyn DynSyslog + Send + Sync>>> = 
120        LazyLock::new(
121            ||
122            {
123                match internal_init_event_log::<SyShared>(None)
124                {
125                    Ok(sy) => sy,
126                    Err(e) => 
127                    {
128                        if cfg!(feature = "logger_lazy_silence_panic")
129                        {
130                            None
131                        }
132                        else
133                        {
134                            panic!("{}", e);
135                        }
136                    }
137                }
138            }
139        );
140}
141
142fn derive_suffix(suffix: Option<&str>, env_name: &'static str) -> Cow<'static, str>
143{
144    if let Some(su) = suffix
145    {
146        Cow::Owned([env_name, "_", su].concat())
147    }
148    else
149    {
150        Cow::Borrowed(env_name)
151    }
152}
153
154#[derive(Debug)]
155struct EnvVars
156{
157    logger_type_res: String,
158    logger_facility: LogFacility,
159    logger_formatter: SyslogFormatters,
160    logger_identity: Option<String>,
161    logger_upto: Option<Priority>,
162}
163
164impl EnvVars
165{
166    fn read_env(env_sufix: Option<&str>) -> SyRes<Option<Self>>
167    {
168        let logger_type_res = 
169            match env::var(derive_suffix(env_sufix, SYSLOG_LOGGER_DST_URI).as_ref())
170            {
171                Ok(val) => 
172                    val,
173                Err(VarError::NotPresent) => 
174                    return Ok(None),
175                Err(VarError::NotUnicode(_e)) =>
176                    throw_error!("{} is not unicode", SYSLOG_LOGGER_DST_URI)
177            };
178
179        let logger_facility= 
180            match env::var(derive_suffix(env_sufix, SYSLOG_LOGGER_LOG_FACILITY).as_ref())
181            {
182                Ok(val) => 
183                    LogFacility::from_str(&val)?,
184                Err(VarError::NotPresent) => 
185                    LogFacility::LOG_DAEMON,
186                Err(VarError::NotUnicode(_e)) =>
187                    throw_error!("{} is not unicode", SYSLOG_LOGGER_LOG_FACILITY)
188            };
189
190        let logger_formatter = 
191            match env::var(derive_suffix(env_sufix, SYSLOG_LOGGER_LOG_FORMATTER).as_ref())
192            {
193                Ok(val) => 
194                    SyslogFormatters::from_str(&val)?,
195                Err(VarError::NotPresent) => 
196                    DEFAULT_SYSLOG_FORMATTER_TYPE,
197                Err(VarError::NotUnicode(_e)) =>
198                    throw_error!("{} is not unicode", SYSLOG_LOGGER_LOG_FORMATTER)
199            };
200
201        let logger_identity = 
202            match env::var(derive_suffix(env_sufix, SYSLOG_LOGGER_LOG_IDENTITY).as_ref())
203            {
204                Ok(val) => 
205                    Some(val),
206                Err(VarError::NotPresent) => 
207                    None,
208                Err(VarError::NotUnicode(_e)) =>
209                    throw_error!("{} is not unicode", SYSLOG_LOGGER_LOG_IDENTITY)
210            };
211
212            
213        let logger_upto = 
214            match env::var(derive_suffix(env_sufix, SYSLOG_LOGGER_LOG_UPTO).as_ref())
215            {
216                Ok(val) => 
217                    Some(Priority::from_str(&val)?),
218                Err(VarError::NotPresent) => 
219                    None,
220                Err(VarError::NotUnicode(_e)) =>
221                    throw_error!("{} is not unicode", SYSLOG_LOGGER_LOG_IDENTITY)
222            };
223
224        return Ok(
225            Some(
226                Self
227                {
228                    logger_type_res, logger_facility, logger_formatter, logger_identity, logger_upto
229                }
230            )
231        );
232    }
233}
234
235#[derive(Debug, PartialEq, Eq)]
236enum LoggerDst<'a>
237{
238    LoLocal{ dst_path: Option<&'a str> },
239    
240    #[cfg(feature = "build_ext_net")]
241    LoUdp{ dst_host: &'a str },
242
243    #[cfg(feature = "build_ext_net")]
244    LoTcp{ dst_host: &'a str },
245
246    #[cfg(feature = "build_ext_tls")]
247    LoTls
248    {
249        dst_host: &'a str,
250        serv_name: &'a str, 
251        root_cert_path: &'a str,
252    },
253
254    #[cfg(feature = "build_ext_file")]
255    LoFile{ dst_path: &'a str }
256}
257
258impl<'a> LoggerDst<'a>
259{
260    fn from_str(lo_sch: &'a str) -> SyRes<Self>
261    {
262        let (dst_type, payload) = 
263            lo_sch
264                .split_once("#")
265                .ok_or_else(||
266                    map_error!("{} incorrect format, expected type#dst:extras", SYSLOG_LOGGER_DST_URI)
267                )?;
268
269        let res = 
270            match dst_type.to_lowercase().as_str()
271            {
272                LOGGER_TYPE_LOCAL =>
273                {
274                    let dst_path = payload.trim();
275
276                    if dst_path.is_empty() == false
277                    {
278                        Self::LoLocal{ dst_path: Some(dst_path) }
279                    }
280                    else
281                    {
282                        Self::LoLocal{ dst_path: None }
283                    }
284                },
285
286                #[cfg(feature = "build_ext_net")]
287                LOGGER_TYPE_UDP => 
288                {
289                    let dst_host = payload.trim();
290                    
291                    Self::LoUdp{ dst_host }
292                },
293
294                #[cfg(not(feature = "build_ext_net"))]
295                LOGGER_TYPE_UDP => 
296                    throw_error!("cannot use UDP, crate compiled without `build_ext_net`!"),
297
298                #[cfg(feature = "build_ext_net")]
299                LOGGER_TYPE_TCP => //SyslogNetTcp
300                {
301                    let dst_host = payload.trim();
302
303                    Self::LoTcp{ dst_host }
304                },
305
306                #[cfg(not(feature = "build_ext_net"))]
307                LOGGER_TYPE_TCP => 
308                    throw_error!("cannot use UDP, crate compiled without `build_ext_net`!"),
309                
310                #[cfg(feature = "build_ext_tls")]
311                LOGGER_TYPE_TLS => 
312                {
313                    let (dst_host, payload) = 
314                        payload
315                            .split_once("#")
316                            .map(|c| (c.0.trim(), c.1))
317                            .ok_or_else(||
318                                map_error!("SYSLOG_LOGGER_DST_URI incorrect format, expected type#dst:extras")
319                            )?;
320
321                    let (serv_name, root_cert_path) = 
322                        payload
323                            .split_once("#")
324                            .map(|c| (c.0.trim(), c.1))
325                            .ok_or_else(||
326                                map_error!("SYSLOG_LOGGER_DST_URI incorrect format, expected type#dst:extras")
327                            )?;
328
329                    Self::LoTls{ dst_host, serv_name, root_cert_path }
330                },
331
332                #[cfg(not(feature = "build_ext_tls"))]
333                LOGGER_TYPE_TLS => 
334                    throw_error!("cannot use UDP, crate compiled without `build_ext_tls`!"),
335
336                #[cfg(feature = "build_ext_file")]
337                LOGGER_TYPE_FILE => 
338                {
339                    let dst_path = payload.trim();
340
341                    Self::LoFile{ dst_path }
342                },
343
344                #[cfg(not(feature = "build_ext_file"))]
345                LOGGER_TYPE_FILE =>
346                    throw_error!("cannot use UDP, crate compiled without `build_ext_file`!"),
347
348                n => 
349                    throw_error!("SYSLOG_LOGGER_DST_URI unknown syslog dest type: {}", n)
350            };
351
352        return Ok(res);
353    }
354}
355
356
357trait LoggerDynRet
358{
359    type RetType;
360
361    fn openlog_with<F, D>(ident: Option<String>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self::RetType>
362    where
363        F: SyslogFormatter,
364        D: SyslogDestination;
365
366    fn set_prio(syslog_inst: &mut Self::RetType, opt_prio: Option<Priority>) -> SyRes<()>;
367}
368
369impl LoggerDynRet for SyShared
370{
371    type RetType = Box<dyn DynSyslog + Send + Sync>;
372
373    fn openlog_with<F, D>(ident: Option<String>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self::RetType>
374    where
375        F: SyslogFormatter,
376        D: SyslogDestination,
377    {
378        Syslog
379            ::<F, D, Self>
380            ::openlog_with(ident.as_ref().map(|d| d.as_str()), 
381                logstat, facility, net_tap_prov)
382                    .map(|sy| sy.make_dyn())
383    }
384
385    fn set_prio(syslog_inst: &mut Self::RetType, opt_prio: Option<Priority>) -> SyRes<()>
386    {
387        if let Some(prio) = opt_prio
388        {
389            syslog_inst.setlogmask(LOG_UPTO!(prio))?;
390        }
391
392        return Ok(());
393    }
394}
395
396#[cfg(feature = "build_with_thread_local")]
397impl LoggerDynRet for SyThreadLocal
398{
399    type RetType = Box<dyn MutDynSyslog>;
400
401    fn openlog_with<F, D>(ident: Option<String>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self::RetType>
402    where
403        F: SyslogFormatter,
404        D: SyslogDestination,
405    {
406        Syslog
407            ::<F, D, Self>
408            ::openlog_with(ident.as_ref().map(|d| d.as_str()), 
409                logstat, facility, net_tap_prov)
410                    .map(|sy| sy.make_dyn())
411    }
412
413    fn set_prio(syslog_inst: &mut Self::RetType, opt_prio: Option<Priority>) -> SyRes<()>
414    {
415        if let Some(prio) = opt_prio
416        {
417            syslog_inst.setlogmask(LOG_UPTO!(prio))?;
418        }
419
420        return Ok(());
421    }
422}
423
424/// Initialzes the logger of type [SyShared] from the environment variables. 
425/// If variables are not configured, exists without error. Should be called only once. 
426/// 
427/// The behaviour after `fork()` was not tested. So, it is advised to reconnect because the
428/// FD will become invalid.
429/// 
430/// # Arguments
431/// 
432/// * `env_suffix` - specifies a specific suffix of environment variables. For example,
433///     `SYSLOG_LOGGER_DST_URI` with suffix set to `XIO`, an `env` will be 
434///     searched as `SYSLOG_LOGGER_DST_URI_XIO`.
435/// 
436/// # Returns
437/// 
438/// An error is returned if:
439/// 
440/// * called twice (re-initialization)
441/// 
442/// * if parsing of the env variables failes due to incorrect structure or whatever
443/// 
444/// * syslog client fails to establish connection (at the moment silencing this error is
445///     not supported)
446/// 
447/// # Examples:
448/// 
449/// Setting the env to:
450/// ```text
451/// SYSLOG_LOGGER_DST_URI="local#"
452/// SYSLOG_LOGGER_LOG_FACILITY="LOG_DAEMON"
453/// SYSLOG_LOGGER_LOG_FORMATTER="RFC3146"
454/// SYSLOG_LOGGER_LOG_IDENTITY="new_identity"
455/// ```
456/// 
457/// should be initialized with:
458/// 
459/// ```ignore
460/// init_event_log(None).unwrap();
461/// ```
462/// -- OR --
463/// 
464/// Setting the env to:
465/// ```text
466/// SYSLOG_LOGGER_DST_URI_XIO="local#"
467/// SYSLOG_LOGGER_LOG_FACILITY_XIO="LOG_DAEMON"
468/// SYSLOG_LOGGER_LOG_FORMATTER_XIO="RFC3146"
469/// SYSLOG_LOGGER_LOG_IDENTITY_XIO="xio0"
470/// ```
471/// 
472/// should be initialized with:
473/// 
474/// ```ignore
475/// init_event_log(Some("XIO")).unwrap();
476/// ```
477#[cfg(all(not(feature = "logger_lazy")))]
478pub 
479fn init_event_log(env_suffix: Option<&str>) -> SyRes<()>
480{
481    let Some(syslog) = internal_init_event_log::<SyShared>(env_suffix)?
482        else { return Ok(()) };
483
484    logger_cells::LOGGER.set(syslog).map_err(|_e| map_error!("duplicate call to init_event_log()"))?;
485
486    return Ok(());
487}
488
489/// Initialzes the logger of type [SyThreadLocal] from the environment variables. 
490/// If variables are not configured, exists without error. Should be called only once. 
491/// 
492/// The behaviour after `fork()` was not tested. So, it is advised to reconnect because the
493/// FD will become invalid.
494/// 
495/// # Arguments
496/// 
497/// * `env_suffix` - specifies a specific suffix of environment variables. For example,
498///     `SYSLOG_LOGGER_DST_URI` with suffix set to `XIO`, an `env` will be 
499///     searched as `SYSLOG_LOGGER_DST_URI_XIO`.
500/// 
501/// # Returns
502/// 
503/// An error is returned if:
504/// 
505/// * called twice (re-initialization)
506/// 
507/// * if parsing of the env variables failes due to incorrect structure or whatever
508/// 
509/// * syslog client fails to establish connection (at the moment silencing this error is
510///     not supported)
511/// 
512/// # Examples:
513/// 
514/// Setting the env to:
515/// ```text
516/// SYSLOG_LOGGER_DST_URI="local#"
517/// SYSLOG_LOGGER_LOG_FACILITY="LOG_DAEMON"
518/// SYSLOG_LOGGER_LOG_FORMATTER="RFC3146"
519/// SYSLOG_LOGGER_LOG_IDENTITY="new_identity"
520/// ```
521/// 
522/// should be initialized with:
523/// 
524/// ```ignore
525/// init_event_log(None).unwrap();
526/// ```
527/// -- OR --
528/// 
529/// Setting the env to:
530/// ```text
531/// SYSLOG_LOGGER_DST_URI_XIO="local#"
532/// SYSLOG_LOGGER_LOG_FACILITY_XIO="LOG_DAEMON"
533/// SYSLOG_LOGGER_LOG_FORMATTER_XIO="RFC3146"
534/// SYSLOG_LOGGER_LOG_IDENTITY_XIO="xio0"
535/// ```
536/// 
537/// should be initialized with:
538/// 
539/// ```ignore
540/// init_event_log(Some("XIO")).unwrap();
541/// ```
542#[cfg(all(feature = "build_with_thread_local", not(feature = "logger_lazy")))]
543pub 
544fn init_thread_local_event_log(env_suffix: Option<&str>) -> SyRes<()>
545{
546    let Some(syslog) = internal_init_event_log::<SyThreadLocal>(env_suffix)?
547        else { return Ok(()) };
548
549    logger_cells::LOGGER_LOCAL.set(Some(syslog));
550
551    return Ok(());
552}
553
554
555
556/// Attaches to the already existing instance of type [SyShared].
557/// 
558/// This is usefull when a executable software whant to receive messages from the
559/// crate which uses `logger`.
560/// 
561/// Cannot be called twice! An error will be returned.
562#[cfg(not(feature = "logger_lazy"))]
563pub 
564fn attach_event_log_shared<F, D>(sy: &Syslog<F, D, SyShared>) -> SyRes<()>
565where 
566    F: SyslogFormatter,
567    D: SyslogDestination,
568{
569    let clonned_sy = sy.clone().make_dyn();
570
571    logger_cells::LOGGER
572        .set(clonned_sy)
573        .map_err(|_e|
574            map_error!("syslog instance has been already initialized!")
575        )
576}
577
578/// Attaches to the already existing instance of type [SyQueued].
579/// 
580/// This is usefull when a executable software whant to receive messages from the
581/// crate which uses `logger`.
582/// 
583/// Cannot be called twice! An error will be returned.
584#[cfg(all(feature = "build_with_queue", not(feature = "logger_lazy")))]
585pub 
586fn attach_event_log_queued<F, D, S>(sy: &Syslog<F, D, SyQueued<S, F, D>>) -> SyRes<()>
587where 
588    F: SyslogFormatter,
589    D: SyslogDestination,
590    S: SyslogQueueChannel<F, D>
591{
592    let clonned_sy = sy.clone().make_dyn();
593
594    logger_cells::LOGGER
595        .set(clonned_sy)
596        .map_err(|_e|
597            map_error!("syslog instance has been already initialized!")
598        )
599}
600
601fn internal_init_event_log<OUT: LoggerDynRet>(env_suffix: Option<&str>) -> SyRes<Option<OUT::RetType>> //Box<dyn DynSyslog + Send + Sync>
602{
603    let Some(envs) = EnvVars::read_env(env_suffix)?
604        else { return Ok(None) };
605
606    let dst = LoggerDst::from_str(&envs.logger_type_res)?;
607    
608    match dst
609    {
610        LoggerDst::LoLocal{ dst_path } =>
611        {
612            let tap = 
613                if let Some(dst_path) = dst_path
614                {
615                    SyslogLocal::new_custom_path(dst_path, false)?
616                }
617                else
618                {
619                    SyslogLocal::new()
620                };
621
622            let mut syslog_inst = 
623                match envs.logger_formatter
624                {
625                    SyslogFormatters::FormatRfc3146 => 
626                    {
627                        OUT::
628                            openlog_with
629                                ::<FormatRfc3146, SyslogLocal>(envs.logger_identity, 
630                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
631                    },
632                    SyslogFormatters::FormatRfc5424 => 
633                    {
634                        OUT::
635                            openlog_with
636                                ::<FormatRfc5424, SyslogLocal>(envs.logger_identity, 
637                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
638                    },
639                    SyslogFormatters::FormatFile => 
640                    {
641                        OUT::
642                            openlog_with
643                                ::<FormatFile, SyslogLocal>(envs.logger_identity, 
644                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
645                    },
646                };
647
648            OUT::set_prio(&mut syslog_inst, envs.logger_upto)?;
649
650            return Ok(Some(syslog_inst));
651        },
652        #[cfg(feature = "build_ext_net")]
653        LoggerDst::LoUdp{ dst_host } => 
654        {
655            let tap = SyslogNetUdp::new(dst_host, None)?;
656            
657            let mut syslog_inst = 
658                match envs.logger_formatter
659                {
660                    SyslogFormatters::FormatRfc3146 => 
661                    {
662                        OUT::
663                            openlog_with
664                                ::<FormatRfc3146, SyslogNetUdp>(envs.logger_identity, 
665                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
666                    },
667                    SyslogFormatters::FormatRfc5424 => 
668                    {
669                        OUT::
670                            openlog_with
671                                ::<FormatRfc5424, SyslogNetUdp>(envs.logger_identity, 
672                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
673                    },
674                    SyslogFormatters::FormatFile => 
675                    {
676                        OUT::
677                            openlog_with
678                                ::<FormatFile, SyslogNetUdp>(envs.logger_identity, 
679                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
680                    }
681                };
682                
683            OUT::set_prio(&mut syslog_inst, envs.logger_upto)?;
684
685            return Ok(Some(syslog_inst));
686        },
687        #[cfg(feature = "build_ext_net")]
688        LoggerDst::LoTcp{ dst_host } => //SyslogNetTcp
689        {
690            let tap = SyslogNetTcp::new(dst_host, None, None)?;
691            
692            let mut syslog_inst = 
693                match envs.logger_formatter
694                {
695                    SyslogFormatters::FormatRfc3146 => 
696                    {
697                        OUT::
698                            openlog_with
699                                ::<FormatRfc3146, SyslogNetTcp>(envs.logger_identity, 
700                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
701                    },
702                    SyslogFormatters::FormatRfc5424 => 
703                    {
704                        OUT::
705                            openlog_with
706                                ::<FormatRfc5424, SyslogNetTcp>(envs.logger_identity, 
707                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
708                    },
709                    SyslogFormatters::FormatFile => 
710                    {
711                        OUT::
712                            openlog_with
713                                ::<FormatFile, SyslogNetTcp>(envs.logger_identity, 
714                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
715                    }
716                };
717                
718            OUT::set_prio(&mut syslog_inst, envs.logger_upto)?;
719
720            return Ok(Some(syslog_inst));
721        },
722
723        #[cfg(feature = "build_ext_tls")]
724        LoggerDst::LoTls{ dst_host, serv_name, root_cert_path } => 
725        {
726            let tap = SyslogTls::new2(dst_host, None, serv_name, root_cert_path, None)?;
727
728            let mut syslog_inst = 
729                match envs.logger_formatter
730                {
731                    SyslogFormatters::FormatRfc3146 => 
732                    {
733                        OUT::
734                            openlog_with
735                                ::<FormatRfc3146, SyslogTls>(envs.logger_identity, 
736                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
737                    },
738                    SyslogFormatters::FormatRfc5424 => 
739                    {
740                        OUT::
741                            openlog_with
742                                ::<FormatRfc5424, SyslogTls>(envs.logger_identity, 
743                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID,envs. logger_facility, tap)?
744                    },
745                    SyslogFormatters::FormatFile => 
746                    {
747                        OUT::
748                            openlog_with
749                                ::<FormatFile, SyslogTls>(envs.logger_identity, 
750                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
751                    }
752                };
753                
754            OUT::set_prio(&mut syslog_inst, envs.logger_upto)?;
755
756            return Ok(Some(syslog_inst));
757        },
758
759         #[cfg(feature = "build_ext_file")]
760        LoggerDst::LoFile{ dst_path } => 
761        {
762            let tap = SyslogFile::new(dst_path);
763
764            let mut syslog_inst = 
765                match envs.logger_formatter
766                {
767                    SyslogFormatters::FormatRfc3146 => 
768                    {
769                        OUT::
770                            openlog_with
771                                ::<FormatRfc3146, SyslogFile>(envs.logger_identity, 
772                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
773                    },
774                    SyslogFormatters::FormatRfc5424 => 
775                    {
776                        OUT::
777                            openlog_with
778                                ::<FormatRfc5424, SyslogFile>(envs.logger_identity, 
779                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
780                    },
781                    SyslogFormatters::FormatFile => 
782                    {
783                        OUT::
784                            openlog_with
785                                ::<FormatFile, SyslogFile>(envs.logger_identity, 
786                                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, envs.logger_facility, tap)?
787                    }
788                };
789                
790            OUT::set_prio(&mut syslog_inst, envs.logger_upto)?;
791
792            return Ok(Some(syslog_inst));
793        }
794    }
795}
796
797/// Sends message via logger.
798#[cfg(all(feature = "build_with_thread_local", not(feature = "logger_lazy")))]
799pub 
800fn logger_log(pri: Priority, msg: String)
801{
802    if let Some(logger) = logger_cells::LOGGER.get()
803    {
804        let _ = logger.syslog(pri, msg);
805    }
806    else
807    {
808        logger_cells::LOGGER_LOCAL
809            .with_borrow_mut(
810                |b|
811                {
812                    let Some(logger) = b
813                    else { return; };
814
815                    let _ = logger.syslog(pri, msg);
816                }
817            );
818    }
819}
820
821#[cfg(all(not(feature = "build_with_thread_local"), not(feature = "logger_lazy")))]
822pub 
823fn logger_log(pri: Priority, msg: String)
824{
825    let Some(logger) = logger_cells::LOGGER.get()
826        else { return; };
827
828    let _ = logger.syslog(pri, msg);
829}
830
831#[cfg(all(feature = "logger_lazy"))]
832pub 
833fn logger_log(pri: Priority, msg: String)
834{
835    let Some(logger) = logger_lazy::LOGGER.as_ref()
836        else { return; };
837
838    let _ = logger.syslog(pri, msg);
839}
840
841/// Sends a message with the [Priority::LOG_DEBUG].
842#[macro_export]
843macro_rules! logger_dbg
844{
845    ($($arg:tt)*) => (
846        $crate::logger::logger_log($crate::Priority::LOG_DEBUG, format!($($arg)*))
847    )
848}
849
850/// Sends a message with the [Priority::LOG_INFO].
851#[macro_export]
852macro_rules! logger_info
853{
854    ($($arg:tt)*) => (
855        $crate::logger::logger_log($crate::Priority::LOG_INFO, format!($($arg)*))
856    )
857}
858
859/// Sends a message with the [Priority::LOG_ERR].
860#[macro_export]
861macro_rules! logger_error
862{
863    ($($arg:tt)*) => (
864        $crate::logger::logger_log($crate::Priority::LOG_ERR, format!($($arg)*))
865    )
866}
867
868/// Sends a message with the [Priority::LOG_WARNING].
869#[macro_export]
870macro_rules! logger_warn
871{
872    ($($arg:tt)*) => (
873        $crate::logger::logger_log($crate::Priority::LOG_WARNING, format!($($arg)*))
874    )
875}
876
877/// Sends a message with the [Priority::LOG_EMERG].
878#[macro_export]
879macro_rules! logger_emerg
880{
881    ($($arg:tt)*) => (
882        $crate::logger::logger_log($crate::Priority::LOG_EMERG, format!($($arg)*))
883    )
884}
885
886/// Sends a message with the user defined priority.
887#[macro_export]
888macro_rules! logger_custom
889{
890    ($prio:expr, $($arg:tt)*) => (
891        $crate::logger::logger_log($prio, format!($($arg)*))
892    )
893}
894
895
896#[test]
897fn test_parsing_logger()
898{
899    let res = LoggerDst::from_str("local#").unwrap();
900    assert_eq!(res, LoggerDst::LoLocal { dst_path: None });
901
902    let res = LoggerDst::from_str("local#/var/run/syslogcustom").unwrap();
903    assert_eq!(res, LoggerDst::LoLocal { dst_path: Some("/var/run/syslogcustom") });
904
905    #[cfg(feature = "build_ext_net")]
906    {
907        let res = LoggerDst::from_str("udp#127.0.0.1:80").unwrap();
908        assert_eq!(res, LoggerDst::LoUdp { dst_host: "127.0.0.1:80" });
909
910        let res = LoggerDst::from_str("udp#[fe00:::0]:80").unwrap();
911        assert_eq!(res, LoggerDst::LoUdp { dst_host: "[fe00:::0]:80" });
912
913        let res = LoggerDst::from_str("udp#www.mydomain.tdl:80").unwrap();
914        assert_eq!(res, LoggerDst::LoUdp { dst_host: "www.mydomain.tdl:80" });
915
916
917        let res = LoggerDst::from_str("tcp#127.0.0.1:80").unwrap();
918        assert_eq!(res, LoggerDst::LoTcp { dst_host: "127.0.0.1:80" });
919
920        let res = LoggerDst::from_str("tcp#[fe00:::0]:80").unwrap();
921        assert_eq!(res, LoggerDst::LoTcp { dst_host: "[fe00:::0]:80" });
922
923        let res = LoggerDst::from_str("tcp#www.mydomain.tdl:80").unwrap();
924        assert_eq!(res, LoggerDst::LoTcp { dst_host: "www.mydomain.tdl:80" });
925    }
926
927
928    #[cfg(feature = "build_ext_tls")]
929    {
930        let res = LoggerDst::from_str("tls#127.0.0.1:80#servername#/tmp/a1.crt").unwrap();
931        assert_eq!(res, LoggerDst::LoTls { dst_host: "127.0.0.1:80", serv_name: "servername", root_cert_path: "/tmp/a1.crt" });
932
933        let res = LoggerDst::from_str("tls#[fe00:::0]:80#servername#/tmp/a1.crt").unwrap();
934        assert_eq!(res, LoggerDst::LoTls { dst_host: "[fe00:::0]:80", serv_name: "servername", root_cert_path: "/tmp/a1.crt" });
935
936        let res = LoggerDst::from_str("tls#www.mydomain.tdl#servername#/tmp/a1.crt").unwrap();
937        assert_eq!(res, LoggerDst::LoTls { dst_host: "www.mydomain.tdl", serv_name: "servername", root_cert_path: "/tmp/a1.crt" });
938    }
939
940    #[cfg(feature = "build_ext_file")]
941    {
942        let res = LoggerDst::from_str("file#/tmp/file1.txt").unwrap();
943        assert_eq!(res, LoggerDst::LoFile { dst_path: "/tmp/file1.txt" });
944    }
945}
946
947#[test]
948fn test_env_set()
949{
950    unsafe { env::set_var(SYSLOG_LOGGER_DST_URI, "file#/tmp/test_log1.txt") };
951    unsafe { env::set_var(SYSLOG_LOGGER_LOG_FACILITY, "LOG_DAEMON") };
952    unsafe { env::set_var(SYSLOG_LOGGER_LOG_FORMATTER, "FILE") };
953    unsafe { env::set_var(SYSLOG_LOGGER_LOG_IDENTITY, "test_logger_file") };
954
955    let re = EnvVars::read_env(None).unwrap().unwrap();
956
957    assert_eq!(re.logger_type_res.as_str(), "file#/tmp/test_log1.txt");
958    assert_eq!(re.logger_facility, LogFacility::LOG_DAEMON);
959    assert_eq!(re.logger_formatter, SyslogFormatters::FormatFile);
960    assert_eq!(re.logger_identity.as_ref().map(|v| v.as_str()), Some("test_logger_file"));
961}
962
963#[test]
964fn test_env_set_suffix()
965{
966    let suf_xio = Some("XIO");
967    unsafe { env::set_var(derive_suffix(suf_xio, SYSLOG_LOGGER_DST_URI).as_ref(), "file#/tmp/xio_out.txt") };
968    unsafe { env::set_var(derive_suffix(suf_xio, SYSLOG_LOGGER_LOG_FACILITY).as_ref(), "LOG_DAEMON") };
969    unsafe { env::set_var(derive_suffix(suf_xio, SYSLOG_LOGGER_LOG_FORMATTER).as_ref(), "FILE") };
970    unsafe { env::set_var(derive_suffix(suf_xio, SYSLOG_LOGGER_LOG_IDENTITY).as_ref(), "xio_rs") };
971
972    let suf_so9 = Some("SOCKET9");
973    unsafe { env::set_var(derive_suffix(suf_so9, SYSLOG_LOGGER_DST_URI).as_ref(), "file#/tmp/socket9_out.txt") };
974    unsafe { env::set_var(derive_suffix(suf_so9, SYSLOG_LOGGER_LOG_FACILITY).as_ref(), "LOG_DAEMON") };
975    unsafe { env::set_var(derive_suffix(suf_so9, SYSLOG_LOGGER_LOG_FORMATTER).as_ref(), "FILE") };
976    unsafe { env::set_var(derive_suffix(suf_so9, SYSLOG_LOGGER_LOG_IDENTITY).as_ref(), "socket9") };
977
978    let re = EnvVars::read_env(suf_so9).unwrap().unwrap();
979
980    assert_eq!(re.logger_type_res.as_str(), "file#/tmp/socket9_out.txt");
981    assert_eq!(re.logger_facility, LogFacility::LOG_DAEMON);
982    assert_eq!(re.logger_formatter, SyslogFormatters::FormatFile);
983    assert_eq!(re.logger_identity.as_ref().map(|v| v.as_str()), Some("socket9"));
984}
985
986#[test]
987fn test_env_set2()
988{
989    unsafe { env::remove_var(SYSLOG_LOGGER_DST_URI) };
990    unsafe { env::set_var(SYSLOG_LOGGER_LOG_FACILITY, "LOG_DAEMON") };
991    unsafe { env::set_var(SYSLOG_LOGGER_LOG_FORMATTER, "FILE") };
992    unsafe { env::set_var(SYSLOG_LOGGER_LOG_IDENTITY, "test_logger_file") };
993
994    let re = EnvVars::read_env(None).unwrap();
995    assert_eq!(re.is_none(), true);
996}