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