Skip to main content

syslog_rs/sync/
syslog_sync_shared.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::marker::PhantomData;
17use std::sync::{Arc, Mutex};
18use std::{str};
19
20use crate::formatters::DefaultSyslogFormatter;
21use crate::sync::{LogItems, SyStream, SyStreamPri, SyStreamSyslogApi};
22use crate::{formatters::SyslogFormatter};
23use crate::{common::*, SyslogDestination};
24use crate::error::SyRes;
25
26
27use crate::sync::syslog_sync_internal::{SyslogSocketLockless};
28
29#[cfg(target_family = "unix")]
30use crate::SyslogLocal;
31
32#[cfg(target_family = "windows")]
33use crate::WindowsEvent;
34
35
36#[cfg(target_family = "unix")]
37pub type DefaultLocalSyslogDestination = SyslogLocal;
38#[cfg(target_family = "windows")]
39pub type DefaultLocalSyslogDestination = WindowsEvent;
40
41/// A `sync`, shared instance of the syslog client which is shared between many
42/// threads. Previously a mutex was used, but since the v5.0.0 a CoW experimental
43/// approach is used. The `CoW` creates clones of the instance without holding
44/// a long mutex locks. When writing, the instance does not hold a long lock on
45/// updated item. Only exclusive lock locks the readers, because the instance is
46/// updated and not usable anyway.
47/// 
48/// If the program has fixed amount of threads, probably the `syslog_threadlocal`
49/// will be better alternative. It has the same functionality, but avoids 
50/// any sync locks by working in current thread.
51/// 
52/// # Traits
53/// 
54/// For this isntance a [SyslogApi] and [SyStreamApi] are implemented.
55/// 
56/// # Examples
57/// 
58/// ```ignore
59/// let log = 
60///     SyncSyslog::openlog(
61///         Some("test1"), 
62///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
63///         LogFacility::LOG_DAEMON,
64///         SyslogLocal::new()
65///     );
66/// ```
67/// 
68/// ```ignore
69/// let log = 
70///     SyncSyslog
71///         ::<DefaultSyslogFormatter, SyslogLocal>
72///         ::openlog_with(
73///             Some("test1"), 
74///             LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
75///             LogFacility::LOG_DAEMON,
76///             SyslogLocal::new()
77///         );
78/// ```
79/// 
80/// ```ignore
81/// pub static SYSLOG3: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogLocal,>> = 
82///     LazyLock::new(|| 
83///         {
84///             SyncSyslog
85///                 ::<DefaultSyslogFormatter, SyslogLocal>
86///                 ::openlog_with(
87///                     Some("test1"), 
88///                     LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
89///                     LogFacility::LOG_DAEMON,
90///                     SyslogLocal::new()
91///                 )
92///                 .unwrap()
93///         }
94///     );
95/// ```
96/// # Streaming
97/// 
98/// A stream is availble via [SyStreamApi].
99/// 
100/// ```ignore
101/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
102/// ```
103/// 
104/// # Generics
105/// 
106/// * `F` - a [SyslogFormatter] which sets the instance which would 
107///     format the message.
108/// 
109/// * `D` - a [SyslogDestination] instance which is either:
110///     [SyslogLocal], [SyslogFile], [SyslogNet], [SyslogTls]. By
111///     default a `SyslogLocal` is selected.
112#[derive(Debug, Clone)]
113pub struct SyncSyslog<F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination>
114where 
115    F: SyslogFormatter, 
116    D: SyslogDestination, 
117{   
118    /// An identification i.e program name, thread name
119    log_items: Arc<Mutex<LogItems>>,
120
121    /// A stream (unixdatagram, udp, tcp)
122    stream: Arc<Mutex<SyslogSocketLockless<D>>>,
123
124     _p: PhantomData<F>,
125}
126
127unsafe impl<F: SyslogFormatter, D: SyslogDestination> Send for SyncSyslog<F, D>
128{}
129
130impl SyncSyslog
131{
132    /// Opens a default connection to the local syslog server with default formatter.
133    /// 
134    /// In order to access the syslog API, use the [SyslogApi].
135    /// 
136    /// # Arguments
137    /// 
138    /// * `ident` - A program name which will appear on the logs. If none, will be determined
139    ///     automatically.
140    /// 
141    /// * `logstat` - [LogStat] an instance config.
142    /// 
143    /// * `facility` - [LogFacility] a syslog facility.
144    /// 
145    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
146    /// 
147    /// # Returns
148    /// 
149    /// A [SyRes] is returned ([Result]) with: 
150    /// 
151    /// * [Result::Ok] - with instance
152    /// 
153    /// * [Result::Err] - with error description.
154    pub 
155    fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility, 
156        net_tap_prov: DefaultLocalSyslogDestination) -> SyRes<Self> 
157    {
158        return Ok( 
159            Self
160            {
161                log_items: 
162                    Arc::new(
163                        Mutex::new(
164                            LogItems::new(ident, 0xff, logstat, facility)
165                        )
166                    ),
167                stream: 
168                    Arc::new(
169                        Mutex::new(
170                            SyslogSocketLockless::<DefaultLocalSyslogDestination>::new(logstat, net_tap_prov)?
171                        )
172                    ),
173                _p: 
174                    PhantomData,
175            }
176        );
177    }
178}
179
180
181impl<F: SyslogFormatter, D: SyslogDestination> SyncSyslog<F, D>
182{
183    /// Opens a special connection to the destination syslog server with specific formatter.
184    /// 
185    /// All struct generic should be specified before calling this function.
186    /// 
187    /// In order to access the syslog API, use the [SyslogApi].
188    /// 
189    /// # Arguments
190    /// 
191    /// * `ident` - A program name which will appear on the logs. If none, will be determined
192    ///     automatically.
193    /// 
194    /// * `logstat` - [LogStat] an instance config.
195    /// 
196    /// * `facility` - [LogFacility] a syslog facility.
197    /// 
198    /// * `net_tap_prov` - a destination server. A specific `D` instance which contains infomation 
199    ///     about the destination server. See `syslog_provider.rs`.
200    /// 
201    /// # Returns
202    /// 
203    /// A [SyRes] is returned ([Result]) with: 
204    /// 
205    /// * [Result::Ok] - with instance
206    /// 
207    /// * [Result::Err] - with error description.
208    pub 
209    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self> 
210    {
211        return Ok( 
212            Self
213            {
214                log_items: 
215                    Arc::new(
216                        Mutex::new(
217                            LogItems::new(ident, 0xff, logstat, facility)
218                        )
219                    ),
220                stream: 
221                    Arc::new(
222                        Mutex::new(
223                            SyslogSocketLockless::<D>::new(logstat, net_tap_prov)?
224                        )
225                    ),
226                _p: 
227                    PhantomData,
228            }
229        );
230    }
231}
232
233
234impl<F: SyslogFormatter, D: SyslogDestination> SyncSyslog<F, D>
235{
236    /// Connects the current instance to the syslog server (destination).
237    pub 
238    fn connectlog(&self) -> SyRes<()>
239    {
240        return 
241            self
242                .stream
243                .lock()
244                .unwrap()
245                .connectlog();
246    }
247
248    /// Sets the logmask to filter out the syslog calls.
249    /// 
250    /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
251    ///
252    /// # Example
253    ///
254    /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
255    ///
256    /// or
257    ///
258    /// ~(LOG_MASK!(Priority::LOG_INFO))
259    /// LOG_UPTO!(Priority::LOG_ERROR)
260    pub 
261    fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
262    {
263        let pri = 
264            self
265                .log_items
266                .lock()
267                .unwrap()
268                .set_logmask(logmask);
269
270        return Ok(pri);
271    }
272
273    /// Closes connection to the syslog server (destination).
274    pub 
275    fn closelog(&self) -> SyRes<()> 
276    {
277        return 
278            self
279                .stream
280                .lock()
281                .unwrap()
282                .disconnectlog();
283    }
284
285    /// Similar to libc, syslog() sends data to syslog server.
286    /// 
287    /// # Arguments
288    ///
289    /// * `pri` - a priority [Priority]
290    ///
291    /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
292    ///     variable argumets amount. In Rust you should create your
293    ///     own macros like format!() or use format!()]. The [String] and ref `'static` 
294    ///     [str] can be passed directly.
295    /// 
296    /// # Returns 
297    /// 
298    /// A [SyRes] is returned which may describe an error.
299    #[inline]
300    pub 
301    fn syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
302    {
303        let Some((formatted_msg, logstat)) = 
304            self.log_items.lock().unwrap().vsyslog1_msg::<F, D>(pri, &fmt)
305        else { return Ok(()) };
306
307        self.stream.lock().unwrap().vsyslog1(logstat, formatted_msg)
308    }
309
310    /// This function can be used to update the facility name, for example
311    /// after fork().
312    /// 
313    /// # Arguments
314    /// 
315    /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
316    ///     If set to [Option::None] would request the program name from OS.
317    pub 
318    fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
319    {
320        self
321            .log_items
322            .lock()
323            .unwrap()
324            .set_identity(ident);
325
326        return Ok(());
327    }
328
329    /// Re-opens the connection to the syslog server. Can be used to 
330    /// rotate logs(handle SIGHUP).
331    /// 
332    /// # Returns
333    /// 
334    /// A [Result] is retured as [SyRes].
335    /// 
336    /// * [Result::Ok] - with empty inner type.
337    /// 
338    /// * [Result::Err] - an error code and description 
339    pub 
340    fn reconnect(&self) -> SyRes<()>
341    {
342        return
343            self
344                .stream
345                .lock()
346                .unwrap()
347                .reconnectlog();
348    }
349
350    /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
351    /// the same variant (type) as current.
352    pub 
353    fn update_tap_data(&self, tap_data: D) -> SyRes<()>
354    {
355        return
356            self
357                .stream
358                .lock()
359                .unwrap()
360                .update_tap_data(tap_data);
361    }
362
363}
364
365impl<F, D> SyncSyslog<F, D>
366where F: SyslogFormatter, D: SyslogDestination
367{
368    /// Returns the streamable [SyStream] instance which can be used with [write!].
369    /// 
370    /// It implements both [std::fmt::Write] and [std::io::Write].
371    /// 
372    /// # Example
373    /// 
374    /// ```ignore
375    /// let log = 
376    ///     SingleSyslog::openlog(
377    ///         Some("test1"), 
378    ///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
379    ///         LogFacility::LOG_DAEMON,
380    ///         SyslogLocal::new()
381    ///     ).unwrap();
382    /// 
383    /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
384    /// ```
385    pub 
386    fn get_stream<'t, PRI>(&'t self) -> SyStream<'t, PRI, D, F, &'t Self>
387    where PRI: SyStreamPri
388    {
389        SyStream
390        {
391            s: Some(self),
392            _p: PhantomData,
393            _p1: PhantomData,
394            _p2: PhantomData
395        }
396    }
397}
398
399
400impl<F: SyslogFormatter, D: SyslogDestination> SyStreamSyslogApi<F, D>  
401for &SyncSyslog<F, D>
402{
403    type SYSLOG<'t> = &'t SyncSyslog<F, D>;
404
405    fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
406    {
407        syslog.syslog(pri, fmt)
408    }
409}
410
411