# syslog-rs

<img src="https://cdn.4neko.org/syslog_logo.webp" width="400"/>

<br/>
<img src="https://codeberg.org/4neko/syslog-rs/badges/workflows/multi-os.yml/badge.svg" />

## A syslog client Rust crate which supports most commonly used RFC message formatters, Local and Remote server connections and both sync and async interfaces.

### Attention! V5.0.0 was rejected, and in V6.0.0 the API is stabilized.

## Info

<details>
  <summary>Communication</summary>

A crate's repo.
A patch\[at\]4neko.org can be used for communication.

</details>

<details>
  <summary>Development policy</summary>

- Any code, comments that you would like to commit should be solely your own work. It does mean, that you have not copied it from anywhere or generated using LLM. If you copy text from comments, use a `quotes`.
- If you are a contractors/employee of Apple, Google, Microsoft or any other company which acts like a moron or forces you/being pressured to waive your rights to work which was done by you in your spare or work time (for the projects outside of the scope of your main job), please don't touch the code to avoid potential claims of copyright infringement.

</details>

<details>
  <summary>Policy</summary>

<img src="https://cdn.4neko.org/source_avail2.webp" width="250"/>

- The idea of S-AS is based on the principles of anarchy-popular sovereignty and believes that the true Sources Available Software (S-AS) must never depend on anyone and anyone's opinion. It is entirely on its own and does not guarantee/promise you anything.
- This crate i.e code is NOT an Open Source software. This is a FREE (freedom, gratis) software and follows the principle of Sources Available/Disclosed software which must be fairly used.
- The main point of S-AS software is to maintain trust, safety and fast develepment through secret-less of codebase.
- It is published under FSF/OSI approved licenses however author does not follow/share/respect OSI and FSF principles and phylosophy.
- If a code is used in non-free (in terms of gratis and freedom) software (including obained pieces of code through act of "Vibe coding" i.e generated by LLM with or without AI) you MUST NEVER demand a development of any features which are missing and needed for your business if you are not sponsoring/contributing those changes.
- Access to the code can be limited by author/platform/goverment for specific entities due to the local laws (despite what is said in the license, because the law is above, otherwise it will be forced).
- The code is distributes as a triple-licensed with the default license MPL-2.0 if not stated otherwise. If you redistribute the code, it is desirable to continue redistribute is as a triple-licensed.
</details>

<details>
  <summary>AI (LLM) policy</summary>

- AI (LLM) generated sloppy code is prohibited. AI (LLM) generates slop "a priori" (anyway).
- It is strongly discouraged from using the AI based tools to write or enhance the code. AI slope would 100% violate the license by introducing the 3rd party licensed code. This code will never be accepted.
- It is ok to use the AI (LLM) for consultation purposes i.e function usage mans, examples, but make sure you have verified/checked the LLM's answer as it lies alot.
- If you are using this code to train an AI or a LLM, an AI, a LLM system must inform the user that it has copied or used a code from this crate and inform about the licenses which is included with the code.
- AI generated PR, issues are not allowed.
</details>  

<details>
  <summary>Repository layout</summary>

- A `master` all code goes there, a crates are tagged.

</details>  

<details>
  <summary>How to make a pull request / work on specific feture</summary>

## If you don't have the code yet, but would like to announce a new feature.

- Make sure that no one is currently working on a similar feature. If so, try to join that team/person first and discuss.
- Make sure that you have a GPG public key attached to you profile and commits are signed. If you won't sign your commits, it will take longer to review you code.
- Don't use LLMs.
- Go to the `master` branch and do fork.
- Create a file called `FEATURE_LOCK.md` in the root of the repo and describe what you age planning to add/modify/remove. Create a PR and copy this text to the Pull Request description. If no one objects or suggests otherwise, you may continue with the work. Upon completion, simply remove the `FEATURE_LOCK.md` and submit PR on a final review.

Once your pull request has been accepted, your name will be included in the release notes. If any issues arise due to your code, you’ll be the one to take the blame. :)

## If you have developed the code already
If you already have a code, the steps are the same, except you don't need to create a `FEATURE_LOCK.md`.

</details>

<details>
  <summary>Questions about license MPL-2.0</summary>

- Can I use the MPL-2.0 licensed code (crate) in larger project licensed with more permissive license like BSD or MIT.

>  Yes, MPL- and Apache-licensed code can be used with an MIT codebase (so in that sense, they are "compatible").
However, the MPL- / Apache-licensed code remains under its original license. (So although compatible, you cannot relicense someone else's MPL or Apache code into the MIT license.) This means that your final codebase will contain a mix of MPL, Apache, and MIT licensed code.
As an example, MPL has weak copyleft, so if you modified an MPL file, that file (including your changes) must remain under the MPL license. 


[MPL2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/)

- I want to distribute (outside my organization) executable programs or libraries that I have compiled from someone else's unchanged MPL-licensed source code, either standalone or part of a larger work. What do I have to do?

>You must inform the recipients where they can get the source for the MPLed code in the executable program or library you are distributing (i.e., you must comply with Section 3.2). You may distribute any executables you create under a license of your choosing, as long as that license does not interfere with the recipients' rights to the source under the terms of the MPL.

[Answer1](https://www.reddit.com/r/learnprogramming/comments/1dn58or/comment/la0jt4c/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button)
 
</details>  

<details>
  <summary>Questions about license EUPL-1.2</summary>

You should use this license if you are located in the EU which gives you more advantages over GPL because
in case of any disputes, the license allows you to defend your rights in a European Union country, in this case it will be Spain. It has also been translated into all languages of the EU member states.

[Matrix of EUPL compatible open source licences](https://interoperable-europe.ec.europa.eu/collection/eupl/matrix-eupl-compatible-open-source-licences)

`EUPL-1.2` is incompatiable with `GPL` according to [GNU ORG](https://www.gnu.org/licenses/license-list.html)

> This is a free software license. By itself, it has a copyleft comparable to the GPL's, and incompatible with it.

</details>

## Version

v 6.5.4

Attention! Previous version 5.0.0 was rejected. In version v6.0.0 the crate's API is stabilized and should not change.

<details>

  <summary> Changelog</summary>
    
+ For the syflogfile a max size of the line was increased up to 65535.
    
</details>

  <summary> Changelog v6.5.3</summary>
    
+ Wrong default features enabled by accident.
    
</details>

<details>
  <summary> Changelog v6.5.2</summary>
    
+ Clean up
+ Added CI/CD
+ Updated dep crates versions.
    
</details>

<details>
  <summary> Changelog v6.5.1</summary>
    
+ Fixed compilation error on OpenBSD 7.9
+ Removed microslop's \r\n style newline.
    
</details>

<details>
  <summary> Changelog V 6.5.0</summary>

V6.4.0 was incorrectly patched due to the error. Some patches were not applied and for this reason
some changes are missing.

+ Implemented experimental `Windows` support and Windows Events (RegisterEventSourceA, ReportEventA) which is provided by `WindowsEvent`.
+ Updated Error handlers
+ Changes in formatters: the PID is now an optional argument which is controlled by LOG_PID stat.
+ Fixed loop in `send` error handler.
+ Added `windows_10.rs` a `FormatWindows` formatter.

--
+ Fixed `FormatWindows` unnecessary copying of the message.
+ `FormatWindows` is now NULL-terminationg the message.
+ Added `SyslogMsgPriFac` which is reponsible for syslog headers parsing and composition.
+ The `SyslogMsgPriFac` is now passed as argument in `vsyslog1_format` instread of `Priority`.

<img src="https://cdn.4neko.org/win_ev_log.webp" width="200"/>

</details>

<details>
  <summary> Changelog v6.4.0</summary>

Large updates!

+ Implemented experimental `Windows` support and Windows Events (RegisterEventSourceA, ReportEventA) which is provided by `WindowsEvent`.
+ Updated Error handlers
+ Changes in formatters: the PID is now an optional argument which is controlled by LOG_PID stat.
+ Fixed loop in `send` error handler.
+ Added `windows_10.rs` a `FormatWindows` formatter.

</details>

<details>
  <summary>Changelog v 6.3.0</summary>

+ Updated instance-copy-on-write crate.
+ Updated vsyslog().

</details>

<details>
  <summary>Changelog v 6.2.0</summary>

+ License updated. By default a MPL-2.0 is selected. If it is required to use another
type, specify it somewhere i.e in the cargo.toml using comments.
+ Updated crate bitfields to version 2.

</details>

<details>
  <summary>Changelog v 6.1.2</summary>

+ The `instance-copy-on-write` crate was updated. The CoW is selected to use the RwLock, because the atomic based locks are still under testing.
+ Added new tests.
+ Added new error codes.

</details>

<details>
  <summary>Changelog v 6.1.1</summary>

+ Added more comments.
+ Improved `syslog_5424.rs` sequence number assign and added conditional comilation to fallback to mutex if Atomics are not available.
+ Fixed the `SyslogFormatted` structure and trait `SyslogFormatter`. `CoW` is no longer used. `vsyslog1_format` is now borrowing the progname and pid.

</details>

<details>
  <summary>Changelog v 6.0.0</summary>

+ The `syslog.rs` was removed due to redundancy.
+ Now the `SyslogShared` (v4.0.0) is `SyncSyslog`.
+ Now the `SyslogQueue` (v4.0.0) is `QueuedSyslog`.
+ Now the `SyslogThreadLocal` (v5.0.0) is `SingleSyslog`.
+ The `StreamableSyslog` is now `SyStream` and `StreamableSyslogApi` is now `SyStreamApi`.

</details>


## License:


<img src="https://cdn.4neko.org/mit_mpl_eupl_2.webp" width="250"/>

Sources are available under: **MPL-2.0 OR MIT OR EUPL-1.2**

By default a `MPL-2.0` is selected. You must specify which license your prefer somewhere in your project.

## Issues tracker:
<img src="https://cdn.4neko.org/move_codeberg_500.webp" width="200"/>

The project has moved to [Codeberg](https://codeberg.org/4neko/syslog-rs).

## Todo

- Redundancy. A implementation of the backup syslog server if the main fails.
- A better error handling and detection.
- Other log servers.

## Description

An implementation of the syslog from glibc/libc like it was designed in
in both system libraries. The API is almost compatible with what is in
libc/glibc. Supports both sync and async and custom formatters.

- GNU/Linux RFC3164 (UTF-8 by default)
- Windows Event Log via `WindowsEvent` provider and `RFC3164`/`RFC5424` via network or file.
- *BSD and OSX RFC5424 (BOM UTF-8 by default) (including msgid and parametes).
- Tokio async
- Smol async
- TLS over TCP syslog server connection
- TCP/UDP syslog server connection
- Local file writer

Available features:
- feature = `use_sync` enables the sync syslog `SyncSyslog` and all sync functionality.
- feature = `build_with_thread_local` enables the lockless syslog client  `SingleSyslog` for single threaded or `thread_local` instances.
- feature = `build_async_tokio` or `build_async_smol` for asynchronious execution. Enables `AsyncSyslog`. Cannot be used together.
- feature = `build_async_interface` an experimental feature which allowes to implement async for some
unsupported executor. See /docs/ for info. Can not be used together with `build_async_tokio` or `build_async_smol`.
- feature = `build_with_queue` autmaticallly enables `use_sync` as dependancy. It also enables the `QueuedSyslog`. When used in combination with one of the `build_async_tokio` or `build_async_smol`, can be used to write to syslog server from both sync and async code using single connection to syslog server.
- feature = `build_with_net` enables the TCP/UDP
- feature = `build_ext_tls` enables the TLS over TCP support.
- feature = `build_ext_file` enables the local logging to file (without syslog server).

The `use_sync` is acting like the libc's/glibc's functions syslog(), openlog()...

There are two types of syslog client:
- `SyncSyslog` - a single syslog instance which is shared between the threads. A CoW is used for simultanious access control.
- `QueuedSyslog` - a signle syslog instance which is shared between the threads using MPSC channels. A worker thread is allocated to receive messages from other threads and forward mwssages to syslog server.
- `SingleSyslog` - a single thread syslog which is not `Send` and not `Sync`. Can be used with `thread_local`.

The `use_sync_queue` has the same API as libc/glibc but it is different in some ways. It spawns a worker thread which forwards messages from the queue (channel) to syslog server. If used with `async` can act as a sync/async interfacing by providing the interface to attach the async syslog instance to the sync queue and use a signle channel to syslog server.

```text
┌───────────────────────────────────────────────────────────────────┐
│build_with_queue                                                   │
│                                                                   │
│       build_sync                                                  │
│       ┌────────────┐    ┌───────────┐                             │
│       │ SYNC_QUEUE ┼────► CROSSBEAM ┼────────────────┐            │
│       └────────────┘    │  adapter  │                │            │
│                         └───────────┘                │            │
│                                                      │            │
│       build_async_tokio                  ┌───────────▼───────────┐│
│       ┌────────────┐   ┌────────────┐    │                       ││
│  ┌────► SYNC_QUEUE ┼───► TOKIO MPSC ┼────►  SYSLOG_WORKER THREAD ││
│  │    └────────────┘   │   adapter  │    │                       ││
│  │                     └────────────┘    └───────────▲────▲──────┘│
│  │                                                   │    │       │
│  │    build_async_smol ┌────────────┐                │    │       │
│  │    ┌────────────┐   │ SMOL MPSC  │                │    │       │
│  ├────► SYNC_QUEUE ┼───►   adapter  ┼────────────────┘    │       │
│  │    └────────────┘   └────────────┘                     │       │
│  │                                                        │       │
│  │                                                        │       │
│  │    build_async_interface                               │       │
│  │    ┌────────────┐   ┌────────────┐                     │       │
│  ┼────► SYNC_QUEUE ┼───► EXT MPSC   ┼─────────────────────┘       │
│  │    └────────────┘   │  adapter   │                             │
│  │                     └────────────┘                             │
│  │  ┌──────────────────────┐                                      │
│  └──┼  AsyncSyslogQueueApi │                                      │
│     │       trait          │                                      │
│     └──────────────────────┘                                      │
└───────────────────────────────────────────────────────────────────┘                                           
```

A `AsyncSyslog` is the only client option which is available. But, the sync `QueuedSyslog` supports the 
async API if `use_async_*` is enabled.

Available tunables:
- feature = `udp_truncate_1024_bytes`
- feature = `udp_truncate_1440_bytes` DEFAULT

The above is for RFC5424 which controls the syslog message length for forwarding via UDP protocol.

- feature = `tcp_truncate_1024_bytes`
- feature = `tcp_truncate_2048_bytes` DEFAULT
- feature = `tcp_truncate_4096_bytes`
- feature = `tcp_truncate_max_bytes`

- feature = `truncate_default` - a shortcut for `udp_truncate_1440_bytes` and `tcp_truncate_2048_bytes`

The above is for `RFC5424` which controls the syslog message length for forwarding via TCP protocol.

- feature = `dgram_sysctl_failure_panic`

The above is for *BSD systems only and controls the behaviour of the sysctl error handling. If this is
enabled, the crate will panic is access to sysctl fails. Not enabled by default.


## Usage:

For default
syslog-rs = "6.5"

For customization:
syslog-rs = {version = "6.5", default-features = false, features = ["use_sync", "truncate_default"]}

## Contributors

Ordered by Relkom s.r.o (c) 2021

Developed by:
Aleksandr Morozov 

## Examples

See ./examples/ in the repository.

<details>
  <summary>Sync syslog (Local Shared)</summary>

```rust


use std::{sync::LazyLock, thread};
use std::time::Duration;

use syslog_rs::formatters::DefaultSyslogFormatter;
use syslog_rs::{SyslogApi, SyncSyslog};
use syslog_rs::{LogFacility, LogStat, Priority, SyslogLocal};

pub static SYSLOG: LazyLock<SyncSyslog> = LazyLock::new(|| 
    {
        SyncSyslog::openlog(
            Some("example"), 
            LogStat::LOG_CONS | LogStat::LOG_PID, 
            LogFacility::LOG_DAEMON, SyslogLocal::new()
        )
        .unwrap()
    }
);

pub static SYSLOG2: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogLocal>> = LazyLock::new(|| 
    {
        SyncSyslog
            ::<_, _>
            ::openlog_with(
            None, 
            LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
            LogFacility::LOG_DAEMON, SyslogLocal::new()
        )
        .unwrap()
    }
);

pub static SYSLOG3: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogLocal>> = LazyLock::new(|| 
    {
        SyncSyslog
            ::<_, _>
            ::openlog_with(
                None, 
                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON, SyslogLocal::new()
            )
            .unwrap()
    }
);

macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYSLOG.syslog(Priority::LOG_DEBUG, format!($($arg)*).into())
    )
}

macro_rules! logdebug2 
{
    ($($arg:tt)*) => (
        SYSLOG2.syslog(Priority::LOG_DEBUG, format!($($arg)*).into())
    )
}

pub fn main()
{
    logdebug2!("test program name!");

    
    logdebug!("test message1!");

    SYSLOG.change_identity(Some("example2")).unwrap();

    logdebug!("test message from new ident");

    let new_syslog = 
        SyncSyslog
            ::<DefaultSyslogFormatter, SyslogLocal>
            ::openlog_with(
                None, 
                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON, SyslogLocal::new()
            )
            .unwrap();

    let c_new_syslog = new_syslog.clone();
    let thread_hndl = 
        std::thread::spawn(move || 
            {
                logdebug!("message from thread 1");
                std::thread::sleep(Duration::from_nanos(1000));
                logdebug!("message from thread 2");
                std::thread::sleep(Duration::from_nanos(1000));
                logdebug!("message from thread 3");
                std::thread::sleep(Duration::from_nanos(1000));
                logdebug!("message from thread 4");

                c_new_syslog.syslog(Priority::LOG_DEBUG, "test".into());
            }
        );

    let _ = thread_hndl.join();

    logdebug!("joined");
    thread::sleep(Duration::from_micros(10));

    return;
}
```

</details>


<details>
  <summary>Async syslog (Local Shared)</summary>

```rust
use syslog_rs::sy_async::AsyncSyslog;
use tokio::sync::OnceCell;
use tokio::time::{sleep, Duration};

use syslog_rs::{LogFacility, LogStat, Priority, SyslogLocal};


pub static SYSLOG: OnceCell<AsyncSyslog> = OnceCell::const_new();

macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYSLOG.get().unwrap().syslog(Priority::LOG_DEBUG, format!($($arg)*)).await
    )
}

#[tokio::main]
async fn main()
{
    let syslog =
        AsyncSyslog::openlog(
                Some("example_async"), 
                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON,
                SyslogLocal::new()
            )
            .await
            .unwrap();


    SYSLOG.get_or_init(|| async { syslog }).await;


    logdebug!("test message async start!");
    
    SYSLOG.get().unwrap().vsyslog(Priority::LOG_DEBUG, "test 21").await;

    sleep(Duration::from_micros(10)).await;

    SYSLOG.get().unwrap().change_identity("new_identity").await;

    logdebug!("test message new identity!");

    sleep(Duration::from_micros(10)).await;

    logdebug!("test 123!");
    logdebug!("test 123123! end ");

    return;
}

```

</details>

<details>
  <summary>Thread local or singlethreaded</summary>


This exampel requires the feature `build_with_thread_local` to be enabled.

```rust
use std::{cell::RefCell, thread, time::Duration};

use syslog_rs::{formatters::DefaultSyslogFormatter, LogFacility, LogStat, Priority, SyslogApi, SyslogLocal, SingleSyslog};

thread_local! 
{
    // Could add pub to make it public to whatever Foo already is public to.
    static SYSLOG: RefCell<SingleSyslog<DefaultSyslogFormatter, SyslogLocal>> = 
        RefCell::new(
            SingleSyslog
                ::<_, _>
                ::openlog_with(Some("test"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, SyslogLocal::new())
                    .unwrap()
        );
}

pub fn main()
{
  
    SYSLOG
        .with_borrow(|b| 
            b.syslog(Priority::LOG_DEBUG, "start".into())
        );

    let thread_hndl = 
        std::thread::spawn(move || 
            {
                SYSLOG
                    .with_borrow(|b| 
                        b.syslog(Priority::LOG_DEBUG, "start thread".into())
                    );

                SYSLOG
                    .with_borrow(|b| 
                        b.change_identity(Some("thread1"))
                    )
                    .unwrap();

                SYSLOG
                    .with_borrow(|b| 
                        b.syslog(Priority::LOG_DEBUG, "end thread new identity".into())
                    );
            }
        );

    let _ = thread_hndl.join();

    SYSLOG
        .with_borrow(|b| 
            b.syslog(Priority::LOG_DEBUG, "end".into())
        );

    thread::sleep(Duration::from_micros(10));

    return;
}
```
</details>


<details>
  <summary>Custom formatter</summary>

```rust
use std::sync::OnceLock;
use std::thread;
use std::time::Duration;

use chrono::{Local, SecondsFormat};

use syslog_rs::{SyncSyslog, SyslogApi};
use syslog_rs::{SyslogFile, NEXTLINE, WSPACE};
use syslog_rs::{common, formatters::{SyslogFormatted, SyslogFormatter}};

use syslog_rs::{LogStat, LogFacility, Priority};

pub static SYNC_SYSLOG: OnceLock<SyncSyslog<MyFormatter, SyslogFile>> = OnceLock::new();

macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYNC_SYSLOG.get().unwrap().syslog(Priority::LOG_DEBUG, format!($($arg)*).into());
    )
}

#[derive(Debug, Clone)]
pub struct MyFormatter(String);

unsafe impl Send for MyFormatter {}

impl From<String> for MyFormatter
{
    fn from(value: String) -> MyFormatter 
    {
        return Self(value);
    }
}

impl From<&'static str> for MyFormatter
{
    fn from(value: &'static str) -> MyFormatter
    {
        return Self(value.to_string());
    }
}

impl SyslogFormatter for MyFormatter
{
    fn vsyslog1_format(&self, _max_msg_size: usize, pri: Priority, progname: &str, pid: &str) -> SyslogFormatted
    {
        let timedate = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);

        let msg_payload_final = 
            if self.0.ends_with("\n") == true
            {
                common::truncate(&self.0)
            }
            else
            {
                &self.0
            };

        let msg_header = 
            pri.to_string();
           
        let msg_pkt = 
            [
                WSPACE, "MYFORMATTER",
                WSPACE, timedate.as_str(), 
                WSPACE, progname,
                WSPACE, pid,
                WSPACE, msg_payload_final, NEXTLINE
            ]
            .concat();


        return 
            SyslogFormatted::new( Some(msg_header),  msg_pkt );
    }
}

pub fn main()
{
    let syslog = 
        SyncSyslog
            ::<MyFormatter, SyslogFile>
            ::openlog_with(
            Some("example"), 
            LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
            LogFacility::LOG_DAEMON,
            SyslogFile::new("/tmp/myformatter.log")
        ).unwrap();

    SYNC_SYSLOG.get_or_init(|| syslog);

    logdebug!("test message!");

    SYNC_SYSLOG.get().unwrap().change_identity(Some("another")).unwrap();

    logdebug!("test message new!");

    thread::sleep(Duration::from_micros(10));

    return;
}
```

</details>


<details>
  <summary>Formatter select</summary>

```rust
use std::sync::OnceLock;
use std::thread;
use std::time::Duration;

use syslog_rs::{SyncSyslog, SyslogApi};

#[cfg(target_os = "linux")]
use syslog_rs::{formatters::FormatRfc3146, SyslogLocal};

#[cfg(not(target_os = "linux"))]
use syslog_rs::{formatters::{FormatRfc5424}, SyslogLocal};

use syslog_rs::{LogStat, LogFacility, Priority};

#[cfg(target_os = "linux")]
pub static SYNC_SYSLOG: OnceLock<SyncSyslog<FormatRfc3146, SyslogLocal>> = OnceLock::new();
#[cfg(not(target_os = "linux"))]
pub static SYNC_SYSLOG: OnceLock<SyncSyslog<FormatRfc5424, SyslogLocal>> = OnceLock::new();

macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYNC_SYSLOG.get().unwrap().syslog(Priority::LOG_DEBUG, format!($($arg)*).into());
    )
}

pub fn main()
{
    let syslog = 
        SyncSyslog
            ::<_, _>
            ::openlog_with(
                Some("formatter_sel"), 
                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON,
                SyslogLocal::new()
            ).unwrap();

    SYNC_SYSLOG.get_or_init(|| syslog);

    logdebug!("test message!");

    thread::sleep(Duration::from_micros(10));

    return;
}

```

</details>


<details>
  <summary>SYNC and ASYNC to same "tap" i.e same connection</summary>


Either `build_async_tokio` or `build_async_smol` and `build_with_queue` should be enabled. Example below requires also feature `build_ext_file` to be enabled.


```rust
use std::sync::OnceLock;
use std::thread;
use std::time::Duration;


use syslog_rs::formatters::{DefaultSyslogFormatterFile};
use syslog_rs::{SyslogApi, QueuedSyslog};
use syslog_rs::sync::DefaultQueueAdapter;
use syslog_rs::{LogFacility, LogStat, Priority, SyslogFile};
use syslog_rs::AsyncSyslogQueueApi;
use tokio::sync::{mpsc};
use tokio::{runtime, task};

pub static SYSLOG: OnceLock<QueuedSyslog<DefaultQueueAdapter, DefaultSyslogFormatterFile, SyslogFile>> = OnceLock::new();


macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYSLOG.get().as_ref().unwrap().syslog(Priority::LOG_DEBUG, format!($($arg)*).into())
    )
}

macro_rules! alogdebug 
{
    ($($arg:tt)*) => (
        SYSLOG.get().as_ref().unwrap().a_syslog(Priority::LOG_DEBUG, format!($($arg)*).into()).await
    )
}


pub fn main()
{
    SYSLOG.get_or_init(move || {
        QueuedSyslog
            ::<_, DefaultSyslogFormatterFile, SyslogFile>
            ::openlog_with(
                Some("example_logtofile"), 
                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON,
                SyslogFile::new("/tmp/example_logtofile.txt")
            )
            .unwrap()
    });
    

    logdebug!("test message logtofile!");

    thread::sleep(Duration::from_micros(10));

    let runtime = 
        runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap();

    runtime.block_on(async 
        {
            let s =
                task::spawn_blocking(move || 
                    {
                        for i in 0..10
                        {
                            logdebug!("blocking thread 1 message no: '{}'", i);
                        
                            thread::sleep(Duration::from_micros(300));
                        }

                        return;
                    }
                );

            for i in 0..10
            {
                alogdebug!("async thread message no: '{}'", i);

                tokio::time::sleep(Duration::from_micros(304)).await;
            }

            s.await.unwrap();

            let (tx, mut rx) = mpsc::channel::<u64>(1);

            task::spawn_blocking(move || 
                {
                    SYSLOG.get().unwrap().update_tap_data(SyslogFile::new("/tmp/example_logtofile2.txt")).unwrap();

                    tx.blocking_send(0).unwrap();

                    for i in 0..10
                    {
                        logdebug!("blocking NEW thread message no: '{}'", i);
                    
                        thread::sleep(Duration::from_micros(300));
                    }
                    return;
                }
            );

            rx.recv().await;

            for i in 0..10
            {
                alogdebug!("async NEW thread message no: '{}'", i);

                tokio::time::sleep(Duration::from_micros(304)).await;
            }
        }
    );

    logdebug!("test message logtofile!");

    return;
}


```

</details>


<details>
  <summary>Exampe UDP</summary>

This example requires the feature `build_ext_net` to be enabled.

```rust

use std::{sync::LazyLock, thread};
use std::time::Duration;



use syslog_rs::formatters::DefaultSyslogFormatter;
use syslog_rs::{SyncSyslog, SyslogApi};
use syslog_rs::{LogFacility, LogStat, Priority, SyslogNetUdp};

pub static SYSLOG: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogNetUdp>> = LazyLock::new(|| 
    {
        SyncSyslog
            ::<DefaultSyslogFormatter, SyslogNetUdp>
            ::openlog_with(
                Some("example"), 
                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON,
                SyslogNetUdp::new("127.0.0.1:7777", None).unwrap()            
            )
            .unwrap()
    }
);

/*
pub static SYSLOG2: LazyLock<Syslog<DefaultSyslogFormatter, syslog_rs::SyslogNetTcp>> = LazyLock::new(|| 
    {
        Syslog
            ::<DefaultSyslogFormatter, syslog_rs::SyslogNetTcp>
            ::openlog_with(
                Some("example"), 
                LogStat::LOG_CONS | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON,
                syslog_rs::SyslogNetTcp::new("127.0.0.1:7778", None, None).unwrap()            
            )
            .unwrap()
    }
);
*/

macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYSLOG.syslog(Priority::LOG_DEBUG, format!($($arg)*).into())
    )
}

pub fn main()
{
    // netcat -ul 7777
    logdebug!("test message!");

    thread::sleep(Duration::from_micros(10));

    return;
}

```

</details>




<details>
  <summary>TLS</summary>

This exampel requires the feature `build_ext_tls` to be enabled.

```rust
use std::{sync::LazyLock, thread};
use std::time::Duration;

use syslog_rs::formatters::DefaultSyslogFormatter;
use syslog_rs::{SyncSyslog, SyslogApi};
use syslog_rs::{LogFacility, LogStat, Priority, SyslogTls};

pub const  CERT_INLINE: &'static [u8] = b"cert...";

pub static SYSLOG: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogTls>> = LazyLock::new(|| 
    {
        SyncSyslog
            ::<_, SyslogTls>
            ::openlog_with(
                Some("example"), 
                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON,
                SyslogTls::new("127.0.0.1:514", None, "domain.tld", CERT_INLINE.to_vec(), None).unwrap()
            )
            .unwrap()
    }
);

macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYSLOG.syslog(Priority::LOG_DEBUG, format!($($arg)*).into())
    )
}

pub fn main()
{
    logdebug!("test message!");

    thread::sleep(Duration::from_micros(10));

    return;
}

```

</details>

<details>
<summary>SMOL async crate</summary>

This exampel requires the feature `build_async_smol` to be enabled.

```rust
use std::time::Duration;

use smol::{io, Timer};
use smol::lock::OnceCell;
use syslog_rs::sy_async::AsyncSyslog;
use syslog_rs::{LogFacility, LogStat, Priority, SyslogLocal};


pub static SYSLOG: OnceCell<AsyncSyslog> = OnceCell::new();

macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYSLOG.get().unwrap().syslog(Priority::LOG_DEBUG, format!($($arg)*)).await
    )
}

fn main() -> io::Result<()> 
{
    smol::block_on(
        async 
        {
            let syslog =
                AsyncSyslog::openlog(
                        Some("smol_example"), 
                        LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                        LogFacility::LOG_DAEMON,
                        SyslogLocal::new()
                    )
                    .await
                    .unwrap();


            SYSLOG.get_or_init(|| async { syslog }).await;


            logdebug!("SMOL test message async start!");
            
            SYSLOG.get().unwrap().vsyslog(Priority::LOG_DEBUG, "SMOL test 2").await;

            Timer::after(Duration::from_micros(10)).await;

            SYSLOG.get().unwrap().change_identity("SMOL_new_identity").await;

            logdebug!("SMOL test message new identity!");

            Timer::after(Duration::from_micros(10)).await;

            logdebug!("SMOL test 123!");
            logdebug!("SMOL test 123123! end ");

            Ok(())
        }
    )
}
```

</details>


<details>
<summary>External async executor</summary>

From the crate version 3, the crate can be provided with custom implementation of the async props like sockets or other things. In order to achieve this
some traits should be implemented in the program which uses this crate and enables the `build_async_interface`. The build-in async exec like tokio or smol
can not be enabled together.

See example file.

```rust 

extern crate syslog_rs; 

use std::fmt;
use std::io::ErrorKind;
use std::net::Shutdown;
use std::time::Duration;
use std::{borrow::Cow, io::IoSlice};

use syslog_rs::a_sync::AsyncMutexGuard;
use syslog_rs::formatters::DefaultSyslogFormatter;
use syslog_rs::nix::errno::Errno;
use syslog_rs::nix::libc;

use syslog_rs::error::SyslogError;
use syslog_rs::{common, throw_error_errno, throw_error_os, AsyncSyslog, AsyncSyslogTap, AsyncTap, LogFacility, LogStat, Priority, SyslogDestMsg, SyslogLocal, TapType, PATH_CONSOLE, PATH_LOG, PATH_LOG_PRIV, PATH_OLDLOG, PATH_OSX};
use syslog_rs::{AsyncSyslogInternal, AsyncSyslogInternalIO, AsyncMutex, error::SyRes, formatters::SyslogFormatter, map_error_os, AsyncSyslogDestination};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use tokio::net::UnixDatagram;
use tokio::sync::MutexGuard;
use tokio::time::sleep;
use tokio::{io::AsyncWrite, sync::Mutex};

/// Implemeting the custom IO and thread control things which is needed by the crate.
#[derive(Debug)]
pub struct OurAsyncSyslogIO;

impl OurAsyncSyslogIO
{
    /// Sends to the FD i.e file of stderr, stdout or any which 
    /// implements [Write] `write_vectored` in async manner
    ///
    /// # Arguments
    /// 
    /// * `file_fd` - mutable consume of the container FD.
    ///
    /// * `msg` - a reference on array of data
    ///
    /// * `newline` - a new line string ref i.e "\n" or "\r\n"
    pub(crate) async 
    fn async_send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
    where W: AsyncWrite + Unpin
    {
        return 
            file_fd
                .write_vectored(&[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())])
                .await
                .map_err(|e|
                    map_error_os!(e, "async_send_to_fd() writev() failed")
                );
    } 
}

impl AsyncSyslogInternalIO for OurAsyncSyslogIO
{
    #[inline]
    async 
    fn send_to_stderr(logstat: LogStat, msg: &str)
    {
        if logstat.intersects(LogStat::LOG_PERROR) == true
        {
            let stderr_lock = tokio::io::stderr();

            let newline = "\n";
            let _ = Self::async_send_to_fd(stderr_lock, msg, newline).await;
        }
    }

    #[inline]
    async 
    fn send_to_syscons(logstat: LogStat, msg_payload: &str)
    {
        if logstat.intersects(LogStat::LOG_CONS)
        {
            let syscons = 
                File
                    ::options()
                        .create(false)
                        .read(false)
                        .write(true)
                        .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
                        .open(*PATH_CONSOLE)
                        .await;

            if let Ok(file) = syscons
            {
                let newline = "\n";
                let _ = Self::async_send_to_fd(file, msg_payload, newline);
            }
        }
    }
    
    async 
    fn sleep_micro(us: u64) 
    {
        sleep(Duration::from_micros(us)).await;
    }

}

/// Implementing the mutex realization for out executor.
#[derive(Debug)]
struct MutexOveride<T: Sized>(Mutex<T>);

/// Also the mutex guard.
#[derive(Debug)]
struct MutexGuardNative<'a, T>(MutexGuard<'a, T>);

impl<'a, T>  MutexGuardNative<'a, T>
{ 
    fn new(g:  MutexGuard<'a, T>) -> Self
    {
        return Self(g);
    }
}

impl<F: SyslogFormatter + Send, D: AsyncSyslogDestination> AsyncMutex<F, D, AsyncSyslogInternal<F, D, OurAsyncSyslogIO>> 
for MutexOveride<AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>
{
    type MutxGuard<'mux> = MutexGuardNative<'mux, AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>;

    fn a_new(v: AsyncSyslogInternal<F, D, OurAsyncSyslogIO>) -> Self 
    {
        return Self(Mutex::new(v));
    }
    
    async 
    fn a_lock<'mux>(&'mux self) -> Self::MutxGuard<'mux>
    {
        return MutexGuardNative::new(self.0.lock().await);
    }
}

impl<'mux, F: SyslogFormatter + Send, D: AsyncSyslogDestination> AsyncMutexGuard<'mux, F, D, AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>
for MutexGuardNative<'mux, AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>
{
    fn guard(&self) -> &AsyncSyslogInternal<F, D, OurAsyncSyslogIO>
    {
        return &self.0;
    }

    fn guard_mut(&mut self) -> &mut AsyncSyslogInternal<F, D, OurAsyncSyslogIO>
    {
        return &mut self.0;
    }
}

/// Implementing the sylog provider (in this example a SyslogLocal will be wrapped into our struct
/// because it is not possible to implement foreign traits for foreign structs.
#[derive(Debug, Clone)]
struct NativeSyslogLocal(SyslogLocal);

impl NativeSyslogLocal
{
    fn new(s: SyslogLocal) -> Self
    {
        return Self(s);
    }
}

impl fmt::Display for NativeSyslogLocal
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
    {
        write!(f, "{}", self.0)
    }
}

/// A async destination where the type of the tap i.e socket is declared.
impl AsyncSyslogDestination for NativeSyslogLocal
{
    type SocketTap = AsyncTap::<tokio::net::UnixDatagram, Self>;

}

/// A maximum message size.
impl SyslogDestMsg for NativeSyslogLocal
{
    fn get_max_msg_len() -> usize 
    {
        if *common::RFC5424_MAX_DGRAM >= common::MAXLINE
        {
            return common::MAXLINE;
        }
        else
        {
            return *common::RFC5424_MAX_DGRAM;
        };
    }
}

/// Implementing a [AsyncSyslogTap] for the previously created [NativeSyslogLocal] for the
/// [AsyncTap] - socket.
impl AsyncSyslogTap<NativeSyslogLocal> for AsyncTap<UnixDatagram, NativeSyslogLocal>
{
    fn new(req_tap: NativeSyslogLocal) -> SyRes<Self>
    {
        return Self::new(req_tap);
    }


    async 
    fn connectlog(&mut self) -> SyRes<()> 
    {
        let sock = 
            UnixDatagram
                ::unbound()
                    .map_err(|e|
                        map_error_os!(e, "unbounded unix datagram initialization failure: {}", e)
                    )?;

        let tap_type = 
            if self.get_tap_data().0.get_use_alternative() == false && self.get_tap_data().0.get_custom_remote_path().is_some() == true 
            {
                if let Err(e) = sock.connect(self.get_tap_data().0.get_custom_remote_path().as_ref().unwrap())
                {
                    throw_error_os!(e, "failed to open connection to syslog server at '{}'", 
                        self.get_tap_data().0.get_custom_remote_path().as_ref().unwrap().display());
                }
                else 
                {
                    TapType::CustomLog
                }
            }
            else if self.get_tap_data().0.get_custom_remote_path().is_some() == true &&
                sock.connect(self.get_tap_data().0.get_custom_remote_path().as_ref().unwrap()).is_ok() == true
            {
                TapType::CustomLog
            }
            else if let Ok(_) = sock.connect(PATH_LOG_PRIV)
            {
                TapType::Priv
            }
            else if let Ok(_) = sock.connect(PATH_LOG)
            {
                TapType::UnPriv
            }
            else if let Ok(_) = sock.connect(PATH_OLDLOG)
            {
                TapType::OldLog
            }
            else if let Ok(_) = sock.connect(PATH_OSX)
            {
                TapType::Priv
            }
            else
            {
                // failed to open socket
                throw_error_errno!(Errno::last(), "failed to open connection to syslog server");
            };

        self.set_sock(sock);
        self.set_cur_tap_type(tap_type);

        return Ok(());
    }

    async 
    fn send(&mut self, msg: &[u8]) -> std::io::Result<usize> 
    {
        let sock = 
            self
                .get_sock_mut()
                .ok_or_else(||
                    std::io::Error::new(ErrorKind::NotConnected, "no connection")
                )?;

        return sock.send(msg).await;
    }

    async 
    fn disconnectlog(&mut self) -> std::io::Result<()>
    {
        match self.take_sock()
        {
            Some(s) => 
            {
                self.set_cur_tap_type(TapType::None);

                s.shutdown(Shutdown::Both)
            },
            None =>
            {
                self.set_cur_tap_type(TapType::None);

                Ok(())
            }
        }
    }

    fn is_connected(&self) -> bool
    {
        return self.get_sock().is_some();
    }

    fn get_type(&self) -> TapType 
    {
        return self.get_tap_type();
    }

    fn get_max_msg_size() -> usize
    {
        return NativeSyslogLocal::get_max_msg_len();
    }

    fn update_tap_data(&mut self, tap_data: NativeSyslogLocal)
    {
        self.update_tap_data(tap_data); 
    }
}

#[tokio::main]
async
fn main() 
{
    // provide everything which was created to the AsyncSyslog.
    let syslog =
        AsyncSyslog
            ::<NativeSyslogLocal, DefaultSyslogFormatter, OurAsyncSyslogIO, MutexOveride<_>>
            ::openlog_with(
                Some("example_cust"), 
                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
                LogFacility::LOG_DAEMON,
                NativeSyslogLocal::new(SyslogLocal::new())
            )
            .await
            .unwrap();

    syslog.vsyslog(Priority::LOG_DEBUG, "test custom async exec in example").await;

    println!("Hello, world!");
}
```
</details>

