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