syslog-rs 6.6.0

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 crate::{
    LogFacility, LogStat, Priority, SyslogDestination, error::SyRes, 
    formatters::{DefaultSyslogFormatter, SyslogFormatter}, 
    sync::
    {
        LogItems, SyStream, SyStreamPri, syslog_stream::SyStreamSyslogApi, 
        syslog_sync_internal::SyslogSocketLockless, DefaultLocalSyslogDestination
    },
};


/// A threal local syslog which is completely lockless! It can be used in signle threaded
/// applications or with the [thread_local] functionality.
/// 
/// ```ignore
/// thread_local! 
/// {
///     // Could add pub to make it public to whatever Foo already is public to.
///     static SYSLOG: RefCell<SingleSyslog> = 
///         RefCell::new(SingleSyslog::openlog_with(Some("test"), LogStat::LOG_PID , 
///             LogFacility::LOG_DAEMON, SyslogLocal::new()).unwrap());
/// }
/// ```
/// 
/// A stream is availble via [SyStreamApi].
/// 
/// ```ignore
/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
/// ```
/// 
/// The instances will be completly separated and have own FD.
/// 
/// # Generics
/// 
/// * `D` - a [SyslogDestination] instance which is either:
///     [SyslogLocal], [crate::syslog_provider::SyslogFile], [crate::syslog_provider::SyslogNet], 
///     [crate::syslog_provider::SyslogTls]. By default a `SyslogLocal` is selected.
/// 
/// * `F` - a [SyslogFormatter] which sets the instance which would 
///     format the message.
/// 
#[derive(Debug)]
pub struct SingleSyslog<F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination>
where 
    F: SyslogFormatter, 
    D: SyslogDestination,   
{
    /// An identification i.e program name, thread name
    log_items: LogItems,

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

    _p: PhantomData<F>,

    _p_not_ss: PhantomData<*const ()>
}

impl SingleSyslog
{
    /// 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> 
    {
        let log_items = 
            LogItems::new(ident, 0xff, logstat, facility);

        let stream = 
            SyslogSocketLockless::<DefaultLocalSyslogDestination>::new(logstat, net_tap_prov)?;
        
        return Ok(
            Self
            {
                log_items,
                stream,
                _p: 
                    PhantomData,
                _p_not_ss: 
                    PhantomData
            }
        );
    }
}

impl<F, D> SingleSyslog<F, D>
where F: SyslogFormatter, D: SyslogDestination
{
    /// Opens a default connection to the local syslog server with default formatter.
    /// 
    /// # 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_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self> 
    {
        let log_items = 
            LogItems::new(ident, 0xff, logstat, facility);

        let stream = 
            SyslogSocketLockless::<D>::new(logstat, net_tap_prov)?;
        
        return Ok(
            Self
            {
                log_items,
                stream,
                _p: 
                    PhantomData,
                _p_not_ss: 
                    PhantomData
            }
        );
    }
}

impl<F, D> SingleSyslog<F, D>
where F: SyslogFormatter, D: SyslogDestination
{
    /// Connects the current instance to the syslog server (destination).
    #[inline]
    pub 
    fn connectlog(&mut self) -> SyRes<()>
    {
        return 
            self
                .stream
                .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)
    #[inline]
    pub 
    fn setlogmask(&mut self, logmask: i32) -> SyRes<i32>
    {
        return Ok(
            self
                .log_items
                .set_logmask(logmask)
        );
    }

    /// Closes connection to the syslog server (destination).
    #[inline]
    pub 
    fn closelog(&mut self) -> SyRes<()> 
    {
        return 
            self
                .stream
                .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(&mut self, pri: Priority, fmt: F) -> SyRes<()>
    {
        let Some((formatted_msg, logstat)) = 
            self.log_items.vsyslog1_msg::<F, D>(pri, &fmt)
            else { return Ok(()) };

        self.stream.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.
    #[inline]
    pub 
    fn change_identity(&mut self, ident: Option<&str>) -> SyRes<()>
    {
        self.log_items.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 
    #[inline]
    pub 
    fn reconnect(&mut self) -> SyRes<()>
    {
        return
            self
                .stream
                .reconnectlog();
    }

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

impl<F, D> SingleSyslog<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 mut self) -> SyStream<'t, PRI, D, F, &'t mut Self>
    where PRI: SyStreamPri
    {
        SyStream
        {
            s: Some(self),
            _p: PhantomData,
            _p1: PhantomData,
            _p2: PhantomData
        }
    }
}


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

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