├── .editorconfig ├── .github ├── FUNDING.yml ├── semantic.yml └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── broadcast_sender.rs ├── native_tls_sender.rs ├── rustls_sender.rs ├── tcp_sender.rs ├── udp_sender.rs └── unix_sender.rs ├── licenserc.toml ├── rust-toolchain.toml ├── rustfmt.toml ├── src ├── facility.rs ├── format.rs ├── internal.rs ├── lib.rs ├── sender │ ├── internal.rs │ ├── mod.rs │ ├── native_tls_impl.rs │ ├── rustls_impl.rs │ ├── tcp_impl.rs │ ├── udp_impl.rs │ └── unix_impl.rs ├── severity.rs └── structured_data.rs ├── taplo.toml └── typos.toml /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_style = space 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.toml] 10 | indent_size = tab 11 | tab_width = 2 12 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | github: tisonkun 16 | -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # The pull request's title should be fulfilled the following pattern: 16 | # 17 | # [optional scope]: 18 | # 19 | # ... where valid types and scopes can be found below; for example: 20 | # 21 | # build(maven): One level down for native profile 22 | # 23 | # More about configurations on https://github.com/Ezard/semantic-prs#configuration 24 | 25 | enabled: true 26 | 27 | titleOnly: true 28 | 29 | types: 30 | - feat 31 | - fix 32 | - docs 33 | - style 34 | - refactor 35 | - perf 36 | - test 37 | - build 38 | - ci 39 | - chore 40 | - revert 41 | 42 | targetUrl: https://github.com/fast/fasyslog/blob/main/.github/semantic.yml 43 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: CI 16 | on: 17 | pull_request: 18 | branches: [ main ] 19 | push: 20 | branches: [ main ] 21 | 22 | # Concurrency strategy: 23 | # github.workflow: distinguish this workflow from others 24 | # github.event_name: distinguish `push` event from `pull_request` event 25 | # github.event.number: set to the number of the pull request if `pull_request` event 26 | # github.run_id: otherwise, it's a `push` event, only cancel if we rerun the workflow 27 | # 28 | # Reference: 29 | # https://docs.github.com/en/actions/using-jobs/using-concurrency 30 | # https://docs.github.com/en/actions/learn-github-actions/contexts#github-context 31 | concurrency: 32 | group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.number || github.run_id }} 33 | cancel-in-progress: true 34 | 35 | jobs: 36 | check: 37 | name: Check 38 | runs-on: ubuntu-22.04 39 | steps: 40 | - uses: actions/checkout@v4 41 | - name: Install toolchain 42 | uses: dtolnay/rust-toolchain@nightly 43 | with: 44 | components: rustfmt,clippy 45 | - uses: Swatinem/rust-cache@v2 46 | - uses: taiki-e/install-action@v2 47 | with: 48 | tool: typos-cli,taplo-cli,hawkeye 49 | - name: Check all 50 | run: | 51 | hawkeye check 52 | taplo format --check 53 | typos 54 | cargo +nightly fmt --all 55 | cargo +nightly clippy --all-targets --all-features -- -D warnings 56 | 57 | test: 58 | name: Run tests 59 | strategy: 60 | matrix: 61 | os: [ ubuntu-22.04, macos-14, windows-2022 ] 62 | rust-version: [ "1.75.0", "stable" ] 63 | runs-on: ${{ matrix.os }} 64 | steps: 65 | - uses: actions/checkout@v4 66 | - uses: Swatinem/rust-cache@v2 67 | - name: Delete rust-toolchain.toml 68 | run: rm rust-toolchain.toml 69 | - name: Install toolchain 70 | uses: dtolnay/rust-toolchain@master 71 | with: 72 | toolchain: ${{ matrix.rust-version }} 73 | - name: Build 74 | run: cargo build --workspace --all-features --bins --tests --examples --benches --lib 75 | - name: Run unit tests 76 | run: cargo test -- --nocapture 77 | shell: bash 78 | 79 | required: 80 | name: Required 81 | runs-on: ubuntu-22.04 82 | if: ${{ always() }} 83 | needs: 84 | - check 85 | - test 86 | steps: 87 | - name: Guardian 88 | run: | 89 | if [[ ! ( \ 90 | "${{ needs.check.result }}" == "success" \ 91 | && "${{ needs.test.result }}" == "success" \ 92 | ) ]]; then 93 | echo "Required jobs haven't been completed successfully." 94 | exit -1 95 | fi 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "aws-lc-rs" 16 | version = "1.12.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "4c2b7ddaa2c56a367ad27a094ad8ef4faacf8a617c2575acb2ba88949df999ca" 19 | dependencies = [ 20 | "aws-lc-sys", 21 | "paste", 22 | "zeroize", 23 | ] 24 | 25 | [[package]] 26 | name = "aws-lc-sys" 27 | version = "0.25.1" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "54ac4f13dad353b209b34cbec082338202cbc01c8f00336b55c750c13ac91f8f" 30 | dependencies = [ 31 | "bindgen", 32 | "cc", 33 | "cmake", 34 | "dunce", 35 | "fs_extra", 36 | "paste", 37 | ] 38 | 39 | [[package]] 40 | name = "bindgen" 41 | version = "0.69.5" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" 44 | dependencies = [ 45 | "bitflags", 46 | "cexpr", 47 | "clang-sys", 48 | "itertools", 49 | "lazy_static", 50 | "lazycell", 51 | "log", 52 | "prettyplease", 53 | "proc-macro2", 54 | "quote", 55 | "regex", 56 | "rustc-hash", 57 | "shlex", 58 | "syn", 59 | "which", 60 | ] 61 | 62 | [[package]] 63 | name = "bitflags" 64 | version = "2.8.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 67 | 68 | [[package]] 69 | name = "byteorder" 70 | version = "1.5.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 73 | 74 | [[package]] 75 | name = "cc" 76 | version = "1.2.13" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" 79 | dependencies = [ 80 | "jobserver", 81 | "libc", 82 | "shlex", 83 | ] 84 | 85 | [[package]] 86 | name = "cexpr" 87 | version = "0.6.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 90 | dependencies = [ 91 | "nom", 92 | ] 93 | 94 | [[package]] 95 | name = "cfg-if" 96 | version = "1.0.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 99 | 100 | [[package]] 101 | name = "cfg_aliases" 102 | version = "0.2.1" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 105 | 106 | [[package]] 107 | name = "clang-sys" 108 | version = "1.8.1" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 111 | dependencies = [ 112 | "glob", 113 | "libc", 114 | "libloading", 115 | ] 116 | 117 | [[package]] 118 | name = "cmake" 119 | version = "0.1.54" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" 122 | dependencies = [ 123 | "cc", 124 | ] 125 | 126 | [[package]] 127 | name = "core-foundation" 128 | version = "0.9.4" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 131 | dependencies = [ 132 | "core-foundation-sys", 133 | "libc", 134 | ] 135 | 136 | [[package]] 137 | name = "core-foundation" 138 | version = "0.10.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" 141 | dependencies = [ 142 | "core-foundation-sys", 143 | "libc", 144 | ] 145 | 146 | [[package]] 147 | name = "core-foundation-sys" 148 | version = "0.8.7" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 151 | 152 | [[package]] 153 | name = "dunce" 154 | version = "1.0.5" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" 157 | 158 | [[package]] 159 | name = "either" 160 | version = "1.13.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 163 | 164 | [[package]] 165 | name = "errno" 166 | version = "0.3.10" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 169 | dependencies = [ 170 | "libc", 171 | "windows-sys 0.59.0", 172 | ] 173 | 174 | [[package]] 175 | name = "fastrand" 176 | version = "2.3.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 179 | 180 | [[package]] 181 | name = "fasyslog" 182 | version = "1.0.1" 183 | dependencies = [ 184 | "cfg-if", 185 | "jiff", 186 | "names", 187 | "native-tls", 188 | "nix", 189 | "rustls", 190 | "rustls-native-certs", 191 | "windows-targets 0.53.0", 192 | ] 193 | 194 | [[package]] 195 | name = "foreign-types" 196 | version = "0.3.2" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 199 | dependencies = [ 200 | "foreign-types-shared", 201 | ] 202 | 203 | [[package]] 204 | name = "foreign-types-shared" 205 | version = "0.1.1" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 208 | 209 | [[package]] 210 | name = "fs_extra" 211 | version = "1.3.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" 214 | 215 | [[package]] 216 | name = "getrandom" 217 | version = "0.2.15" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 220 | dependencies = [ 221 | "cfg-if", 222 | "libc", 223 | "wasi 0.11.0+wasi-snapshot-preview1", 224 | ] 225 | 226 | [[package]] 227 | name = "getrandom" 228 | version = "0.3.1" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" 231 | dependencies = [ 232 | "cfg-if", 233 | "libc", 234 | "wasi 0.13.3+wasi-0.2.2", 235 | "windows-targets 0.52.6", 236 | ] 237 | 238 | [[package]] 239 | name = "glob" 240 | version = "0.3.2" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 243 | 244 | [[package]] 245 | name = "home" 246 | version = "0.5.11" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 249 | dependencies = [ 250 | "windows-sys 0.59.0", 251 | ] 252 | 253 | [[package]] 254 | name = "itertools" 255 | version = "0.12.1" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 258 | dependencies = [ 259 | "either", 260 | ] 261 | 262 | [[package]] 263 | name = "jiff" 264 | version = "0.2.0" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "ba926fdd8e5b5e7f9700355b0831d8c416afe94b014b1023424037a187c9c582" 267 | dependencies = [ 268 | "jiff-tzdb-platform", 269 | "log", 270 | "portable-atomic", 271 | "portable-atomic-util", 272 | "serde", 273 | "windows-sys 0.59.0", 274 | ] 275 | 276 | [[package]] 277 | name = "jiff-tzdb" 278 | version = "0.1.2" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "cf2cec2f5d266af45a071ece48b1fb89f3b00b2421ac3a5fe10285a6caaa60d3" 281 | 282 | [[package]] 283 | name = "jiff-tzdb-platform" 284 | version = "0.1.2" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "a63c62e404e7b92979d2792352d885a7f8f83fd1d0d31eea582d77b2ceca697e" 287 | dependencies = [ 288 | "jiff-tzdb", 289 | ] 290 | 291 | [[package]] 292 | name = "jobserver" 293 | version = "0.1.32" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 296 | dependencies = [ 297 | "libc", 298 | ] 299 | 300 | [[package]] 301 | name = "lazy_static" 302 | version = "1.5.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 305 | 306 | [[package]] 307 | name = "lazycell" 308 | version = "1.3.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 311 | 312 | [[package]] 313 | name = "libc" 314 | version = "0.2.172" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 317 | 318 | [[package]] 319 | name = "libloading" 320 | version = "0.8.6" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" 323 | dependencies = [ 324 | "cfg-if", 325 | "windows-targets 0.52.6", 326 | ] 327 | 328 | [[package]] 329 | name = "linux-raw-sys" 330 | version = "0.4.15" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 333 | 334 | [[package]] 335 | name = "log" 336 | version = "0.4.25" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 339 | 340 | [[package]] 341 | name = "memchr" 342 | version = "2.7.4" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 345 | 346 | [[package]] 347 | name = "minimal-lexical" 348 | version = "0.2.1" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 351 | 352 | [[package]] 353 | name = "names" 354 | version = "0.14.0" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" 357 | dependencies = [ 358 | "rand", 359 | ] 360 | 361 | [[package]] 362 | name = "native-tls" 363 | version = "0.2.13" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" 366 | dependencies = [ 367 | "libc", 368 | "log", 369 | "openssl", 370 | "openssl-probe", 371 | "openssl-sys", 372 | "schannel", 373 | "security-framework 2.11.1", 374 | "security-framework-sys", 375 | "tempfile", 376 | ] 377 | 378 | [[package]] 379 | name = "nix" 380 | version = "0.30.1" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" 383 | dependencies = [ 384 | "bitflags", 385 | "cfg-if", 386 | "cfg_aliases", 387 | "libc", 388 | ] 389 | 390 | [[package]] 391 | name = "nom" 392 | version = "7.1.3" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 395 | dependencies = [ 396 | "memchr", 397 | "minimal-lexical", 398 | ] 399 | 400 | [[package]] 401 | name = "once_cell" 402 | version = "1.20.3" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 405 | 406 | [[package]] 407 | name = "openssl" 408 | version = "0.10.70" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" 411 | dependencies = [ 412 | "bitflags", 413 | "cfg-if", 414 | "foreign-types", 415 | "libc", 416 | "once_cell", 417 | "openssl-macros", 418 | "openssl-sys", 419 | ] 420 | 421 | [[package]] 422 | name = "openssl-macros" 423 | version = "0.1.1" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 426 | dependencies = [ 427 | "proc-macro2", 428 | "quote", 429 | "syn", 430 | ] 431 | 432 | [[package]] 433 | name = "openssl-probe" 434 | version = "0.1.6" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 437 | 438 | [[package]] 439 | name = "openssl-sys" 440 | version = "0.9.105" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" 443 | dependencies = [ 444 | "cc", 445 | "libc", 446 | "pkg-config", 447 | "vcpkg", 448 | ] 449 | 450 | [[package]] 451 | name = "paste" 452 | version = "1.0.15" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 455 | 456 | [[package]] 457 | name = "pkg-config" 458 | version = "0.3.31" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 461 | 462 | [[package]] 463 | name = "portable-atomic" 464 | version = "1.10.0" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" 467 | 468 | [[package]] 469 | name = "portable-atomic-util" 470 | version = "0.2.4" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 473 | dependencies = [ 474 | "portable-atomic", 475 | ] 476 | 477 | [[package]] 478 | name = "ppv-lite86" 479 | version = "0.2.20" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 482 | dependencies = [ 483 | "zerocopy", 484 | ] 485 | 486 | [[package]] 487 | name = "prettyplease" 488 | version = "0.2.29" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" 491 | dependencies = [ 492 | "proc-macro2", 493 | "syn", 494 | ] 495 | 496 | [[package]] 497 | name = "proc-macro2" 498 | version = "1.0.93" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 501 | dependencies = [ 502 | "unicode-ident", 503 | ] 504 | 505 | [[package]] 506 | name = "quote" 507 | version = "1.0.38" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 510 | dependencies = [ 511 | "proc-macro2", 512 | ] 513 | 514 | [[package]] 515 | name = "rand" 516 | version = "0.8.5" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 519 | dependencies = [ 520 | "libc", 521 | "rand_chacha", 522 | "rand_core", 523 | ] 524 | 525 | [[package]] 526 | name = "rand_chacha" 527 | version = "0.3.1" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 530 | dependencies = [ 531 | "ppv-lite86", 532 | "rand_core", 533 | ] 534 | 535 | [[package]] 536 | name = "rand_core" 537 | version = "0.6.4" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 540 | dependencies = [ 541 | "getrandom 0.2.15", 542 | ] 543 | 544 | [[package]] 545 | name = "regex" 546 | version = "1.11.1" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 549 | dependencies = [ 550 | "aho-corasick", 551 | "memchr", 552 | "regex-automata", 553 | "regex-syntax", 554 | ] 555 | 556 | [[package]] 557 | name = "regex-automata" 558 | version = "0.4.9" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 561 | dependencies = [ 562 | "aho-corasick", 563 | "memchr", 564 | "regex-syntax", 565 | ] 566 | 567 | [[package]] 568 | name = "regex-syntax" 569 | version = "0.8.5" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 572 | 573 | [[package]] 574 | name = "ring" 575 | version = "0.17.8" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 578 | dependencies = [ 579 | "cc", 580 | "cfg-if", 581 | "getrandom 0.2.15", 582 | "libc", 583 | "spin", 584 | "untrusted", 585 | "windows-sys 0.52.0", 586 | ] 587 | 588 | [[package]] 589 | name = "rustc-hash" 590 | version = "1.1.0" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 593 | 594 | [[package]] 595 | name = "rustix" 596 | version = "0.38.44" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 599 | dependencies = [ 600 | "bitflags", 601 | "errno", 602 | "libc", 603 | "linux-raw-sys", 604 | "windows-sys 0.59.0", 605 | ] 606 | 607 | [[package]] 608 | name = "rustls" 609 | version = "0.23.23" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" 612 | dependencies = [ 613 | "aws-lc-rs", 614 | "log", 615 | "once_cell", 616 | "rustls-pki-types", 617 | "rustls-webpki", 618 | "subtle", 619 | "zeroize", 620 | ] 621 | 622 | [[package]] 623 | name = "rustls-native-certs" 624 | version = "0.8.1" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" 627 | dependencies = [ 628 | "openssl-probe", 629 | "rustls-pki-types", 630 | "schannel", 631 | "security-framework 3.2.0", 632 | ] 633 | 634 | [[package]] 635 | name = "rustls-pki-types" 636 | version = "1.11.0" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 639 | 640 | [[package]] 641 | name = "rustls-webpki" 642 | version = "0.102.8" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 645 | dependencies = [ 646 | "aws-lc-rs", 647 | "ring", 648 | "rustls-pki-types", 649 | "untrusted", 650 | ] 651 | 652 | [[package]] 653 | name = "schannel" 654 | version = "0.1.27" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 657 | dependencies = [ 658 | "windows-sys 0.59.0", 659 | ] 660 | 661 | [[package]] 662 | name = "security-framework" 663 | version = "2.11.1" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 666 | dependencies = [ 667 | "bitflags", 668 | "core-foundation 0.9.4", 669 | "core-foundation-sys", 670 | "libc", 671 | "security-framework-sys", 672 | ] 673 | 674 | [[package]] 675 | name = "security-framework" 676 | version = "3.2.0" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" 679 | dependencies = [ 680 | "bitflags", 681 | "core-foundation 0.10.0", 682 | "core-foundation-sys", 683 | "libc", 684 | "security-framework-sys", 685 | ] 686 | 687 | [[package]] 688 | name = "security-framework-sys" 689 | version = "2.14.0" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 692 | dependencies = [ 693 | "core-foundation-sys", 694 | "libc", 695 | ] 696 | 697 | [[package]] 698 | name = "serde" 699 | version = "1.0.217" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 702 | dependencies = [ 703 | "serde_derive", 704 | ] 705 | 706 | [[package]] 707 | name = "serde_derive" 708 | version = "1.0.217" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 711 | dependencies = [ 712 | "proc-macro2", 713 | "quote", 714 | "syn", 715 | ] 716 | 717 | [[package]] 718 | name = "shlex" 719 | version = "1.3.0" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 722 | 723 | [[package]] 724 | name = "spin" 725 | version = "0.9.8" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 728 | 729 | [[package]] 730 | name = "subtle" 731 | version = "2.6.1" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 734 | 735 | [[package]] 736 | name = "syn" 737 | version = "2.0.98" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 740 | dependencies = [ 741 | "proc-macro2", 742 | "quote", 743 | "unicode-ident", 744 | ] 745 | 746 | [[package]] 747 | name = "tempfile" 748 | version = "3.16.0" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" 751 | dependencies = [ 752 | "cfg-if", 753 | "fastrand", 754 | "getrandom 0.3.1", 755 | "once_cell", 756 | "rustix", 757 | "windows-sys 0.59.0", 758 | ] 759 | 760 | [[package]] 761 | name = "unicode-ident" 762 | version = "1.0.16" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" 765 | 766 | [[package]] 767 | name = "untrusted" 768 | version = "0.9.0" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 771 | 772 | [[package]] 773 | name = "vcpkg" 774 | version = "0.2.15" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 777 | 778 | [[package]] 779 | name = "wasi" 780 | version = "0.11.0+wasi-snapshot-preview1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 783 | 784 | [[package]] 785 | name = "wasi" 786 | version = "0.13.3+wasi-0.2.2" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" 789 | dependencies = [ 790 | "wit-bindgen-rt", 791 | ] 792 | 793 | [[package]] 794 | name = "which" 795 | version = "4.4.2" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 798 | dependencies = [ 799 | "either", 800 | "home", 801 | "once_cell", 802 | "rustix", 803 | ] 804 | 805 | [[package]] 806 | name = "windows-sys" 807 | version = "0.52.0" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 810 | dependencies = [ 811 | "windows-targets 0.52.6", 812 | ] 813 | 814 | [[package]] 815 | name = "windows-sys" 816 | version = "0.59.0" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 819 | dependencies = [ 820 | "windows-targets 0.52.6", 821 | ] 822 | 823 | [[package]] 824 | name = "windows-targets" 825 | version = "0.52.6" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 828 | dependencies = [ 829 | "windows_aarch64_gnullvm 0.52.6", 830 | "windows_aarch64_msvc 0.52.6", 831 | "windows_i686_gnu 0.52.6", 832 | "windows_i686_gnullvm 0.52.6", 833 | "windows_i686_msvc 0.52.6", 834 | "windows_x86_64_gnu 0.52.6", 835 | "windows_x86_64_gnullvm 0.52.6", 836 | "windows_x86_64_msvc 0.52.6", 837 | ] 838 | 839 | [[package]] 840 | name = "windows-targets" 841 | version = "0.53.0" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" 844 | dependencies = [ 845 | "windows_aarch64_gnullvm 0.53.0", 846 | "windows_aarch64_msvc 0.53.0", 847 | "windows_i686_gnu 0.53.0", 848 | "windows_i686_gnullvm 0.53.0", 849 | "windows_i686_msvc 0.53.0", 850 | "windows_x86_64_gnu 0.53.0", 851 | "windows_x86_64_gnullvm 0.53.0", 852 | "windows_x86_64_msvc 0.53.0", 853 | ] 854 | 855 | [[package]] 856 | name = "windows_aarch64_gnullvm" 857 | version = "0.52.6" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 860 | 861 | [[package]] 862 | name = "windows_aarch64_gnullvm" 863 | version = "0.53.0" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 866 | 867 | [[package]] 868 | name = "windows_aarch64_msvc" 869 | version = "0.52.6" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 872 | 873 | [[package]] 874 | name = "windows_aarch64_msvc" 875 | version = "0.53.0" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 878 | 879 | [[package]] 880 | name = "windows_i686_gnu" 881 | version = "0.52.6" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 884 | 885 | [[package]] 886 | name = "windows_i686_gnu" 887 | version = "0.53.0" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 890 | 891 | [[package]] 892 | name = "windows_i686_gnullvm" 893 | version = "0.52.6" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 896 | 897 | [[package]] 898 | name = "windows_i686_gnullvm" 899 | version = "0.53.0" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 902 | 903 | [[package]] 904 | name = "windows_i686_msvc" 905 | version = "0.52.6" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 908 | 909 | [[package]] 910 | name = "windows_i686_msvc" 911 | version = "0.53.0" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 914 | 915 | [[package]] 916 | name = "windows_x86_64_gnu" 917 | version = "0.52.6" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 920 | 921 | [[package]] 922 | name = "windows_x86_64_gnu" 923 | version = "0.53.0" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 926 | 927 | [[package]] 928 | name = "windows_x86_64_gnullvm" 929 | version = "0.52.6" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 932 | 933 | [[package]] 934 | name = "windows_x86_64_gnullvm" 935 | version = "0.53.0" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 938 | 939 | [[package]] 940 | name = "windows_x86_64_msvc" 941 | version = "0.52.6" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 944 | 945 | [[package]] 946 | name = "windows_x86_64_msvc" 947 | version = "0.53.0" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 950 | 951 | [[package]] 952 | name = "wit-bindgen-rt" 953 | version = "0.33.0" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" 956 | dependencies = [ 957 | "bitflags", 958 | ] 959 | 960 | [[package]] 961 | name = "zerocopy" 962 | version = "0.7.35" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 965 | dependencies = [ 966 | "byteorder", 967 | "zerocopy-derive", 968 | ] 969 | 970 | [[package]] 971 | name = "zerocopy-derive" 972 | version = "0.7.35" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 975 | dependencies = [ 976 | "proc-macro2", 977 | "quote", 978 | "syn", 979 | ] 980 | 981 | [[package]] 982 | name = "zeroize" 983 | version = "1.8.1" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 986 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [package] 16 | name = "fasyslog" 17 | version = "1.0.1" 18 | 19 | categories = ["development-tools::debugging"] 20 | description = "A fast syslog client written in Rust." 21 | documentation = "https://docs.rs/fasyslog" 22 | edition = "2021" 23 | keywords = ["syslog", "logs", "logging"] 24 | license = "Apache-2.0" 25 | repository = "https://github.com/fast/fasyslog" 26 | rust-version = "1.75.0" 27 | 28 | [package.metadata.docs.rs] 29 | all-features = true 30 | cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] 31 | rustdoc-args = ["--cfg", "docsrs"] 32 | 33 | [features] 34 | native-tls = ["dep:native-tls"] 35 | rustls = ["dep:rustls", "dep:rustls-native-certs"] 36 | 37 | [dependencies] 38 | jiff = { version = "0.2" } 39 | 40 | # Optional dependencies 41 | native-tls = { version = "0.2.12", optional = true } 42 | rustls = { version = "0.23.20", optional = true } 43 | rustls-native-certs = { version = "0.8.1", optional = true } 44 | 45 | [target.'cfg(unix)'.dependencies] 46 | cfg-if = { version = "1.0.0" } 47 | nix = { version = "0.30.1", features = ["hostname"] } 48 | 49 | [target.'cfg(windows)'.dependencies] 50 | windows-targets = { version = "0.53.0" } 51 | 52 | [dev-dependencies] 53 | names = { version = "0.14.0", default-features = false } 54 | 55 | [[example]] 56 | doc-scrape-examples = true 57 | name = "broadcast_sender" 58 | path = "examples/broadcast_sender.rs" 59 | 60 | [[example]] 61 | doc-scrape-examples = true 62 | name = "native_tls_sender" 63 | path = "examples/native_tls_sender.rs" 64 | required-features = ["native-tls"] 65 | 66 | [[example]] 67 | doc-scrape-examples = true 68 | name = "rustls_sender" 69 | path = "examples/rustls_sender.rs" 70 | required-features = ["rustls"] 71 | 72 | [[example]] 73 | doc-scrape-examples = true 74 | name = "tcp_sender" 75 | path = "examples/tcp_sender.rs" 76 | 77 | [[example]] 78 | doc-scrape-examples = true 79 | name = "udp_sender" 80 | path = "examples/udp_sender.rs" 81 | 82 | [[example]] 83 | doc-scrape-examples = true 84 | name = "unix_sender" 85 | path = "examples/unix_sender.rs" 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fasyslog: A fast syslog client written in Rust 2 | 3 | [![Crates.io][crates-badge]][crates-url] 4 | [![Documentation][docs-badge]][docs-url] 5 | [![MSRV 1.75][msrv-badge]](https://www.whatrustisit.com) 6 | [![Apache 2.0 licensed][license-badge]][license-url] 7 | [![Build Status][actions-badge]][actions-url] 8 | 9 | [crates-badge]: https://img.shields.io/crates/v/fasyslog.svg 10 | [crates-url]: https://crates.io/crates/fasyslog 11 | [docs-badge]: https://docs.rs/fasyslog/badge.svg 12 | [msrv-badge]: https://img.shields.io/badge/MSRV-1.75-green?logo=rust 13 | [docs-url]: https://docs.rs/fasyslog 14 | [license-badge]: https://img.shields.io/crates/l/fasyslog 15 | [license-url]: LICENSE 16 | [actions-badge]: https://github.com/fast/fasyslog/workflows/CI/badge.svg 17 | [actions-url]:https://github.com/fast/fasyslog/actions?query=workflow%3ACI 18 | 19 | ## Description 20 | 21 | Client library written in Rust to send messages to a Syslog server. Support implementations: 22 | 23 | * RFC-3164 Formatter: [The BSD syslog Protocol](https://datatracker.ietf.org/doc/html/rfc3164) 24 | * RFC-5424 Formatter: [The Syslog Protocol](https://datatracker.ietf.org/doc/html/rfc5424) 25 | * `UdpSender`: [RFC 5426 - Transmission of Syslog Messages over UDP](https://datatracker.ietf.org/doc/html/rfc5426) 26 | * `TcpSender`: [RFC 6587 - Transmission of Syslog Messages over TCP](https://datatracker.ietf.org/doc/html/rfc6587) 27 | * `NativeTlsSender`: [RFC 5425 - Transport Layer Security (TLS) Transport Mapping for Syslog](https://datatracker.ietf.org/doc/html/rfc5425) 28 | * This implementation is based on [`native-tls`](https://crates.io/crates/native-tls) and requires features `native-tls` turned on. 29 | * `RustlsSender`: [RFC 5425 - Transport Layer Security (TLS) Transport Mapping for Syslog](https://datatracker.ietf.org/doc/html/rfc5425) 30 | * This implementation is based on [`rustls`](https://crates.io/crates/rustls) and requires features `rustls` turned on. 31 | * (unix only) Unix domain socket sender (datagram or stream) 32 | 33 | ## Getting Started 34 | 35 | Add `fasyslog` to your `Cargo.toml`: 36 | 37 | ```shell 38 | cargo add fasyslog 39 | ``` 40 | 41 | ```rust 42 | use fasyslog::Severity; 43 | 44 | fn main() { 45 | let mut sender = fasyslog::sender::tcp_well_known().unwrap(); 46 | let message = format!("Hello, fasyslog!"); 47 | // send a message with RFC 3164 format 48 | sender.send_rfc3164(Severity::ERROR, message).unwrap(); 49 | sender.flush().unwrap(); 50 | 51 | // send a message with RFC 5424 format 52 | const EMPTY_MSGID: Option<&str> = None; 53 | const EMPTY_STRUCTURED_DATA: Vec = Vec::new(); 54 | sender.send_rfc5424(Severity::ERROR, EMPTY_MSGID, EMPTY_STRUCTURED_DATA, message).unwrap(); 55 | sender.flush().unwrap(); 56 | } 57 | ``` 58 | 59 | If you'd like to integrate with `log` crate, you can try the `logforth` example: 60 | 61 | ```toml 62 | [dependencies] 63 | log = { version = "..." } 64 | logforth = { version = "...", features = ["syslog"] } 65 | ``` 66 | 67 | ```rust 68 | use logforth::append::syslog::SyslogBuilder; 69 | 70 | fn main() { 71 | let (append, _guard) = SyslogBuilder::tcp_well_known().unwrap().build(); 72 | 73 | logforth::builder() 74 | .dispatch(|d| d.filter(log::LevelFilter::Trace).append(append)) 75 | .apply(); 76 | 77 | log::info!("This log will be written to syslog."); 78 | } 79 | ``` 80 | 81 | ## Example 82 | 83 | Check the [examples](examples) directory for more examples. 84 | 85 | ## Documentation 86 | 87 | Read the online documents at https://docs.rs/fasyslog. 88 | 89 | ## Minimum Supported Rust Version (MSRV) 90 | 91 | This crate is built against the latest stable release, and its minimum supported rustc version is 1.75.0. 92 | 93 | The policy is that the minimum Rust version required to use this crate can be increased in minor version updates. For example, if Fasyslog 1.0 requires Rust 1.20.0, then Fasyslog 1.0.z for all values of z will also require Rust 1.20.0 or newer. However, Fasyslog 1.y for y > 0 may require a newer minimum version of Rust. 94 | 95 | ## License 96 | 97 | This project is licensed under [Apache License, Version 2.0](LICENSE). 98 | -------------------------------------------------------------------------------- /examples/broadcast_sender.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use fasyslog::Severity; 16 | 17 | fn main() { 18 | let mut sender = fasyslog::sender::broadcast_well_known().unwrap(); 19 | let mut generator = names::Generator::default(); 20 | for _ in 0..100 { 21 | let name = generator.next().unwrap(); 22 | let message = format!("Broadcast {name} to everyone!"); 23 | sender 24 | .send_rfc5424(Severity::ERROR, None::, Vec::new(), message) 25 | .unwrap(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/native_tls_sender.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use fasyslog::Severity; 16 | 17 | fn main() { 18 | let mut sender = fasyslog::sender::native_tls_well_known("127.0.0.1").unwrap(); 19 | let mut generator = names::Generator::default(); 20 | for _ in 0..100 { 21 | let name = generator.next().unwrap(); 22 | let message = format!("Hello, native-tls with {name}!"); 23 | sender 24 | .send_rfc5424(Severity::ERROR, None::, Vec::new(), message) 25 | .unwrap(); 26 | } 27 | sender.flush().unwrap(); 28 | } 29 | -------------------------------------------------------------------------------- /examples/rustls_sender.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use fasyslog::Severity; 16 | 17 | fn main() { 18 | let mut sender = fasyslog::sender::rustls_well_known("127.0.0.1").unwrap(); 19 | let mut generator = names::Generator::default(); 20 | for _ in 0..100 { 21 | let name = generator.next().unwrap(); 22 | let message = format!("Hello, rustls with {name}!"); 23 | sender 24 | .send_rfc5424(Severity::ERROR, None::, Vec::new(), message) 25 | .unwrap(); 26 | } 27 | sender.flush().unwrap(); 28 | } 29 | -------------------------------------------------------------------------------- /examples/tcp_sender.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use fasyslog::Severity; 16 | 17 | fn main() { 18 | let mut sender = fasyslog::sender::tcp_well_known().unwrap(); 19 | let mut generator = names::Generator::default(); 20 | for _ in 0..100 { 21 | let name = generator.next().unwrap(); 22 | let message = format!("Hello, {name}!"); 23 | let mut element = fasyslog::SDElement::new("exampleSDID@32473").unwrap(); 24 | element.add_param("iut", "3").unwrap(); 25 | sender 26 | .send_rfc5424(Severity::ERROR, Some("TCPIN"), vec![element], message) 27 | .unwrap(); 28 | } 29 | sender.flush().unwrap(); 30 | } 31 | -------------------------------------------------------------------------------- /examples/udp_sender.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use fasyslog::Severity; 16 | 17 | fn main() { 18 | let mut sender = fasyslog::sender::udp_well_known().unwrap(); 19 | let mut generator = names::Generator::default(); 20 | for _ in 0..100 { 21 | let name = generator.next().unwrap(); 22 | let message = format!("Hello, {name}!"); 23 | let mut element = fasyslog::SDElement::new("exampleSDID@16253").unwrap(); 24 | element.add_param("jno", "sul").unwrap(); 25 | sender 26 | .send_rfc5424(Severity::ERROR, Some("UDPIN"), vec![element], message) 27 | .unwrap(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/unix_sender.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use fasyslog::Severity; 16 | 17 | #[cfg(unix)] 18 | fn main() { 19 | let mut sender = fasyslog::sender::unix_well_known().unwrap(); 20 | let mut generator = names::Generator::default(); 21 | for _ in 0..100 { 22 | let name = generator.next().unwrap(); 23 | let message = format!("Hello, {name}!"); 24 | sender.send_rfc3164(Severity::ERROR, message).unwrap(); 25 | } 26 | sender.flush().unwrap(); 27 | } 28 | 29 | #[cfg(not(unix))] 30 | fn main() { 31 | println!("This example is only for Unix-like systems."); 32 | } 33 | -------------------------------------------------------------------------------- /licenserc.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | headerPath = "Apache-2.0.txt" 16 | 17 | includes = ['**/*.proto', '**/*.rs', '**/*.yml', '**/*.yaml', '**/*.toml'] 18 | 19 | [properties] 20 | copyrightOwner = "FastLabs Developers" 21 | inceptionYear = 2024 22 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [toolchain] 16 | channel = "stable" 17 | components = ["cargo", "rustfmt", "clippy", "rust-analyzer"] 18 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | comment_width = 120 16 | format_code_in_doc_comments = true 17 | group_imports = "StdExternalCrate" 18 | imports_granularity = "Item" 19 | wrap_comments = true 20 | -------------------------------------------------------------------------------- /src/facility.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::fmt; 16 | use std::str::FromStr; 17 | 18 | /// Syslog facility as defined in [RFC 5424] (The Syslog Protocol). 19 | /// 20 | /// See also [RFC 5427] (Textual Conventions for Syslog Management) for the labels. 21 | /// 22 | /// [RFC 5424]: https://datatracker.ietf.org/doc/html/rfc5424. 23 | /// [RFC 5427]: https://datatracker.ietf.org/doc/html/rfc5427. 24 | #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] 25 | #[repr(u8)] 26 | pub enum Facility { 27 | /// Kernel messages, numerical code 0. 28 | KERN = 0, 29 | /// User-level messages, numerical code 1. 30 | USER = 1, 31 | /// Mail system, numerical code 2. 32 | MAIL = 2, 33 | /// System daemons, numerical code 3. 34 | DAEMON = 3, 35 | /// Security/authorization messages, numerical code 4. 36 | AUTH = 4, 37 | /// Messages generated internally by syslogd, numerical code 5. 38 | SYSLOG = 5, 39 | /// Line printer subsystem, numerical code 6. 40 | LPR = 6, 41 | /// Network news subsystem, numerical code 7. 42 | NEWS = 7, 43 | /// UUCP subsystem, numerical code 8 44 | UUCP = 8, 45 | /// Clock daemon, numerical code 9. 46 | CRON = 9, 47 | /// Security/authorization messages, numerical code 10. 48 | AUTHPRIV = 10, 49 | /// FTP daemon, numerical code 11. 50 | FTP = 11, 51 | /// NTP subsystem, numerical code 12. 52 | NTP = 12, 53 | /// Log audit, numerical code 13. 54 | AUDIT = 13, 55 | /// Log alert, numerical code 14. 56 | ALERT = 14, 57 | /// Clock daemon, numerical code 15. 58 | CLOCK = 15, 59 | /// Reserved for local use, numerical code 16. 60 | LOCAL0 = 16, 61 | /// Reserved for local use, numerical code 17. 62 | LOCAL1 = 17, 63 | /// Reserved for local use, numerical code 18. 64 | LOCAL2 = 18, 65 | /// Reserved for local use, numerical code 19. 66 | LOCAL3 = 19, 67 | /// Reserved for local use, numerical code 20. 68 | LOCAL4 = 20, 69 | /// Reserved for local use, numerical code 21. 70 | LOCAL5 = 21, 71 | /// Reserved for local use, numerical code 22. 72 | LOCAL6 = 22, 73 | /// Reserved for local use, numerical code 23. 74 | LOCAL7 = 23, 75 | } 76 | 77 | impl Default for Facility { 78 | /// Returns the default facility `Facility::USER` as defined in [syslog(3)]. 79 | /// 80 | /// [syslog(3)]: https://www.man7.org/linux/man-pages/man3/syslog.3.html 81 | fn default() -> Self { 82 | Facility::USER 83 | } 84 | } 85 | 86 | impl Facility { 87 | /// Returns the numerical code of the facility. 88 | pub fn code(self) -> u8 { 89 | self as u8 90 | } 91 | 92 | /// Returns the label of the facility. 93 | pub fn label(self) -> &'static str { 94 | match self { 95 | Facility::KERN => "KERN", 96 | Facility::USER => "USER", 97 | Facility::MAIL => "MAIL", 98 | Facility::DAEMON => "DAEMON", 99 | Facility::AUTH => "AUTH", 100 | Facility::SYSLOG => "SYSLOG", 101 | Facility::LPR => "LPR", 102 | Facility::NEWS => "NEWS", 103 | Facility::UUCP => "UUCP", 104 | Facility::CRON => "CRON", 105 | Facility::AUTHPRIV => "AUTHPRIV", 106 | Facility::FTP => "FTP", 107 | Facility::NTP => "NTP", 108 | Facility::AUDIT => "AUDIT", 109 | Facility::ALERT => "ALERT", 110 | Facility::CLOCK => "CLOCK", 111 | Facility::LOCAL0 => "LOCAL0", 112 | Facility::LOCAL1 => "LOCAL1", 113 | Facility::LOCAL2 => "LOCAL2", 114 | Facility::LOCAL3 => "LOCAL3", 115 | Facility::LOCAL4 => "LOCAL4", 116 | Facility::LOCAL5 => "LOCAL5", 117 | Facility::LOCAL6 => "LOCAL6", 118 | Facility::LOCAL7 => "LOCAL7", 119 | } 120 | } 121 | } 122 | 123 | impl TryFrom for Facility { 124 | type Error = (); 125 | 126 | fn try_from(value: u8) -> Result { 127 | match value { 128 | 0 => Ok(Facility::KERN), 129 | 1 => Ok(Facility::USER), 130 | 2 => Ok(Facility::MAIL), 131 | 3 => Ok(Facility::DAEMON), 132 | 4 => Ok(Facility::AUTH), 133 | 5 => Ok(Facility::SYSLOG), 134 | 6 => Ok(Facility::LPR), 135 | 7 => Ok(Facility::NEWS), 136 | 8 => Ok(Facility::UUCP), 137 | 9 => Ok(Facility::CRON), 138 | 10 => Ok(Facility::AUTHPRIV), 139 | 11 => Ok(Facility::FTP), 140 | 12 => Ok(Facility::NTP), 141 | 13 => Ok(Facility::AUDIT), 142 | 14 => Ok(Facility::ALERT), 143 | 15 => Ok(Facility::CLOCK), 144 | 16 => Ok(Facility::LOCAL0), 145 | 17 => Ok(Facility::LOCAL1), 146 | 18 => Ok(Facility::LOCAL2), 147 | 19 => Ok(Facility::LOCAL3), 148 | 20 => Ok(Facility::LOCAL4), 149 | 21 => Ok(Facility::LOCAL5), 150 | 22 => Ok(Facility::LOCAL6), 151 | 23 => Ok(Facility::LOCAL7), 152 | _ => Err(()), 153 | } 154 | } 155 | } 156 | 157 | impl TryFrom<&str> for Facility { 158 | type Error = (); 159 | 160 | fn try_from(value: &str) -> Result { 161 | match &value.to_lowercase()[..] { 162 | "kern" => Ok(Facility::KERN), 163 | "user" => Ok(Facility::USER), 164 | "mail" => Ok(Facility::MAIL), 165 | "daemon" => Ok(Facility::DAEMON), 166 | "auth" => Ok(Facility::AUTH), 167 | "syslog" => Ok(Facility::SYSLOG), 168 | "lpr" => Ok(Facility::LPR), 169 | "news" => Ok(Facility::NEWS), 170 | "uucp" => Ok(Facility::UUCP), 171 | "cron" => Ok(Facility::CRON), 172 | "authpriv" => Ok(Facility::AUTHPRIV), 173 | "ftp" => Ok(Facility::FTP), 174 | "ntp" => Ok(Facility::NTP), 175 | "audit" => Ok(Facility::AUDIT), 176 | "alert" => Ok(Facility::ALERT), 177 | "clock" => Ok(Facility::CLOCK), 178 | "local0" => Ok(Facility::LOCAL0), 179 | "local1" => Ok(Facility::LOCAL1), 180 | "local2" => Ok(Facility::LOCAL2), 181 | "local3" => Ok(Facility::LOCAL3), 182 | "local4" => Ok(Facility::LOCAL4), 183 | "local5" => Ok(Facility::LOCAL5), 184 | "local6" => Ok(Facility::LOCAL6), 185 | "local7" => Ok(Facility::LOCAL7), 186 | _ => Err(()), 187 | } 188 | } 189 | } 190 | 191 | impl FromStr for Facility { 192 | type Err = (); 193 | 194 | fn from_str(s: &str) -> Result { 195 | Self::try_from(s) 196 | } 197 | } 198 | 199 | impl fmt::Display for Facility { 200 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 201 | write!(f, "{}", self.label()) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/format.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Format Syslog messages according to the referred standards. 16 | 17 | use std::fmt; 18 | use std::fmt::Formatter; 19 | 20 | use jiff::Timestamp; 21 | use jiff::Zoned; 22 | 23 | use crate::internal::hostname; 24 | use crate::Facility; 25 | use crate::SDElement; 26 | use crate::Severity; 27 | 28 | const NILVALUE: &str = "-"; 29 | 30 | /// Shared context for constructing Syslog messages. 31 | #[derive(Debug, Clone)] 32 | pub struct SyslogContext { 33 | facility: Facility, 34 | hostname: Option, 35 | appname: Option, 36 | procid: Option, 37 | } 38 | 39 | impl Default for SyslogContext { 40 | fn default() -> Self { 41 | Self::new() 42 | } 43 | } 44 | 45 | impl SyslogContext { 46 | /// Create a new blank `SyslogContext`. 47 | pub const fn const_new() -> Self { 48 | Self { 49 | facility: Facility::USER, 50 | hostname: None, 51 | appname: None, 52 | procid: None, 53 | } 54 | } 55 | 56 | /// Create a new `SyslogContext` with system default values. 57 | pub fn new() -> Self { 58 | let procid = std::process::id(); 59 | let appname = std::env::current_exe().ok().and_then(|path| { 60 | // NOTE - cannot chain because 'file_name'/'to_str' return a temporary reference 61 | path.file_name() 62 | .and_then(|name| name.to_str()) 63 | .map(|name| name.to_string()) 64 | }); 65 | let hostname = hostname().and_then(|name| name.to_str().map(|name| name.to_string())); 66 | Self { 67 | facility: Facility::USER, 68 | hostname, 69 | appname, 70 | procid: Some(procid.to_string()), 71 | } 72 | } 73 | 74 | /// Set the facility of the Syslog message. 75 | pub fn facility(&mut self, facility: Facility) -> &mut Self { 76 | self.facility = facility; 77 | self 78 | } 79 | 80 | /// Set the hostname of the Syslog message. 81 | pub fn hostname(&mut self, hostname: impl Into) -> &mut Self { 82 | self.hostname = Some(hostname.into()); 83 | self 84 | } 85 | 86 | /// Set the appname of the Syslog message. 87 | pub fn appname(&mut self, appname: impl Into) -> &mut Self { 88 | self.appname = Some(appname.into()); 89 | self 90 | } 91 | 92 | /// Set the procid of the Syslog message. 93 | pub fn procid(&mut self, procid: impl Into) -> &mut Self { 94 | self.procid = Some(procid.into()); 95 | self 96 | } 97 | 98 | /// Format the Syslog message with the given severity as defined in RFC-3164. 99 | pub fn format_rfc3164(&self, severity: Severity, message: Option) -> RFC3164Formatter { 100 | RFC3164Formatter { 101 | context: self, 102 | severity, 103 | message, 104 | } 105 | } 106 | 107 | /// Format the Syslog message with the given severity as defined in RFC-5424. 108 | pub fn format_rfc5424( 109 | &self, 110 | severity: Severity, 111 | msgid: Option, 112 | elements: Vec, 113 | message: Option, 114 | ) -> RFC5424Formatter 115 | where 116 | S: Into, 117 | M: fmt::Display, 118 | { 119 | let msgid = msgid.map(|s| s.into()); 120 | RFC5424Formatter { 121 | context: self, 122 | severity, 123 | msgid, 124 | elements, 125 | message, 126 | } 127 | } 128 | } 129 | 130 | /// Shared format logic for nullable value. 131 | fn nullable_value(value: Option<&str>) -> &str { 132 | value.unwrap_or(NILVALUE) 133 | } 134 | 135 | /// Format the Syslog message as [RFC-3164] (BSD syslog Protocol). 136 | /// 137 | /// [RFC-3164]: https://datatracker.ietf.org/doc/html/rfc3164 138 | #[derive(Debug)] 139 | pub struct RFC3164Formatter<'a, M> { 140 | context: &'a SyslogContext, 141 | severity: Severity, 142 | message: Option, 143 | } 144 | 145 | impl fmt::Display for RFC3164Formatter<'_, M> 146 | where 147 | M: fmt::Display, 148 | { 149 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 150 | // PRI (priority) Part 151 | // https://datatracker.ietf.org/doc/html/rfc3164#section-4.1.1 152 | let pri = (self.context.facility.code() << 3) | self.severity.code(); 153 | // HEADER Part of a syslog Packet 154 | // https://datatracker.ietf.org/doc/html/rfc3164#section-4.1.2 155 | let ts = Zoned::now().strftime("%b %e %T"); 156 | let hostname = nullable_value(self.context.hostname.as_deref()); 157 | let appname = nullable_value(self.context.appname.as_deref()); 158 | write!(f, "<{pri}>{ts} {hostname} {appname}")?; 159 | // Conventions defined in RFC-3164 §5.3 160 | // At least, this is the behavior of Ubuntu 24.04 LTS. 161 | if let Some(procid) = &self.context.procid { 162 | write!(f, "[{procid}]")?; 163 | } 164 | if let Some(message) = &self.message { 165 | write!(f, ": {message}")?; 166 | } 167 | Ok(()) 168 | } 169 | } 170 | 171 | /// Format the Syslog message as [RFC 5424] (The Syslog Protocol) 172 | /// 173 | /// [RFC 5424]: https://datatracker.ietf.org/doc/html/rfc5424 174 | #[derive(Debug)] 175 | pub struct RFC5424Formatter<'a, M> { 176 | context: &'a SyslogContext, 177 | severity: Severity, 178 | msgid: Option, 179 | elements: Vec, 180 | message: Option, 181 | } 182 | 183 | impl fmt::Display for RFC5424Formatter<'_, M> 184 | where 185 | M: fmt::Display, 186 | { 187 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 188 | // The PRI (priority) part is defined in RFC-5424 §6.2.1. 189 | // https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1 190 | let pri = (self.context.facility.code() << 3) | self.severity.code(); 191 | // The VERSION field denotes the version of the syslog protocol specification. 192 | // https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.2 193 | let ver = 1; 194 | // Jiff ensures that Timestamp is always displayed as an RFC-3339 compliant string. 195 | // https://docs.rs/jiff/*/jiff/struct.Timestamp.html#impl-Display-for-Timestamp 196 | let ts = Timestamp::now(); 197 | let hostname = nullable_value(self.context.hostname.as_deref()); 198 | let appname = nullable_value(self.context.appname.as_deref()); 199 | let procid = nullable_value(self.context.procid.as_deref()); 200 | let msgid = nullable_value(self.msgid.as_deref()); 201 | write!( 202 | f, 203 | "<{pri}>{ver} {ts:.6} {hostname} {appname} {procid} {msgid} " 204 | )?; 205 | if self.elements.is_empty() { 206 | write!(f, "-")?; 207 | } else { 208 | for element in &self.elements { 209 | write!(f, "{element}")?; 210 | } 211 | } 212 | if let Some(message) = &self.message { 213 | write!(f, " {message}")?; 214 | } 215 | Ok(()) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/internal.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::ffi::OsString; 16 | 17 | /// Get the standard host name for the current machine. 18 | pub(crate) fn hostname() -> Option { 19 | #[cfg(unix)] 20 | { 21 | nix::unistd::gethostname().ok() 22 | } 23 | 24 | #[cfg(windows)] 25 | { 26 | // This code snippet is derived from https://github.com/swsnr/gethostname.rs/blob/main/src/lib.rs. 27 | 28 | use std::os::windows::ffi::OsStringExt; 29 | 30 | // The DNS host name of the local computer. If the local computer is a node 31 | // in a cluster, lpBuffer receives the DNS host name of the local computer, 32 | // not the name of the cluster virtual server. 33 | pub const COMPUTER_NAME_PHYSICAL_DNS_HOSTNAME: i32 = 5; 34 | 35 | // https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw 36 | ::windows_targets::link!("kernel32.dll" "system" fn GetComputerNameExW(nametype: i32, lpbuffer: *mut u16, nsize: *mut u32) -> i32); 37 | 38 | let mut buffer_size: u32 = 0; 39 | 40 | unsafe { 41 | // This call always fails with ERROR_MORE_DATA, because we pass NULL to 42 | // get the required buffer size. GetComputerNameExW then fills buffer_size with the 43 | // size of the host name string plus a trailing zero byte. 44 | GetComputerNameExW( 45 | COMPUTER_NAME_PHYSICAL_DNS_HOSTNAME, 46 | std::ptr::null_mut(), 47 | &mut buffer_size, 48 | ) 49 | }; 50 | assert!( 51 | 0 < buffer_size, 52 | "GetComputerNameExW did not provide buffer size" 53 | ); 54 | 55 | let mut buffer = vec![0_u16; buffer_size as usize]; 56 | unsafe { 57 | if GetComputerNameExW( 58 | COMPUTER_NAME_PHYSICAL_DNS_HOSTNAME, 59 | buffer.as_mut_ptr(), 60 | &mut buffer_size, 61 | ) == 0 62 | { 63 | panic!( 64 | "GetComputerNameExW failed to read hostname. 65 | Please report this issue to !" 66 | ); 67 | } 68 | } 69 | assert!( 70 | // GetComputerNameExW returns the size _without_ the trailing zero byte on the second 71 | // call 72 | buffer_size as usize == buffer.len() - 1, 73 | "GetComputerNameExW changed the buffer size unexpectedly" 74 | ); 75 | 76 | let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); 77 | Some(OsString::from_wide(&buffer[0..end])) 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use std::process::Command; 84 | 85 | #[test] 86 | fn test_hostname_matches_system_hostname() { 87 | let output = Command::new("hostname") 88 | .output() 89 | .expect("failed to get hostname"); 90 | if output.status.success() { 91 | let system_hostname = String::from_utf8_lossy(&output.stdout); 92 | let system_hostname = system_hostname.trim_end().to_lowercase(); 93 | assert!(!system_hostname.is_empty()); 94 | 95 | let hostname = super::hostname().unwrap(); 96 | let hostname = hostname.into_string().unwrap().to_lowercase(); 97 | println!("system_hostname={system_hostname}, hostname={hostname}"); 98 | assert_eq!(system_hostname, hostname); 99 | } else { 100 | panic!( 101 | "failed to get hostname: {}", 102 | String::from_utf8_lossy(&output.stderr) 103 | ); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 16 | 17 | //! `fasyslog` is a fast syslog client written in Rust. 18 | //! 19 | //! # Overview 20 | //! 21 | //! This crate provides facilities to send log messages via syslog. Support implementations: 22 | //! 23 | //! * [RFC-3164 Formatter]: [The BSD syslog Protocol](https://datatracker.ietf.org/doc/html/rfc3164) 24 | //! * [RFC-5424 Formatter]: [The Syslog Protocol](https://datatracker.ietf.org/doc/html/rfc5424) 25 | //! * [`UdpSender`]: [RFC 5426 - Transmission of Syslog Messages over UDP](https://datatracker.ietf.org/doc/html/rfc5426) 26 | //! * [`TcpSender`]: [RFC 6587 - Transmission of Syslog Messages over TCP](https://datatracker.ietf.org/doc/html/rfc6587) 27 | //! * [`NativeTlsSender`]: [RFC 5425 - Transport Layer Security (TLS) Transport Mapping for Syslog](https://datatracker.ietf.org/doc/html/rfc5425) 28 | //! * This implementation is based on [`native-tls`](https://crates.io/crates/native-tls) and 29 | //! requires features `native-tls` turned on. 30 | //! * [`RustlsSender`]: [RFC 5425 - Transport Layer Security (TLS) Transport Mapping for Syslog](https://datatracker.ietf.org/doc/html/rfc5425) 31 | //! * This implementation is based on [`rustls`](https://crates.io/crates/rustls) and requires 32 | //! features `rustls` turned on. 33 | //! * (unix only) Unix domain socket sender (datagram or stream) 34 | //! 35 | //! [RFC-3164 Formatter]: format::RFC3164Formatter 36 | //! [RFC-5424 Formatter]: format::RFC5424Formatter 37 | //! [`UdpSender`]: sender::UdpSender 38 | //! [`TcpSender`]: sender::TcpSender 39 | //! [`NativeTlsSender`]: sender::NativeTlsSender 40 | //! [`RustlsSender`]: sender::RustlsSender 41 | //! 42 | //! # Example 43 | //! 44 | //! Send a message to a remote syslog server: 45 | //! 46 | //! ```rust, no_run 47 | //! let mut sender = fasyslog::sender::tcp_well_known().unwrap(); 48 | //! sender 49 | //! .send_rfc3164(fasyslog::Severity::INFORMATIONAL, "Hello, syslog!") 50 | //! .unwrap(); 51 | //! sender.flush().unwrap(); 52 | //! 53 | //! let mut element = fasyslog::SDElement::new("exampleSDID@32473").unwrap(); 54 | //! element.add_param("iut", "3").unwrap(); 55 | //! sender 56 | //! .send_rfc5424( 57 | //! fasyslog::Severity::NOTICE, 58 | //! Some("TCPIN"), 59 | //! vec![element], 60 | //! "Hello, syslog!", 61 | //! ) 62 | //! .unwrap(); 63 | //! sender.flush().unwrap(); 64 | //! ``` 65 | 66 | mod facility; 67 | pub use facility::*; 68 | 69 | mod severity; 70 | pub use severity::*; 71 | 72 | mod structured_data; 73 | pub use structured_data::*; 74 | 75 | pub mod format; 76 | pub mod sender; 77 | 78 | mod internal; 79 | -------------------------------------------------------------------------------- /src/sender/internal.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | macro_rules! impl_syslog_sender_common { 16 | ($sender:ident) => { 17 | impl $sender { 18 | /// Send a message with the given severity as defined in RFC-3164. 19 | pub fn send_rfc3164( 20 | &mut self, 21 | severity: $crate::Severity, 22 | message: M, 23 | ) -> std::io::Result<()> { 24 | let message = self.context.format_rfc3164(severity, Some(message)); 25 | self.send_formatted(message.to_string().as_bytes()) 26 | } 27 | 28 | /// Send a message with the given severity as defined in RFC-5424. 29 | pub fn send_rfc5424, M: std::fmt::Display>( 30 | &mut self, 31 | severity: $crate::Severity, 32 | msgid: Option, 33 | elements: Vec<$crate::SDElement>, 34 | message: M, 35 | ) -> std::io::Result<()> { 36 | let message = self 37 | .context 38 | .format_rfc5424(severity, msgid, elements, Some(message)); 39 | self.send_formatted(message.to_string().as_bytes()) 40 | } 41 | } 42 | }; 43 | } 44 | 45 | pub(crate) use impl_syslog_sender_common; 46 | 47 | macro_rules! impl_syslog_stream_send_formatted { 48 | ($sender:ident) => { 49 | impl $sender { 50 | /// Send a formatted message to the stream. 51 | pub fn send_formatted(&mut self, message: &[u8]) -> std::io::Result<()> { 52 | use std::io::Write; 53 | self.writer.write_all(message)?; 54 | self.writer.write_all(self.postfix.as_bytes())?; 55 | Ok(()) 56 | } 57 | 58 | /// Flush the stream. 59 | pub fn flush(&mut self) -> std::io::Result<()> { 60 | use std::io::Write; 61 | self.writer.flush() 62 | } 63 | } 64 | }; 65 | } 66 | 67 | pub(crate) use impl_syslog_stream_send_formatted; 68 | -------------------------------------------------------------------------------- /src/sender/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Send syslog messages to a syslog server. 16 | 17 | use std::fmt; 18 | use std::io; 19 | 20 | use crate::SDElement; 21 | use crate::Severity; 22 | 23 | #[cfg(unix)] 24 | mod unix_impl; 25 | #[cfg(unix)] 26 | pub use unix_impl::*; 27 | 28 | #[cfg(feature = "native-tls")] 29 | mod native_tls_impl; 30 | #[cfg(feature = "native-tls")] 31 | pub use native_tls_impl::*; 32 | 33 | #[cfg(feature = "rustls")] 34 | mod rustls_impl; 35 | #[cfg(feature = "rustls")] 36 | pub use rustls_impl::*; 37 | 38 | mod tcp_impl; 39 | pub use tcp_impl::*; 40 | 41 | mod udp_impl; 42 | pub use udp_impl::*; 43 | 44 | pub(crate) mod internal; 45 | 46 | /// Static dispatch for the different sender types. 47 | #[derive(Debug)] 48 | pub enum SyslogSender { 49 | Tcp(TcpSender), 50 | Udp(UdpSender), 51 | #[cfg(feature = "native-tls")] 52 | NativeTlsSender(NativeTlsSender), 53 | #[cfg(feature = "rustls")] 54 | RustlsSender(Box), 55 | #[cfg(unix)] 56 | UnixDatagram(UnixDatagramSender), 57 | #[cfg(unix)] 58 | UnixStream(UnixStreamSender), 59 | } 60 | 61 | impl SyslogSender { 62 | /// Send a message with the given severity as defined in RFC-3164. 63 | pub fn send_rfc3164( 64 | &mut self, 65 | severity: Severity, 66 | message: M, 67 | ) -> io::Result<()> { 68 | match self { 69 | SyslogSender::Tcp(sender) => sender.send_rfc3164(severity, message), 70 | SyslogSender::Udp(sender) => sender.send_rfc3164(severity, message), 71 | #[cfg(feature = "native-tls")] 72 | SyslogSender::NativeTlsSender(sender) => sender.send_rfc3164(severity, message), 73 | #[cfg(feature = "rustls")] 74 | SyslogSender::RustlsSender(sender) => sender.send_rfc3164(severity, message), 75 | #[cfg(unix)] 76 | SyslogSender::UnixDatagram(sender) => sender.send_rfc3164(severity, message), 77 | #[cfg(unix)] 78 | SyslogSender::UnixStream(sender) => sender.send_rfc3164(severity, message), 79 | } 80 | } 81 | 82 | /// Send a message with the given severity as defined in RFC-5424. 83 | pub fn send_rfc5424, M: fmt::Display>( 84 | &mut self, 85 | severity: Severity, 86 | msgid: Option, 87 | elements: Vec, 88 | message: M, 89 | ) -> io::Result<()> { 90 | match self { 91 | SyslogSender::Tcp(sender) => sender.send_rfc5424(severity, msgid, elements, message), 92 | SyslogSender::Udp(sender) => sender.send_rfc5424(severity, msgid, elements, message), 93 | #[cfg(feature = "native-tls")] 94 | SyslogSender::NativeTlsSender(sender) => { 95 | sender.send_rfc5424(severity, msgid, elements, message) 96 | } 97 | #[cfg(feature = "rustls")] 98 | SyslogSender::RustlsSender(sender) => { 99 | sender.send_rfc5424(severity, msgid, elements, message) 100 | } 101 | #[cfg(unix)] 102 | SyslogSender::UnixDatagram(sender) => { 103 | sender.send_rfc5424(severity, msgid, elements, message) 104 | } 105 | #[cfg(unix)] 106 | SyslogSender::UnixStream(sender) => { 107 | sender.send_rfc5424(severity, msgid, elements, message) 108 | } 109 | } 110 | } 111 | 112 | /// Send a pre-formatted message. 113 | pub fn send_formatted(&mut self, formatted: &[u8]) -> io::Result<()> { 114 | match self { 115 | SyslogSender::Tcp(sender) => sender.send_formatted(formatted), 116 | SyslogSender::Udp(sender) => sender.send_formatted(formatted), 117 | #[cfg(feature = "native-tls")] 118 | SyslogSender::NativeTlsSender(sender) => sender.send_formatted(formatted), 119 | #[cfg(feature = "rustls")] 120 | SyslogSender::RustlsSender(sender) => sender.send_formatted(formatted), 121 | #[cfg(unix)] 122 | SyslogSender::UnixDatagram(sender) => sender.send_formatted(formatted), 123 | #[cfg(unix)] 124 | SyslogSender::UnixStream(sender) => sender.send_formatted(formatted), 125 | } 126 | } 127 | 128 | /// Flush the underlying writer if needed. 129 | /// 130 | /// When the underlying writer is a streaming writer (TCP, UnixStream, etc.), periodically 131 | /// flush is essential to ensure that the message is sent to the syslog server [1]. 132 | /// 133 | /// When the underlying writer is a datagram writer (UDP, UnixDatagram, etc.), flush is a no-op, 134 | /// and every call to `send_xxx` defines the boundary of the packet. 135 | /// 136 | /// [1]: https://github.com/Geal/rust-syslog/issues/69 137 | pub fn flush(&mut self) -> io::Result<()> { 138 | match self { 139 | SyslogSender::Tcp(sender) => sender.flush(), 140 | SyslogSender::Udp(_) => Ok(()), 141 | #[cfg(feature = "native-tls")] 142 | SyslogSender::NativeTlsSender(sender) => sender.flush(), 143 | #[cfg(feature = "rustls")] 144 | SyslogSender::RustlsSender(sender) => sender.flush(), 145 | #[cfg(unix)] 146 | SyslogSender::UnixDatagram(_) => Ok(()), 147 | #[cfg(unix)] 148 | SyslogSender::UnixStream(sender) => sender.flush(), 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/sender/native_tls_impl.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub extern crate native_tls; 16 | 17 | use std::borrow::Cow; 18 | use std::io; 19 | use std::io::BufWriter; 20 | use std::net::TcpStream; 21 | use std::net::ToSocketAddrs; 22 | 23 | use native_tls::TlsConnector; 24 | use native_tls::TlsConnectorBuilder; 25 | use native_tls::TlsStream; 26 | 27 | use crate::format::SyslogContext; 28 | use crate::sender::internal::impl_syslog_sender_common; 29 | use crate::sender::internal::impl_syslog_stream_send_formatted; 30 | 31 | /// Create a TLS sender that sends messages to the well-known port (6514). 32 | /// 33 | /// See also [RFC-5425] §4.1 Port Assignment. 34 | /// 35 | /// [RFC-5425]: https://datatracker.ietf.org/doc/html/rfc5425#section-4.1 36 | pub fn native_tls_well_known>(domain: S) -> io::Result { 37 | let domain = domain.as_ref(); 38 | native_tls(format!("{domain}:6514"), domain) 39 | } 40 | 41 | /// Create a TLS sender that sends messages to the given address. 42 | pub fn native_tls>( 43 | addr: A, 44 | domain: S, 45 | ) -> io::Result { 46 | native_tls_with(addr, domain, TlsConnector::builder()) 47 | } 48 | 49 | /// Create a TLS sender that sends messages to the given address with certificate builder. 50 | pub fn native_tls_with>( 51 | addr: A, 52 | domain: S, 53 | builder: TlsConnectorBuilder, 54 | ) -> io::Result { 55 | NativeTlsSender::connect(addr, domain, builder) 56 | } 57 | 58 | /// A syslog sender that sends messages to a TCP socket over TLS. 59 | /// 60 | /// Users can obtain a `NativeTlsSender` by calling [`native_tls_well_known()`], [`native_tls()`], 61 | /// or [`native_tls_with()`]. 62 | #[derive(Debug)] 63 | pub struct NativeTlsSender { 64 | writer: BufWriter>, 65 | context: SyslogContext, 66 | postfix: Cow<'static, str>, 67 | } 68 | 69 | impl NativeTlsSender { 70 | /// Connect to a TCP socket over TLS at the given address. 71 | pub fn connect>( 72 | addr: A, 73 | domain: S, 74 | builder: TlsConnectorBuilder, 75 | ) -> io::Result { 76 | let domain = domain.as_ref(); 77 | let stream = TcpStream::connect(addr)?; 78 | let connector = builder.build().map_err(io::Error::other)?; 79 | let stream = connector 80 | .connect(domain, stream) 81 | .map_err(io::Error::other)?; 82 | Ok(Self { 83 | writer: BufWriter::new(stream), 84 | context: SyslogContext::default(), 85 | postfix: Cow::Borrowed("\r\n"), 86 | }) 87 | } 88 | 89 | /// Set the postfix when formatting Syslog message. 90 | /// 91 | /// This is generally '\r\n' as defined in [RFC-6587] §3.4.2. 92 | /// 93 | /// [RFC-6587]: https://datatracker.ietf.org/doc/html/rfc6587 94 | pub fn set_postfix(&mut self, postfix: impl Into>) { 95 | self.postfix = postfix.into(); 96 | } 97 | 98 | /// Set the context when formatting Syslog message. 99 | pub fn set_context(mut self, context: SyslogContext) { 100 | self.context = context; 101 | } 102 | 103 | /// Mutate the context when formatting Syslog message. 104 | pub fn mut_context(&mut self) -> &mut SyslogContext { 105 | &mut self.context 106 | } 107 | } 108 | 109 | impl_syslog_sender_common!(NativeTlsSender); 110 | impl_syslog_stream_send_formatted!(NativeTlsSender); 111 | -------------------------------------------------------------------------------- /src/sender/rustls_impl.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub extern crate rustls; 16 | 17 | use std::borrow::Cow; 18 | use std::io; 19 | use std::io::BufWriter; 20 | use std::net::TcpStream; 21 | use std::net::ToSocketAddrs; 22 | use std::sync::Arc; 23 | 24 | use rustls::pki_types::ServerName; 25 | use rustls::ClientConfig; 26 | use rustls::ClientConnection; 27 | use rustls::RootCertStore; 28 | use rustls::StreamOwned; 29 | 30 | use crate::format::SyslogContext; 31 | use crate::sender::internal::impl_syslog_sender_common; 32 | use crate::sender::internal::impl_syslog_stream_send_formatted; 33 | 34 | /// Create a TLS sender that sends messages to the well-known port (6514). 35 | /// 36 | /// See also [RFC-5425] §4.1 Port Assignment. 37 | /// 38 | /// [RFC-5425]: https://datatracker.ietf.org/doc/html/rfc5425#section-4.1 39 | pub fn rustls_well_known>(domain: S) -> io::Result { 40 | let domain = domain.into(); 41 | rustls(format!("{domain}:6514"), domain) 42 | } 43 | 44 | /// Create a TLS sender that sends messages to the given address. 45 | pub fn rustls>(addr: A, domain: S) -> io::Result { 46 | let mut roots = RootCertStore::empty(); 47 | for cert in rustls_native_certs::load_native_certs().certs { 48 | roots.add(cert).unwrap(); 49 | } 50 | 51 | let config = ClientConfig::builder() 52 | .with_root_certificates(roots) 53 | .with_no_client_auth(); 54 | 55 | rustls_with(addr, domain, Arc::new(config)) 56 | } 57 | 58 | /// Create a TLS sender that sends messages to the given address with certificate builder. 59 | pub fn rustls_with>( 60 | addr: A, 61 | domain: S, 62 | config: Arc, 63 | ) -> io::Result { 64 | RustlsSender::connect(addr, domain, config) 65 | } 66 | 67 | /// A syslog sender that sends messages to a TCP socket over TLS. 68 | /// 69 | /// Users can obtain a `RustlsSender` by calling [`rustls_well_known()`], [`rustls()`], 70 | /// or [`rustls_with()`]. 71 | #[derive(Debug)] 72 | pub struct RustlsSender { 73 | writer: BufWriter>, 74 | context: SyslogContext, 75 | postfix: Cow<'static, str>, 76 | } 77 | 78 | impl RustlsSender { 79 | /// Connect to a TCP socket over TLS at the given address. 80 | pub fn connect>( 81 | addr: A, 82 | domain: S, 83 | config: Arc, 84 | ) -> io::Result { 85 | let domain = domain.into(); 86 | let domain = ServerName::try_from(domain).map_err(io::Error::other)?; 87 | let stream = TcpStream::connect(addr)?; 88 | let conn = ClientConnection::new(config, domain).map_err(io::Error::other)?; 89 | let stream = StreamOwned::new(conn, stream); 90 | Ok(Self { 91 | writer: BufWriter::new(stream), 92 | context: SyslogContext::default(), 93 | postfix: Cow::Borrowed("\r\n"), 94 | }) 95 | } 96 | 97 | /// Set the postfix when formatting Syslog message. 98 | /// 99 | /// This is generally '\r\n' as defined in [RFC-6587] §3.4.2. 100 | /// 101 | /// [RFC-6587]: https://datatracker.ietf.org/doc/html/rfc6587 102 | pub fn set_postfix(&mut self, postfix: impl Into>) { 103 | self.postfix = postfix.into(); 104 | } 105 | 106 | /// Set the context when formatting Syslog message. 107 | pub fn set_context(mut self, context: SyslogContext) { 108 | self.context = context; 109 | } 110 | 111 | /// Mutate the context when formatting Syslog message. 112 | pub fn mut_context(&mut self) -> &mut SyslogContext { 113 | &mut self.context 114 | } 115 | } 116 | 117 | impl_syslog_sender_common!(RustlsSender); 118 | impl_syslog_stream_send_formatted!(RustlsSender); 119 | -------------------------------------------------------------------------------- /src/sender/tcp_impl.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::borrow::Cow; 16 | use std::io; 17 | use std::io::BufWriter; 18 | use std::net::TcpStream; 19 | use std::net::ToSocketAddrs; 20 | 21 | use crate::format::SyslogContext; 22 | use crate::sender::internal::impl_syslog_sender_common; 23 | use crate::sender::internal::impl_syslog_stream_send_formatted; 24 | 25 | /// Create a TCP sender that sends messages to the well-known port (601). 26 | /// 27 | /// See also [RFC-3195] §9.2 The System (Well-Known) TCP port number for syslog-conn. 28 | /// 29 | /// [RFC-3195]: https://datatracker.ietf.org/doc/html/rfc3195#section-9.2 30 | pub fn tcp_well_known() -> io::Result { 31 | tcp("127.0.0.1:601") 32 | } 33 | 34 | /// Create a TCP sender that sends messages to the given address. 35 | pub fn tcp(addr: A) -> io::Result { 36 | TcpSender::connect(addr) 37 | } 38 | 39 | /// A syslog sender that sends messages to a TCP socket. 40 | /// 41 | /// Users can obtain a `TcpSender` by calling [`tcp_well_known`] or [`tcp`]. 42 | #[derive(Debug)] 43 | pub struct TcpSender { 44 | writer: BufWriter, 45 | context: SyslogContext, 46 | postfix: Cow<'static, str>, 47 | } 48 | 49 | impl TcpSender { 50 | /// Connect to a TCP socket at the given address. 51 | pub fn connect(addr: A) -> io::Result { 52 | let stream = TcpStream::connect(addr)?; 53 | Ok(Self { 54 | writer: BufWriter::new(stream), 55 | context: SyslogContext::default(), 56 | postfix: Cow::Borrowed("\r\n"), 57 | }) 58 | } 59 | 60 | /// Set the postfix when formatting Syslog message. 61 | /// 62 | /// This is generally '\r\n' as defined in [RFC-6587] §3.4.2. 63 | /// 64 | /// [RFC-6587]: https://datatracker.ietf.org/doc/html/rfc6587 65 | pub fn set_postfix(&mut self, postfix: impl Into>) { 66 | self.postfix = postfix.into(); 67 | } 68 | 69 | /// Set the context when formatting Syslog message. 70 | pub fn set_context(mut self, context: SyslogContext) { 71 | self.context = context; 72 | } 73 | 74 | /// Mutate the context when formatting Syslog message. 75 | pub fn mut_context(&mut self) -> &mut SyslogContext { 76 | &mut self.context 77 | } 78 | } 79 | 80 | impl_syslog_sender_common!(TcpSender); 81 | impl_syslog_stream_send_formatted!(TcpSender); 82 | -------------------------------------------------------------------------------- /src/sender/udp_impl.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::io; 16 | use std::net::ToSocketAddrs; 17 | use std::net::UdpSocket; 18 | 19 | use crate::format::SyslogContext; 20 | use crate::sender::internal::impl_syslog_sender_common; 21 | 22 | /// Create a UDP sender that sends messages to the well-known port (514). 23 | /// 24 | /// See also [RFC-3164] §2 Transport Layer Protocol. 25 | /// 26 | /// [RFC-3164]: https://datatracker.ietf.org/doc/html/rfc3164#section-2 27 | pub fn udp_well_known() -> io::Result { 28 | udp("0.0.0.0:0", "127.0.0.1:514") 29 | } 30 | 31 | /// Create a UDP sender that sends messages to the given address. 32 | pub fn udp(local: L, remote: R) -> io::Result { 33 | UdpSender::connect(local, remote) 34 | } 35 | 36 | /// Create a UDP sender that broadcast messages to the well-known port (514). 37 | /// 38 | /// See also [RFC-3164] §2 Transport Layer Protocol. 39 | /// 40 | /// [RFC-3164]: https://datatracker.ietf.org/doc/html/rfc3164#section-2 41 | pub fn broadcast_well_known() -> io::Result { 42 | broadcast(514) 43 | } 44 | 45 | /// Create a UDP sender that broadcast messages to the given port. 46 | pub fn broadcast(port: u16) -> io::Result { 47 | let socket = UdpSocket::bind("0.0.0.0:0")?; 48 | socket.set_broadcast(true)?; 49 | socket.connect(format!("255.255.255.255:{port}"))?; 50 | Ok(UdpSender::new(socket)) 51 | } 52 | 53 | /// A syslog sender that sends messages to a UDP socket. 54 | /// 55 | /// Users can obtain a `UdpSender` by calling [`udp_well_known`], [`udp`], [`broadcast_well_known`], 56 | /// or [`broadcast`]. 57 | #[derive(Debug)] 58 | pub struct UdpSender { 59 | socket: UdpSocket, 60 | context: SyslogContext, 61 | } 62 | 63 | impl UdpSender { 64 | /// Connect to a UDP socket at the given address. 65 | pub fn connect(local: L, remote: R) -> io::Result { 66 | let socket = UdpSocket::bind(local)?; 67 | socket.connect(remote)?; 68 | Ok(Self::new(socket)) 69 | } 70 | 71 | /// Create a new UDP sender with the given socket. 72 | /// 73 | /// This is useful when users want to configure the socket in fine-grained. Note that the 74 | /// passed `socket` MUST be connected to the remote address. 75 | pub fn new(socket: UdpSocket) -> Self { 76 | Self { 77 | socket, 78 | context: SyslogContext::default(), 79 | } 80 | } 81 | 82 | /// Set the context when formatting Syslog message. 83 | pub fn set_context(&mut self, context: SyslogContext) { 84 | self.context = context; 85 | } 86 | 87 | /// Mutate the context when formatting Syslog message. 88 | pub fn mut_context(&mut self) -> &mut SyslogContext { 89 | &mut self.context 90 | } 91 | 92 | /// Send a pre-formatted message. 93 | pub fn send_formatted(&mut self, formatted: &[u8]) -> io::Result<()> { 94 | self.socket.send(formatted)?; 95 | Ok(()) 96 | } 97 | } 98 | 99 | impl_syslog_sender_common!(UdpSender); 100 | -------------------------------------------------------------------------------- /src/sender/unix_impl.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::borrow::Cow; 16 | use std::io; 17 | use std::io::BufWriter; 18 | use std::os::unix::net::UnixDatagram; 19 | use std::os::unix::net::UnixStream; 20 | use std::path::Path; 21 | 22 | use crate::format::SyslogContext; 23 | use crate::sender::internal::impl_syslog_sender_common; 24 | use crate::sender::internal::impl_syslog_stream_send_formatted; 25 | use crate::sender::SyslogSender; 26 | 27 | /// Create a Unix datagram sender that sends messages to the given path. 28 | pub fn unix_datagram(path: impl AsRef) -> io::Result { 29 | UnixDatagramSender::connect(path) 30 | } 31 | 32 | /// Create a Unix stream sender that sends messages to the given path. 33 | pub fn unix_stream(path: impl AsRef) -> io::Result { 34 | UnixStreamSender::connect(path) 35 | } 36 | 37 | /// Create a Unix sender that sends messages to the given path. 38 | /// 39 | /// Automatically chooses between Unix datagram and Unix stream based on the path. 40 | pub fn unix(path: impl AsRef) -> io::Result { 41 | const EPROTOTYPE: i32 = nix::errno::Errno::EPROTOTYPE as i32; 42 | let path = path.as_ref(); 43 | match unix_datagram(path) { 44 | Ok(sender) => Ok(SyslogSender::UnixDatagram(sender)), 45 | Err(err) => match err.raw_os_error() { 46 | Some(EPROTOTYPE) => unix_stream(path).map(SyslogSender::UnixStream), 47 | _ => Err(err), 48 | }, 49 | } 50 | } 51 | 52 | pub fn unix_well_known() -> io::Result { 53 | cfg_if::cfg_if! { 54 | if #[cfg(target_os = "macos")] { 55 | // NOTE: This may not work on Monterey (12.x) and above, 56 | // see also https://github.com/python/cpython/issues/91070. 57 | unix("/var/run/syslog") 58 | } else if #[cfg(target_os = "freebsd")] { 59 | unix("/var/run/log") 60 | } else { 61 | unix("/dev/log") 62 | } 63 | } 64 | } 65 | 66 | /// A syslog sender that sends messages to a Unix datagram socket. 67 | /// 68 | /// Typically, this sender is used to send messages to the local syslog daemon. Typical paths are 69 | /// `/dev/log`, `/var/run/syslog`, or `/var/run/log`. 70 | #[derive(Debug)] 71 | pub struct UnixDatagramSender { 72 | socket: UnixDatagram, 73 | context: SyslogContext, 74 | } 75 | 76 | impl UnixDatagramSender { 77 | /// Connect to a Unix datagram socket at the given path. 78 | pub fn connect(path: impl AsRef) -> io::Result { 79 | let socket = UnixDatagram::unbound()?; 80 | socket.connect(path)?; 81 | Ok(Self { 82 | socket, 83 | context: SyslogContext::default(), 84 | }) 85 | } 86 | 87 | /// Set the context when formatting Syslog message. 88 | pub fn set_context(&mut self, context: SyslogContext) { 89 | self.context = context; 90 | } 91 | 92 | /// Mutate the context when formatting Syslog message. 93 | pub fn mut_context(&mut self) -> &mut SyslogContext { 94 | &mut self.context 95 | } 96 | 97 | /// Send a pre-formatted message. 98 | pub fn send_formatted(&mut self, formatted: &[u8]) -> io::Result<()> { 99 | self.socket.send(formatted)?; 100 | Ok(()) 101 | } 102 | } 103 | 104 | impl_syslog_sender_common!(UnixDatagramSender); 105 | 106 | /// A syslog sender that sends messages to a Unix stream socket. 107 | /// 108 | /// Typically, this sender is used to send messages to the local syslog daemon. Typical paths are 109 | /// `/dev/log`, `/var/run/syslog`, or `/var/run/log`. 110 | #[derive(Debug)] 111 | pub struct UnixStreamSender { 112 | writer: BufWriter, 113 | context: SyslogContext, 114 | postfix: Cow<'static, str>, 115 | } 116 | 117 | impl UnixStreamSender { 118 | /// Connect to a Unix stream socket at the given path. 119 | pub fn connect(path: impl AsRef) -> io::Result { 120 | let socket = UnixStream::connect(path)?; 121 | Ok(Self { 122 | writer: BufWriter::new(socket), 123 | context: SyslogContext::default(), 124 | postfix: Cow::Borrowed("\r\n"), 125 | }) 126 | } 127 | 128 | /// Set the postfix when formatting Syslog message. 129 | /// 130 | /// Default is "\r\n". You can use empty string to set no postfix. 131 | pub fn set_postfix(&mut self, postfix: impl Into>) { 132 | self.postfix = postfix.into(); 133 | } 134 | 135 | /// Set the context when formatting Syslog message. 136 | pub fn set_context(&mut self, context: SyslogContext) { 137 | self.context = context; 138 | } 139 | 140 | /// Mutate the context when formatting Syslog message. 141 | pub fn mut_context(&mut self) -> &mut SyslogContext { 142 | &mut self.context 143 | } 144 | } 145 | 146 | impl_syslog_sender_common!(UnixStreamSender); 147 | impl_syslog_stream_send_formatted!(UnixStreamSender); 148 | -------------------------------------------------------------------------------- /src/severity.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::fmt; 16 | use std::str::FromStr; 17 | 18 | /// Syslog severity as defined in [RFC 5424] (The Syslog Protocol). 19 | /// 20 | /// [RFC 5424]: https://datatracker.ietf.org/doc/html/rfc5424. 21 | #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] 22 | #[repr(u8)] 23 | pub enum Severity { 24 | /// Emergency: system is unusable, numerical code 0. 25 | EMERGENCY = 0, 26 | /// Alert: action must be taken immediately, numerical code 1. 27 | ALERT = 1, 28 | /// Critical: critical conditions, numerical code 2. 29 | CRITICAL = 2, 30 | /// Error: error conditions, numerical code 3. 31 | ERROR = 3, 32 | /// Warning: warning conditions, numerical code 4. 33 | WARNING = 4, 34 | /// Notice: normal but significant condition, numerical code 5. 35 | NOTICE = 5, 36 | /// Informational: informational messages, numerical code 6. 37 | INFORMATIONAL = 6, 38 | /// Debug: debug-level messages, numerical code 7. 39 | DEBUG = 7, 40 | } 41 | 42 | impl Severity { 43 | /// Returns the numerical code of the severity. 44 | pub fn code(self) -> u8 { 45 | self as u8 46 | } 47 | 48 | /// Returns the label of the severity. 49 | pub fn label(self) -> &'static str { 50 | match self { 51 | Severity::EMERGENCY => "EMERGENCY", 52 | Severity::ALERT => "ALERT", 53 | Severity::CRITICAL => "CRITICAL", 54 | Severity::ERROR => "ERROR", 55 | Severity::WARNING => "WARNING", 56 | Severity::NOTICE => "NOTICE", 57 | Severity::INFORMATIONAL => "INFORMATIONAL", 58 | Severity::DEBUG => "DEBUG", 59 | } 60 | } 61 | } 62 | 63 | impl TryFrom for Severity { 64 | type Error = (); 65 | 66 | fn try_from(value: u8) -> Result { 67 | match value { 68 | 0 => Ok(Severity::EMERGENCY), 69 | 1 => Ok(Severity::ALERT), 70 | 2 => Ok(Severity::CRITICAL), 71 | 3 => Ok(Severity::ERROR), 72 | 4 => Ok(Severity::WARNING), 73 | 5 => Ok(Severity::NOTICE), 74 | 6 => Ok(Severity::INFORMATIONAL), 75 | 7 => Ok(Severity::DEBUG), 76 | _ => Err(()), 77 | } 78 | } 79 | } 80 | 81 | impl TryFrom<&str> for Severity { 82 | type Error = (); 83 | 84 | fn try_from(value: &str) -> Result { 85 | match &value.to_lowercase()[..] { 86 | "emergency" => Ok(Severity::EMERGENCY), 87 | "alert" => Ok(Severity::ALERT), 88 | "critical" => Ok(Severity::CRITICAL), 89 | "error" => Ok(Severity::ERROR), 90 | "warning" => Ok(Severity::WARNING), 91 | "notice" => Ok(Severity::NOTICE), 92 | "informational" => Ok(Severity::INFORMATIONAL), 93 | "debug" => Ok(Severity::DEBUG), 94 | _ => Err(()), 95 | } 96 | } 97 | } 98 | 99 | impl FromStr for Severity { 100 | type Err = (); 101 | 102 | fn from_str(s: &str) -> Result { 103 | Self::try_from(s) 104 | } 105 | } 106 | 107 | impl fmt::Display for Severity { 108 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 109 | write!(f, "{}", self.label()) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/structured_data.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 FastLabs Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Implementations of the structured data types (RFC-5424 §6.3). 16 | 17 | use std::fmt; 18 | 19 | /// A structured data parameter. 20 | /// 21 | /// Each SD-PARAM consists of a name, referred to as PARAM-NAME, and a value, referred to as 22 | /// PARAM-VALUE. 23 | #[derive(Clone, Debug, PartialEq, Eq)] 24 | #[non_exhaustive] // only allow construction via the `new` function 25 | pub struct SDParam { 26 | pub name: String, 27 | pub value: String, 28 | } 29 | 30 | impl SDParam { 31 | /// Create a new SD-PARAM. 32 | pub fn new(name: impl Into, value: impl Into) -> Result { 33 | let name = name.into(); 34 | Self::validate_name(&name)?; 35 | let value = value.into(); 36 | Ok(Self { name, value }) 37 | } 38 | 39 | /// Return the escaped PARAM-VALUE (RFC-5424 §6.3.3). 40 | pub fn escape_value(&self) -> String { 41 | let mut escaped = String::new(); 42 | for c in self.value.chars() { 43 | if matches!(c, '\\' | '"' | ']') { 44 | escaped.push('\\'); 45 | } 46 | escaped.push(c); 47 | } 48 | escaped 49 | } 50 | 51 | // SD-NAME = 1*32PRINTUSASCII 52 | // ; except '=', SP, ']', %d34 (") 53 | // SP = %d32 54 | // PRINTUSASCII = %d33-126 55 | fn validate_name(name: &str) -> Result<(), String> { 56 | if name.is_empty() { 57 | return Err("PARAM-NAME must not be empty".to_string()); 58 | } 59 | 60 | if name.len() > 32 { 61 | return Err(format!( 62 | "PARAM-NAME must not be less than 32 characters: {name}" 63 | )); 64 | } 65 | 66 | for c in name.chars() { 67 | match c { 68 | '=' => return Err(format!("PARAM-NAME must not contain '=': {name}")), 69 | ']' => return Err(format!("PARAM-NAME must not contain ']': {name}")), 70 | ' ' => return Err(format!("PARAM-NAME must not contain ' ': {name}")), 71 | '"' => return Err(format!("PARAM-NAME must not contain '\"': {name}")), 72 | c => { 73 | let codepoint = c as u32; 74 | if !(33..=126).contains(&codepoint) { 75 | return Err(format!( 76 | "PARAM-NAME must only contain printable ASCII characters: {name}" 77 | )); 78 | } 79 | } 80 | } 81 | } 82 | 83 | Ok(()) 84 | } 85 | } 86 | 87 | impl fmt::Display for SDParam { 88 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 89 | write!(f, "{}=\"{}\"", self.name, self.escape_value()) 90 | } 91 | } 92 | 93 | /// A structured data element. 94 | /// 95 | /// An SD-ELEMENT consists of a name and parameter name-value pairs. The name is referred to as 96 | /// SD-ID. The name-value pairs are referred to as "SD-PARAM". 97 | #[derive(Clone, Debug, PartialEq, Eq)] 98 | pub struct SDElement { 99 | pub id: String, 100 | params: Vec, 101 | } 102 | 103 | impl SDElement { 104 | /// Create a new SD-ELEMENT. 105 | pub fn new(id: impl Into) -> Result { 106 | let id = id.into(); 107 | Self::validate_id(&id)?; 108 | Ok(Self { id, params: vec![] }) 109 | } 110 | 111 | /// Add a new SD-PARAM to the SD-ELEMENT. 112 | pub fn add_param( 113 | &mut self, 114 | name: impl Into, 115 | value: impl Into, 116 | ) -> Result<(), String> { 117 | let param = SDParam::new(name, value)?; 118 | // TODO(tisonkun): check PARAM-NAME when SD-ID is registered? 119 | self.params.push(param); 120 | Ok(()) 121 | } 122 | 123 | // Registered SD-IDs as documented in RFC-5424 §9.2. 124 | const fn registered_ids() -> [&'static str; 3] { 125 | ["timeQuality", "origin", "meta"] 126 | } 127 | 128 | // SD-ID = SD-NAME 129 | // SD-NAME = 1*32PRINTUSASCII 130 | // ; except '=', SP, ']', %d34 (") 131 | // SP = %d32 132 | // PRINTUSASCII = %d33-126 133 | fn validate_id(id: &str) -> Result<(), String> { 134 | if id.is_empty() { 135 | return Err("SD-ID must not be empty".to_string()); 136 | } 137 | 138 | if id.len() > 32 { 139 | return Err(format!("SD-ID must not be less than 32 characters: {id}")); 140 | } 141 | 142 | let mut has_at_sign = false; 143 | for c in id.chars() { 144 | match c { 145 | '@' => has_at_sign = true, 146 | '=' => return Err(format!("SD-ID must not contain '=': {id}")), 147 | ']' => return Err(format!("SD-ID must not contain ']': {id}")), 148 | ' ' => return Err(format!("SD-ID must not contain ' ': {id}")), 149 | '"' => return Err(format!("SD-ID must not contain '\"': {id}")), 150 | c => { 151 | let codepoint = c as u32; 152 | if !(33..=126).contains(&codepoint) { 153 | return Err(format!( 154 | "SD-ID must only contain printable ASCII characters: {id}" 155 | )); 156 | } 157 | } 158 | } 159 | } 160 | 161 | if !has_at_sign && !Self::registered_ids().contains(&id) { 162 | return Err(format!( 163 | "SD-ID must contain '@' or be one of the registered IDs: {id}" 164 | )); 165 | } 166 | 167 | Ok(()) 168 | } 169 | } 170 | 171 | impl fmt::Display for SDElement { 172 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 173 | write!(f, "[{}", self.id)?; 174 | for param in &self.params { 175 | write!(f, " {param}")?; 176 | } 177 | write!(f, "]") 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /taplo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | include = ["Cargo.toml", "**/*.toml"] 16 | 17 | [formatting] 18 | # Align consecutive entries vertically. 19 | align_entries = false 20 | # Append trailing commas for multi-line arrays. 21 | array_trailing_comma = true 22 | # Expand arrays to multiple lines that exceed the maximum column width. 23 | array_auto_expand = true 24 | # Collapse arrays that don't exceed the maximum column width and don't contain comments. 25 | array_auto_collapse = true 26 | # Omit white space padding from single-line arrays 27 | compact_arrays = true 28 | # Omit white space padding from the start and end of inline tables. 29 | compact_inline_tables = false 30 | # Maximum column width in characters, affects array expansion and collapse, this doesn't take whitespace into account. 31 | # Note that this is not set in stone, and works on a best-effort basis. 32 | column_width = 80 33 | # Indent based on tables and arrays of tables and their subtables, subtables out of order are not indented. 34 | indent_tables = false 35 | # The substring that is used for indentation, should be tabs or spaces (but technically can be anything). 36 | indent_string = ' ' 37 | # Add trailing newline at the end of the file if not present. 38 | trailing_newline = true 39 | # Alphabetically reorder keys that are not separated by empty lines. 40 | reorder_keys = true 41 | # Maximum amount of allowed consecutive blank lines. This does not affect the whitespace at the end of the document, as it is always stripped. 42 | allowed_blank_lines = 1 43 | # Use CRLF for line endings. 44 | crlf = false 45 | -------------------------------------------------------------------------------- /typos.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 FastLabs Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [default.extend-words] 16 | 17 | [files] 18 | extend-exclude = [] 19 | --------------------------------------------------------------------------------