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