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