syslog-rs 6.6.1

A native Rust implementation of the glibc/libc/windows syslog client and windows native log for logging.
Documentation
/*-
 * syslog-rs - a syslog client translated from libc to rust
 * 
 * Copyright 2025 Aleksandr Morozov
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */

use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use std::{str};

use crate::formatters::DefaultSyslogFormatter;
use crate::sync::{LogItems, SyStream, SyStreamPri, SyStreamSyslogApi};
use crate::{formatters::SyslogFormatter};
use crate::{DefaultLocalSyslogDestination, SyslogDestination, common::*};
use crate::error::SyRes;


use crate::sync::syslog_sync_internal::{SyslogSocketLockless};



/// A `sync`, shared instance of the syslog client which is shared between many
/// threads. Previously a mutex was used, but since the v5.0.0 a CoW experimental
/// approach is used. The `CoW` creates clones of the instance without holding
/// a long mutex locks. When writing, the instance does not hold a long lock on
/// updated item. Only exclusive lock locks the readers, because the instance is
/// updated and not usable anyway.
/// 
/// If the program has fixed amount of threads, probably the `syslog_threadlocal`
/// will be better alternative. It has the same functionality, but avoids 
/// any sync locks by working in current thread.
/// 
/// # Traits
/// 
/// For this isntance a [SyslogApi] and [SyStreamApi] are implemented.
/// 
/// # Examples
/// 
/// ```ignore
/// let log = 
///     SyncSyslog::openlog(
///         Some("test1"), 
///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
///         LogFacility::LOG_DAEMON,
///         SyslogLocal::new()
///     );
/// ```
/// 
/// ```ignore
/// let log = 
///     SyncSyslog
///         ::<DefaultSyslogFormatter, SyslogLocal>
///         ::openlog_with(
///             Some("test1"), 
///             LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
///             LogFacility::LOG_DAEMON,
///             SyslogLocal::new()
///         );
/// ```
/// 
/// ```ignore
/// pub static SYSLOG3: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogLocal,>> = 
///     LazyLock::new(|| 
///         {
///             SyncSyslog
///                 ::<DefaultSyslogFormatter, SyslogLocal>
///                 ::openlog_with(
///                     Some("test1"), 
///                     LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
///                     LogFacility::LOG_DAEMON,
///                     SyslogLocal::new()
///                 )
///                 .unwrap()
///         }
///     );
/// ```
/// # Streaming
/// 
/// A stream is availble via [SyStreamApi].
/// 
/// ```ignore
/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
/// ```
/// 
/// # Generics
/// 
/// * `F` - a [SyslogFormatter] which sets the instance which would 
///     format the message.
/// 
/// * `D` - a [SyslogDestination] instance which is either:
///     [SyslogLocal], [SyslogFile], [SyslogNet], [SyslogTls]. By
///     default a `SyslogLocal` is selected.
#[derive(Debug, Clone)]
pub struct SyncSyslog<F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination>
where 
    F: SyslogFormatter, 
    D: SyslogDestination, 
{   
    /// An identification i.e program name, thread name
    log_items: Arc<Mutex<LogItems>>,

    /// A stream (unixdatagram, udp, tcp)
    stream: Arc<Mutex<SyslogSocketLockless<D>>>,

     _p: PhantomData<F>,
}

unsafe impl<F: SyslogFormatter, D: SyslogDestination> Send for SyncSyslog<F, D>
{}

impl SyncSyslog
{
    /// Opens a default connection to the local syslog server with default formatter.
    /// 
    /// In order to access the syslog API, use the [SyslogApi].
    /// 
    /// # Arguments
    /// 
    /// * `ident` - A program name which will appear on the logs. If none, will be determined
    ///     automatically.
    /// 
    /// * `logstat` - [LogStat] an instance config.
    /// 
    /// * `facility` - [LogFacility] a syslog facility.
    /// 
    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
    /// 
    /// # Returns
    /// 
    /// A [SyRes] is returned ([Result]) with: 
    /// 
    /// * [Result::Ok] - with instance
    /// 
    /// * [Result::Err] - with error description.
    pub 
    fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility, 
        net_tap_prov: DefaultLocalSyslogDestination) -> SyRes<Self> 
    {
        return Ok( 
            Self
            {
                log_items: 
                    Arc::new(
                        Mutex::new(
                            LogItems::new(ident, 0xff, logstat, facility)
                        )
                    ),
                stream: 
                    Arc::new(
                        Mutex::new(
                            SyslogSocketLockless::<DefaultLocalSyslogDestination>::new(logstat, net_tap_prov)?
                        )
                    ),
                _p: 
                    PhantomData,
            }
        );
    }
}


impl<F: SyslogFormatter, D: SyslogDestination> SyncSyslog<F, D>
{
    /// Opens a special connection to the destination syslog server with specific formatter.
    /// 
    /// All struct generic should be specified before calling this function.
    /// 
    /// In order to access the syslog API, use the [SyslogApi].
    /// 
    /// # Arguments
    /// 
    /// * `ident` - A program name which will appear on the logs. If none, will be determined
    ///     automatically.
    /// 
    /// * `logstat` - [LogStat] an instance config.
    /// 
    /// * `facility` - [LogFacility] a syslog facility.
    /// 
    /// * `net_tap_prov` - a destination server. A specific `D` instance which contains infomation 
    ///     about the destination server. See `syslog_provider.rs`.
    /// 
    /// # Returns
    /// 
    /// A [SyRes] is returned ([Result]) with: 
    /// 
    /// * [Result::Ok] - with instance
    /// 
    /// * [Result::Err] - with error description.
    pub 
    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self> 
    {
        return Ok( 
            Self
            {
                log_items: 
                    Arc::new(
                        Mutex::new(
                            LogItems::new(ident, 0xff, logstat, facility)
                        )
                    ),
                stream: 
                    Arc::new(
                        Mutex::new(
                            SyslogSocketLockless::<D>::new(logstat, net_tap_prov)?
                        )
                    ),
                _p: 
                    PhantomData,
            }
        );
    }
}


impl<F: SyslogFormatter, D: SyslogDestination> SyncSyslog<F, D>
{
    /// Connects the current instance to the syslog server (destination).
    pub 
    fn connectlog(&self) -> SyRes<()>
    {
        return 
            self
                .stream
                .lock()
                .unwrap()
                .connectlog();
    }

    /// Sets the logmask to filter out the syslog calls.
    /// 
    /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
    ///
    /// # Example
    ///
    /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
    ///
    /// or
    ///
    /// ~(LOG_MASK!(Priority::LOG_INFO))
    /// LOG_UPTO!(Priority::LOG_ERROR)
    pub 
    fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
    {
        let pri = 
            self
                .log_items
                .lock()
                .unwrap()
                .set_logmask(logmask);

        return Ok(pri);
    }

    /// Closes connection to the syslog server (destination).
    pub 
    fn closelog(&self) -> SyRes<()> 
    {
        return 
            self
                .stream
                .lock()
                .unwrap()
                .disconnectlog();
    }

    /// Similar to libc, syslog() sends data to syslog server.
    /// 
    /// # Arguments
    ///
    /// * `pri` - a priority [Priority]
    ///
    /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
    ///     variable argumets amount. In Rust you should create your
    ///     own macros like format!() or use format!()]. The [String] and ref `'static` 
    ///     [str] can be passed directly.
    /// 
    /// # Returns 
    /// 
    /// A [SyRes] is returned which may describe an error.
    #[inline]
    pub 
    fn syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
    {
        let Some((formatted_msg, logstat)) = 
            self.log_items.lock().unwrap().vsyslog1_msg::<F, D>(pri, &fmt)
        else { return Ok(()) };

        self.stream.lock().unwrap().vsyslog1(logstat, formatted_msg)
    }

    /// This function can be used to update the facility name, for example
    /// after fork().
    /// 
    /// # Arguments
    /// 
    /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
    ///     If set to [Option::None] would request the program name from OS.
    pub 
    fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
    {
        self
            .log_items
            .lock()
            .unwrap()
            .set_identity(ident);

        return Ok(());
    }

    /// Re-opens the connection to the syslog server. Can be used to 
    /// rotate logs(handle SIGHUP).
    /// 
    /// # Returns
    /// 
    /// A [Result] is retured as [SyRes].
    /// 
    /// * [Result::Ok] - with empty inner type.
    /// 
    /// * [Result::Err] - an error code and description 
    pub 
    fn reconnect(&self) -> SyRes<()>
    {
        return
            self
                .stream
                .lock()
                .unwrap()
                .reconnectlog();
    }

    /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
    /// the same variant (type) as current.
    pub 
    fn update_tap_data(&self, tap_data: D) -> SyRes<()>
    {
        return
            self
                .stream
                .lock()
                .unwrap()
                .update_tap_data(tap_data);
    }

}

impl<F, D> SyncSyslog<F, D>
where F: SyslogFormatter, D: SyslogDestination
{
    /// Returns the streamable [SyStream] instance which can be used with [write!].
    /// 
    /// It implements both [std::fmt::Write] and [std::io::Write].
    /// 
    /// # Example
    /// 
    /// ```ignore
    /// let log = 
    ///     SingleSyslog::openlog(
    ///         Some("test1"), 
    ///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
    ///         LogFacility::LOG_DAEMON,
    ///         SyslogLocal::new()
    ///     ).unwrap();
    /// 
    /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
    /// ```
    pub 
    fn get_stream<'t, PRI>(&'t self) -> SyStream<'t, PRI, D, F, &'t Self>
    where PRI: SyStreamPri
    {
        SyStream
        {
            s: Some(self),
            _p: PhantomData,
            _p1: PhantomData,
            _p2: PhantomData
        }
    }
}


impl<F: SyslogFormatter, D: SyslogDestination> SyStreamSyslogApi<F, D>  
for &SyncSyslog<F, D>
{
    type SYSLOG<'t> = &'t SyncSyslog<F, D>;

    fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
    {
        syslog.syslog(pri, fmt)
    }
}