Skip to main content

syslog_rs/sync/
syslog_sync.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::{fmt, marker::PhantomData, sync::{Arc, Mutex}};
17
18use crate::{DefaultLocalSyslogDestination, LogFacility, LogStat, Priority, SyslogDestination, error::SyRes, formatters::{DefaultSyslogFormatter, SyslogFormatter}, sync::{LogItems, SyStream, SyStreamPri, SyStreamSyslogApi, syslog_sync_internal::SyslogSocketLockless}};
19
20pub trait DynSyslog: Send + Sync
21{
22    fn connectlog(&self) -> SyRes<()>;
23    fn setlogmask(&self, logmask: i32) -> SyRes<i32>;
24    fn closelog(&self) -> SyRes<()>;
25    fn syslog(&self, pri: Priority, fmt: String) -> SyRes<()>;
26    fn change_identity(&self, ident: Option<&str>) -> SyRes<()>;
27    fn reconnect(&self) -> SyRes<()>;
28}
29
30pub trait SyslogType: fmt::Debug
31{
32    type LockItemsBase;
33
34    type LockItems;
35
36    type LockStreamBase<D: SyslogDestination>;
37
38    type LockStream<D: SyslogDestination>;
39
40    type MoveLockType;
41
42    fn wrap_items(items: Self::LockItemsBase) -> Self::LockItems;
43    fn wrap_stream<D: SyslogDestination>(stream: Self::LockStreamBase<D>) -> Self::LockStream<D>;
44}
45
46
47
48//<D: SyslogDestination = DefaultLocalSyslogDestination>     D: SyslogDestination, 
49
50#[derive(Debug)]
51pub struct Syslog<F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination, SL = SyShared>
52where 
53    F: SyslogFormatter, 
54    D: SyslogDestination,
55    SL: SyslogType
56{   
57    /// An identification i.e program name, thread name
58    log_items: SL::LockItems,
59
60    /// A stream (unixdatagram, udp, tcp)
61    stream: SL::LockStream<D>,
62
63    _p: PhantomData<F>,
64
65    _p_not_ss: SL::MoveLockType
66}
67
68impl Syslog
69{
70    /// Opens a default connection to the local syslog server with default formatter.
71    /// 
72    /// In order to access the syslog API, use the [SyslogApi].
73    /// 
74    /// # Arguments
75    /// 
76    /// * `ident` - A program name which will appear on the logs. If none, will be determined
77    ///     automatically.
78    /// 
79    /// * `logstat` - [LogStat] an instance config.
80    /// 
81    /// * `facility` - [LogFacility] a syslog facility.
82    /// 
83    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
84    /// 
85    /// # Returns
86    /// 
87    /// A [SyRes] is returned ([Result]) with: 
88    /// 
89    /// * [Result::Ok] - with instance
90    /// 
91    /// * [Result::Err] - with error description.
92    pub 
93    fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility, 
94        net_tap_prov: DefaultLocalSyslogDestination) -> SyRes<Self> 
95    {
96        return Ok( 
97            Self
98            {
99                log_items: 
100                    <SyShared as SyslogType>
101                        ::wrap_items(
102                            <SyShared as SyslogType>::LockItemsBase::new(ident, 0xff, logstat, facility)
103                        ),
104                stream: 
105                    <SyShared as SyslogType>
106                        ::wrap_stream(
107                            <SyShared as SyslogType>::LockStreamBase::<DefaultLocalSyslogDestination>::new(logstat, net_tap_prov)?,
108                        ),
109                _p: 
110                    PhantomData,
111                _p_not_ss: 
112                    PhantomData
113            }
114        );
115    }
116}
117
118#[cfg(feature = "build_with_thread_local")]
119pub mod build_with_thread_local
120{
121    use super::*;
122
123    /// A threal local syslog which is completely lockless! It can be used in signle threaded
124    /// applications or with the [thread_local] functionality.
125    /// 
126    /// ```ignore
127    /// thread_local! 
128    /// {
129    ///     // Could add pub to make it public to whatever Foo already is public to.
130    ///     static SYSLOG: RefCell<Syslog<DefaultSyslogFormatter, SyslogLocal, SyThreadLocal>> = 
131    ///         RefCell::new(
132    ///             Syslog
133    ///                 ::<_, _, SyThreadLocal>
134    ////                ::openlog_with(Some("test"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, SyslogLocal::new())
135    ///                     .unwrap()
136    ///         );
137    /// }
138    /// ```
139    /// 
140    /// A stream is availble via [SyStreamApi].
141    /// 
142    /// ```ignore
143    /// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
144    /// ```
145    /// 
146    /// The instances will be completly separated and have own FD.
147    /// 
148    /// # Generics
149    /// 
150    /// * `D` - a [SyslogDestination] instance which is either:
151    ///     [SyslogLocal], [crate::syslog_provider::SyslogFile], [crate::syslog_provider::SyslogNet], 
152    ///     [crate::syslog_provider::SyslogTls]. By default a `SyslogLocal` is selected.
153    /// 
154    /// * `F` - a [SyslogFormatter] which sets the instance which would 
155    ///     format the message.
156    /// 
157    #[derive(Debug)]
158    pub struct SyThreadLocal;
159
160    impl SyslogType for SyThreadLocal
161    {
162        type LockItemsBase = LogItems;
163        type LockItems = Self::LockItemsBase;
164
165        type LockStreamBase<D: SyslogDestination> = SyslogSocketLockless<D>;
166        type LockStream<D: SyslogDestination> = Self::LockStreamBase<D>;
167        
168        type MoveLockType = PhantomData<*const ()>;
169
170        fn wrap_items(items: Self::LockItemsBase) -> Self::LockItems 
171        {
172            items
173        }
174
175        fn wrap_stream<D: SyslogDestination>(stream: Self::LockStreamBase<D>) -> Self::LockStream<D> 
176        {
177            stream
178        }
179    }
180
181    impl<F, D> Syslog<F, D, SyThreadLocal>
182    where 
183        F: SyslogFormatter,
184        D: SyslogDestination,
185    {
186        /// Opens a default connection to the local syslog server with default formatter.
187        /// 
188        /// # Arguments
189        /// 
190        /// * `ident` - A program name which will appear on the logs. If none, will be determined
191        ///     automatically.
192        /// 
193        /// * `logstat` - [LogStat] an instance config.
194        /// 
195        /// * `facility` - [LogFacility] a syslog facility.
196        /// 
197        /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
198        /// 
199        /// # Returns
200        /// 
201        /// A [SyRes] is returned ([Result]) with: 
202        /// 
203        /// * [Result::Ok] - with instance
204        /// 
205        /// * [Result::Err] - with error description.
206        pub 
207        fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self> 
208        {
209            return Ok(
210                Self
211                {
212                    log_items:
213                        <SyThreadLocal as SyslogType>
214                            ::wrap_items(
215                                <SyThreadLocal as SyslogType>::LockItems::new(ident, 0xff, logstat, facility)
216                            ),
217                    stream:
218                        <SyThreadLocal as SyslogType>
219                            ::wrap_stream(
220                                <SyThreadLocal as SyslogType>::LockStream::new(logstat, net_tap_prov)?
221                            ),
222                    _p: 
223                        PhantomData,
224                    _p_not_ss: 
225                        PhantomData
226                }
227            );
228        }
229    }
230
231    impl<F, D> Syslog<F, D, SyThreadLocal>
232    where 
233        F: SyslogFormatter,
234        D: SyslogDestination,
235    {
236        /// Connects the current instance to the syslog server (destination).
237        #[inline]
238        pub 
239        fn connectlog(&mut self) -> SyRes<()>
240        {
241            return 
242                self
243                    .stream
244                    .connectlog();
245        }
246
247        /// Sets the logmask to filter out the syslog calls.
248        /// 
249        /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
250        ///
251        /// # Example
252        ///
253        /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
254        ///
255        /// or
256        ///
257        /// ~(LOG_MASK!(Priority::LOG_INFO))
258        /// LOG_UPTO!(Priority::LOG_ERROR)
259        #[inline]
260        pub 
261        fn setlogmask(&mut self, logmask: i32) -> SyRes<i32>
262        {
263            return Ok(
264                self
265                    .log_items
266                    .set_logmask(logmask)
267            );
268        }
269
270        /// Closes connection to the syslog server (destination).
271        #[inline]
272        pub 
273        fn closelog(&mut self) -> SyRes<()> 
274        {
275            return 
276                self
277                    .stream
278                    .disconnectlog();
279        }
280
281        /// Similar to libc, syslog() sends data to syslog server.
282        /// 
283        /// # Arguments
284        ///
285        /// * `pri` - a priority [Priority]
286        ///
287        /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
288        ///     variable argumets amount. In Rust you should create your
289        ///     own macros like format!() or use format!()]. The [String] and ref `'static` 
290        ///     [str] can be passed directly.
291        /// 
292        /// # Returns 
293        /// 
294        /// A [SyRes] is returned which may describe an error.
295        #[inline]
296        pub 
297        fn syslog(&mut self, pri: Priority, fmt: F) -> SyRes<()>
298        {
299            let Some((formatted_msg, logstat)) = 
300                self.log_items.vsyslog1_msg::<F, D>(pri, &fmt)
301                else { return Ok(()) };
302
303            self.stream.vsyslog1(logstat, formatted_msg)
304        }
305
306        /// This function can be used to update the facility name, for example
307        /// after fork().
308        /// 
309        /// # Arguments
310        /// 
311        /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
312        ///     If set to [Option::None] would request the program name from OS.
313        #[inline]
314        pub 
315        fn change_identity(&mut self, ident: Option<&str>) -> SyRes<()>
316        {
317            self.log_items.set_identity(ident);
318
319            return Ok(());
320        }
321
322        /// Re-opens the connection to the syslog server. Can be used to 
323        /// rotate logs(handle SIGHUP).
324        /// 
325        /// # Returns
326        /// 
327        /// A [Result] is retured as [SyRes].
328        /// 
329        /// * [Result::Ok] - with empty inner type.
330        /// 
331        /// * [Result::Err] - an error code and description 
332        #[inline]
333        pub 
334        fn reconnect(&mut self) -> SyRes<()>
335        {
336            return
337                self
338                    .stream
339                    .reconnectlog();
340        }
341
342        /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
343        /// the same variant (type) as current.
344        #[inline]
345        pub 
346        fn update_tap_data(&mut self, tap_data: D) -> SyRes<()>
347        {
348            return 
349                self
350                    .stream
351                    .update_tap_data(tap_data.clone());
352        }
353    }
354
355    impl<F, D> Syslog<F, D, SyThreadLocal>
356    where 
357        F: SyslogFormatter,
358        D: SyslogDestination,
359    {
360        /// Returns the streamable [SyStream] instance which can be used with [write!].
361        /// 
362        /// It implements both [std::fmt::Write] and [std::io::Write].
363        /// 
364        /// # Example
365        /// 
366        /// ```ignore
367        /// let log = 
368        ///     SingleSyslog::openlog(
369        ///         Some("test1"), 
370        ///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
371        ///         LogFacility::LOG_DAEMON,
372        ///         SyslogLocal::new()
373        ///     ).unwrap();
374        /// 
375        /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
376        /// ```
377        pub 
378        fn get_stream<'t, PRI>(&'t mut self) -> SyStream<'t, PRI, D, F, &'t mut Self>
379        where PRI: SyStreamPri
380        {
381            SyStream
382            {
383                s: Some(self),
384                _p: PhantomData,
385                _p1: PhantomData,
386                _p2: PhantomData
387            }
388        }
389    }
390
391    impl<F, D> SyStreamSyslogApi<F, D>  
392    for &mut Syslog<F, D, SyThreadLocal>
393    where 
394        F: SyslogFormatter,
395        D: SyslogDestination,
396    {
397        type SYSLOG<'t> = &'t mut Syslog<F, D, SyThreadLocal>;
398
399        fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
400        {
401            syslog.syslog(pri, fmt)
402        }
403    }
404}
405
406#[cfg(feature = "build_with_thread_local")]
407pub use self::build_with_thread_local::SyThreadLocal;
408
409// ---- SYSLOG SHARED
410
411/// A `sync`, shared instance of the syslog client which is shared between many
412/// threads. Previously a mutex was used, but since the v5.0.0 a CoW experimental
413/// approach is used. The `CoW` creates clones of the instance without holding
414/// a long mutex locks. When writing, the instance does not hold a long lock on
415/// updated item. Only exclusive lock locks the readers, because the instance is
416/// updated and not usable anyway.
417/// 
418/// If the program has fixed amount of threads, probably the `syslog_threadlocal`
419/// will be better alternative. It has the same functionality, but avoids 
420/// any sync locks by working in current thread.
421/// 
422/// # Traits
423/// 
424/// For this isntance a [SyslogApi] and [SyStreamApi] are implemented.
425/// 
426/// # Examples
427/// 
428/// ```ignore
429/// let log = 
430///     Syslog::openlog(
431///         Some("test1"), 
432///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
433///         LogFacility::LOG_DAEMON,
434///         SyslogLocal::new()
435///     );
436/// ```
437/// 
438/// ```ignore
439/// let log = 
440///     Syslog
441///         ::<DefaultSyslogFormatter, SyslogLocal, SyShared>
442///         ::openlog_with(
443///             Some("test1"), 
444///             LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
445///             LogFacility::LOG_DAEMON,
446///             SyslogLocal::new()
447///         );
448/// ```
449/// 
450/// ```ignore
451/// pub static SYSLOG3: LazyLock<Syslog<DefaultSyslogFormatter, SyslogLocal, SyShared>> = 
452///     LazyLock::new(|| 
453///         {
454///             Syslog
455///                 ::<DefaultSyslogFormatter, SyslogLocal, SyShared>
456///                 ::openlog_with(
457///                     Some("test1"), 
458///                     LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
459///                     LogFacility::LOG_DAEMON,
460///                     SyslogLocal::new()
461///                 )
462///                 .unwrap()
463///         }
464///     );
465/// ```
466/// # Streaming
467/// 
468/// A stream is availble via [SyStreamApi].
469/// 
470/// ```ignore
471/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
472/// ```
473/// 
474/// # Generics
475/// 
476/// * `F` - a [SyslogFormatter] which sets the instance which would 
477///     format the message.
478/// 
479/// * `D` - a [SyslogDestination] instance which is either:
480///     [SyslogLocal], [SyslogFile], [SyslogNet], [SyslogTls]. By
481///     default a `SyslogLocal` is selected.
482#[derive(Debug)]
483pub struct SyShared;
484
485impl SyslogType for SyShared
486{
487    type LockItemsBase = LogItems;
488    type LockItems = Arc<Mutex<Self::LockItemsBase>>;
489
490    type LockStreamBase<D: SyslogDestination> = SyslogSocketLockless<D>;
491    type LockStream<D: SyslogDestination> = Arc<Mutex<Self::LockStreamBase<D>>>;
492
493    type MoveLockType = PhantomData<()>;
494
495    fn wrap_items(items: Self::LockItemsBase) -> Self::LockItems 
496    {
497        Arc::new(Mutex::new(items))
498    }
499
500    fn wrap_stream<D: SyslogDestination>(stream: Self::LockStreamBase<D>) -> Self::LockStream<D> 
501    {
502        Arc::new(Mutex::new(stream))
503    }
504}
505
506impl<F, D> Clone for Syslog<F, D, SyShared>
507where 
508    F: SyslogFormatter,
509    D: SyslogDestination,
510{
511    fn clone(&self) -> Self 
512    {
513        Self 
514        { 
515            log_items: 
516                self.log_items.clone(), 
517            stream: 
518                self.stream.clone(), 
519            _p: 
520                PhantomData, 
521            _p_not_ss: 
522                PhantomData,
523        }
524    }
525}
526
527impl<F, D> DynSyslog for Syslog<F, D, SyShared>
528where 
529    F: SyslogFormatter,
530    D: SyslogDestination,
531{
532    #[inline]
533    fn connectlog(&self) -> SyRes<()> 
534    {
535        self.connectlog()
536    }
537
538    #[inline]
539    fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
540    {
541        self.setlogmask(logmask)
542    }
543
544    #[inline]
545    fn closelog(&self) -> SyRes<()>
546    {
547        self.closelog()
548    }
549    
550    fn syslog(&self, pri: Priority, fmt: String) -> SyRes<()> 
551    {
552        self.syslog(pri, fmt.into())
553    }
554    
555    fn change_identity(&self, ident: Option<&str>) -> SyRes<()> 
556    {
557        self.change_identity(ident)
558    }
559    
560    fn reconnect(&self) -> SyRes<()> 
561    {
562        self.reconnect()
563    }
564}
565
566
567impl<F, D> Syslog<F, D, SyShared>
568where 
569    F: SyslogFormatter,
570    D: SyslogDestination,
571{
572    pub 
573    fn make_dyn(self) -> Box<dyn DynSyslog>
574    {
575        Box::new(self)
576    }
577
578    /// Opens a special connection to the destination syslog server with specific formatter.
579    /// 
580    /// All struct generic should be specified before calling this function.
581    /// 
582    /// In order to access the syslog API, use the [SyslogApi].
583    /// 
584    /// # Arguments
585    /// 
586    /// * `ident` - A program name which will appear on the logs. If none, will be determined
587    ///     automatically.
588    /// 
589    /// * `logstat` - [LogStat] an instance config.
590    /// 
591    /// * `facility` - [LogFacility] a syslog facility.
592    /// 
593    /// * `net_tap_prov` - a destination server. A specific `D` instance which contains infomation 
594    ///     about the destination server. See `syslog_provider.rs`.
595    /// 
596    /// # Returns
597    /// 
598    /// A [SyRes] is returned ([Result]) with: 
599    /// 
600    /// * [Result::Ok] - with instance
601    /// 
602    /// * [Result::Err] - with error description.
603    pub 
604    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self> 
605    {
606        return Ok( 
607            Self
608            {
609                log_items: 
610                    <SyShared as SyslogType>
611                        ::wrap_items(
612                            <SyShared as SyslogType>::LockItemsBase::new(ident, 0xff, logstat, facility)
613                        ),
614                stream: 
615                    <SyShared as SyslogType>
616                        ::wrap_stream(
617                            <SyShared as SyslogType>::LockStreamBase::<D>::new(logstat, net_tap_prov)?,
618                        ),
619                _p: 
620                    PhantomData,
621                _p_not_ss:
622                    PhantomData
623            }
624        );
625    }
626
627    /// Just initializes the instance from other instance. For example, in the crate which uses
628    /// this type of logging. 
629    /// 
630    /// Will work only if `F` and `D` are same.
631    pub 
632    fn attach(other: &Syslog<F, D, SyShared>) -> SyRes<Self>
633    {
634        Ok(other.clone())
635    }
636}
637
638impl<F, D> Syslog<F, D, SyShared>
639where 
640    F: SyslogFormatter,
641    D: SyslogDestination,
642{
643    /// Connects the current instance to the syslog server (destination).
644    pub 
645    fn connectlog(&self) -> SyRes<()>
646    {
647        return 
648            self
649                .stream
650                .lock()
651                .unwrap()
652                .connectlog();
653    }
654
655    /// Sets the logmask to filter out the syslog calls.
656    /// 
657    /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
658    ///
659    /// # Example
660    ///
661    /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
662    ///
663    /// or
664    ///
665    /// ~(LOG_MASK!(Priority::LOG_INFO))
666    /// LOG_UPTO!(Priority::LOG_ERROR)
667    pub 
668    fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
669    {
670        let pri = 
671            self
672                .log_items
673                .lock()
674                .unwrap()
675                .set_logmask(logmask);
676
677        return Ok(pri);
678    }
679
680    /// Closes connection to the syslog server (destination).
681    pub 
682    fn closelog(&self) -> SyRes<()> 
683    {
684        return 
685            self
686                .stream
687                .lock()
688                .unwrap()
689                .disconnectlog();
690    }
691
692    /// Similar to libc, syslog() sends data to syslog server.
693    /// 
694    /// # Arguments
695    ///
696    /// * `pri` - a priority [Priority]
697    ///
698    /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
699    ///     variable argumets amount. In Rust you should create your
700    ///     own macros like format!() or use format!()]. The [String] and ref `'static` 
701    ///     [str] can be passed directly.
702    /// 
703    /// # Returns 
704    /// 
705    /// A [SyRes] is returned which may describe an error.
706    #[inline]
707    pub 
708    fn syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
709    {
710        let Some((formatted_msg, logstat)) = 
711            self.log_items.lock().unwrap().vsyslog1_msg::<F, D>(pri, &fmt)
712        else { return Ok(()) };
713
714        self.stream.lock().unwrap().vsyslog1(logstat, formatted_msg)
715    }
716
717    /// This function can be used to update the facility name, for example
718    /// after fork().
719    /// 
720    /// # Arguments
721    /// 
722    /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
723    ///     If set to [Option::None] would request the program name from OS.
724    pub 
725    fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
726    {
727        self
728            .log_items
729            .lock()
730            .unwrap()
731            .set_identity(ident);
732
733        return Ok(());
734    }
735
736    /// Re-opens the connection to the syslog server. Can be used to 
737    /// rotate logs(handle SIGHUP).
738    /// 
739    /// # Returns
740    /// 
741    /// A [Result] is retured as [SyRes].
742    /// 
743    /// * [Result::Ok] - with empty inner type.
744    /// 
745    /// * [Result::Err] - an error code and description 
746    pub 
747    fn reconnect(&self) -> SyRes<()>
748    {
749        return
750            self
751                .stream
752                .lock()
753                .unwrap()
754                .reconnectlog();
755    }
756
757    /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
758    /// the same variant (type) as current.
759    pub 
760    fn update_tap_data(&self, tap_data: D) -> SyRes<()>
761    {
762        return
763            self
764                .stream
765                .lock()
766                .unwrap()
767                .update_tap_data(tap_data);
768    }
769}
770
771impl<F, D> Syslog<F, D, SyShared>
772where 
773    F: SyslogFormatter,
774    D: SyslogDestination,
775{
776    /// Returns the streamable [SyStream] instance which can be used with [write!].
777    /// 
778    /// It implements both [std::fmt::Write] and [std::io::Write].
779    /// 
780    /// # Example
781    /// 
782    /// ```ignore
783    /// let log = 
784    ///     SingleSyslog::openlog(
785    ///         Some("test1"), 
786    ///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
787    ///         LogFacility::LOG_DAEMON,
788    ///         SyslogLocal::new()
789    ///     ).unwrap();
790    /// 
791    /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
792    /// ```
793    pub 
794    fn get_stream<'t, PRI>(&'t self) -> SyStream<'t, PRI, D, F, &'t Self>
795    where PRI: SyStreamPri
796    {
797        SyStream
798        {
799            s: Some(self),
800            _p: PhantomData,
801            _p1: PhantomData,
802            _p2: PhantomData
803        }
804    }
805}
806
807impl<F: SyslogFormatter, D: SyslogDestination> SyStreamSyslogApi<F, D>  
808for &Syslog<F, D, SyShared>
809{
810    type SYSLOG<'t> = &'t Syslog<F, D, SyShared>;
811
812    fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
813    {
814        syslog.syslog(pri, fmt)
815    }
816}
817
818// --- QUEUE
819
820
821#[cfg(feature = "build_with_queue")]
822pub mod build_with_queue
823{
824    use std::{sync::{Arc, Mutex, Weak, atomic::{AtomicBool, Ordering}}, thread};
825
826    use crate::{DefaultLocalSyslogDestination, SyslogDestination, formatters::{DefaultSyslogFormatter, SyslogFormatter}, map_error, sy_sync_queue::{SyCmd, SyslogInternal, SyslogQueueChanSnd, SyslogQueueChannel, SyslogQueueOneChanRcv}, sync::DefaultQueueAdapter};
827
828    use super::*;
829
830    #[derive(Debug, Clone)]
831    pub struct SyQueued<S = DefaultQueueAdapter, F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination>
832    where 
833        F: SyslogFormatter, 
834        D: SyslogDestination,
835        S: SyslogQueueChannel<F, D>,
836
837    {
838        /// Control flag
839        run_control: Weak<AtomicBool>,
840
841        /// commands channel
842        pub(crate) tasks: S::ChannelSnd,
843
844        /// process thread
845        thread: Arc<Mutex<Option<thread::JoinHandle<()>>>>,
846    }
847
848    impl<F, DD, S> SyslogType for SyQueued<S, F, DD>
849    where F: SyslogFormatter, DD: SyslogDestination, S: SyslogQueueChannel<F, DD>
850    {
851        type LockItemsBase = ();
852        type LockItems = Self::LockItemsBase;
853
854        type LockStreamBase<D: SyslogDestination> = SyQueued<S, F, DD>;
855        type LockStream<D: SyslogDestination> = Self::LockStreamBase<D>;
856
857        type MoveLockType = PhantomData<()>;
858
859        fn wrap_items(items: Self::LockItemsBase) -> Self::LockItems 
860        {
861            items
862        }
863
864        fn wrap_stream<D: SyslogDestination>(stream: Self::LockStreamBase<D>) -> Self::LockStream<D> 
865        {
866            stream
867        }
868    }
869
870    unsafe impl<F, D, S> Send for SyQueued<S, F, D> 
871    where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
872    {}
873
874    impl<F, D, S> Drop for SyQueued<S, F, D>
875    where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
876    {
877        fn drop(&mut self) 
878        {
879            if let Some(ctrl) = self.run_control.upgrade()
880            {   
881                ctrl.store(false, Ordering::SeqCst);
882
883                if let Err(_e) = self.tasks.q_send_blocking(SyCmd::form_stop())
884                {
885
886                }
887
888                let join_handle = self.thread.lock().unwrap().take().unwrap();
889
890                let _ = join_handle.join();
891            }
892        }
893    }
894
895    impl<F, D, S> Clone for Syslog<F, D, SyQueued<S, F, D>>
896    where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
897    {
898        fn clone(&self) -> Self 
899        {
900            Self 
901            { 
902                log_items: 
903                    self.log_items.clone(), 
904                stream: 
905                    self.stream.clone(), 
906                _p: 
907                    PhantomData, 
908                _p_not_ss: 
909                    PhantomData
910            }
911        }
912    }
913
914    impl<F, D, S> DynSyslog for Syslog<F, D, SyQueued<S, F, D>>
915    where 
916        F: SyslogFormatter,
917        D: SyslogDestination,
918        S: SyslogQueueChannel<F, D>
919    {
920        #[inline]
921        fn connectlog(&self) -> SyRes<()> 
922        {
923            self.connectlog()
924        }
925
926        #[inline]
927        fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
928        {
929            self.setlogmask(logmask)
930        }
931
932        #[inline]
933        fn closelog(&self) -> SyRes<()>
934        {
935            self.closelog()
936        }
937        
938        fn syslog(&self, pri: Priority, fmt: String) -> SyRes<()> 
939        {
940            self.syslog(pri, fmt.into())
941        }
942        
943        fn change_identity(&self, ident: Option<&str>) -> SyRes<()> 
944        {
945            self.change_identity(ident)
946        }
947        
948        fn reconnect(&self) -> SyRes<()> 
949        {
950            self.reconnect()
951        }
952    }
953
954    impl<F, D, S> Syslog<F, D, SyQueued<S, F, D>>
955    where 
956        F: SyslogFormatter,
957        D: SyslogDestination,
958        S: SyslogQueueChannel<F, D>
959    {
960        /// Converts the current instance into dyncamic object. See [DynSyslog] for the interface.
961        pub 
962        fn make_dyn(self) -> Box<dyn DynSyslog>
963        {
964            Box::new(self)
965        }
966
967        /// Opens a default connection to the local syslog server with default formatter with
968        /// provided generics.
969        /// 
970        /// # Arguments
971        /// 
972        /// * `ident` - A program name which will appear on the logs. If none, will be determined
973        ///     automatically.
974        /// 
975        /// * `logstat` - [LogStat] an instance config.
976        /// 
977        /// * `facility` - [LogFacility] a syslog facility.
978        /// 
979        /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
980        /// 
981        /// # Returns
982        /// 
983        /// A [SyRes] is returned ([Result]) with: 
984        /// 
985        /// * [Result::Ok] - with instance
986        /// 
987        /// * [Result::Err] - with error description.
988        pub 
989        fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self>
990        {
991            // creating internal syslog struct
992
993            let log_items = 
994                LogItems::new(ident, 0xff, logstat, facility);
995
996            let stream = 
997                SyslogSocketLockless::<D>::new(logstat, net_tap_prov)?;
998
999            let (inst, sender, run_ctrl) = 
1000                SyslogInternal::<F, D, S>::new(log_items, stream)?;
1001            
1002            
1003            let thr_name: String = "syslog_queue/0".into();
1004
1005            // initiate a thread
1006            let thread_hnd = 
1007                thread::Builder::new()
1008                    .name(thr_name.clone())
1009                    .spawn(move || SyslogInternal::<F, D, S>::thread_worker(inst))
1010                    .map_err(|e| 
1011                        map_error!("{} thread spawn failed. {}", thr_name, e)
1012                    )?;
1013
1014            let sy_q = 
1015                SyQueued
1016                {
1017                    run_control: 
1018                        run_ctrl,
1019                    tasks: 
1020                        sender,
1021                    thread: 
1022                        Arc::new(Mutex::new(Some(thread_hnd))),
1023                };
1024            
1025            return Ok( 
1026                Self
1027                {
1028                    log_items: 
1029                        (),
1030                    stream: 
1031                        sy_q,
1032                    _p: 
1033                        PhantomData,
1034                    _p_not_ss:
1035                        PhantomData
1036                }
1037            );
1038        }
1039    }
1040
1041    impl<F, D, S> Syslog<F, D, SyQueued<S, F, D>>
1042    where 
1043        F: SyslogFormatter,
1044        D: SyslogDestination,
1045        S: SyslogQueueChannel<F, D>
1046    {
1047        /// Connects the current instance to the syslog server (destination).
1048        pub 
1049        fn connectlog(&self) -> SyRes<()>
1050        {
1051            let (sy_cmd, loopback) = 
1052                SyCmd::form_connectlog();
1053
1054            self.stream.tasks.q_send_blocking(sy_cmd)?;
1055
1056            return 
1057                loopback
1058                    .recv_once_blocking()?;
1059        }
1060
1061        /// Sets the logmask to filter out the syslog calls.
1062        /// 
1063        /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
1064        ///
1065        /// # Example
1066        ///
1067        /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
1068        ///
1069        /// or
1070        ///
1071        /// ~(LOG_MASK!(Priority::LOG_INFO))
1072        /// LOG_UPTO!(Priority::LOG_ERROR)
1073        pub 
1074        fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
1075        {
1076            let (sy_cmd, loopback) = 
1077                SyCmd::form_logmask(logmask);
1078
1079            self.stream.tasks.q_send_blocking(sy_cmd)?;
1080
1081            return 
1082                loopback
1083                    .recv_once_blocking();
1084        }
1085
1086        /// Closes connection to the syslog server (destination).
1087        pub 
1088        fn closelog(&self) -> SyRes<()> 
1089        {
1090            let (sy_cmd, loopback) = 
1091                SyCmd::form_disconnectlog();
1092
1093            // send stop
1094            self.stream.tasks.q_send_blocking(sy_cmd)?;
1095
1096            return 
1097                loopback
1098                    .recv_once_blocking()?;
1099        }
1100
1101        /// Similar to libc, syslog() sends data to syslog server.
1102        /// 
1103        /// # Arguments
1104        ///
1105        /// * `pri` - a priority [Priority]
1106        ///
1107        /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
1108        ///     variable argumets amount. In Rust you should create your
1109        ///     own macros like format!() or use format!()]. The [String] and ref `'static` 
1110        ///     [str] can be passed directly.
1111        /// 
1112        /// # Returns 
1113        /// 
1114        /// A [SyRes] is returned which may describe an error.
1115        pub 
1116        fn syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
1117        {
1118            // even if the thread is in a process of termination, there is
1119            // no need to sync access to the run_control field as even if
1120            // syslog thread will terminate before someone push something on the
1121            // queue, it will be left in the queue until the end of program's time.
1122            
1123            let sy_cmd = SyCmd::form_syslog(pri, fmt);
1124
1125            return self.stream.tasks.q_send_blocking(sy_cmd);
1126        }
1127
1128        /// This function can be used to update the facility name, for example
1129        /// after fork().
1130        /// 
1131        /// # Arguments
1132        /// 
1133        /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
1134        ///     If set to [Option::None] would request the program name from OS.
1135        pub 
1136        fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
1137        {
1138            let sy_cmd = 
1139                SyCmd::form_change_ident(ident.map(|v| v.to_string()));
1140
1141            return 
1142                self.stream.tasks.q_send_blocking(sy_cmd);
1143        }
1144
1145        /// Re-opens the connection to the syslog server. Can be used to 
1146        /// rotate logs(handle SIGHUP).
1147        /// 
1148        /// # Returns
1149        /// 
1150        /// A [Result] is retured as [SyRes].
1151        /// 
1152        /// * [Result::Ok] - with empty inner type.
1153        /// 
1154        /// * [Result::Err] - an error code and description 
1155        pub 
1156        fn reconnect(&self) -> SyRes<()>
1157        {
1158            return 
1159                self.stream.tasks.q_send_blocking(SyCmd::form_reconnect());
1160        }
1161
1162        /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
1163        /// the same variant (type) as current.
1164        pub 
1165        fn update_tap_data(&self, tap_data: D) -> SyRes<()>
1166        {
1167            let (tap_data_cmd, loopback) = SyCmd::form_update_tap(tap_data);
1168
1169            self.stream.tasks.q_send_blocking(tap_data_cmd)?;
1170
1171            return 
1172                loopback
1173                    .recv_once_blocking()?;
1174        }
1175    }
1176
1177    impl<F, D, S> Syslog<F, D, SyQueued<S, F, D>>
1178    where 
1179        F: SyslogFormatter,
1180        D: SyslogDestination,
1181        S: SyslogQueueChannel<F, D>
1182    {
1183        /// Returns the streamable [SyStream] instance which can be used with [write!].
1184        /// 
1185        /// It implements both [std::fmt::Write] and [std::io::Write].
1186        /// 
1187        /// # Example
1188        /// 
1189        /// ```ignore
1190        /// let log = 
1191        ///     SingleSyslog::openlog(
1192        ///         Some("test1"), 
1193        ///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
1194        ///         LogFacility::LOG_DAEMON,
1195        ///         SyslogLocal::new()
1196        ///     ).unwrap();
1197        /// 
1198        /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
1199        /// ```
1200        pub 
1201        fn get_stream<'t, PRI>(&'t self) -> SyStream<'t, PRI, D, F, &'t Self>
1202        where PRI: SyStreamPri
1203        {
1204            SyStream
1205            {
1206                s: Some(self),
1207                _p: PhantomData,
1208                _p1: PhantomData,
1209                _p2: PhantomData
1210            }
1211        }
1212    }
1213
1214    impl<F, D, S> SyStreamSyslogApi<F, D>  
1215    for &Syslog<F, D, SyQueued<S, F, D>>
1216    where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
1217    {
1218        type SYSLOG<'t> = &'t Syslog<F, D, SyQueued<S, F, D>>;
1219
1220        fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
1221        {
1222            syslog.syslog(pri, fmt)
1223        }
1224    }
1225
1226
1227    /// A queued implementation for ASYNC.
1228    #[cfg(feature = "async_enabled")]
1229    pub mod syslog_async_queue
1230    {
1231        use crate::error::SyRes;
1232        use crate::sy_sync_queue::{SyCmd, SyslogQueueChanSnd, SyslogQueueChannel, SyslogQueueOneChanRcv};
1233        use crate::{Priority, Syslog};
1234        use crate::{formatters::SyslogFormatter, SyslogDestination, SyQueued};
1235        use crate::a_sync::syslog_trait::AsyncSyslogQueueApi;
1236
1237        impl<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>> AsyncSyslogQueueApi<F, D> 
1238        for Syslog<F, D, SyQueued<S, F, D>>
1239        {
1240            async 
1241            fn a_connectlog(&mut self) -> SyRes<()> 
1242            {
1243                let (sy_cmd, loopback) = 
1244                    SyCmd::form_connectlog();
1245
1246                self.stream.tasks.q_send(sy_cmd).await?;
1247
1248                return 
1249                    loopback
1250                        .recv_once()
1251                        .await?;
1252            }
1253
1254            /// Sets the logmask to filter out the syslog calls. This function behaves 
1255            /// differently as it behaves in syslog_sync.rs or syslog_async.rs.
1256            /// It may return an error if: syslog thread had exit and some thread calls
1257            /// this function. Or something happened with channel. 
1258            /// This function blocks until the previous mask is received.
1259            /// 
1260            /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
1261            ///
1262            /// # Example
1263            ///
1264            /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
1265            ///
1266            /// or
1267            ///
1268            /// ~(LOG_MASK!(Priority::LOG_INFO))
1269            /// LOG_UPTO!(Priority::LOG_ERROR) 
1270            async 
1271            fn a_setlogmask(&self, logmask: i32) -> SyRes<i32>
1272            {
1273                let (sy_cmd, loopback) = 
1274                    SyCmd::form_logmask(logmask);
1275
1276                self.stream.tasks.q_send(sy_cmd).await?;
1277
1278                return 
1279                    loopback
1280                        .recv_once()
1281                        .await;
1282            }
1283
1284            /// Closes connection to the syslog server
1285            async 
1286            fn a_closelog(&self) -> SyRes<()>
1287            {
1288                let (sy_cmd, loopback) = 
1289                    SyCmd::form_disconnectlog();
1290
1291                // send stop
1292                self.stream.tasks.q_send(sy_cmd).await?;
1293
1294                return 
1295                    loopback
1296                        .recv_once()
1297                        .await?;
1298            }
1299
1300            /// Similar to libc, syslog() sends data to syslog server, but asynchroniously.
1301            /// 
1302            /// # Arguments
1303            ///
1304            /// * `pri` - a priority [Priority]
1305            ///
1306            /// * `fmt` - a program's message to be sent as payload. The message is encoded with the
1307            ///     [SyslogFormatter] and may be different for different formatters.
1308            #[inline]
1309            async 
1310            fn a_syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
1311            {
1312                let sy_cmd = SyCmd::form_syslog(pri, fmt);
1313
1314                return self.stream.tasks.q_send(sy_cmd).await;
1315            }
1316
1317
1318            /// Performs the reconnection to the syslog server or file re-open.
1319            /// 
1320            /// # Returns
1321            /// 
1322            /// A [Result] is retured as [SyRes].
1323            /// 
1324            /// * [Result::Ok] - with empty inner type.
1325            /// 
1326            /// * [Result::Err] - an error code and description
1327            async 
1328            fn a_reconnect(&self) -> SyRes<()>
1329            {
1330                return 
1331                    self.stream.tasks.q_send(SyCmd::form_reconnect()).await;
1332            }
1333
1334            async 
1335            fn a_change_identity(&self, ident: &str) -> SyRes<()> 
1336            {
1337                let sy_cmd = 
1338                    SyCmd::form_change_ident(Some(ident.to_string()));
1339
1340                return 
1341                    self.stream.tasks.q_send(sy_cmd).await;
1342            }
1343
1344            /// Updates the inner instance destionation i.e path to file
1345            /// or server address. The type of destination can not be changed.
1346            /// 
1347            /// This function disconnects from syslog server if previously was 
1348            /// connected (and reconnects if was connected previously).
1349            /// 
1350            /// # Arguments 
1351            /// 
1352            /// * `new_tap` - a consumed instance of type `D` [SyslogDestination]
1353            /// 
1354            /// # Returns 
1355            /// 
1356            /// A [SyRes] is returned. An error may be returned if:
1357            /// 
1358            /// * connection to server was failed
1359            /// 
1360            /// * incorrect type
1361            /// 
1362            /// * disconnect frm server failed
1363            async 
1364            fn a_update_tap_data(&self, new_tap: D) -> SyRes<()>
1365            {
1366                let (tap_data_cmd, loopback) = SyCmd::form_update_tap(new_tap);
1367
1368                self.stream.tasks.q_send(tap_data_cmd).await?;
1369
1370                return 
1371                    loopback
1372                        .recv_once()
1373                        .await?;
1374            }
1375        }
1376    }
1377}
1378
1379#[cfg(feature = "build_with_queue")]
1380pub use self::build_with_queue::SyQueued;