syslog_rs/sync/
syslog.rs

1/*-
2 * syslog-rs - a syslog client translated from libc to rust
3 * 
4 * Copyright 2025 Aleksandr Morozov
5 * 
6 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
7 * the European Commission - subsequent versions of the EUPL (the "Licence").
8 * 
9 * You may not use this work except in compliance with the Licence.
10 * 
11 * You may obtain a copy of the Licence at:
12 * 
13 *    https://joinup.ec.europa.eu/software/page/eupl
14 * 
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 * Licence for the specific language governing permissions and limitations
19 * under the Licence.
20 */
21
22use std::{fmt::{self, Arguments, Write}, marker::PhantomData};
23
24use crate::{error::SyRes, formatters::{DefaultSyslogFormatter, SyslogFormatter}, sy_sync_queue::SyslogQueueAdapter, syslog_provider::*, LogFacility, LogStat, Priority};
25
26#[cfg(feature = "use_sync_queue")]
27use crate::sy_sync_queue::SyslogQueue;
28
29
30use super::syslog_sync_shared::SyslogShared;
31use super::{syslog_stream::SyslogStream, syslog_trait::SyslogApi};
32
33
34/// A main instance of the Syslog client.
35/// 
36/// * `D` - a [SyslogDestination] instance which is either:
37///     [SyslogLocal], [SyslogFile], [SyslogNet], [SyslogTls]. By
38///     default a `SyslogLocal` is set.
39/// 
40/// * `F` - a [SyslogFormatter] a message formatter for the destination
41///     server. See `formatters` i.e `FormatRfc3146`, `FormatRfc5424`, `FormatFile`. Or
42///     `DefaultSyslogFormatter`, `DefaultSyslogFormatterFile`.
43/// 
44/// * `S` - a [SyslogApi] sybsystem. It is either [SyslogShared] or [SyslogQueue] with 
45///     the same 'F' - formatter type. By default a [SyslogShared] is set.
46#[derive(Debug)]
47pub struct Syslog<D = SyslogLocal, F = DefaultSyslogFormatter, S = SyslogShared<F>>
48    (S, PhantomData<F>, PhantomData<D>)
49where D: SyslogDestination, F: SyslogFormatter, S: SyslogApi;
50
51impl Syslog
52{
53    /// Opens a default connection to the local syslog server with default formatter.
54    /// 
55    /// # Arguments
56    /// 
57    /// * `ident` - A program name which will appear on the logs. If none, will be determined
58    ///     automatically.
59    /// 
60    /// * `logstat` - [LogStat] an instance config.
61    /// 
62    /// * `facility` - [LogFacility] a syslog facility.
63    /// 
64    /// * `dest` - a destination server. Allows to connect to the different local syslog server or
65    ///     will be determined automatically.
66    /// 
67    /// # Returns
68    /// 
69    /// A [SyRes] is returned ([Result]) with: 
70    /// 
71    /// * [Result::Ok] - with instance
72    /// 
73    /// * [Result::Err] - with error description.
74    pub 
75    fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility, dest: SyslogLocal) -> SyRes<Self>
76    {
77        let net_tap = dest.init()?;
78        
79        let syslog = SyslogShared::<DefaultSyslogFormatter>::openlog(ident, logstat, facility, net_tap)?;
80
81        return Ok(Self(syslog, PhantomData::<DefaultSyslogFormatter>, PhantomData::<SyslogLocal>));
82    }
83}
84
85#[cfg(feature = "use_sync")]
86impl<D: SyslogDestination, F: SyslogFormatter> Syslog<D, F, SyslogShared<F>>
87{
88    /// Opens a special connection to the destination syslog server with specific formatter.
89    /// 
90    /// All struct generic should be specified before calling this function.
91    /// 
92    /// # Arguments
93    /// 
94    /// * `ident` - A program name which will appear on the logs. If none, will be determined
95    ///     automatically.
96    /// 
97    /// * `logstat` - [LogStat] an instance config.
98    /// 
99    /// * `facility` - [LogFacility] a syslog facility.
100    /// 
101    /// * `dest` - a destination server. Allows to connect to the different local syslog server or
102    ///     will be determined automatically.
103    /// 
104    /// # Returns
105    /// 
106    /// A [SyRes] is returned ([Result]) with: 
107    /// 
108    /// * [Result::Ok] - with instance
109    /// 
110    /// * [Result::Err] - with error description.
111    pub 
112    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, dest: D) -> SyRes<Syslog<D, F, SyslogShared<F>>>
113    {
114        let net_tap = dest.init()?;
115        
116        let syslog = SyslogShared::<F>::openlog(ident, logstat, facility, net_tap)?;
117
118        return Ok(Self(syslog, PhantomData::<F>, PhantomData::<D>));
119    }
120
121    /// Sets the logmask to filter out the syslog calls. This function behaves 
122    /// differently as it behaves in syslog_sync.rs or syslog_async.rs.
123    /// It may return an error if: syslog thread had exit and some thread calls
124    /// this function. Or something happened with channel. 
125    /// This function blocks until the previous mask is received.
126    /// 
127    /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
128    ///
129    /// # Example
130    ///
131    /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
132    ///
133    /// or
134    ///
135    /// ~(LOG_MASK!(Priority::LOG_INFO))
136    /// LOG_UPTO!(Priority::LOG_ERROR) 
137    pub 
138    fn setlogmask(&self, logmask: i32) -> SyRes<i32>
139    {
140        return self.0.setlogmask(logmask);
141    }
142
143    /// Changes the identity i.e program name which will appear on the logs.
144    /// 
145    /// Can return error if mutex is poisoned.
146    pub 
147    fn change_identity(&self, ident: &str) -> SyRes<()>
148    {
149        return self.0.change_identity(ident);
150    }
151
152    /// Closes connection to the syslog server
153    pub 
154    fn closelog(&self) -> SyRes<()>
155    {
156        return self.0.closelog();
157    }
158
159    /// Similar to libc, syslog() sends data to syslog server.
160    /// 
161    /// # Arguments
162    ///
163    /// * `pri` - a priority [Priority]
164    ///
165    /// * `fmt` - a program's message to be sent as payload.
166    #[inline]
167    pub 
168    fn syslog(&self, pri: Priority, fmt: String)
169    {
170        self.0.syslog(pri, fmt);
171    }
172
173    /// Sends message to syslog (same as `syslog`).
174    #[inline]
175    pub 
176    fn vsyslog<M: AsRef<str>>(&self, pri: Priority, fmt: M)
177    {
178        self.0.vsyslog(pri, fmt);
179    }
180
181    /// Performs the reconnection to the syslog server or file re-open.
182    /// 
183    /// # Returns
184    /// 
185    /// A [Result] is retured as [SyRes].
186    /// 
187    /// * [Result::Ok] - with empty inner type.
188    /// 
189    /// * [Result::Err] - an error code and description
190    pub  
191    fn reconnect(&self) -> SyRes<()>
192    {
193        return self.0.reconnect();
194    }
195
196    /// Updates the inner instance destionation i.e path to file
197    /// or server address. The type of destination can not be changed.
198    /// 
199    /// This function disconnects from syslog server if previously was 
200    /// connected (and reconnects if was connected previously).
201    /// 
202    /// # Arguments 
203    /// 
204    /// * `new_tap` - a consumed instance of type `D` [SyslogDestination]
205    /// 
206    /// # Returns 
207    /// 
208    /// A [SyRes] is returned. An error may be returned if:
209    /// 
210    /// * connection to server was failed
211    /// 
212    /// * incorrect type
213    /// 
214    /// * disconnect frm server failed
215    pub 
216    fn update_tap(&self, new_tap: D) -> SyRes<()>
217    {
218        return self.0.update_tap_data(new_tap.init()?);
219    }
220    
221    /// Derives from active syslog intance an [fmt::Write] instance.
222    /// 
223    /// A boxed `dyn` instance of [SyslogStream] is returned.
224    pub 
225    fn make_stream(&self, pri: Priority) -> Box<dyn SyslogStream>
226    {
227        let inst = 
228            StreamableSyslog
229            {
230                inner: self.0.clone(),
231                pri: pri,
232                _p: PhantomData::<F>,
233            };
234
235        return Box::new(inst);
236    }
237}
238
239#[cfg(feature = "use_sync_queue")]
240impl<D: SyslogDestination, F: SyslogFormatter> Syslog<D, F, SyslogQueue<F>>
241{
242    /// Opens a special connection to the destination syslog server with specific formatter.
243    /// 
244    /// All struct generic should be specified before calling this function.
245    /// 
246    /// # Arguments
247    /// 
248    /// * `ident` - A program name which will appear on the logs. If none, will be determined
249    ///     automatically.
250    /// 
251    /// * `logstat` - [LogStat] an instance config.
252    /// 
253    /// * `facility` - [LogFacility] a syslog facility.
254    /// 
255    /// * `dest` - a destination server. Allows to connect to the different local syslog server or
256    ///     will be determined automatically.
257    /// 
258    /// # Returns
259    /// 
260    /// A [SyRes] is returned ([Result]) with: 
261    /// 
262    /// * [Result::Ok] - with instance
263    /// 
264    /// * [Result::Err] - with error description.
265    pub 
266    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, dest: D) -> SyRes<Syslog<D, F, SyslogQueue<F>>>
267    {
268        let net_tap = dest.init()?;
269        
270        let syslog = SyslogQueue::<F>::openlog(ident, logstat, facility, net_tap)?;
271    
272        return Ok(Self(syslog, PhantomData::<F>, PhantomData::<D>));
273    }
274
275    /// Sets the logmask to filter out the syslog calls. This function behaves 
276    /// differently as it behaves in syslog_sync.rs or syslog_async.rs.
277    /// It may return an error if: syslog thread had exit and some thread calls
278    /// this function. Or something happened with channel. 
279    /// This function blocks until the previous mask is received.
280    /// 
281    /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
282    ///
283    /// # Example
284    ///
285    /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
286    ///
287    /// or
288    ///
289    /// ~(LOG_MASK!(Priority::LOG_INFO))
290    /// LOG_UPTO!(Priority::LOG_ERROR) 
291    pub 
292    fn setlogmask(&self, logmask: i32) -> SyRes<i32>
293    {
294        return self.0.setlogmask(logmask);
295    }
296
297    /// Closes connection to the syslog server
298    pub 
299    fn closelog(&self) -> SyRes<()>
300    {
301        return self.0.closelog();
302    }
303
304    /// Similar to libc, syslog() sends data to syslog server.
305    /// 
306    /// # Arguments
307    ///
308    /// * `pri` - a priority [Priority]
309    ///
310    /// * `fmt` - a program's message to be sent as payload.
311    #[inline]
312    pub 
313    fn syslog(&self, pri: Priority, fmt: String)
314    {
315        self.0.syslog(pri, fmt);
316    }
317
318    /// Sends message to syslog (same as `syslog`).
319    #[inline]
320    pub 
321    fn vsyslog<M: AsRef<str>>(&self, pri: Priority, fmt: M)
322    {
323        self.0.vsyslog(pri, fmt);
324    }
325
326    /// Performs the reconnection to the syslog server or file re-open.
327    /// 
328    /// # Returns
329    /// 
330    /// A [Result] is retured as [SyRes].
331    /// 
332    /// * [Result::Ok] - with empty inner type.
333    /// 
334    /// * [Result::Err] - an error code and description
335    pub  
336    fn reconnect(&self) -> SyRes<()>
337    {
338        return self.0.reconnect();
339    }
340
341    /// Updates the inner instance destionation i.e path to file
342    /// or server address. The type of destination can not be changed.
343    /// 
344    /// This function disconnects from syslog server if previously was 
345    /// connected (and reconnects if was connected previously).
346    /// 
347    /// # Arguments 
348    /// 
349    /// * `new_tap` - a consumed instance of type `D` [SyslogDestination]
350    /// 
351    /// # Returns 
352    /// 
353    /// A [SyRes] is returned. An error may be returned if:
354    /// 
355    /// * connection to server was failed
356    /// 
357    /// * incorrect type
358    /// 
359    /// * disconnect frm server failed
360    pub 
361    fn update_tap(&self, new_tap: D) -> SyRes<()>
362    {
363        return self.0.update_tap_data(new_tap.init()?);
364    }
365    
366    /// Derives from active syslog intance an [fmt::Write] instance.
367    /// 
368    /// A boxed `dyn` instance of [SyslogStream] is returned.
369    pub 
370    fn make_stream(&self, pri: Priority) -> Box<dyn SyslogStream>
371    {
372        let inst = 
373            StreamableSyslog
374            {
375                inner: self.0.clone(),
376                pri: pri,
377                _p: PhantomData::<F>,
378            };
379
380        return Box::new(inst);
381    }
382}
383
384#[cfg(all(feature = "use_sync_queue", feature = "use_async"))]
385impl<D: SyslogDestination, F: SyslogFormatter> Syslog<D, F, SyslogQueue<F>>
386{
387    pub 
388    fn make_adapter(&self) -> SyslogQueueAdapter
389    {
390        return self.0.make_adapter();
391    }
392}
393
394#[cfg(feature = "build_with_net")]
395impl<F: SyslogFormatter, S: SyslogApi> Syslog<SyslogNet, F, S>
396{
397
398}
399
400#[cfg(feature = "build_with_file")]
401impl<F: SyslogFormatter, S: SyslogApi> Syslog<SyslogFile, F, S>
402{
403
404}
405
406impl<F: SyslogFormatter, S: SyslogApi> Syslog<SyslogLocal, F, S>
407{
408
409}
410
411#[cfg(feature = "build_with_tls")]
412impl<F: SyslogFormatter, S: SyslogApi> Syslog<SyslogTls, F, S>
413{
414
415}
416
417
418/// An implementation which allows to [write!] to syslog.
419/// 
420/// Will block awaiting while inner mutex is locked.
421/// You need to make sure that [SyslogStream] is stored as
422/// mutable instance.
423struct StreamableSyslog<F: SyslogFormatter, S: SyslogApi>
424{
425    /// Syslog instance
426    inner: S,
427
428    /// Priority
429    pri: Priority,
430
431    _p: PhantomData<F>
432}
433
434impl<F: SyslogFormatter, S: SyslogApi> SyslogStream for StreamableSyslog<F, S>
435{
436    /// Updates the pri [Priority] 
437    fn update_pri(&mut self, new_pri: Priority) -> Priority
438    {
439        let prev = self.pri;
440        self.pri = new_pri;
441
442        return prev;
443    }
444}
445
446impl<F: SyslogFormatter, S: SyslogApi> Write for StreamableSyslog<F, S>
447{
448    fn write_str(&mut self, s: &str) -> fmt::Result 
449    {
450        self.inner.vsyslog(self.pri, s);
451
452        return Ok(());
453    }
454
455    fn write_fmt(self: &mut Self, args: Arguments<'_>) -> fmt::Result
456    {
457        if let Some(s) = args.as_str() 
458        {
459            return self.write_str(s);
460        } 
461        else 
462        {
463            return self.write_str(&args.to_string());
464        }
465    }
466}
467
468/*
469#[test]
470fn test1()
471{
472    let sy = 
473        Syslog
474            ::<SyslogNet, DefaultSyslogFormatter, SyslogShared<DefaultSyslogFormatter>>
475            ::openlog_with(None, LogStat::empty(), LogFacility::empty(), SyslogNet::new())
476                .unwrap();
477
478    //sy.change_net();
479}
480    */
481#[cfg(feature = "use_sync")]
482#[cfg(test)]
483mod tests_shared
484{
485    use super::*;
486
487    #[test]
488    fn test_single_message()
489    {
490
491
492        let log = 
493                Syslog::openlog(
494                    Some("test1"), 
495                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
496                    LogFacility::LOG_DAEMON,
497                SyslogLocal::new());
498
499        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
500
501        let log = log.unwrap();
502
503        let now = std::time::Instant::now();
504
505        log.syslog(Priority::LOG_DEBUG, format!("test UTF-8 проверка BOM UTF-8"));
506
507        let dur = now.elapsed();
508        println!("{:?}", dur);
509
510        let now = std::time::Instant::now();
511
512        log.syslog(Priority::LOG_DEBUG, format!("test UTF-8  きるさお命泉ぶねりよ日子金れっ"));
513
514        let dur = now.elapsed();
515        println!("{:?}", dur);
516
517        let _ = log.closelog();
518
519        return;
520    }
521
522    #[test]
523    fn test_single_message_perror()
524    {
525        /*use std::sync::Arc;
526        use std::thread;
527        use std::time::Duration;
528        use super::{LOG_MASK};*/
529
530        let log = 
531            Syslog::openlog(
532                    Some("test1"), 
533                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID | LogStat::LOG_PERROR, 
534                    LogFacility::LOG_DAEMON,
535                    SyslogLocal::new()
536                );
537
538        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
539
540        let log = log.unwrap();
541
542        log.syslog(Priority::LOG_DEBUG, format!("test UTF-8 きるさお命泉ぶねりよ日子金れっ проверка BOM"));
543
544        let _ = log.closelog();
545
546        return;
547    }
548
549    #[test]
550    fn test_multithreading()
551    {
552        use std::sync::Arc;
553        use std::thread;
554        use std::time::{Instant, Duration};
555
556        let log = 
557                Syslog::openlog(
558                    Some("test1"), 
559                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
560                    LogFacility::LOG_DAEMON,
561                SyslogLocal::new()
562            );
563
564        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
565
566        let log = Arc::new(log.unwrap());
567        let c1_log = log.clone();
568        let c2_log = log.clone();
569
570        thread::spawn(move|| {
571            for i in 0..5
572            {
573                thread::sleep(Duration::from_nanos(200));
574                let now = Instant::now();
575                c1_log.syslog(Priority::LOG_DEBUG, format!("a message from thread 1 #{}[]", i));
576                let elapsed = now.elapsed();
577                println!("t1: {:?}", elapsed);
578            }
579        });
580
581        thread::spawn(move|| {
582            for i in 0..5
583            {
584                thread::sleep(Duration::from_nanos(201));
585                let now = Instant::now();
586                c2_log.syslog(Priority::LOG_DEBUG, format!("きるさお命泉ぶねりよ日子金れっ {}", i));
587                let elapsed = now.elapsed();
588                println!("t2: {:?}", elapsed);
589            }
590        });
591
592        let now = Instant::now();
593        log.syslog(Priority::LOG_DEBUG, format!("A message from main, きるさお命泉ぶねりよ日子金れっ"));
594        let elapsed = now.elapsed();
595        println!("main: {:?}", elapsed);
596
597        thread::sleep(Duration::from_secs(2));
598
599        let _ = log.closelog();
600
601        return;
602    }
603
604    #[test]
605    fn long_msg_test()
606    {
607        // 40 EMSGSIZE
608        let msg = 
609    "7赤ざクぽな載覧な改申ほふ取容う主坊酸ヱ司戦ヒソオ力端めゃ間真ル実記キ団需くスルて学回県仁京ぴ熱完かあもく。上ょぜ催5強変却ソヲキ転入ク記事購シリ断衝ぽ玲面たぽつへ様態の善無かー勢加ヨマナキ趣会撮さはこ違42器のレうゆ。稿エスタ己報照アイネル岩撲ムニ半者サアキツ画47込死請誌8策ノ再然ださそ禁断しにた高否リ続最ユケ山芸ロ去群づへ索芭あン掲佳怖斎澤クね。
610    書キマコヒ上広ざばてわ会佐のにりリ学総点フわすか頼野ぜイよば株約ネキヱメ必軽チヲ録使ラ下能ナウコ紀捜れ霊別イ摯療じらどト相3聞めびら情拳ユフエ確経板植えーぜあ。無ど曲注程クタル系新どばくい都面村リと計安ワヱ月見モヌエノ完臨健85定テサコミ貸容ミテタカ写載とかえげ詐訃だみそな。代け設養テヨナ乱54服料ニ止画ら不暮ッ強治ぱンやし供方広づじもあ般判ごくドラ示刊ヱサトキ速事取さふぞ授共西れッ。
611    陸オク康59面くこ惑7伊エノモ候余ロソウ政投ナク転文だけス香両4誕よ数真ひぞぴざ空加ユラ勢隣ゃびよ移72士シヌ図際想カマテ覧費活輸権因ん。東がイ価変や濃恒ヒメネエ学見ょ理供カオムヱ針中わば文63転ヨリホミ礼民ネトマツ速果還ばが転世晴げでんが。犯レト同岸サケ写新ゆぐぱひ人捕けほゅえ的人第いぜ転連ぎど京分株ニムトヒ家及ユフケヌ記定み歩死質をゃいイ都末ワ革量んつ覧打百レいん。
612    下ゃとぞら川株以東ぴそぱ費更ウマヨメ覧論ウスレモ八度ずんとイ発平う果構つ小各ぼス夢国フラ連石はちい雪飲聴ゅいと。門ぱし続消上ト文影投設テユチ設川ルラシケ引押ヤマセメ権書ハテ例発リロソイ環載ぴ企実カマリト店鉄ワソロフ逆子先チルマ楽法どゆね声東女携抜爆んけみ。14報ふラめて怒止に開謙こさ促大ヘチツ遺飯ケ営長コキ価空少ろッひぱ読信ケテウ者育ヒミク代57柱浪フ。
613    ";
614
615    println!("{}", msg.len());
616
617        let log = 
618                Syslog::openlog(
619                    Some("test1"), 
620                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
621                    LogFacility::LOG_DAEMON,
622                    SyslogLocal::new()
623                );
624
625        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
626
627        let log = log.unwrap();
628
629        log.syslog(Priority::LOG_DEBUG, format!("{}", msg));
630                    
631        let _ = log.closelog();
632
633        return;
634    }
635}
636    
637#[cfg(feature = "use_sync_queue")]
638#[cfg(test)]
639mod tests_queue
640{
641    use crate::{formatters::DefaultSyslogFormatter, sy_sync::Syslog, LogFacility, LogStat, Priority, SyslogLocal, SyslogQueue};
642
643
644    #[test]
645    fn test_multithreading()
646    {
647        use std::sync::Arc;
648        use std::thread;
649        use std::time::{Instant, Duration};
650        use crate::LOG_MASK;
651
652        let log = 
653            Syslog
654                ::<SyslogLocal, DefaultSyslogFormatter, SyslogQueue<_>>
655                ::openlog_with(
656                Some("test1"), 
657                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
658                LogFacility::LOG_DAEMON, SyslogLocal::new()
659            );
660
661        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
662
663        let log = Arc::new(log.unwrap());
664        let c1_log = log.clone();
665        let c2_log = log.clone();
666
667        thread::spawn(move|| {
668            for i in 0..5
669            {
670                thread::sleep(Duration::from_nanos(200));
671                let now = Instant::now();
672                c1_log.syslog(Priority::LOG_DEBUG, format!("a message from thread 1 #{}[]", i));
673                let elapsed = now.elapsed();
674                println!("t1: {:?}", elapsed);
675            }
676        });
677
678        thread::spawn(move|| {
679            for i in 0..5
680            {
681                thread::sleep(Duration::from_nanos(201));
682                let now = Instant::now();
683                c2_log.syslog(Priority::LOG_DEBUG, format!("きるさお命泉ぶねりよ日子金れっ {}", i));
684                let elapsed = now.elapsed();
685                println!("t2: {:?}", elapsed);
686            }
687        });
688
689        let res = log.setlogmask(!LOG_MASK!(Priority::LOG_ERR));
690
691        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
692        assert_eq!(res.unwrap(), 0xff, "should be 0xff");
693
694        let now = Instant::now();
695        log.syslog(Priority::LOG_DEBUG, format!("A message from main, сообщение от главнюка"));
696        let elapsed = now.elapsed();
697        println!("main: {:?}", elapsed);
698
699        thread::sleep(Duration::from_secs(2));
700
701        let _ = log.closelog();
702
703        thread::sleep(Duration::from_millis(500));
704
705        let res = log.setlogmask(!LOG_MASK!(Priority::LOG_ERR));
706
707        assert_eq!(res.is_ok(), true);
708
709        return;
710    }
711
712    #[cfg(feature = "build_with_file")]
713    #[test]
714    fn test_file_multithreading()
715    {
716        use std::sync::Arc;
717        use std::thread;
718        use std::time::{Instant, Duration};
719        use crate::formatters::DefaultSyslogFormatterFile;
720        use crate::{SyslogFile, SyslogQueue, LOG_MASK};
721
722        let log = 
723            Syslog
724                ::<SyslogFile, DefaultSyslogFormatterFile, SyslogQueue<_>>
725                ::openlog_with(
726                    Some("test2"), 
727                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
728                    LogFacility::LOG_DAEMON, 
729                    SyslogFile::new("/tmp/syslog_rs_test2.log")
730                );
731                    
732
733        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
734
735        let log = Arc::new(log.unwrap());
736        let c1_log = log.clone();
737        let c2_log = log.clone();
738
739        thread::spawn(move|| {
740            for i in 0..5
741            {
742                thread::sleep(Duration::from_nanos(200));
743                let now = Instant::now();
744                c1_log.syslog(Priority::LOG_ALERT, format!("a message from thread 1 #{}[]", i));
745                let elapsed = now.elapsed();
746                println!("t1: {:?}", elapsed);
747            }
748        });
749
750        thread::spawn(move|| {
751            for i in 0..5
752            {
753                thread::sleep(Duration::from_nanos(201));
754                let now = Instant::now();
755                c2_log.syslog(Priority::LOG_DEBUG, format!("きるさお命泉ぶねりよ日子金れっ {}", i));
756                let elapsed = now.elapsed();
757                println!("t2: {:?}", elapsed);
758            }
759        });
760
761        let res = log.setlogmask(!LOG_MASK!(Priority::LOG_ERR));
762
763        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
764        assert_eq!(res.unwrap(), 0xff, "should be 0xff");
765
766        let now = Instant::now();
767        log.syslog(Priority::LOG_DEBUG, format!("A message from main, きるさお命泉ぶねりよ日子金れっ"));
768        let elapsed = now.elapsed();
769        println!("main: {:?}", elapsed);
770
771        thread::sleep(Duration::from_secs(2));
772
773        let _ = log.closelog();
774
775        thread::sleep(Duration::from_millis(500));
776
777        let res = log.setlogmask(!LOG_MASK!(Priority::LOG_ERR));
778
779        assert_eq!(res.is_ok(), true);
780
781        return;
782    }
783
784}
785
786