Skip to main content

syslog_rs/sync/
syslog_threadlocal.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};
17
18use crate::{
19    LogFacility, LogStat, Priority, SyslogDestination, error::SyRes, 
20    formatters::{DefaultSyslogFormatter, SyslogFormatter}, 
21    sync::
22    {
23        LogItems, SyStream, SyStreamPri, syslog_stream::SyStreamSyslogApi, 
24        syslog_sync_internal::SyslogSocketLockless, DefaultLocalSyslogDestination
25    },
26};
27
28
29/// A threal local syslog which is completely lockless! It can be used in signle threaded
30/// applications or with the [thread_local] functionality.
31/// 
32/// ```ignore
33/// thread_local! 
34/// {
35///     // Could add pub to make it public to whatever Foo already is public to.
36///     static SYSLOG: RefCell<SingleSyslog> = 
37///         RefCell::new(SingleSyslog::openlog_with(Some("test"), LogStat::LOG_PID , 
38///             LogFacility::LOG_DAEMON, SyslogLocal::new()).unwrap());
39/// }
40/// ```
41/// 
42/// A stream is availble via [SyStreamApi].
43/// 
44/// ```ignore
45/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
46/// ```
47/// 
48/// The instances will be completly separated and have own FD.
49/// 
50/// # Generics
51/// 
52/// * `D` - a [SyslogDestination] instance which is either:
53///     [SyslogLocal], [crate::syslog_provider::SyslogFile], [crate::syslog_provider::SyslogNet], 
54///     [crate::syslog_provider::SyslogTls]. By default a `SyslogLocal` is selected.
55/// 
56/// * `F` - a [SyslogFormatter] which sets the instance which would 
57///     format the message.
58/// 
59#[derive(Debug)]
60pub struct SingleSyslog<F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination>
61where 
62    F: SyslogFormatter, 
63    D: SyslogDestination,   
64{
65    /// An identification i.e program name, thread name
66    log_items: LogItems,
67
68    /// A stream (unixdatagram, udp, tcp)
69    stream: SyslogSocketLockless<D>,
70
71    _p: PhantomData<F>,
72
73    _p_not_ss: PhantomData<*const ()>
74}
75
76impl SingleSyslog
77{
78    /// Opens a default connection to the local syslog server with default formatter.
79    /// 
80    /// In order to access the syslog API, use the [SyslogApi].
81    /// 
82    /// # Arguments
83    /// 
84    /// * `ident` - A program name which will appear on the logs. If none, will be determined
85    ///     automatically.
86    /// 
87    /// * `logstat` - [LogStat] an instance config.
88    /// 
89    /// * `facility` - [LogFacility] a syslog facility.
90    /// 
91    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
92    /// 
93    /// # Returns
94    /// 
95    /// A [SyRes] is returned ([Result]) with: 
96    /// 
97    /// * [Result::Ok] - with instance
98    /// 
99    /// * [Result::Err] - with error description.
100    pub 
101    fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: DefaultLocalSyslogDestination) -> SyRes<Self> 
102    {
103        let log_items = 
104            LogItems::new(ident, 0xff, logstat, facility);
105
106        let stream = 
107            SyslogSocketLockless::<DefaultLocalSyslogDestination>::new(logstat, net_tap_prov)?;
108        
109        return Ok(
110            Self
111            {
112                log_items,
113                stream,
114                _p: 
115                    PhantomData,
116                _p_not_ss: 
117                    PhantomData
118            }
119        );
120    }
121}
122
123impl<F, D> SingleSyslog<F, D>
124where F: SyslogFormatter, D: SyslogDestination
125{
126    /// Opens a default connection to the local syslog server with default formatter.
127    /// 
128    /// # Arguments
129    /// 
130    /// * `ident` - A program name which will appear on the logs. If none, will be determined
131    ///     automatically.
132    /// 
133    /// * `logstat` - [LogStat] an instance config.
134    /// 
135    /// * `facility` - [LogFacility] a syslog facility.
136    /// 
137    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
138    /// 
139    /// # Returns
140    /// 
141    /// A [SyRes] is returned ([Result]) with: 
142    /// 
143    /// * [Result::Ok] - with instance
144    /// 
145    /// * [Result::Err] - with error description.
146    pub 
147    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self> 
148    {
149        let log_items = 
150            LogItems::new(ident, 0xff, logstat, facility);
151
152        let stream = 
153            SyslogSocketLockless::<D>::new(logstat, net_tap_prov)?;
154        
155        return Ok(
156            Self
157            {
158                log_items,
159                stream,
160                _p: 
161                    PhantomData,
162                _p_not_ss: 
163                    PhantomData
164            }
165        );
166    }
167}
168
169impl<F, D> SingleSyslog<F, D>
170where F: SyslogFormatter, D: SyslogDestination
171{
172    /// Connects the current instance to the syslog server (destination).
173    #[inline]
174    pub 
175    fn connectlog(&mut self) -> SyRes<()>
176    {
177        return 
178            self
179                .stream
180                .connectlog();
181    }
182
183    /// Sets the logmask to filter out the syslog calls.
184    /// 
185    /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
186    ///
187    /// # Example
188    ///
189    /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
190    ///
191    /// or
192    ///
193    /// ~(LOG_MASK!(Priority::LOG_INFO))
194    /// LOG_UPTO!(Priority::LOG_ERROR)
195    #[inline]
196    pub 
197    fn setlogmask(&mut self, logmask: i32) -> SyRes<i32>
198    {
199        return Ok(
200            self
201                .log_items
202                .set_logmask(logmask)
203        );
204    }
205
206    /// Closes connection to the syslog server (destination).
207    #[inline]
208    pub 
209    fn closelog(&mut self) -> SyRes<()> 
210    {
211        return 
212            self
213                .stream
214                .disconnectlog();
215    }
216
217    /// Similar to libc, syslog() sends data to syslog server.
218    /// 
219    /// # Arguments
220    ///
221    /// * `pri` - a priority [Priority]
222    ///
223    /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
224    ///     variable argumets amount. In Rust you should create your
225    ///     own macros like format!() or use format!()]. The [String] and ref `'static` 
226    ///     [str] can be passed directly.
227    /// 
228    /// # Returns 
229    /// 
230    /// A [SyRes] is returned which may describe an error.
231    #[inline]
232    pub 
233    fn syslog(&mut self, pri: Priority, fmt: F) -> SyRes<()>
234    {
235        let Some((formatted_msg, logstat)) = 
236            self.log_items.vsyslog1_msg::<F, D>(pri, &fmt)
237            else { return Ok(()) };
238
239        self.stream.vsyslog1(logstat, formatted_msg)
240    }
241
242    /// This function can be used to update the facility name, for example
243    /// after fork().
244    /// 
245    /// # Arguments
246    /// 
247    /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
248    ///     If set to [Option::None] would request the program name from OS.
249    #[inline]
250    pub 
251    fn change_identity(&mut self, ident: Option<&str>) -> SyRes<()>
252    {
253        self.log_items.set_identity(ident);
254
255        return Ok(());
256    }
257
258    /// Re-opens the connection to the syslog server. Can be used to 
259    /// rotate logs(handle SIGHUP).
260    /// 
261    /// # Returns
262    /// 
263    /// A [Result] is retured as [SyRes].
264    /// 
265    /// * [Result::Ok] - with empty inner type.
266    /// 
267    /// * [Result::Err] - an error code and description 
268    #[inline]
269    pub 
270    fn reconnect(&mut self) -> SyRes<()>
271    {
272        return
273            self
274                .stream
275                .reconnectlog();
276    }
277
278    /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
279    /// the same variant (type) as current.
280    #[inline]
281    pub 
282    fn update_tap_data(&mut self, tap_data: D) -> SyRes<()>
283    {
284        return 
285            self
286                .stream
287                .update_tap_data(tap_data.clone());
288    }
289}
290
291impl<F, D> SingleSyslog<F, D>
292where F: SyslogFormatter, D: SyslogDestination
293{
294    /// Returns the streamable [SyStream] instance which can be used with [write!].
295    /// 
296    /// It implements both [std::fmt::Write] and [std::io::Write].
297    /// 
298    /// # Example
299    /// 
300    /// ```ignore
301    /// let log = 
302    ///     SingleSyslog::openlog(
303    ///         Some("test1"), 
304    ///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
305    ///         LogFacility::LOG_DAEMON,
306    ///         SyslogLocal::new()
307    ///     ).unwrap();
308    /// 
309    /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
310    /// ```
311    pub 
312    fn get_stream<'t, PRI>(&'t mut self) -> SyStream<'t, PRI, D, F, &'t mut Self>
313    where PRI: SyStreamPri
314    {
315        SyStream
316        {
317            s: Some(self),
318            _p: PhantomData,
319            _p1: PhantomData,
320            _p2: PhantomData
321        }
322    }
323}
324
325
326impl<F: SyslogFormatter, D: SyslogDestination> SyStreamSyslogApi<F, D>  
327for &mut SingleSyslog<F, D>
328{
329    type SYSLOG<'t> = &'t mut SingleSyslog<F, D>;
330
331    fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
332    {
333        syslog.syslog(pri, fmt)
334    }
335}
336
337