Skip to main content

syslog_rs/sync/
syslog_sync_queue.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
16
17use std::marker::PhantomData;
18use std::{fmt, thread};
19use std::sync::{Arc, Mutex, Weak};
20use std::sync::atomic::{AtomicBool, Ordering};
21
22use crate::formatters::{DefaultSyslogFormatter, SyslogFormatter};
23use crate::sync::syslog_sync_internal::SyslogSocketLockless;
24use crate::sync::{LogItems, SyStream};
25use crate::sync::DefaultQueueAdapter;
26
27use crate::{map_error, SyStreamApi, SyslogDestination};
28
29#[cfg(target_family = "unix")]
30use crate::SyslogLocal;
31
32#[cfg(target_family = "windows")]
33use crate::WindowsEvent;
34
35#[cfg(target_family = "unix")]
36pub type DefaultLocalSyslogDestination = SyslogLocal;
37#[cfg(target_family = "windows")]
38pub type DefaultLocalSyslogDestination = WindowsEvent;
39
40use crate::common::*;
41use crate::error::SyRes;
42
43use super::syslog_trait::SyslogApi;
44
45
46/// A wrapper for the data commands in the queue
47pub enum SyCmd<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>>
48{
49    /// A message to syslog server
50    Syslog
51    {
52        pri: Priority,
53        msg: F
54    },
55
56    /// A reuest to change logmask
57    Logmask
58    {
59        logmask: i32, 
60        loopback: S::OneShotChannelSnd<i32>,
61    },
62
63    /// A request to change identity
64    ChangeIdentity
65    {
66        identity: Option<String>,
67    },
68
69    /// Updates the tap settings
70    UpdateTap
71    {
72        tap_type: D,//TapTypeData,
73        loopback: S::OneShotChannelSnd<SyRes<()>>,
74    },
75
76    ConnectLog
77    {
78        loopback: S::OneShotChannelSnd<SyRes<()>>
79    },
80
81    DisconnectLog
82    {
83        loopback: S::OneShotChannelSnd<SyRes<()>>
84    },
85
86    /// A request to rotate file or reconnect.
87    Reconnect,
88
89    /// A request to stop processing and quit
90    #[allow(unused)]
91    Stop,
92}
93
94
95impl<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>> SyCmd<F, D, S>
96{
97    /// Construct a message with data to send.
98    pub(crate) 
99    fn form_syslog(pri: Priority, msg: F) -> Self
100    {
101        return 
102            Self::Syslog
103            {
104                pri, msg
105            };
106    }
107
108    pub(crate) 
109    fn form_connectlog() -> (Self, S::OneShotChannelRcv<SyRes<()>>)
110    {
111        let (tx, rx) = S::create_oneshot_channel::<SyRes<()>>();
112
113        return 
114            (Self::ConnectLog{ loopback: tx }, rx);
115    }
116
117    pub(crate) 
118    fn form_disconnectlog() -> (Self, S::OneShotChannelRcv<SyRes<()>>)
119    {
120        let (tx, rx) = S::create_oneshot_channel::<SyRes<()>>();
121
122        return 
123            (Self::DisconnectLog{ loopback: tx }, rx);
124    }
125
126    /// Constructs a message to make logmask with or without previous PRI.
127    /// 
128    /// # Arguments
129    /// 
130    /// * `logmask` - a new logmask
131    pub(crate) 
132    fn form_logmask(logmask: i32) -> (Self, S::OneShotChannelRcv<i32>)
133    {
134        let (tx, rx) = S::create_oneshot_channel::<i32>();
135
136        return 
137            (Self::Logmask{ logmask, loopback: tx }, rx);
138    }
139
140    /// Constructs a message which should change the identity (appname) of the
141    /// instance.
142    pub(crate) 
143    fn form_change_ident(identity: Option<String>) -> Self
144    {
145        return 
146            Self::ChangeIdentity
147            {
148                identity: identity
149            };
150    }
151
152    /// Constructs a message which changes the destination of the log messages i.e
153    /// changing path of the dst file or address. The `new_tap_type` 
154    /// should be the same variant [TapTypeData] as previous.
155    pub(crate)
156    fn form_update_tap(new_tap_type: D/*TapTypeData*/) -> (Self, S::OneShotChannelRcv<SyRes<()>>)
157    {
158        let (tx, rx) = S::create_oneshot_channel::<SyRes<()>>();
159
160        return (
161            Self::UpdateTap  
162            { 
163                tap_type: new_tap_type,
164                loopback: tx
165            },
166            rx
167        );
168    }
169
170    /// Constructs a message to handle SIGHUP. This is usefull only when the instance
171    /// is writing directly into the file. Or just reconnect.
172    pub(crate) 
173    fn form_reconnect() -> Self
174    {
175        return Self::Reconnect;
176    }
177
178    /// Constructs a message to stop thread gracefully. After receiving this
179    /// message a thread will quit and all messages that would be sent after
180    /// this message will be cleared from queue and a new messages will not be
181    /// received.
182    #[allow(unused)]
183    pub(crate) 
184    fn form_stop() -> Self
185    {
186        return Self::Stop;
187    }
188}
189
190/// Internal struct of the syslog client thread.
191struct SyslogInternal<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>>
192{
193    /// A explicit stop flag
194    run_flag: Arc<AtomicBool>,
195
196    /// commands channel
197    tasks: S::ChannelRcv,
198
199    /// Log config
200    log_items: LogItems,
201
202    /// socket
203    socket: SyslogSocketLockless<D>,
204}
205
206
207
208impl<F, D, S> SyslogInternal<F, D, S>
209where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
210{
211    fn new(log_items: LogItems, socket: SyslogSocketLockless<D>) -> SyRes<(Self, S::ChannelSnd, Weak<AtomicBool>)>
212    {
213        // control flag
214        let run_flag: Arc<AtomicBool> = Arc::new(AtomicBool::new(true));
215        let run_control = Arc::downgrade(&run_flag);
216
217        // creating queue for messages
218        let (sender, receiver) = S::create_channel();
219
220        // creating internal syslog struct
221        let mut inst = 
222            SyslogInternal
223            {
224                run_flag: run_flag,
225                tasks: receiver,
226                log_items: log_items,
227                socket: socket
228            };
229
230        if inst.log_items.logstat.contains(LogStat::LOG_NDELAY) == true
231        {
232            inst.socket.connectlog()?;
233        }
234
235        return Ok((inst, sender, run_control));
236    }
237
238    fn thread_worker(mut self)
239    {
240        loop
241        {
242            // self will be dropped as soon as thread will be stopped
243            if self.run_flag.load(Ordering::Relaxed) == false
244            {
245                // force leave
246                break;
247            }	
248
249            match self.tasks.q_recv_blocking()
250			{
251				Some(task) =>
252				{
253                    match task
254                    {
255                        SyCmd::Syslog{ pri, msg } =>
256                        {
257                            let Some(formatted) = self.log_items.vsyslog1_msg::<F, D>(pri, &msg)
258                                else { continue };
259
260                            self.socket.vsyslog1(formatted.1, formatted.0);
261                        },
262                        SyCmd::Logmask{ logmask, loopback } =>
263                        {
264                            let pri = self.log_items.set_logmask(logmask);
265
266                            let _ = loopback.send_once_blocking(pri);
267                        },
268                        SyCmd::ChangeIdentity{ identity } =>
269                        {
270                            self.log_items.set_identity(identity.as_ref().map(|v| v.as_str()));
271                        },
272
273                        SyCmd::UpdateTap{ tap_type, loopback } =>
274                        {
275                            let res = self.socket.update_tap_data(tap_type);
276                            
277                            if let Err(Err(e)) = loopback.send_once_blocking(res)
278                            {
279                                self.log_items.logstat.send_to_stderr(&e.to_string());
280                            }
281                        },
282
283                        SyCmd::ConnectLog{ loopback} => 
284                        {
285                            if let Err(Err(e)) = loopback.send_once_blocking(self.socket.connectlog())
286                            {
287                                self.log_items.logstat.send_to_stderr(&e.to_string());
288                            }
289                        },
290
291                        SyCmd::DisconnectLog{ loopback} => 
292                        {
293                            if let Err(Err(e)) = loopback.send_once_blocking(self.socket.disconnectlog())
294                            {
295                                self.log_items.logstat.send_to_stderr(&e.to_string());
296                            }
297                        },
298
299                        SyCmd::Reconnect =>
300                        {
301                            if let Err(e) = self.socket.disconnectlog()
302                            {
303                                self.log_items.logstat.send_to_stderr(&e.to_string());
304                            }
305                                
306                            if let Err(e) = self.socket.connectlog()
307                            {
308                                self.log_items.logstat.send_to_stderr(&e.to_string());
309                            }
310                        },
311                        SyCmd::Stop =>
312                        {
313                            // ignore the rest
314                            break;
315                        }
316                    }
317                },
318                None =>
319                {
320                    break;
321                }
322            } // match
323
324        } // loop
325
326        return;
327    }
328}
329
330/// A trait which should be implemented by the channel provider which forms a command queue.
331/// This trait provides a blocking receive interface on the receiver side.
332pub trait SyslogQueueChanRcv<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>>: fmt::Debug + Send
333{
334    /// Receive from channel in blocking mode.
335    fn q_recv_blocking(&mut self) -> Option<SyCmd<F, D, S>>;
336}
337
338/// A trait which should be implemented by the channel provider which forms a command queue.
339/// This trait provides both or either the blocking send interface.
340#[allow(async_fn_in_trait)]
341pub trait SyslogQueueChanSnd<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>>: fmt::Debug + Clone
342{
343    /// Sends the message over the channel in blocking mode.
344    fn q_send_blocking(&self, msg: SyCmd<F, D, S>) -> SyRes<()>;
345
346    /// Sends the message over the channel in async mode. By default, it returns error.
347    async fn q_send(&self, _msg: SyCmd<F, D, S>) -> SyRes<()>
348    {
349        crate::throw_error!("async is not availabe here"); 
350    }
351}
352
353/// A trait which should be implemented by the channel provider which forms a command queue.
354/// This trait provides a Oneshot channel a receive interface for both sync and async modes.
355#[allow(async_fn_in_trait)]
356pub trait SyslogQueueOneChanRcv<C>
357{
358    /// Receives once from the channel in sync mode.
359    fn recv_once_blocking(self) -> SyRes<C>;
360
361    /// Receives once from the channel in async mode. By default, it returns error if not 
362    /// implemented.
363    async fn recv_once(self) -> SyRes<C> where Self: Sized
364    {
365        crate::throw_error!("async is not availabe here"); 
366    }
367}
368
369/// A trait which should be implemented by the channel provider which forms a command queue.
370/// This trait provides a Oneshot channel a send interface for blocking mode.
371pub trait SyslogQueueOneChanSnd<C: Send>: fmt::Debug + Send
372{
373    /// Send to channel in blocing mode.
374    fn send_once_blocking(self, data: C) -> Result<(), C>;
375}
376
377/// A trait which should be implemented by the channel provider which forms a command queue.
378/// This trait provides a common interface which includes everything i.e channel, oneshot 
379/// channel and manipulations.
380pub trait SyslogQueueChannel<F: SyslogFormatter, D: SyslogDestination>: fmt::Debug + Send + Clone + 'static
381{
382    const ADAPTER_NAME: &'static str;
383    
384    /// A send side of the channel type.
385    type ChannelSnd: SyslogQueueChanSnd<F, D, Self>;
386    
387    /// A receive side of the channel type.
388    type ChannelRcv: SyslogQueueChanRcv<F, D, Self>;
389
390    /// A oneshot send side of the channel type.
391    type OneShotChannelSnd<C: Send + fmt::Debug>: SyslogQueueOneChanSnd<C>;
392
393    /// A oneshor receive side of the channel type.
394    type OneShotChannelRcv<C>: SyslogQueueOneChanRcv<C>;
395
396    /// Creates unbounded channel.
397    fn create_channel() -> (Self::ChannelSnd, Self::ChannelRcv);   
398
399    /// Creates oneshot channel.
400    fn create_oneshot_channel<C: Send + fmt::Debug>() -> (Self::OneShotChannelSnd<C>, Self::OneShotChannelRcv<C>);
401}
402
403
404
405/// A parallel, shared instance of the syslog client which is running in the 
406/// separate thread and uses a crossbeam channel to receive the messages from 
407/// the program. It is also capable to combine sync and async i.e sync code and
408/// async code is writing to the same syslog connection.
409/// 
410/// For this isntance a [SyslogApi] and [SyStreamApi] are implemented.
411/// 
412/// Also if `async` is enabled, a [AsyncSyslogQueueApi] is implemented.
413/// 
414/// ```ignore
415/// let log = 
416///     QueuedSyslog::openlog(
417///         Some("test1"), 
418///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
419///         LogFacility::LOG_DAEMON,
420///         SyslogLocal::new()
421///     );
422/// ```
423/// 
424/// ```ignore
425/// let log = 
426///     QueuedSyslog
427///         ::<DefaultQueueAdapter, DefaultSyslogFormatter, SyslogLocal>
428///         ::openlog_with(
429///             Some("test1"), 
430///             LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
431///             LogFacility::LOG_DAEMON,
432///             SyslogLocal::new()
433///         );
434/// ```
435/// 
436/// ```ignore
437/// pub static SYSLOG3: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogLocal,>> = 
438///     LazyLock::new(|| 
439///         {
440///             QueuedSyslog
441///                 ::<DefaultQueueAdapter, DefaultSyslogFormatter, SyslogLocal>
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///                 .unwrap()
449///         }
450///     );
451/// ```
452/// 
453/// A stream is availble via [SyStreamApi].
454/// 
455/// ```ignore
456/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
457/// ```
458/// 
459/// # Generics
460/// 
461/// * `S` - a [SyslogQueueChannel] a MPSC provider.
462/// 
463/// * `F` - a [SyslogFormatter] which sets the instance which would 
464///     format the message.
465/// 
466/// * `D` - a [SyslogDestination] instance which is either:
467///     [SyslogLocal], [SyslogFile], [SyslogNet], [SyslogTls]. By
468///     default a `SyslogLocal` is selected.
469#[derive(Debug, Clone)]
470pub struct QueuedSyslog<S = DefaultQueueAdapter, F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination>
471where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
472{   
473    /// Control flag
474    run_control: Weak<AtomicBool>,
475
476    /// commands channel
477    pub(crate) tasks: S::ChannelSnd,//q_adapter::SendChannelSyCmd<D>,
478
479    /// process thread
480    thread: Arc<Mutex<Option<thread::JoinHandle<()>>>>,
481
482    /// phantom for [SyslogFormatter]
483    _p: PhantomData<F>,
484
485    /// phantom for [SyslogDestination]
486    _p2: PhantomData<D>,
487}
488
489
490unsafe impl<F, D, S> Send for QueuedSyslog<S, F, D> 
491where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
492{}
493
494
495impl<F, D, S> Drop for QueuedSyslog<S, F, D>
496where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
497{
498    fn drop(&mut self) 
499    {
500        if let Some(ctrl) = self.run_control.upgrade()
501        {   
502            ctrl.store(false, Ordering::SeqCst);
503
504            if let Err(_e) = self.tasks.q_send_blocking(SyCmd::form_stop())
505            {
506
507            }
508
509            let join_handle = self.thread.lock().unwrap().take().unwrap();
510
511            let _ = join_handle.join();
512        }
513    }
514}
515
516impl QueuedSyslog
517{
518    /// Opens a default connection to the local syslog server with default formatter with
519    /// formatter [SyslogFormatter] and destination [SyslogLocal].
520    /// 
521    /// # Arguments
522    /// 
523    /// * `ident` - A program name which will appear on the logs. If none, will be determined
524    ///     automatically.
525    /// 
526    /// * `logstat` - [LogStat] an instance config.
527    /// 
528    /// * `facility` - [LogFacility] a syslog facility.
529    /// 
530    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
531    /// 
532    /// # Returns
533    /// 
534    /// A [SyRes] is returned ([Result]) with: 
535    /// 
536    /// * [Result::Ok] - with instance
537    /// 
538    /// * [Result::Err] - with error description.
539    pub 
540    fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: DefaultLocalSyslogDestination) -> SyRes<Self>
541    {
542        // creating internal syslog struct
543
544         let log_items = 
545                LogItems::new(ident, 0xff, logstat, facility);
546
547        let stream = 
548            SyslogSocketLockless::<DefaultLocalSyslogDestination>::new(logstat, net_tap_prov)?;
549
550        let (inst, sender, run_ctrl) = 
551            SyslogInternal
552                ::<DefaultSyslogFormatter, DefaultLocalSyslogDestination, DefaultQueueAdapter>
553                ::new(log_items, stream)?;
554        
555        
556        let thr_name: String = "syslog_queue/0".into();
557
558        // initiate a thread
559        let thread_hnd = 
560            thread::Builder::new()
561                .name(thr_name.clone())
562                .spawn(move || 
563                    SyslogInternal
564                        ::<DefaultSyslogFormatter, DefaultLocalSyslogDestination, DefaultQueueAdapter>
565                        ::thread_worker(inst)
566                )
567                .map_err(|e| 
568                    map_error!("{} thread spawn failed. {}", thr_name, e)
569                )?;
570
571        // creating a syslog public struct instance
572        let ret = 
573            Self
574            {
575                run_control: run_ctrl,
576                tasks: sender,
577                thread: Arc::new(Mutex::new(Some(thread_hnd))),
578                _p: PhantomData,
579                _p2: PhantomData
580            };
581
582        return Ok(ret);
583    }
584}
585
586impl<F, D, S> QueuedSyslog<S, F, D>
587where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
588{
589    /// Opens a default connection to the local syslog server with default formatter with
590    /// provided generics.
591    /// 
592    /// # Arguments
593    /// 
594    /// * `ident` - A program name which will appear on the logs. If none, will be determined
595    ///     automatically.
596    /// 
597    /// * `logstat` - [LogStat] an instance config.
598    /// 
599    /// * `facility` - [LogFacility] a syslog facility.
600    /// 
601    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
602    /// 
603    /// # Returns
604    /// 
605    /// A [SyRes] is returned ([Result]) with: 
606    /// 
607    /// * [Result::Ok] - with instance
608    /// 
609    /// * [Result::Err] - with error description.
610    pub 
611    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<QueuedSyslog<S, F, D>>
612    {
613        // creating internal syslog struct
614
615         let log_items = 
616                LogItems::new(ident, 0xff, logstat, facility);
617
618        let stream = 
619            SyslogSocketLockless::<D>::new(logstat, net_tap_prov)?;
620
621        let (inst, sender, run_ctrl) = 
622            SyslogInternal::<F, D, S>::new(log_items, stream)?;
623        
624        
625        let thr_name: String = "syslog_queue/0".into();
626
627        // initiate a thread
628        let thread_hnd = 
629            thread::Builder::new()
630                .name(thr_name.clone())
631                .spawn(move || SyslogInternal::<F, D, S>::thread_worker(inst))
632                .map_err(|e| 
633                    map_error!("{} thread spawn failed. {}", thr_name, e)
634                )?;
635
636        // creating a syslog public struct instance
637        let ret = 
638            Self
639            {
640                run_control: run_ctrl,
641                tasks: sender,
642                thread: Arc::new(Mutex::new(Some(thread_hnd))),
643                _p: PhantomData::<F>,
644                _p2: PhantomData::<D>,
645            };
646
647        return Ok(ret);
648    }
649}
650
651
652impl<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>> SyslogApi<F, D> 
653for QueuedSyslog<S, F, D>
654{
655    fn connectlog(&self) -> SyRes<()>
656    {
657        let (sy_cmd, loopback) = 
658            SyCmd::form_connectlog();
659
660        self.tasks.q_send_blocking(sy_cmd)?;
661
662        return 
663            loopback
664                .recv_once_blocking()?;
665    }
666
667    fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
668    {
669        let (sy_cmd, loopback) = 
670            SyCmd::form_logmask(logmask);
671
672        self.tasks.q_send_blocking(sy_cmd)?;
673
674        return 
675            loopback
676                .recv_once_blocking();
677    }
678
679    fn closelog(&self) -> SyRes<()> 
680    {
681        let (sy_cmd, loopback) = 
682            SyCmd::form_disconnectlog();
683
684        // send stop
685        self.tasks.q_send_blocking(sy_cmd)?;
686
687        return 
688            loopback
689                .recv_once_blocking()?;
690    }
691
692    fn syslog(&self, pri: Priority, fmt: F) 
693    {
694        // even if the thread is in a process of termination, there is
695        // no need to sync access to the run_control field as even if
696        // syslog thread will terminate before someone push something on the
697        // queue, it will be left in the queue until the end of program's time.
698        
699        let sy_cmd = SyCmd::form_syslog(pri, fmt);
700
701        let _ = self.tasks.q_send_blocking(sy_cmd);
702        //tasks.send(sy_cmd);
703
704        return;
705    }
706
707    fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
708    {
709        let sy_cmd = 
710            SyCmd::form_change_ident(ident.map(|v| v.to_string()));
711
712        return 
713            self.tasks.q_send_blocking(sy_cmd);
714    }
715
716    fn reconnect(&self) -> SyRes<()>
717    {
718        return 
719            self.tasks.q_send_blocking(SyCmd::form_reconnect());
720    }
721
722    fn update_tap_data(&self, tap_data: D) -> SyRes<()>
723    {
724        let (tap_data_cmd, loopback) = SyCmd::form_update_tap(tap_data);
725
726        self.tasks.q_send_blocking(tap_data_cmd)?;
727
728        return 
729            loopback
730                .recv_once_blocking()?;
731    }
732}
733
734
735impl<'stream, F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>> SyStreamApi<'stream, F, D, QueuedSyslog<S, F, D>> 
736for QueuedSyslog<S, F, D>
737{
738    fn stream(&'stream self, pri: Priority) -> SyStream<'stream, D, F, QueuedSyslog<S, F, D>> 
739    {
740        return 
741            SyStream 
742            { 
743                inner: self, 
744                pri: pri, 
745                _p: PhantomData, 
746                _p1: PhantomData 
747            };
748    }
749}
750
751/// A queued implementation for ASYNC.
752#[cfg(all(feature = "build_with_queue", feature = "async_enabled"))]
753pub mod syslog_async_queue
754{
755    use crate::error::SyRes;
756    use crate::sy_sync_queue::{SyCmd, SyslogQueueChanSnd, SyslogQueueChannel, SyslogQueueOneChanRcv};
757    use crate::Priority;
758    use crate::{formatters::SyslogFormatter, SyslogDestination, QueuedSyslog};
759    use crate::a_sync::syslog_trait::AsyncSyslogQueueApi;
760
761    impl<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>> AsyncSyslogQueueApi<F, D> 
762    for QueuedSyslog<S, F, D>
763    {
764        async 
765        fn a_connectlog(&mut self) -> SyRes<()> 
766        {
767            let (sy_cmd, loopback) = 
768                SyCmd::form_connectlog();
769
770            self.tasks.q_send(sy_cmd).await?;
771
772            return 
773                loopback
774                    .recv_once()
775                    .await?;
776        }
777
778        /// Sets the logmask to filter out the syslog calls. This function behaves 
779        /// differently as it behaves in syslog_sync.rs or syslog_async.rs.
780        /// It may return an error if: syslog thread had exit and some thread calls
781        /// this function. Or something happened with channel. 
782        /// This function blocks until the previous mask is received.
783        /// 
784        /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
785        ///
786        /// # Example
787        ///
788        /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
789        ///
790        /// or
791        ///
792        /// ~(LOG_MASK!(Priority::LOG_INFO))
793        /// LOG_UPTO!(Priority::LOG_ERROR) 
794        async 
795        fn a_setlogmask(&self, logmask: i32) -> SyRes<i32>
796        {
797            let (sy_cmd, loopback) = 
798                SyCmd::form_logmask(logmask);
799
800            self.tasks.q_send(sy_cmd).await?;
801
802            return 
803                loopback
804                    .recv_once()
805                    .await;
806        }
807
808        /// Closes connection to the syslog server
809        async 
810        fn a_closelog(&self) -> SyRes<()>
811        {
812            let (sy_cmd, loopback) = 
813                SyCmd::form_disconnectlog();
814
815            // send stop
816            self.tasks.q_send(sy_cmd).await?;
817
818            return 
819                loopback
820                    .recv_once()
821                    .await?;
822        }
823
824        /// Similar to libc, syslog() sends data to syslog server, but asynchroniously.
825        /// 
826        /// # Arguments
827        ///
828        /// * `pri` - a priority [Priority]
829        ///
830        /// * `fmt` - a program's message to be sent as payload. The message is encoded with the
831        ///     [SyslogFormatter] and may be different for different formatters.
832        #[inline]
833        async 
834        fn a_syslog(&self, pri: Priority, fmt: F)
835        {
836            let sy_cmd = SyCmd::form_syslog(pri, fmt);
837
838            let _ = self.tasks.q_send(sy_cmd).await;
839
840            return;
841        }
842
843
844        /// Performs the reconnection to the syslog server or file re-open.
845        /// 
846        /// # Returns
847        /// 
848        /// A [Result] is retured as [SyRes].
849        /// 
850        /// * [Result::Ok] - with empty inner type.
851        /// 
852        /// * [Result::Err] - an error code and description
853        async 
854        fn a_reconnect(&self) -> SyRes<()>
855        {
856            return 
857                self.tasks.q_send(SyCmd::form_reconnect()).await;
858        }
859
860        async 
861        fn a_change_identity(&self, ident: &str) -> SyRes<()> 
862        {
863            let sy_cmd = 
864                SyCmd::form_change_ident(Some(ident.to_string()));
865
866            return 
867                self.tasks.q_send(sy_cmd).await;
868        }
869
870        /// Updates the inner instance destionation i.e path to file
871        /// or server address. The type of destination can not be changed.
872        /// 
873        /// This function disconnects from syslog server if previously was 
874        /// connected (and reconnects if was connected previously).
875        /// 
876        /// # Arguments 
877        /// 
878        /// * `new_tap` - a consumed instance of type `D` [SyslogDestination]
879        /// 
880        /// # Returns 
881        /// 
882        /// A [SyRes] is returned. An error may be returned if:
883        /// 
884        /// * connection to server was failed
885        /// 
886        /// * incorrect type
887        /// 
888        /// * disconnect frm server failed
889        async 
890        fn a_update_tap_data(&self, new_tap: D) -> SyRes<()>
891        {
892            let (tap_data_cmd, loopback) = SyCmd::form_update_tap(new_tap);
893
894            self.tasks.q_send(tap_data_cmd).await?;
895
896            return 
897                loopback
898                    .recv_once()
899                    .await?;
900        }
901    }
902}
903
904
905
906#[cfg(all(feature = "build_with_queue", not(feature = "async_enabled")))]
907#[cfg(test)]
908mod test_queue_sync
909{
910    use crate::{formatters::DefaultSyslogFormatter, sync::{crossbeam_queue_adapter::DefaultQueueAdapter}, LogFacility, LogStat, Priority, SyslogApi, QueuedSyslog};
911
912    use super::DefaultLocalSyslogDestination;
913
914    #[cfg(target_family = "unix")]
915    use crate::SyslogLocal;
916
917    #[cfg(target_family = "windows")]
918    use crate::WindowsEvent;
919
920    #[test]
921    fn test_queue_single_message()
922    {
923        #[cfg(target_family = "unix")]
924        let syslog_provider = 
925            SyslogLocal::new();
926
927        #[cfg(target_family = "windows")]   
928        let syslog_provider = 
929            WindowsEvent::new(); 
930
931        let log = 
932                QueuedSyslog
933                    ::<DefaultQueueAdapter, DefaultSyslogFormatter, DefaultLocalSyslogDestination>
934                    ::openlog_with(
935                    Some("queue_single_message"), 
936                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
937                    LogFacility::LOG_DAEMON,
938                    syslog_provider
939                    );
940
941        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
942
943        let log = log.unwrap();
944
945        let msg1 = format!("test UTF-8 проверка BOM UTF-8");
946        let now = std::time::Instant::now();
947
948        log.syslog(Priority::LOG_DEBUG, msg1.into());
949
950        let dur = now.elapsed();
951        println!("{:?}", dur);
952
953        let msg2 = format!("test UTF-8  きるさお命泉ぶねりよ日子金れっ");
954
955        let now = std::time::Instant::now();
956
957        log.syslog(Priority::LOG_DEBUG, msg2.into());
958
959        let dur = now.elapsed();
960        println!("{:?}", dur);
961
962        let _ = log.closelog();
963
964        return;
965    }
966}
967
968#[cfg(all(feature = "build_with_queue", feature = "async_embedded"))]
969#[cfg(test)]
970mod tests_queue
971{
972    use crate::{formatters::DefaultSyslogFormatter, sync::DefaultQueueAdapter, LogFacility, LogStat, Priority, QueuedSyslog, SyslogApi};
973 
974    use super::DefaultLocalSyslogDestination;
975    
976    #[cfg(target_family = "unix")]
977    use crate::SyslogLocal;
978
979    #[cfg(target_family = "windows")]
980    use crate::WindowsEvent;
981
982    #[test]
983    fn test_multithreading()
984    {
985        use std::sync::Arc;
986        use std::thread;
987        use std::time::{Instant, Duration};
988        use crate::LOG_MASK;
989
990        #[cfg(target_family = "unix")]
991        let syslog_provider = 
992            SyslogLocal::new();
993
994        #[cfg(target_family = "windows")]   
995        let syslog_provider = 
996            WindowsEvent::new(); 
997
998
999        let log = 
1000            QueuedSyslog
1001                ::<DefaultQueueAdapter, DefaultSyslogFormatter, DefaultLocalSyslogDestination>
1002                ::openlog_with(
1003                    Some("test5"), 
1004                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
1005                    LogFacility::LOG_DAEMON, syslog_provider
1006                );
1007
1008        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
1009
1010        let log = Arc::new(log.unwrap());
1011        let c1_log = log.clone();
1012        let c2_log = log.clone();
1013
1014        thread::spawn(move|| {
1015            for i in 0..5
1016            {
1017                thread::sleep(Duration::from_nanos(200));
1018                let now = Instant::now();
1019                c1_log.syslog(Priority::LOG_DEBUG, format!("a message from thread 1 #{}[]", i).into());
1020                let elapsed = now.elapsed();
1021                println!("t1: {:?}", elapsed);
1022            }
1023        });
1024
1025        thread::spawn(move|| {
1026            for i in 0..5
1027            {
1028                thread::sleep(Duration::from_nanos(201));
1029                let now = Instant::now();
1030                c2_log.syslog(Priority::LOG_DEBUG, format!("きるさお命泉ぶねりよ日子金れっ {}", i).into());
1031                let elapsed = now.elapsed();
1032                println!("t2: {:?}", elapsed);
1033            }
1034        });
1035
1036        let res = log.setlogmask(!LOG_MASK!(Priority::LOG_ERR));
1037
1038        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1039        assert_eq!(res.unwrap(), 0xff, "should be 0xff");
1040
1041        let now = Instant::now();
1042        log.syslog(Priority::LOG_DEBUG, format!("A message from main, сообщение от главнюка").into());
1043        let elapsed = now.elapsed();
1044        println!("main: {:?}", elapsed);
1045
1046        thread::sleep(Duration::from_secs(2));
1047
1048        let _ = log.closelog();
1049
1050        thread::sleep(Duration::from_millis(500));
1051
1052        let res = log.setlogmask(!LOG_MASK!(Priority::LOG_ERR));
1053
1054        assert_eq!(res.is_ok(), true);
1055
1056        return;
1057    }
1058
1059    #[cfg(feature = "build_ext_file")]
1060    #[test]
1061    fn test_file_multithreading()
1062    {
1063        use std::sync::Arc;
1064        use std::thread;
1065        use std::time::{Instant, Duration};
1066        use crate::formatters::DefaultSyslogFormatterFile;
1067        use crate::{SyslogFile, LOG_MASK};
1068
1069        let log = 
1070            QueuedSyslog
1071                ::<DefaultQueueAdapter, DefaultSyslogFormatterFile, SyslogFile>
1072                ::openlog_with(
1073                    Some("test2"), 
1074                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
1075                    LogFacility::LOG_DAEMON, 
1076                    SyslogFile::new("/tmp/syslog_rs_test2.log")
1077                );
1078                    
1079
1080        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
1081
1082        let log = Arc::new(log.unwrap());
1083        let c1_log = log.clone();
1084        let c2_log = log.clone();
1085
1086        thread::spawn(move|| {
1087            for i in 0..5
1088            {
1089                thread::sleep(Duration::from_nanos(200));
1090                let now = Instant::now();
1091                c1_log.syslog(Priority::LOG_ALERT, format!("a message from thread 1 #{}[]", i).into());
1092                let elapsed = now.elapsed();
1093                println!("t1: {:?}", elapsed);
1094            }
1095        });
1096
1097        thread::spawn(move|| {
1098            for i in 0..5
1099            {
1100                thread::sleep(Duration::from_nanos(201));
1101                let now = Instant::now();
1102                c2_log.syslog(Priority::LOG_DEBUG, format!("きるさお命泉ぶねりよ日子金れっ {}", i).into());
1103                let elapsed = now.elapsed();
1104                println!("t2: {:?}", elapsed);
1105            }
1106        });
1107
1108        let res = log.setlogmask(!LOG_MASK!(Priority::LOG_ERR));
1109
1110        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
1111        assert_eq!(res.unwrap(), 0xff, "should be 0xff");
1112
1113        let now = Instant::now();
1114        log.syslog(Priority::LOG_DEBUG, format!("A message from main, きるさお命泉ぶねりよ日子金れっ").into());
1115        let elapsed = now.elapsed();
1116        println!("main: {:?}", elapsed);
1117
1118        thread::sleep(Duration::from_secs(2));
1119
1120        let _ = log.closelog();
1121
1122        thread::sleep(Duration::from_millis(500));
1123
1124        let res = log.setlogmask(!LOG_MASK!(Priority::LOG_ERR));
1125
1126        assert_eq!(res.is_ok(), true);
1127
1128        return;
1129    }
1130}