diff --git a/.github/workflows/after-pub.yml b/.github/workflows/after-pub.yml new file mode 100644 index 0000000..58f3c14 --- /dev/null +++ b/.github/workflows/after-pub.yml @@ -0,0 +1,51 @@ +name: Check deployed package + +on: + workflow_dispatch: + workflow_run: + workflows: ["Release-plz"] + types: + - completed + branches: + - main + +env: + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: 0 + +jobs: + run-after-pub: + name: Run LogDash after publish check + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v4 + - name: Install Rust Stable + uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Wait for crates.io to update + run: sleep 60 + - name: Create temp project and test + env: + LOGDASH_API_KEY: ${{ secrets.LOGDASH_API_KEY }} + run: | + cargo new temp-test + cd temp-test + cargo add logdash + echo 'fn main() { rs   18:39 + // Create a new Logdash client with default configuration + let (l, m) = logdash::create_logdash(logdash::Config::default()); + + // Send an info log message + l.info("Rust SDK example"); + + // Send a metric message + m.set("user".into(), 0.0); + + // Sleep for 10 seconds to allow the log message to be sent + // This is just for demonstration purposes + // In a real application, you would not want to sleep the thread like this + std::thread::sleep(std::time::Duration::from_secs(10)); + }' > src/main.rs + cargo run + cargo test diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..046e019 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,24 @@ +name: Security Audit + +on: + push: + branches: + - master + paths: + - "**/Cargo.toml" + schedule: + - cron: "0 2 * * *" # run at 2 AM UTC + +permissions: + contents: read + +jobs: + cargo-deny: + permissions: + checks: write + contents: read + issues: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@v2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..eaddd08 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,57 @@ +name: CI + +on: + push: + pull_request: + +env: + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: 0 + +permissions: + contents: read + +jobs: + build-all: + name: build all toolchain and OS + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: ["ubuntu-latest", "macos-latest", "windows-latest"] + steps: + - uses: actions/checkout@v4 + - name: "install Rust Stable" + uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: build + run: cargo build + - name: test + run: cargo test + + fmt: + name: fmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust Stable + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - uses: Swatinem/rust-cache@v2 + # Check fmt + - name: "cargo fmt" + run: cargo fmt --all -- --check + + clippy: + name: clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust Stable + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + # Run clippy + - name: "clippy --all" + run: cargo clippy --all --tests --no-deps diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml new file mode 100644 index 0000000..3920cb8 --- /dev/null +++ b/.github/workflows/release-plz.yml @@ -0,0 +1,58 @@ +name: Release-plz + +permissions: + pull-requests: write + contents: write + +on: + push: + branches: + - main + +jobs: + # Release unpublished packages. + release-plz-release: + name: Release-plz release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + # Create a PR with the new versions and changelog, preparing the next release. + release-plz-pr: + name: Release-plz PR + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + concurrency: + group: release-plz-${{ github.ref }} + cancel-in-progress: false + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release-pr + config: release-plz.toml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b2e2716 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1124 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "logdash" +version = "1.0.0" +dependencies = [ + "owo-colors", + "serde", + "serde_json", + "time", + "ureq", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "owo-colors" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e9af6113ecd57b8c63d3cd76a385b2e3881365f1f489e54f49801d0c83ea" +dependencies = [ + "base64", + "cookie_store", + "der", + "flate2", + "log", + "native-tls", + "percent-encoding", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "ureq-proto", + "utf-8", + "webpki-root-certs 0.26.11", + "webpki-roots 0.26.11", +] + +[[package]] +name = "ureq-proto" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "webpki-root-certs" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.0", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9fbcb21 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "logdash" +version = "1.0.0" +edition = "2024" +license = "MIT" +authors = ["firesz25 "] +readme = "README.md" +repository = "https://github.com/logdash-io/rust-sdk" +homepage = "https://github.com/logdash-io/rust-sdk" +description = "Logdash.io simple client" + +[dependencies] +owo-colors = { version = "4.2.1" } +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" +time = { version = "0.3.41", features = ["formatting", "serde"] } +ureq = { version = "3.0.11", default-features = false, features = [ + "json", + "gzip", +] } + +[features] +default = ["rustls"] +rustls = ["ureq/rustls"] +native-tls = ["ureq/native-tls"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..158c583 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# logdash - RUST SDK + +Logdash is a zero-config observability platform. This package serves an rust interface to use it. + +## Pre-requisites + +Setup your free project in less than 2 minutes at [logdash.io](https://logdash.io/) + +## Installation + +``` +cargo add logdash +``` + +## Logging + +get API KEY from env LOGDASH_API_KEY +```rust + +let (logger, _) = logdash::create_logdash(logdash::Config::default()); + +// Send an info log message +logger.info("Rust SDK example"); +``` + +get API KEY from config +```rust +let (l, _) = logdash::create_logdash(logdash::Config::default().api_key("Your Api Key".into())); + +// Send an info log message +l.info("Rust SDK example with API key"); +``` + + +## Metrics + +```rust +let (_, metrics) = logdash::create_logdash(logdash::Config::default()); + +// create a metric +metrics.set("user".into(), 0.0) +// update the metric +metrics.mutate("user".into(), 1.0) +``` + +get API KEY from config +```rust +let (_, m) = logdash::create_logdash(logdash::Config::default().api_key("Your Api Key".into())); + +// create a metric +m.set("user".into(), 0.0) +// update the metric +m.mutate("user".into(), 1.0) +``` + + +## View + +To see the logs or metrics, go to your project dashboard + +![logs](docs/logs.png) +![delta](docs/delta.png) + +## Configuration + +| Parameter | Required | Default | Description | +| --------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------ | +| `apiKey` | no | - | Api key used to authorize against logdash servers. If you don't provide one, logs will be logged into local console only | +| `host` | no | - | Custom API host, useful with self-hosted instances | +| `verbose` | no | - | Useful for debugging purposes | + +## License + +This project is licensed under the MIT License. + +## Contributing + +Contributions are welcome! Feel free to open issues or submit pull requests. + +## Support + +If you encounter any issues, please open an issue on GitHub or let us know at [contact@logdash.io](mailto:contact@logdash.io). diff --git a/docs/delta.png b/docs/delta.png new file mode 100644 index 0000000..b311d3d Binary files /dev/null and b/docs/delta.png differ diff --git a/docs/logs.png b/docs/logs.png new file mode 100644 index 0000000..62f6872 Binary files /dev/null and b/docs/logs.png differ diff --git a/examples/client_from_config.rs b/examples/client_from_config.rs new file mode 100644 index 0000000..a949252 --- /dev/null +++ b/examples/client_from_config.rs @@ -0,0 +1,19 @@ +fn main() { + // Create a new Logdash client with default configuration + let (l, m) = logdash::create_logdash( + logdash::Config::default() + .api_key("Your Api Key".into()) + .verbose(true), + ); + + // Send an info log message + l.info("Rust SDK example"); + + // Send a metric message + m.set("user".into(), 0.0); + + // Sleep for 10 seconds to allow the log message to be sent + // This is just for demonstration purposes + // In a real application, you would not want to sleep the thread like this + std::thread::sleep(std::time::Duration::from_secs(10)); +} diff --git a/examples/client_fron_env.rs b/examples/client_fron_env.rs new file mode 100644 index 0000000..ae28428 --- /dev/null +++ b/examples/client_fron_env.rs @@ -0,0 +1,15 @@ +fn main() { + // Create a new Logdash client with default configuration + let (l, m) = logdash::create_logdash(logdash::Config::default().verbose(true)); + + // Send an info log message + l.info("Rust SDK example"); + + // Send a metric message + m.set("user".into(), 0.0); + + // Sleep for 10 seconds to allow the log message to be sent + // This is just for demonstration purposes + // In a real application, you would not want to sleep the thread like this + std::thread::sleep(std::time::Duration::from_secs(10)); +} diff --git a/release-plz.toml b/release-plz.toml new file mode 100644 index 0000000..a133610 --- /dev/null +++ b/release-plz.toml @@ -0,0 +1,36 @@ +[changelog] +body = """ + +## [{{ version }}]\ + {%- if release_link -%}\ + ({{ release_link }})\ + {% endif %} \ + - {{ timestamp | date(format="%Y-%m-%d") }} +{% for group, commits in commits | group_by(attribute="group") %} +### {{ group | upper_first }} + + {% for commit in commits %} + {%- if commit.scope -%} + - *({{commit.scope}})* {% if commit.breaking %}[**breaking**] {% endif %}\ + {{ commit.message }}{{ self::username(commit=commit) }}\ + {%- if commit.links %} \ + ({% for link in commit.links %}[{{link.text}}]({{link.href}}) {% endfor -%})\ + {% endif %} + {% else -%} + - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message }}{{ self::username(commit=commit) }}{{ self::pr(commit=commit) }} + {% endif -%} + {% endfor -%} +{% endfor %} +{%- if remote.contributors %} +### Contributors +{% for contributor in remote.contributors %} + * @{{ contributor.username }} +{%- endfor %} +{% endif -%} +{%- macro username(commit) -%} + {% if commit.remote.username %} (by @{{ commit.remote.username }}){% endif -%} +{% endmacro -%} +{%- macro pr(commit) -%} + {% if commit.remote.pr_number %} - #{{ commit.remote.pr_number }}{% endif -%} +{% endmacro -%} +""" diff --git a/src/dispatch.rs b/src/dispatch.rs new file mode 100644 index 0000000..c418ffe --- /dev/null +++ b/src/dispatch.rs @@ -0,0 +1,37 @@ +use crate::{log::LogMessage, metric::MetricMessage, propagator::Propagator}; +use std::fmt::Debug; +use std::sync::{Arc, OnceLock}; + +pub struct Dispatch { + propagator: Arc, +} + +impl Debug for Dispatch { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Dispatch").finish() + } +} + +pub static DISPATCH: OnceLock = OnceLock::new(); + +pub fn init_dispatch(collector: Arc) { + DISPATCH + .set(Dispatch::new(collector)) + .expect("Dispatch already initialized"); +} + +impl Dispatch { + fn new(propagator: Arc) -> Self { + Self { propagator } + } + + #[inline] + pub fn dispatch_log(&self, msg: LogMessage) { + self.propagator.propagate_log(msg); + } + + #[inline] + pub fn dispatch_metric(&self, msg: MetricMessage) { + self.propagator.propagate_metric(msg); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c7d6905 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,20 @@ +mod dispatch; +mod log; +mod metric; +mod propagator; +pub use log::LogCollector; +pub use metric::MetricCollector; +use propagator::Propagator; +pub use propagator::PropagatorConfig as Config; +use std::sync::Arc; + +pub fn create_logdash(cfg: Config) -> (LogCollector, MetricCollector) { + if cfg.local() { + let propagator: Arc = Arc::new(propagator::terminal(cfg)); + dispatch::init_dispatch(propagator); + } else { + let propagator: Arc = Arc::new(propagator::http(cfg)); + dispatch::init_dispatch(propagator); + } + (LogCollector::new(), MetricCollector::new()) +} diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..cc25c5c --- /dev/null +++ b/src/log.rs @@ -0,0 +1,130 @@ +use crate::dispatch::DISPATCH; +use serde::Serialize; +use std::fmt; +use std::sync::atomic::{AtomicU64, Ordering}; +use time::OffsetDateTime; + +pub struct LogCollector { + sequence_number: AtomicU64, +} + +impl LogCollector { + pub(crate) fn new() -> Self { + Self { + sequence_number: AtomicU64::new(0), + } + } + #[inline(always)] + fn send(&self, msg: LogMessage) { + DISPATCH.get().unwrap().dispatch_log(msg); + } + + pub fn error(&self, message: impl IntoLog) { + self.send(LogMessage { + level: LogLevel::Error, + message: message.into_log(), + created_at: OffsetDateTime::now_utc(), + sequence_number: self.sequence_number.fetch_add(1, Ordering::SeqCst), + }); + } + + pub fn warn(&self, message: impl IntoLog) { + self.send(LogMessage { + level: LogLevel::Warn, + message: message.into_log(), + created_at: OffsetDateTime::now_utc(), + sequence_number: self.sequence_number.fetch_add(1, Ordering::SeqCst), + }); + } + + pub fn info(&self, message: impl IntoLog) { + self.send(LogMessage { + level: LogLevel::Info, + message: message.into_log(), + created_at: OffsetDateTime::now_utc(), + sequence_number: self.sequence_number.fetch_add(1, Ordering::SeqCst), + }); + } + + pub fn http(&self, message: impl IntoLog) { + self.send(LogMessage { + level: LogLevel::Http, + message: message.into_log(), + created_at: OffsetDateTime::now_utc(), + sequence_number: self.sequence_number.fetch_add(1, Ordering::SeqCst), + }); + } + + pub fn verbose(&self, message: impl IntoLog) { + self.send(LogMessage { + level: LogLevel::Verbose, + message: message.into_log(), + created_at: OffsetDateTime::now_utc(), + sequence_number: self.sequence_number.fetch_add(1, Ordering::SeqCst), + }); + } + + pub fn debug(&self, message: impl IntoLog) { + self.send(LogMessage { + level: LogLevel::Debug, + message: message.into_log(), + created_at: OffsetDateTime::now_utc(), + sequence_number: self.sequence_number.fetch_add(1, Ordering::SeqCst), + }); + } + + pub fn silly(&self, message: impl IntoLog) { + self.send(LogMessage { + level: LogLevel::Silly, + message: message.into_log(), + created_at: OffsetDateTime::now_utc(), + sequence_number: self.sequence_number.fetch_add(1, Ordering::SeqCst), + }); + } +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct LogMessage { + pub message: String, + pub level: LogLevel, + #[serde(with = "time::serde::rfc3339")] + pub created_at: OffsetDateTime, + pub sequence_number: u64, +} + +#[derive(Debug, Clone, Copy, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum LogLevel { + Error, + Warn, + Info, + Http, + Verbose, + Debug, + Silly, +} + +impl fmt::Display for LogLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LogLevel::Error => write!(f, "error"), + LogLevel::Warn => write!(f, "warn"), + LogLevel::Info => write!(f, "info"), + LogLevel::Http => write!(f, "http"), + LogLevel::Verbose => write!(f, "verbose"), + LogLevel::Debug => write!(f, "debug"), + LogLevel::Silly => write!(f, "silly"), + } + } +} + +pub trait IntoLog { + fn into_log(self) -> String; +} + +impl IntoLog for T { + fn into_log(self) -> String { + self.to_string() + } +} diff --git a/src/metric.rs b/src/metric.rs new file mode 100644 index 0000000..f8e4218 --- /dev/null +++ b/src/metric.rs @@ -0,0 +1,54 @@ +use crate::dispatch::DISPATCH; +use serde::Serialize; +pub struct MetricCollector(()); + +impl MetricCollector { + pub(crate) fn new() -> Self { + Self(()) + } + + #[inline(always)] + fn send(&self, msg: MetricMessage) { + DISPATCH.get().unwrap().dispatch_metric(msg); + } + + pub fn set(&self, name: String, value: f64) { + self.send(MetricMessage { + name, + value, + operation: MetricOperation::Set, + }); + } + + pub fn mutate(&self, name: String, value: f64) { + self.send(MetricMessage { + name, + value, + operation: MetricOperation::Change, + }); + } +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MetricMessage { + pub name: String, + pub value: f64, + pub operation: MetricOperation, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum MetricOperation { + Set, + Change, +} + +impl std::fmt::Display for MetricOperation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MetricOperation::Set => write!(f, "set"), + MetricOperation::Change => write!(f, "change"), + } + } +} diff --git a/src/propagator/http.rs b/src/propagator/http.rs new file mode 100644 index 0000000..9e112f7 --- /dev/null +++ b/src/propagator/http.rs @@ -0,0 +1,58 @@ +use crate::metric::MetricOperation; + +use super::{ + MessageType, PropagatorConfig, + worker::{Worker, WorkerJob}, +}; +use std::sync::mpsc::Receiver; +pub struct HttpPropagator { + _priv: (), +} + +pub fn http(cfg: PropagatorConfig) -> Worker { + Worker::::new(cfg) +} + +impl WorkerJob for HttpPropagator { + fn job(cfg: PropagatorConfig, rx: Receiver) { + let log_url = format!("{}/logs", cfg.api_url); + let metric_url = format!("{}/metrics", cfg.api_url); + let api_key = cfg.api_key.unwrap(); + loop { + match rx.recv() { + Ok(msg) => match msg { + MessageType::Log(log) => { + ureq::post(&log_url) + .header("project-api-key", &api_key) + .send_json(&log) + .unwrap(); + if cfg.verbose { + println!("Log send: {} {}", log.level, log.message); + } + } + MessageType::Metric(metric) => { + ureq::put(&metric_url) + .header("project-api-key", &api_key) + .send_json(&metric) + .unwrap(); + if cfg.verbose { + match metric.operation.clone() { + MetricOperation::Set => { + println!("Metric set: {} = {}", metric.name, metric.value); + } + + MetricOperation::Change => { + println!("Metric changed: {} = {}", metric.name, metric.value); + } + } + } + } + }, + Err(e) => { + eprintln!("Error receiving message: {}", e); + break; + } + } + } + } +} diff --git a/src/propagator/mod.rs b/src/propagator/mod.rs new file mode 100644 index 0000000..59ded09 --- /dev/null +++ b/src/propagator/mod.rs @@ -0,0 +1,64 @@ +pub mod http; +pub mod terminal; +mod worker; +use std::sync::Arc; + +use crate::{log::LogMessage, metric::MetricMessage}; +pub use http::http; +pub use terminal::terminal; +#[derive(Debug, Clone)] +pub struct PropagatorConfig { + pub(crate) api_key: Option, + api_url: String, + verbose: bool, +} + +impl Default for PropagatorConfig { + fn default() -> Self { + Self { + api_key: std::env::var("LOGDASH_API_KEY").ok(), + api_url: "https://api.logdash.io".to_string(), + verbose: false, + } + } +} + +impl PropagatorConfig { + pub fn api_key(mut self, api_key: String) -> Self { + self.api_key = Some(api_key); + self + } + pub fn api_url(mut self, api_url: String) -> Self { + self.api_url = api_url; + self + } + + pub fn verbose(mut self, verbose: bool) -> Self { + self.verbose = verbose; + self + } + #[inline] + pub(crate) fn local(&self) -> bool { + self.api_key.is_none() + } +} + +pub enum MessageType { + Log(LogMessage), + Metric(MetricMessage), +} + +pub trait Propagator: Send + Sync + 'static { + fn propagate_log(&self, msg: LogMessage); + fn propagate_metric(&self, msg: MetricMessage); +} + +impl Propagator for Arc { + fn propagate_log(&self, msg: LogMessage) { + self.as_ref().propagate_log(msg); + } + + fn propagate_metric(&self, msg: MetricMessage) { + self.as_ref().propagate_metric(msg); + } +} diff --git a/src/propagator/terminal.rs b/src/propagator/terminal.rs new file mode 100644 index 0000000..fc16a77 --- /dev/null +++ b/src/propagator/terminal.rs @@ -0,0 +1,93 @@ +use super::{ + MessageType, PropagatorConfig, + worker::{Worker, WorkerJob}, +}; +use owo_colors::OwoColorize; +use std::sync::mpsc::Receiver; + +pub struct TerminalPropagator { + _priv: (), +} + +pub fn terminal(cfg: PropagatorConfig) -> Worker { + Worker::::new(cfg) +} + +impl WorkerJob for TerminalPropagator { + fn job(_cfg: PropagatorConfig, rx: Receiver) { + loop { + match rx.recv() { + Ok(msg) => match msg { + MessageType::Log(log) => match log.level { + crate::log::LogLevel::Error => { + println!( + "{} [{}] {}", + "Error:".truecolor(231, 0, 11), + log.created_at, + log.message + ) + } + crate::log::LogLevel::Warn => { + println!( + "{} [{}] {}", + "Warn:".truecolor(254, 154, 0), + log.created_at, + log.message + ) + } + crate::log::LogLevel::Info => { + println!( + "{} [{}] {}", + "Info:".truecolor(21, 93, 252), + log.created_at, + log.message + ) + } + crate::log::LogLevel::Http => { + println!( + "{} [{}] {}", + "Http:".truecolor(0, 166, 166), + log.created_at, + log.message + ) + } + crate::log::LogLevel::Verbose => { + println!( + "{} [{}] {}", + "Verbose:".truecolor(0, 166, 0), + log.created_at, + log.message + ) + } + crate::log::LogLevel::Debug => { + println!( + "{} [{}] {}", + "Debug:".truecolor(0, 166, 62), + log.created_at, + log.message + ) + } + crate::log::LogLevel::Silly => { + println!( + "{} [{}] {}", + "Silly:".truecolor(80, 80, 80), + log.created_at, + log.message + ) + } + }, + MessageType::Metric(metric) => { + println!( + "Metric: {} = {} {}", + metric.name, metric.value, metric.operation + ); + } + }, + Err(e) => { + eprintln!("Error receiving message: {}", e); + break; + } + } + } + } +} diff --git a/src/propagator/worker.rs b/src/propagator/worker.rs new file mode 100644 index 0000000..63a9293 --- /dev/null +++ b/src/propagator/worker.rs @@ -0,0 +1,37 @@ +use super::{MessageType, Propagator, PropagatorConfig}; +use std::{ + marker::PhantomData, + sync::mpsc::{Receiver, SyncSender, sync_channel}, +}; +pub trait WorkerJob { + fn job(cfg: PropagatorConfig, rx: Receiver); +} + +pub struct Worker { + sender: SyncSender, + _propagator: PhantomData

, +} + +impl Worker { + pub fn new(config: PropagatorConfig) -> Self { + let (tx, rx) = sync_channel(30); + std::thread::Builder::new() + .name("logdash-worker".into()) + .spawn(move || T::job(config, rx)) + .unwrap(); + Self { + sender: tx, + _propagator: PhantomData, + } + } +} + +impl Propagator for Worker { + fn propagate_log(&self, msg: crate::log::LogMessage) { + self.sender.send(MessageType::Log(msg)).unwrap(); + } + + fn propagate_metric(&self, msg: crate::metric::MetricMessage) { + self.sender.send(MessageType::Metric(msg)).unwrap(); + } +}