syslog_rs/sync/syslog_sync.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::{fmt, marker::PhantomData, sync::{Arc, Mutex}};
17
18use crate::{DefaultLocalSyslogDestination, LogFacility, LogStat, Priority, SyslogDestination, error::SyRes, formatters::{DefaultSyslogFormatter, SyslogFormatter}, sync::{LogItems, SyStream, SyStreamPri, SyStreamSyslogApi, syslog_sync_internal::SyslogSocketLockless}};
19
20pub trait DynSyslog: Send + Sync
21{
22 fn connectlog(&self) -> SyRes<()>;
23 fn setlogmask(&self, logmask: i32) -> SyRes<i32>;
24 fn closelog(&self) -> SyRes<()>;
25 fn syslog(&self, pri: Priority, fmt: String) -> SyRes<()>;
26 fn change_identity(&self, ident: Option<&str>) -> SyRes<()>;
27 fn reconnect(&self) -> SyRes<()>;
28}
29
30pub trait SyslogType: fmt::Debug
31{
32 type LockItemsBase;
33
34 type LockItems;
35
36 type LockStreamBase<D: SyslogDestination>;
37
38 type LockStream<D: SyslogDestination>;
39
40 type MoveLockType;
41
42 fn wrap_items(items: Self::LockItemsBase) -> Self::LockItems;
43 fn wrap_stream<D: SyslogDestination>(stream: Self::LockStreamBase<D>) -> Self::LockStream<D>;
44}
45
46
47
48//<D: SyslogDestination = DefaultLocalSyslogDestination> D: SyslogDestination,
49
50#[derive(Debug)]
51pub struct Syslog<F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination, SL = SyShared>
52where
53 F: SyslogFormatter,
54 D: SyslogDestination,
55 SL: SyslogType
56{
57 /// An identification i.e program name, thread name
58 log_items: SL::LockItems,
59
60 /// A stream (unixdatagram, udp, tcp)
61 stream: SL::LockStream<D>,
62
63 _p: PhantomData<F>,
64
65 _p_not_ss: SL::MoveLockType
66}
67
68impl Syslog
69{
70 /// Opens a default connection to the local syslog server with default formatter.
71 ///
72 /// In order to access the syslog API, use the [SyslogApi].
73 ///
74 /// # Arguments
75 ///
76 /// * `ident` - A program name which will appear on the logs. If none, will be determined
77 /// automatically.
78 ///
79 /// * `logstat` - [LogStat] an instance config.
80 ///
81 /// * `facility` - [LogFacility] a syslog facility.
82 ///
83 /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
84 ///
85 /// # Returns
86 ///
87 /// A [SyRes] is returned ([Result]) with:
88 ///
89 /// * [Result::Ok] - with instance
90 ///
91 /// * [Result::Err] - with error description.
92 pub
93 fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility,
94 net_tap_prov: DefaultLocalSyslogDestination) -> SyRes<Self>
95 {
96 return Ok(
97 Self
98 {
99 log_items:
100 <SyShared as SyslogType>
101 ::wrap_items(
102 <SyShared as SyslogType>::LockItemsBase::new(ident, 0xff, logstat, facility)
103 ),
104 stream:
105 <SyShared as SyslogType>
106 ::wrap_stream(
107 <SyShared as SyslogType>::LockStreamBase::<DefaultLocalSyslogDestination>::new(logstat, net_tap_prov)?,
108 ),
109 _p:
110 PhantomData,
111 _p_not_ss:
112 PhantomData
113 }
114 );
115 }
116}
117
118#[cfg(feature = "build_with_thread_local")]
119pub mod build_with_thread_local
120{
121 use super::*;
122
123 /// A threal local syslog which is completely lockless! It can be used in signle threaded
124 /// applications or with the [thread_local] functionality.
125 ///
126 /// ```ignore
127 /// thread_local!
128 /// {
129 /// // Could add pub to make it public to whatever Foo already is public to.
130 /// static SYSLOG: RefCell<Syslog<DefaultSyslogFormatter, SyslogLocal, SyThreadLocal>> =
131 /// RefCell::new(
132 /// Syslog
133 /// ::<_, _, SyThreadLocal>
134 //// ::openlog_with(Some("test"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, SyslogLocal::new())
135 /// .unwrap()
136 /// );
137 /// }
138 /// ```
139 ///
140 /// A stream is availble via [SyStreamApi].
141 ///
142 /// ```ignore
143 /// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
144 /// ```
145 ///
146 /// The instances will be completly separated and have own FD.
147 ///
148 /// # Generics
149 ///
150 /// * `D` - a [SyslogDestination] instance which is either:
151 /// [SyslogLocal], [crate::syslog_provider::SyslogFile], [crate::syslog_provider::SyslogNet],
152 /// [crate::syslog_provider::SyslogTls]. By default a `SyslogLocal` is selected.
153 ///
154 /// * `F` - a [SyslogFormatter] which sets the instance which would
155 /// format the message.
156 ///
157 #[derive(Debug)]
158 pub struct SyThreadLocal;
159
160 impl SyslogType for SyThreadLocal
161 {
162 type LockItemsBase = LogItems;
163 type LockItems = Self::LockItemsBase;
164
165 type LockStreamBase<D: SyslogDestination> = SyslogSocketLockless<D>;
166 type LockStream<D: SyslogDestination> = Self::LockStreamBase<D>;
167
168 type MoveLockType = PhantomData<*const ()>;
169
170 fn wrap_items(items: Self::LockItemsBase) -> Self::LockItems
171 {
172 items
173 }
174
175 fn wrap_stream<D: SyslogDestination>(stream: Self::LockStreamBase<D>) -> Self::LockStream<D>
176 {
177 stream
178 }
179 }
180
181 impl<F, D> Syslog<F, D, SyThreadLocal>
182 where
183 F: SyslogFormatter,
184 D: SyslogDestination,
185 {
186 /// Opens a default connection to the local syslog server with default formatter.
187 ///
188 /// # Arguments
189 ///
190 /// * `ident` - A program name which will appear on the logs. If none, will be determined
191 /// automatically.
192 ///
193 /// * `logstat` - [LogStat] an instance config.
194 ///
195 /// * `facility` - [LogFacility] a syslog facility.
196 ///
197 /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
198 ///
199 /// # Returns
200 ///
201 /// A [SyRes] is returned ([Result]) with:
202 ///
203 /// * [Result::Ok] - with instance
204 ///
205 /// * [Result::Err] - with error description.
206 pub
207 fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self>
208 {
209 return Ok(
210 Self
211 {
212 log_items:
213 <SyThreadLocal as SyslogType>
214 ::wrap_items(
215 <SyThreadLocal as SyslogType>::LockItems::new(ident, 0xff, logstat, facility)
216 ),
217 stream:
218 <SyThreadLocal as SyslogType>
219 ::wrap_stream(
220 <SyThreadLocal as SyslogType>::LockStream::new(logstat, net_tap_prov)?
221 ),
222 _p:
223 PhantomData,
224 _p_not_ss:
225 PhantomData
226 }
227 );
228 }
229 }
230
231 impl<F, D> Syslog<F, D, SyThreadLocal>
232 where
233 F: SyslogFormatter,
234 D: SyslogDestination,
235 {
236 /// Connects the current instance to the syslog server (destination).
237 #[inline]
238 pub
239 fn connectlog(&mut self) -> SyRes<()>
240 {
241 return
242 self
243 .stream
244 .connectlog();
245 }
246
247 /// Sets the logmask to filter out the syslog calls.
248 ///
249 /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
250 ///
251 /// # Example
252 ///
253 /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
254 ///
255 /// or
256 ///
257 /// ~(LOG_MASK!(Priority::LOG_INFO))
258 /// LOG_UPTO!(Priority::LOG_ERROR)
259 #[inline]
260 pub
261 fn setlogmask(&mut self, logmask: i32) -> SyRes<i32>
262 {
263 return Ok(
264 self
265 .log_items
266 .set_logmask(logmask)
267 );
268 }
269
270 /// Closes connection to the syslog server (destination).
271 #[inline]
272 pub
273 fn closelog(&mut self) -> SyRes<()>
274 {
275 return
276 self
277 .stream
278 .disconnectlog();
279 }
280
281 /// Similar to libc, syslog() sends data to syslog server.
282 ///
283 /// # Arguments
284 ///
285 /// * `pri` - a priority [Priority]
286 ///
287 /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
288 /// variable argumets amount. In Rust you should create your
289 /// own macros like format!() or use format!()]. The [String] and ref `'static`
290 /// [str] can be passed directly.
291 ///
292 /// # Returns
293 ///
294 /// A [SyRes] is returned which may describe an error.
295 #[inline]
296 pub
297 fn syslog(&mut self, pri: Priority, fmt: F) -> SyRes<()>
298 {
299 let Some((formatted_msg, logstat)) =
300 self.log_items.vsyslog1_msg::<F, D>(pri, &fmt)
301 else { return Ok(()) };
302
303 self.stream.vsyslog1(logstat, formatted_msg)
304 }
305
306 /// This function can be used to update the facility name, for example
307 /// after fork().
308 ///
309 /// # Arguments
310 ///
311 /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
312 /// If set to [Option::None] would request the program name from OS.
313 #[inline]
314 pub
315 fn change_identity(&mut self, ident: Option<&str>) -> SyRes<()>
316 {
317 self.log_items.set_identity(ident);
318
319 return Ok(());
320 }
321
322 /// Re-opens the connection to the syslog server. Can be used to
323 /// rotate logs(handle SIGHUP).
324 ///
325 /// # Returns
326 ///
327 /// A [Result] is retured as [SyRes].
328 ///
329 /// * [Result::Ok] - with empty inner type.
330 ///
331 /// * [Result::Err] - an error code and description
332 #[inline]
333 pub
334 fn reconnect(&mut self) -> SyRes<()>
335 {
336 return
337 self
338 .stream
339 .reconnectlog();
340 }
341
342 /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
343 /// the same variant (type) as current.
344 #[inline]
345 pub
346 fn update_tap_data(&mut self, tap_data: D) -> SyRes<()>
347 {
348 return
349 self
350 .stream
351 .update_tap_data(tap_data.clone());
352 }
353 }
354
355 impl<F, D> Syslog<F, D, SyThreadLocal>
356 where
357 F: SyslogFormatter,
358 D: SyslogDestination,
359 {
360 /// Returns the streamable [SyStream] instance which can be used with [write!].
361 ///
362 /// It implements both [std::fmt::Write] and [std::io::Write].
363 ///
364 /// # Example
365 ///
366 /// ```ignore
367 /// let log =
368 /// SingleSyslog::openlog(
369 /// Some("test1"),
370 /// LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID,
371 /// LogFacility::LOG_DAEMON,
372 /// SyslogLocal::new()
373 /// ).unwrap();
374 ///
375 /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
376 /// ```
377 pub
378 fn get_stream<'t, PRI>(&'t mut self) -> SyStream<'t, PRI, D, F, &'t mut Self>
379 where PRI: SyStreamPri
380 {
381 SyStream
382 {
383 s: Some(self),
384 _p: PhantomData,
385 _p1: PhantomData,
386 _p2: PhantomData
387 }
388 }
389 }
390
391 impl<F, D> SyStreamSyslogApi<F, D>
392 for &mut Syslog<F, D, SyThreadLocal>
393 where
394 F: SyslogFormatter,
395 D: SyslogDestination,
396 {
397 type SYSLOG<'t> = &'t mut Syslog<F, D, SyThreadLocal>;
398
399 fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
400 {
401 syslog.syslog(pri, fmt)
402 }
403 }
404}
405
406#[cfg(feature = "build_with_thread_local")]
407pub use self::build_with_thread_local::SyThreadLocal;
408
409// ---- SYSLOG SHARED
410
411/// A `sync`, shared instance of the syslog client which is shared between many
412/// threads. Previously a mutex was used, but since the v5.0.0 a CoW experimental
413/// approach is used. The `CoW` creates clones of the instance without holding
414/// a long mutex locks. When writing, the instance does not hold a long lock on
415/// updated item. Only exclusive lock locks the readers, because the instance is
416/// updated and not usable anyway.
417///
418/// If the program has fixed amount of threads, probably the `syslog_threadlocal`
419/// will be better alternative. It has the same functionality, but avoids
420/// any sync locks by working in current thread.
421///
422/// # Traits
423///
424/// For this isntance a [SyslogApi] and [SyStreamApi] are implemented.
425///
426/// # Examples
427///
428/// ```ignore
429/// let log =
430/// Syslog::openlog(
431/// Some("test1"),
432/// LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID,
433/// LogFacility::LOG_DAEMON,
434/// SyslogLocal::new()
435/// );
436/// ```
437///
438/// ```ignore
439/// let log =
440/// Syslog
441/// ::<DefaultSyslogFormatter, SyslogLocal, SyShared>
442/// ::openlog_with(
443/// Some("test1"),
444/// LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID,
445/// LogFacility::LOG_DAEMON,
446/// SyslogLocal::new()
447/// );
448/// ```
449///
450/// ```ignore
451/// pub static SYSLOG3: LazyLock<Syslog<DefaultSyslogFormatter, SyslogLocal, SyShared>> =
452/// LazyLock::new(||
453/// {
454/// Syslog
455/// ::<DefaultSyslogFormatter, SyslogLocal, SyShared>
456/// ::openlog_with(
457/// Some("test1"),
458/// LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID,
459/// LogFacility::LOG_DAEMON,
460/// SyslogLocal::new()
461/// )
462/// .unwrap()
463/// }
464/// );
465/// ```
466/// # Streaming
467///
468/// A stream is availble via [SyStreamApi].
469///
470/// ```ignore
471/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
472/// ```
473///
474/// # Generics
475///
476/// * `F` - a [SyslogFormatter] which sets the instance which would
477/// format the message.
478///
479/// * `D` - a [SyslogDestination] instance which is either:
480/// [SyslogLocal], [SyslogFile], [SyslogNet], [SyslogTls]. By
481/// default a `SyslogLocal` is selected.
482#[derive(Debug)]
483pub struct SyShared;
484
485impl SyslogType for SyShared
486{
487 type LockItemsBase = LogItems;
488 type LockItems = Arc<Mutex<Self::LockItemsBase>>;
489
490 type LockStreamBase<D: SyslogDestination> = SyslogSocketLockless<D>;
491 type LockStream<D: SyslogDestination> = Arc<Mutex<Self::LockStreamBase<D>>>;
492
493 type MoveLockType = PhantomData<()>;
494
495 fn wrap_items(items: Self::LockItemsBase) -> Self::LockItems
496 {
497 Arc::new(Mutex::new(items))
498 }
499
500 fn wrap_stream<D: SyslogDestination>(stream: Self::LockStreamBase<D>) -> Self::LockStream<D>
501 {
502 Arc::new(Mutex::new(stream))
503 }
504}
505
506impl<F, D> Clone for Syslog<F, D, SyShared>
507where
508 F: SyslogFormatter,
509 D: SyslogDestination,
510{
511 fn clone(&self) -> Self
512 {
513 Self
514 {
515 log_items:
516 self.log_items.clone(),
517 stream:
518 self.stream.clone(),
519 _p:
520 PhantomData,
521 _p_not_ss:
522 PhantomData,
523 }
524 }
525}
526
527impl<F, D> DynSyslog for Syslog<F, D, SyShared>
528where
529 F: SyslogFormatter,
530 D: SyslogDestination,
531{
532 #[inline]
533 fn connectlog(&self) -> SyRes<()>
534 {
535 self.connectlog()
536 }
537
538 #[inline]
539 fn setlogmask(&self, logmask: i32) -> SyRes<i32>
540 {
541 self.setlogmask(logmask)
542 }
543
544 #[inline]
545 fn closelog(&self) -> SyRes<()>
546 {
547 self.closelog()
548 }
549
550 fn syslog(&self, pri: Priority, fmt: String) -> SyRes<()>
551 {
552 self.syslog(pri, fmt.into())
553 }
554
555 fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
556 {
557 self.change_identity(ident)
558 }
559
560 fn reconnect(&self) -> SyRes<()>
561 {
562 self.reconnect()
563 }
564}
565
566
567impl<F, D> Syslog<F, D, SyShared>
568where
569 F: SyslogFormatter,
570 D: SyslogDestination,
571{
572 pub
573 fn make_dyn(self) -> Box<dyn DynSyslog>
574 {
575 Box::new(self)
576 }
577
578 /// Opens a special connection to the destination syslog server with specific formatter.
579 ///
580 /// All struct generic should be specified before calling this function.
581 ///
582 /// In order to access the syslog API, use the [SyslogApi].
583 ///
584 /// # Arguments
585 ///
586 /// * `ident` - A program name which will appear on the logs. If none, will be determined
587 /// automatically.
588 ///
589 /// * `logstat` - [LogStat] an instance config.
590 ///
591 /// * `facility` - [LogFacility] a syslog facility.
592 ///
593 /// * `net_tap_prov` - a destination server. A specific `D` instance which contains infomation
594 /// about the destination server. See `syslog_provider.rs`.
595 ///
596 /// # Returns
597 ///
598 /// A [SyRes] is returned ([Result]) with:
599 ///
600 /// * [Result::Ok] - with instance
601 ///
602 /// * [Result::Err] - with error description.
603 pub
604 fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self>
605 {
606 return Ok(
607 Self
608 {
609 log_items:
610 <SyShared as SyslogType>
611 ::wrap_items(
612 <SyShared as SyslogType>::LockItemsBase::new(ident, 0xff, logstat, facility)
613 ),
614 stream:
615 <SyShared as SyslogType>
616 ::wrap_stream(
617 <SyShared as SyslogType>::LockStreamBase::<D>::new(logstat, net_tap_prov)?,
618 ),
619 _p:
620 PhantomData,
621 _p_not_ss:
622 PhantomData
623 }
624 );
625 }
626
627 /// Just initializes the instance from other instance. For example, in the crate which uses
628 /// this type of logging.
629 ///
630 /// Will work only if `F` and `D` are same.
631 pub
632 fn attach(other: &Syslog<F, D, SyShared>) -> SyRes<Self>
633 {
634 Ok(other.clone())
635 }
636}
637
638impl<F, D> Syslog<F, D, SyShared>
639where
640 F: SyslogFormatter,
641 D: SyslogDestination,
642{
643 /// Connects the current instance to the syslog server (destination).
644 pub
645 fn connectlog(&self) -> SyRes<()>
646 {
647 return
648 self
649 .stream
650 .lock()
651 .unwrap()
652 .connectlog();
653 }
654
655 /// Sets the logmask to filter out the syslog calls.
656 ///
657 /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
658 ///
659 /// # Example
660 ///
661 /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
662 ///
663 /// or
664 ///
665 /// ~(LOG_MASK!(Priority::LOG_INFO))
666 /// LOG_UPTO!(Priority::LOG_ERROR)
667 pub
668 fn setlogmask(&self, logmask: i32) -> SyRes<i32>
669 {
670 let pri =
671 self
672 .log_items
673 .lock()
674 .unwrap()
675 .set_logmask(logmask);
676
677 return Ok(pri);
678 }
679
680 /// Closes connection to the syslog server (destination).
681 pub
682 fn closelog(&self) -> SyRes<()>
683 {
684 return
685 self
686 .stream
687 .lock()
688 .unwrap()
689 .disconnectlog();
690 }
691
692 /// Similar to libc, syslog() sends data to syslog server.
693 ///
694 /// # Arguments
695 ///
696 /// * `pri` - a priority [Priority]
697 ///
698 /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
699 /// variable argumets amount. In Rust you should create your
700 /// own macros like format!() or use format!()]. The [String] and ref `'static`
701 /// [str] can be passed directly.
702 ///
703 /// # Returns
704 ///
705 /// A [SyRes] is returned which may describe an error.
706 #[inline]
707 pub
708 fn syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
709 {
710 let Some((formatted_msg, logstat)) =
711 self.log_items.lock().unwrap().vsyslog1_msg::<F, D>(pri, &fmt)
712 else { return Ok(()) };
713
714 self.stream.lock().unwrap().vsyslog1(logstat, formatted_msg)
715 }
716
717 /// This function can be used to update the facility name, for example
718 /// after fork().
719 ///
720 /// # Arguments
721 ///
722 /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
723 /// If set to [Option::None] would request the program name from OS.
724 pub
725 fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
726 {
727 self
728 .log_items
729 .lock()
730 .unwrap()
731 .set_identity(ident);
732
733 return Ok(());
734 }
735
736 /// Re-opens the connection to the syslog server. Can be used to
737 /// rotate logs(handle SIGHUP).
738 ///
739 /// # Returns
740 ///
741 /// A [Result] is retured as [SyRes].
742 ///
743 /// * [Result::Ok] - with empty inner type.
744 ///
745 /// * [Result::Err] - an error code and description
746 pub
747 fn reconnect(&self) -> SyRes<()>
748 {
749 return
750 self
751 .stream
752 .lock()
753 .unwrap()
754 .reconnectlog();
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 return
763 self
764 .stream
765 .lock()
766 .unwrap()
767 .update_tap_data(tap_data);
768 }
769}
770
771impl<F, D> Syslog<F, D, SyShared>
772where
773 F: SyslogFormatter,
774 D: SyslogDestination,
775{
776 /// Returns the streamable [SyStream] instance which can be used with [write!].
777 ///
778 /// It implements both [std::fmt::Write] and [std::io::Write].
779 ///
780 /// # Example
781 ///
782 /// ```ignore
783 /// let log =
784 /// SingleSyslog::openlog(
785 /// Some("test1"),
786 /// LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID,
787 /// LogFacility::LOG_DAEMON,
788 /// SyslogLocal::new()
789 /// ).unwrap();
790 ///
791 /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
792 /// ```
793 pub
794 fn get_stream<'t, PRI>(&'t self) -> SyStream<'t, PRI, D, F, &'t Self>
795 where PRI: SyStreamPri
796 {
797 SyStream
798 {
799 s: Some(self),
800 _p: PhantomData,
801 _p1: PhantomData,
802 _p2: PhantomData
803 }
804 }
805}
806
807impl<F: SyslogFormatter, D: SyslogDestination> SyStreamSyslogApi<F, D>
808for &Syslog<F, D, SyShared>
809{
810 type SYSLOG<'t> = &'t Syslog<F, D, SyShared>;
811
812 fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
813 {
814 syslog.syslog(pri, fmt)
815 }
816}
817
818// --- QUEUE
819
820
821#[cfg(feature = "build_with_queue")]
822pub mod build_with_queue
823{
824 use std::{sync::{Arc, Mutex, Weak, atomic::{AtomicBool, Ordering}}, thread};
825
826 use crate::{DefaultLocalSyslogDestination, SyslogDestination, formatters::{DefaultSyslogFormatter, SyslogFormatter}, map_error, sy_sync_queue::{SyCmd, SyslogInternal, SyslogQueueChanSnd, SyslogQueueChannel, SyslogQueueOneChanRcv}, sync::DefaultQueueAdapter};
827
828 use super::*;
829
830 #[derive(Debug, Clone)]
831 pub struct SyQueued<S = DefaultQueueAdapter, F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination>
832 where
833 F: SyslogFormatter,
834 D: SyslogDestination,
835 S: SyslogQueueChannel<F, D>,
836
837 {
838 /// Control flag
839 run_control: Weak<AtomicBool>,
840
841 /// commands channel
842 pub(crate) tasks: S::ChannelSnd,
843
844 /// process thread
845 thread: Arc<Mutex<Option<thread::JoinHandle<()>>>>,
846 }
847
848 impl<F, DD, S> SyslogType for SyQueued<S, F, DD>
849 where F: SyslogFormatter, DD: SyslogDestination, S: SyslogQueueChannel<F, DD>
850 {
851 type LockItemsBase = ();
852 type LockItems = Self::LockItemsBase;
853
854 type LockStreamBase<D: SyslogDestination> = SyQueued<S, F, DD>;
855 type LockStream<D: SyslogDestination> = Self::LockStreamBase<D>;
856
857 type MoveLockType = PhantomData<()>;
858
859 fn wrap_items(items: Self::LockItemsBase) -> Self::LockItems
860 {
861 items
862 }
863
864 fn wrap_stream<D: SyslogDestination>(stream: Self::LockStreamBase<D>) -> Self::LockStream<D>
865 {
866 stream
867 }
868 }
869
870 unsafe impl<F, D, S> Send for SyQueued<S, F, D>
871 where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
872 {}
873
874 impl<F, D, S> Drop for SyQueued<S, F, D>
875 where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
876 {
877 fn drop(&mut self)
878 {
879 if let Some(ctrl) = self.run_control.upgrade()
880 {
881 ctrl.store(false, Ordering::SeqCst);
882
883 if let Err(_e) = self.tasks.q_send_blocking(SyCmd::form_stop())
884 {
885
886 }
887
888 let join_handle = self.thread.lock().unwrap().take().unwrap();
889
890 let _ = join_handle.join();
891 }
892 }
893 }
894
895 impl<F, D, S> Clone for Syslog<F, D, SyQueued<S, F, D>>
896 where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
897 {
898 fn clone(&self) -> Self
899 {
900 Self
901 {
902 log_items:
903 self.log_items.clone(),
904 stream:
905 self.stream.clone(),
906 _p:
907 PhantomData,
908 _p_not_ss:
909 PhantomData
910 }
911 }
912 }
913
914 impl<F, D, S> DynSyslog for Syslog<F, D, SyQueued<S, F, D>>
915 where
916 F: SyslogFormatter,
917 D: SyslogDestination,
918 S: SyslogQueueChannel<F, D>
919 {
920 #[inline]
921 fn connectlog(&self) -> SyRes<()>
922 {
923 self.connectlog()
924 }
925
926 #[inline]
927 fn setlogmask(&self, logmask: i32) -> SyRes<i32>
928 {
929 self.setlogmask(logmask)
930 }
931
932 #[inline]
933 fn closelog(&self) -> SyRes<()>
934 {
935 self.closelog()
936 }
937
938 fn syslog(&self, pri: Priority, fmt: String) -> SyRes<()>
939 {
940 self.syslog(pri, fmt.into())
941 }
942
943 fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
944 {
945 self.change_identity(ident)
946 }
947
948 fn reconnect(&self) -> SyRes<()>
949 {
950 self.reconnect()
951 }
952 }
953
954 impl<F, D, S> Syslog<F, D, SyQueued<S, F, D>>
955 where
956 F: SyslogFormatter,
957 D: SyslogDestination,
958 S: SyslogQueueChannel<F, D>
959 {
960 /// Converts the current instance into dyncamic object. See [DynSyslog] for the interface.
961 pub
962 fn make_dyn(self) -> Box<dyn DynSyslog>
963 {
964 Box::new(self)
965 }
966
967 /// Opens a default connection to the local syslog server with default formatter with
968 /// provided generics.
969 ///
970 /// # Arguments
971 ///
972 /// * `ident` - A program name which will appear on the logs. If none, will be determined
973 /// automatically.
974 ///
975 /// * `logstat` - [LogStat] an instance config.
976 ///
977 /// * `facility` - [LogFacility] a syslog facility.
978 ///
979 /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
980 ///
981 /// # Returns
982 ///
983 /// A [SyRes] is returned ([Result]) with:
984 ///
985 /// * [Result::Ok] - with instance
986 ///
987 /// * [Result::Err] - with error description.
988 pub
989 fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self>
990 {
991 // creating internal syslog struct
992
993 let log_items =
994 LogItems::new(ident, 0xff, logstat, facility);
995
996 let stream =
997 SyslogSocketLockless::<D>::new(logstat, net_tap_prov)?;
998
999 let (inst, sender, run_ctrl) =
1000 SyslogInternal::<F, D, S>::new(log_items, stream)?;
1001
1002
1003 let thr_name: String = "syslog_queue/0".into();
1004
1005 // initiate a thread
1006 let thread_hnd =
1007 thread::Builder::new()
1008 .name(thr_name.clone())
1009 .spawn(move || SyslogInternal::<F, D, S>::thread_worker(inst))
1010 .map_err(|e|
1011 map_error!("{} thread spawn failed. {}", thr_name, e)
1012 )?;
1013
1014 let sy_q =
1015 SyQueued
1016 {
1017 run_control:
1018 run_ctrl,
1019 tasks:
1020 sender,
1021 thread:
1022 Arc::new(Mutex::new(Some(thread_hnd))),
1023 };
1024
1025 return Ok(
1026 Self
1027 {
1028 log_items:
1029 (),
1030 stream:
1031 sy_q,
1032 _p:
1033 PhantomData,
1034 _p_not_ss:
1035 PhantomData
1036 }
1037 );
1038 }
1039 }
1040
1041 impl<F, D, S> Syslog<F, D, SyQueued<S, F, D>>
1042 where
1043 F: SyslogFormatter,
1044 D: SyslogDestination,
1045 S: SyslogQueueChannel<F, D>
1046 {
1047 /// Connects the current instance to the syslog server (destination).
1048 pub
1049 fn connectlog(&self) -> SyRes<()>
1050 {
1051 let (sy_cmd, loopback) =
1052 SyCmd::form_connectlog();
1053
1054 self.stream.tasks.q_send_blocking(sy_cmd)?;
1055
1056 return
1057 loopback
1058 .recv_once_blocking()?;
1059 }
1060
1061 /// Sets the logmask to filter out the syslog calls.
1062 ///
1063 /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
1064 ///
1065 /// # Example
1066 ///
1067 /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
1068 ///
1069 /// or
1070 ///
1071 /// ~(LOG_MASK!(Priority::LOG_INFO))
1072 /// LOG_UPTO!(Priority::LOG_ERROR)
1073 pub
1074 fn setlogmask(&self, logmask: i32) -> SyRes<i32>
1075 {
1076 let (sy_cmd, loopback) =
1077 SyCmd::form_logmask(logmask);
1078
1079 self.stream.tasks.q_send_blocking(sy_cmd)?;
1080
1081 return
1082 loopback
1083 .recv_once_blocking();
1084 }
1085
1086 /// Closes connection to the syslog server (destination).
1087 pub
1088 fn closelog(&self) -> SyRes<()>
1089 {
1090 let (sy_cmd, loopback) =
1091 SyCmd::form_disconnectlog();
1092
1093 // send stop
1094 self.stream.tasks.q_send_blocking(sy_cmd)?;
1095
1096 return
1097 loopback
1098 .recv_once_blocking()?;
1099 }
1100
1101 /// Similar to libc, syslog() sends data to syslog server.
1102 ///
1103 /// # Arguments
1104 ///
1105 /// * `pri` - a priority [Priority]
1106 ///
1107 /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
1108 /// variable argumets amount. In Rust you should create your
1109 /// own macros like format!() or use format!()]. The [String] and ref `'static`
1110 /// [str] can be passed directly.
1111 ///
1112 /// # Returns
1113 ///
1114 /// A [SyRes] is returned which may describe an error.
1115 pub
1116 fn syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
1117 {
1118 // even if the thread is in a process of termination, there is
1119 // no need to sync access to the run_control field as even if
1120 // syslog thread will terminate before someone push something on the
1121 // queue, it will be left in the queue until the end of program's time.
1122
1123 let sy_cmd = SyCmd::form_syslog(pri, fmt);
1124
1125 return self.stream.tasks.q_send_blocking(sy_cmd);
1126 }
1127
1128 /// This function can be used to update the facility name, for example
1129 /// after fork().
1130 ///
1131 /// # Arguments
1132 ///
1133 /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
1134 /// If set to [Option::None] would request the program name from OS.
1135 pub
1136 fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
1137 {
1138 let sy_cmd =
1139 SyCmd::form_change_ident(ident.map(|v| v.to_string()));
1140
1141 return
1142 self.stream.tasks.q_send_blocking(sy_cmd);
1143 }
1144
1145 /// Re-opens the connection to the syslog server. Can be used to
1146 /// rotate logs(handle SIGHUP).
1147 ///
1148 /// # Returns
1149 ///
1150 /// A [Result] is retured as [SyRes].
1151 ///
1152 /// * [Result::Ok] - with empty inner type.
1153 ///
1154 /// * [Result::Err] - an error code and description
1155 pub
1156 fn reconnect(&self) -> SyRes<()>
1157 {
1158 return
1159 self.stream.tasks.q_send_blocking(SyCmd::form_reconnect());
1160 }
1161
1162 /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
1163 /// the same variant (type) as current.
1164 pub
1165 fn update_tap_data(&self, tap_data: D) -> SyRes<()>
1166 {
1167 let (tap_data_cmd, loopback) = SyCmd::form_update_tap(tap_data);
1168
1169 self.stream.tasks.q_send_blocking(tap_data_cmd)?;
1170
1171 return
1172 loopback
1173 .recv_once_blocking()?;
1174 }
1175 }
1176
1177 impl<F, D, S> Syslog<F, D, SyQueued<S, F, D>>
1178 where
1179 F: SyslogFormatter,
1180 D: SyslogDestination,
1181 S: SyslogQueueChannel<F, D>
1182 {
1183 /// Returns the streamable [SyStream] instance which can be used with [write!].
1184 ///
1185 /// It implements both [std::fmt::Write] and [std::io::Write].
1186 ///
1187 /// # Example
1188 ///
1189 /// ```ignore
1190 /// let log =
1191 /// SingleSyslog::openlog(
1192 /// Some("test1"),
1193 /// LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID,
1194 /// LogFacility::LOG_DAEMON,
1195 /// SyslogLocal::new()
1196 /// ).unwrap();
1197 ///
1198 /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
1199 /// ```
1200 pub
1201 fn get_stream<'t, PRI>(&'t self) -> SyStream<'t, PRI, D, F, &'t Self>
1202 where PRI: SyStreamPri
1203 {
1204 SyStream
1205 {
1206 s: Some(self),
1207 _p: PhantomData,
1208 _p1: PhantomData,
1209 _p2: PhantomData
1210 }
1211 }
1212 }
1213
1214 impl<F, D, S> SyStreamSyslogApi<F, D>
1215 for &Syslog<F, D, SyQueued<S, F, D>>
1216 where F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>
1217 {
1218 type SYSLOG<'t> = &'t Syslog<F, D, SyQueued<S, F, D>>;
1219
1220 fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
1221 {
1222 syslog.syslog(pri, fmt)
1223 }
1224 }
1225
1226
1227 /// A queued implementation for ASYNC.
1228 #[cfg(feature = "async_enabled")]
1229 pub mod syslog_async_queue
1230 {
1231 use crate::error::SyRes;
1232 use crate::sy_sync_queue::{SyCmd, SyslogQueueChanSnd, SyslogQueueChannel, SyslogQueueOneChanRcv};
1233 use crate::{Priority, Syslog};
1234 use crate::{formatters::SyslogFormatter, SyslogDestination, SyQueued};
1235 use crate::a_sync::syslog_trait::AsyncSyslogQueueApi;
1236
1237 impl<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>> AsyncSyslogQueueApi<F, D>
1238 for Syslog<F, D, SyQueued<S, F, D>>
1239 {
1240 async
1241 fn a_connectlog(&mut self) -> SyRes<()>
1242 {
1243 let (sy_cmd, loopback) =
1244 SyCmd::form_connectlog();
1245
1246 self.stream.tasks.q_send(sy_cmd).await?;
1247
1248 return
1249 loopback
1250 .recv_once()
1251 .await?;
1252 }
1253
1254 /// Sets the logmask to filter out the syslog calls. This function behaves
1255 /// differently as it behaves in syslog_sync.rs or syslog_async.rs.
1256 /// It may return an error if: syslog thread had exit and some thread calls
1257 /// this function. Or something happened with channel.
1258 /// This function blocks until the previous mask is received.
1259 ///
1260 /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
1261 ///
1262 /// # Example
1263 ///
1264 /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
1265 ///
1266 /// or
1267 ///
1268 /// ~(LOG_MASK!(Priority::LOG_INFO))
1269 /// LOG_UPTO!(Priority::LOG_ERROR)
1270 async
1271 fn a_setlogmask(&self, logmask: i32) -> SyRes<i32>
1272 {
1273 let (sy_cmd, loopback) =
1274 SyCmd::form_logmask(logmask);
1275
1276 self.stream.tasks.q_send(sy_cmd).await?;
1277
1278 return
1279 loopback
1280 .recv_once()
1281 .await;
1282 }
1283
1284 /// Closes connection to the syslog server
1285 async
1286 fn a_closelog(&self) -> SyRes<()>
1287 {
1288 let (sy_cmd, loopback) =
1289 SyCmd::form_disconnectlog();
1290
1291 // send stop
1292 self.stream.tasks.q_send(sy_cmd).await?;
1293
1294 return
1295 loopback
1296 .recv_once()
1297 .await?;
1298 }
1299
1300 /// Similar to libc, syslog() sends data to syslog server, but asynchroniously.
1301 ///
1302 /// # Arguments
1303 ///
1304 /// * `pri` - a priority [Priority]
1305 ///
1306 /// * `fmt` - a program's message to be sent as payload. The message is encoded with the
1307 /// [SyslogFormatter] and may be different for different formatters.
1308 #[inline]
1309 async
1310 fn a_syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
1311 {
1312 let sy_cmd = SyCmd::form_syslog(pri, fmt);
1313
1314 return self.stream.tasks.q_send(sy_cmd).await;
1315 }
1316
1317
1318 /// Performs the reconnection to the syslog server or file re-open.
1319 ///
1320 /// # Returns
1321 ///
1322 /// A [Result] is retured as [SyRes].
1323 ///
1324 /// * [Result::Ok] - with empty inner type.
1325 ///
1326 /// * [Result::Err] - an error code and description
1327 async
1328 fn a_reconnect(&self) -> SyRes<()>
1329 {
1330 return
1331 self.stream.tasks.q_send(SyCmd::form_reconnect()).await;
1332 }
1333
1334 async
1335 fn a_change_identity(&self, ident: &str) -> SyRes<()>
1336 {
1337 let sy_cmd =
1338 SyCmd::form_change_ident(Some(ident.to_string()));
1339
1340 return
1341 self.stream.tasks.q_send(sy_cmd).await;
1342 }
1343
1344 /// Updates the inner instance destionation i.e path to file
1345 /// or server address. The type of destination can not be changed.
1346 ///
1347 /// This function disconnects from syslog server if previously was
1348 /// connected (and reconnects if was connected previously).
1349 ///
1350 /// # Arguments
1351 ///
1352 /// * `new_tap` - a consumed instance of type `D` [SyslogDestination]
1353 ///
1354 /// # Returns
1355 ///
1356 /// A [SyRes] is returned. An error may be returned if:
1357 ///
1358 /// * connection to server was failed
1359 ///
1360 /// * incorrect type
1361 ///
1362 /// * disconnect frm server failed
1363 async
1364 fn a_update_tap_data(&self, new_tap: D) -> SyRes<()>
1365 {
1366 let (tap_data_cmd, loopback) = SyCmd::form_update_tap(new_tap);
1367
1368 self.stream.tasks.q_send(tap_data_cmd).await?;
1369
1370 return
1371 loopback
1372 .recv_once()
1373 .await?;
1374 }
1375 }
1376 }
1377}
1378
1379#[cfg(feature = "build_with_queue")]
1380pub use self::build_with_queue::SyQueued;