tracing-tree-0.4.0/.cargo_vcs_info.json0000644000000001360000000000100134210ustar { "git": { "sha1": "e6f1a9090b1c2ec3c37c67100ae7fac240e1cfab" }, "path_in_vcs": "" }tracing-tree-0.4.0/.github/workflows/ci.yml000064400000000000000000000026201046102023000167240ustar 00000000000000on: [push, pull_request] name: Continuous integration jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: check test: name: Test Suite runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: test fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add clippy - uses: actions-rs/cargo@v1 with: command: clippy args: -- -D warnings tracing-tree-0.4.0/.github/workflows/release.yml000064400000000000000000000047071046102023000177610ustar 00000000000000name: Release new version on: workflow_dispatch: secrets: CARGO_REGISTRY_TOKEN: required: true env: RUST_BACKTRACE: 1 CARGO_TERM_COLOR: always jobs: create-release: name: Create release runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - name: Checkout uses: actions/checkout@v3 with: persist-credentials: true - name: Install rust uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal override: true - uses: Swatinem/rust-cache@v2 # Determine which version we're about to publish, so we can tag it appropriately. # If the tag already exists, then we've already published this version. - name: Determine current version id: version-check run: | # Fail on first error, on undefined variables, and on errors in pipes. set -euo pipefail export VERSION="$(cargo metadata --format-version 1 | \ jq --arg crate_name tracing-tree --exit-status -r \ '.packages[] | select(.name == $crate_name) | .version')" echo "version=$VERSION" >> $GITHUB_OUTPUT if [[ "$(git tag -l "$VERSION")" != '' ]]; then echo "Aborting: Version $VERSION is already published, we found its tag in the repo." exit 1 fi # TODO: Replace this with the cargo-semver-checks v2 GitHub Action when it's stabilized: # https://github.com/obi1kenobi/cargo-semver-checks-action/pull/21 - name: Semver-check run: | # Fail on first error, on undefined variables, and on errors in pipes. set -euo pipefail cargo install --locked cargo-semver-checks cargo semver-checks check-release - name: Publish run: cargo publish env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - name: Tag the version run: | # Fail on first error, on undefined variables, and on errors in pipes. set -euo pipefail git tag "${{ steps.version-check.outputs.version }}" git push origin "${{ steps.version-check.outputs.version }}" - uses: taiki-e/create-gh-release-action@v1 name: Create GitHub release with: branch: main ref: refs/tags/${{ steps.version-check.outputs.version }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} tracing-tree-0.4.0/.gitignore000064400000000000000000000000221046102023000141730ustar 00000000000000target Cargo.lock tracing-tree-0.4.0/Cargo.lock0000644000000541020000000000100113760ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bstr" version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", "regex-automata", "serde", ] [[package]] name = "camino" version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" dependencies = [ "serde", ] [[package]] name = "cargo-platform" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" dependencies = [ "serde", ] [[package]] name = "cargo_metadata" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", "thiserror", ] [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "color-eyre" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" dependencies = [ "backtrace", "color-spantrace", "eyre", "indenter", "once_cell", "owo-colors", "tracing-error", ] [[package]] name = "color-spantrace" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" dependencies = [ "once_cell", "owo-colors", "tracing-core", "tracing-error", ] [[package]] name = "colored" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ "is-terminal", "lazy_static", "windows-sys", ] [[package]] name = "crossbeam-channel" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "deranged" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "errno" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", "windows-sys", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "eyre" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" dependencies = [ "indenter", "once_cell", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn 2.0.37", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "gimli" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "hermit-abi" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", "windows-sys", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "linux-raw-sys" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "miniz_oxide" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "nu-ansi-term" version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14" dependencies = [ "windows-sys", ] [[package]] name = "num_threads" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] [[package]] name = "object" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "owo-colors" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "rustfix" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481" dependencies = [ "anyhow", "log", "serde", "serde_json", ] [[package]] name = "rustix" version = "0.38.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" dependencies = [ "bitflags 2.4.0", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "semver" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" dependencies = [ "serde", ] [[package]] name = "serde" version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", "syn 2.0.37", ] [[package]] name = "serde_json" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "sharded-slab" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ "lazy_static", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", "windows-sys", ] [[package]] name = "thiserror" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", "syn 2.0.37", ] [[package]] name = "thread_local" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "time" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ "deranged", "itoa", "libc", "num_threads", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] [[package]] name = "tracing" version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "tracing-core" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-error" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ "tracing", "tracing-subscriber", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "sharded-slab", "thread_local", "tracing-core", ] [[package]] name = "tracing-tree" version = "0.4.0" dependencies = [ "futures", "log", "nu-ansi-term", "time", "tracing", "tracing-core", "tracing-log", "tracing-subscriber", "ui_test", ] [[package]] name = "ui_test" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b5b4baac6b9b74d53ea224bc6dd7ed7276a7fb6785145ba7cfa96c01b63f4cb" dependencies = [ "bstr", "cargo-platform", "cargo_metadata", "color-eyre", "colored", "crossbeam-channel", "diff", "lazy_static", "regex", "rustc_version", "rustfix", "serde", "serde_json", "tempfile", ] [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" tracing-tree-0.4.0/Cargo.toml0000644000000031230000000000100114160ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.70" name = "tracing-tree" version = "0.4.0" authors = [ "David Barsky ", "Nathan Whitaker", "Oli Scherer ", ] description = "A Tracing Layer which prints a tree of spans and events." readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/davidbarsky/tracing-tree" resolver = "2" [[test]] name = "ui" harness = false [dependencies.nu-ansi-term] version = "0.50.0" [dependencies.time] version = "0.3.20" features = [ "formatting", "local-offset", ] optional = true [dependencies.tracing-core] version = "0.1" [dependencies.tracing-log] version = "0.2" features = [ "std", "log-tracer", ] optional = true default-features = false [dependencies.tracing-subscriber] version = "0.3" features = [ "registry", "fmt", "std", ] default-features = false [dev-dependencies.futures] version = "0.3" [dev-dependencies.log] version = "0.4" [dev-dependencies.tracing] version = "0.1" [dev-dependencies.ui_test] version = "0.7" [features] default = ["tracing-log"] tracing-log = ["dep:tracing-log"] tracing-tree-0.4.0/Cargo.toml.orig000064400000000000000000000017361046102023000151070ustar 00000000000000[package] name = "tracing-tree" version = "0.4.0" authors = ["David Barsky ", "Nathan Whitaker", "Oli Scherer "] edition = "2018" license = "MIT OR Apache-2.0" description = "A Tracing Layer which prints a tree of spans and events." repository = "https://github.com/davidbarsky/tracing-tree" readme = "README.md" rust-version = "1.70" resolver = "2" [dependencies] tracing-core = "0.1" tracing-subscriber = { version = "0.3", default-features = false, features = [ "registry", "fmt", "std", ] } nu-ansi-term = "0.50.0" tracing-log = { version = "0.2", optional = true, default-features = false, features = [ "std", "log-tracer", ] } time = { version = "0.3.20", optional = true, features = [ "formatting", "local-offset", ] } [features] default = ["tracing-log"] tracing-log = ["dep:tracing-log"] [dev-dependencies] tracing = "0.1" ui_test = "0.7" futures = "0.3" log = "0.4" [[test]] name = "ui" harness = false tracing-tree-0.4.0/LICENSE-APACHE000064400000000000000000000261331046102023000141420ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2021 tracing-tree contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. tracing-tree-0.4.0/LICENSE-MIT000064400000000000000000000020721046102023000136460ustar 00000000000000MIT License Copyright (c) 2021 tracing-tree contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tracing-tree-0.4.0/README.md000064400000000000000000000012461046102023000134730ustar 00000000000000# tracing-tree Instrument your application with [tracing](https://github.com/tokio-rs/tracing) and get tree-structured summaries of your application activity with timing information on the console: https://github.com/davidbarsky/tracing-tree/blob/483cc0a118c3170f4246d6fa4a9f018a00d8f0a9/examples/quiet.stdout#L1-L28 (Format inspired by [slog-term](https://github.com/slog-rs/slog#terminal-output-example)) ## Setup After instrumenting your app with [tracing](https://github.com/tokio-rs/tracing), add this subscriber like this: ```rust let subscriber = Registry::default().with(HierarchicalLayer::new(2)); tracing::subscriber::set_global_default(subscriber).unwrap(); ```tracing-tree-0.4.0/examples/basic.rs000064400000000000000000000051451046102023000154630ustar 00000000000000use tracing::{debug, error, info, instrument, span, warn, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) .with_verbose_exit(true) .with_verbose_entry(true) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); #[cfg(feature = "tracing-log")] tracing_log::LogTracer::init().unwrap(); let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::TRACE, "server", host = "localhost", port = 8080); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(3000)); info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { debug!("connected"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 2, "message received"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { std::thread::sleep(std::time::Duration::from_millis(300)); debug!("connected"); }); drop(peer2); let peer3 = span!( Level::TRACE, "foomp", normal_var = 43, "{} <- format string", 42 ); peer3.in_scope(|| { error!("hello"); }); drop(peer3); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { warn!(algo = "xor", "weak encryption requested"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { debug!(length = 5, "message received"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer2); warn!("internal error"); log::error!("this is a log message"); info!("exit"); } #[instrument] fn call_a(name: &str) { info!(name, "got a name"); call_b(name) } #[instrument] fn call_b(name: &str) { info!(name, "got a name"); } tracing-tree-0.4.0/examples/basic.stdout000064400000000000000000000041431046102023000163560ustar 000000000000001:main┐basic::hierarchical-example version=0.1 1:main└┐basic::hierarchical-example version=0.1 1:main └┐basic::server host="localhost", port=8080 1:main ├─ INFO basic starting 1:main ├─ INFO basic listening 1:main └┐basic::server host="localhost", port=8080 1:main └┐basic::conn peer_addr="82.9.9.9", port=42381 1:main ├─ DEBUG basic connected 1:main ├─ DEBUG basic message received, length=2 1:main ┌┘basic::conn peer_addr="82.9.9.9", port=42381 1:main ┌┘basic::server host="localhost", port=8080 1:main └┐basic::server host="localhost", port=8080 1:main └┐basic::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG basic connected 1:main ┌┘basic::conn peer_addr="8.8.8.8", port=18230 1:main ┌┘basic::server host="localhost", port=8080 1:main └┐basic::server host="localhost", port=8080 1:main └┐basic::foomp 42 <- format string, normal_var=43 1:main ├─ ERROR basic hello 1:main ┌┘basic::foomp 42 <- format string, normal_var=43 1:main ┌┘basic::server host="localhost", port=8080 1:main └┐basic::server host="localhost", port=8080 1:main └┐basic::conn peer_addr="82.9.9.9", port=42381 1:main ├─ WARN basic weak encryption requested, algo="xor" 1:main ├─ DEBUG basic response sent, length=8 1:main ├─ DEBUG basic disconnected 1:main ┌┘basic::conn peer_addr="82.9.9.9", port=42381 1:main ┌┘basic::server host="localhost", port=8080 1:main └┐basic::server host="localhost", port=8080 1:main └┐basic::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG basic message received, length=5 1:main ├─ DEBUG basic response sent, length=8 1:main ├─ DEBUG basic disconnected 1:main ┌┘basic::conn peer_addr="8.8.8.8", port=18230 1:main ┌┘basic::server host="localhost", port=8080 1:main ├─ WARN basic internal error 1:main ├─ ERROR basic this is a log message 1:main ├─ INFO basic exit 1:main ┌┘basic::server host="localhost", port=8080 1:main┌┘basic::hierarchical-example version=0.1 1:main┘basic::hierarchical-example version=0.1 tracing-tree-0.4.0/examples/basic_non_verbose.rs000064400000000000000000000050421046102023000200560ustar 00000000000000use tracing::{debug, error, info, instrument, span, warn, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); #[cfg(feature = "tracing-log")] tracing_log::LogTracer::init().unwrap(); let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::TRACE, "server", host = "localhost", port = 8080); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(3000)); info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { debug!("connected"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 2, "message received"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { std::thread::sleep(std::time::Duration::from_millis(300)); debug!("connected"); }); drop(peer2); let peer3 = span!( Level::TRACE, "foomp", normal_var = 43, "{} <- format string", 42 ); peer3.in_scope(|| { error!("hello"); }); drop(peer3); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { warn!(algo = "xor", "weak encryption requested"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { debug!(length = 5, "message received"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer2); warn!("internal error"); log::error!("this is a log message"); info!("exit"); } #[instrument] fn call_a(name: &str) { info!(name, "got a name"); call_b(name) } #[instrument] fn call_b(name: &str) { info!(name, "got a name"); } tracing-tree-0.4.0/examples/basic_non_verbose.stdout000064400000000000000000000026761046102023000207660ustar 000000000000001:main┐basic_non_verbose::hierarchical-example version=0.1 1:main└─┐basic_non_verbose::server host="localhost", port=8080 1:main ├─ INFO basic_non_verbose starting 1:main ├─ INFO basic_non_verbose listening 1:main └─┐basic_non_verbose::conn peer_addr="82.9.9.9", port=42381 1:main ├─ DEBUG basic_non_verbose connected 1:main ├─ DEBUG basic_non_verbose message received, length=2 1:main ┌─┘ 1:main └─┐basic_non_verbose::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG basic_non_verbose connected 1:main ┌─┘ 1:main └─┐basic_non_verbose::foomp 42 <- format string, normal_var=43 1:main ├─ ERROR basic_non_verbose hello 1:main ┌─┘ 1:main └─┐basic_non_verbose::conn peer_addr="82.9.9.9", port=42381 1:main ├─ WARN basic_non_verbose weak encryption requested, algo="xor" 1:main ├─ DEBUG basic_non_verbose response sent, length=8 1:main ├─ DEBUG basic_non_verbose disconnected 1:main ┌─┘ 1:main └─┐basic_non_verbose::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG basic_non_verbose message received, length=5 1:main ├─ DEBUG basic_non_verbose response sent, length=8 1:main ├─ DEBUG basic_non_verbose disconnected 1:main ┌─┘ 1:main ├─ WARN basic_non_verbose internal error 1:main ├─ ERROR basic_non_verbose this is a log message 1:main ├─ INFO basic_non_verbose exit 1:main┌─┘ 1:main┘ tracing-tree-0.4.0/examples/basic_verbose_entry.rs000064400000000000000000000051461046102023000204320ustar 00000000000000use tracing::{debug, error, info, instrument, span, warn, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) .with_verbose_exit(false) .with_verbose_entry(true) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); #[cfg(feature = "tracing-log")] tracing_log::LogTracer::init().unwrap(); let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::TRACE, "server", host = "localhost", port = 8080); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(3000)); info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { debug!("connected"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 2, "message received"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { std::thread::sleep(std::time::Duration::from_millis(300)); debug!("connected"); }); drop(peer2); let peer3 = span!( Level::TRACE, "foomp", normal_var = 43, "{} <- format string", 42 ); peer3.in_scope(|| { error!("hello"); }); drop(peer3); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { warn!(algo = "xor", "weak encryption requested"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { debug!(length = 5, "message received"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer2); warn!("internal error"); log::error!("this is a log message"); info!("exit"); } #[instrument] fn call_a(name: &str) { info!(name, "got a name"); call_b(name) } #[instrument] fn call_b(name: &str) { info!(name, "got a name"); } tracing-tree-0.4.0/examples/basic_verbose_entry.stdout000064400000000000000000000035761046102023000213350ustar 000000000000001:main┐basic_verbose_entry::hierarchical-example version=0.1 1:main└┐basic_verbose_entry::hierarchical-example version=0.1 1:main └┐basic_verbose_entry::server host="localhost", port=8080 1:main ├─ INFO basic_verbose_entry starting 1:main ├─ INFO basic_verbose_entry listening 1:main └┐basic_verbose_entry::server host="localhost", port=8080 1:main └┐basic_verbose_entry::conn peer_addr="82.9.9.9", port=42381 1:main ├─ DEBUG basic_verbose_entry connected 1:main ├─ DEBUG basic_verbose_entry message received, length=2 1:main ┌─┘ 1:main └┐basic_verbose_entry::server host="localhost", port=8080 1:main └┐basic_verbose_entry::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG basic_verbose_entry connected 1:main ┌─┘ 1:main └┐basic_verbose_entry::server host="localhost", port=8080 1:main └┐basic_verbose_entry::foomp 42 <- format string, normal_var=43 1:main ├─ ERROR basic_verbose_entry hello 1:main ┌─┘ 1:main └┐basic_verbose_entry::server host="localhost", port=8080 1:main └┐basic_verbose_entry::conn peer_addr="82.9.9.9", port=42381 1:main ├─ WARN basic_verbose_entry weak encryption requested, algo="xor" 1:main ├─ DEBUG basic_verbose_entry response sent, length=8 1:main ├─ DEBUG basic_verbose_entry disconnected 1:main ┌─┘ 1:main └┐basic_verbose_entry::server host="localhost", port=8080 1:main └┐basic_verbose_entry::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG basic_verbose_entry message received, length=5 1:main ├─ DEBUG basic_verbose_entry response sent, length=8 1:main ├─ DEBUG basic_verbose_entry disconnected 1:main ┌─┘ 1:main ├─ WARN basic_verbose_entry internal error 1:main ├─ ERROR basic_verbose_entry this is a log message 1:main ├─ INFO basic_verbose_entry exit 1:main┌─┘ 1:main┘ tracing-tree-0.4.0/examples/basic_verbose_exit.rs000064400000000000000000000051461046102023000202420ustar 00000000000000use tracing::{debug, error, info, instrument, span, warn, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) .with_verbose_exit(true) .with_verbose_entry(false) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); #[cfg(feature = "tracing-log")] tracing_log::LogTracer::init().unwrap(); let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::TRACE, "server", host = "localhost", port = 8080); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(3000)); info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { debug!("connected"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 2, "message received"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { std::thread::sleep(std::time::Duration::from_millis(300)); debug!("connected"); }); drop(peer2); let peer3 = span!( Level::TRACE, "foomp", normal_var = 43, "{} <- format string", 42 ); peer3.in_scope(|| { error!("hello"); }); drop(peer3); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { warn!(algo = "xor", "weak encryption requested"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { debug!(length = 5, "message received"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer2); warn!("internal error"); log::error!("this is a log message"); info!("exit"); } #[instrument] fn call_a(name: &str) { info!(name, "got a name"); call_b(name) } #[instrument] fn call_b(name: &str) { info!(name, "got a name"); } tracing-tree-0.4.0/examples/basic_verbose_exit.stdout000064400000000000000000000043521046102023000211360ustar 000000000000001:main┐basic_verbose_exit::hierarchical-example version=0.1 1:main└─┐basic_verbose_exit::server host="localhost", port=8080 1:main ├─ INFO basic_verbose_exit starting 1:main ├─ INFO basic_verbose_exit listening 1:main └─┐basic_verbose_exit::conn peer_addr="82.9.9.9", port=42381 1:main ├─ DEBUG basic_verbose_exit connected 1:main ├─ DEBUG basic_verbose_exit message received, length=2 1:main ┌┘basic_verbose_exit::conn peer_addr="82.9.9.9", port=42381 1:main ┌┘basic_verbose_exit::server host="localhost", port=8080 1:main └─┐basic_verbose_exit::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG basic_verbose_exit connected 1:main ┌┘basic_verbose_exit::conn peer_addr="8.8.8.8", port=18230 1:main ┌┘basic_verbose_exit::server host="localhost", port=8080 1:main └─┐basic_verbose_exit::foomp 42 <- format string, normal_var=43 1:main ├─ ERROR basic_verbose_exit hello 1:main ┌┘basic_verbose_exit::foomp 42 <- format string, normal_var=43 1:main ┌┘basic_verbose_exit::server host="localhost", port=8080 1:main └─┐basic_verbose_exit::conn peer_addr="82.9.9.9", port=42381 1:main ├─ WARN basic_verbose_exit weak encryption requested, algo="xor" 1:main ├─ DEBUG basic_verbose_exit response sent, length=8 1:main ├─ DEBUG basic_verbose_exit disconnected 1:main ┌┘basic_verbose_exit::conn peer_addr="82.9.9.9", port=42381 1:main ┌┘basic_verbose_exit::server host="localhost", port=8080 1:main └─┐basic_verbose_exit::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG basic_verbose_exit message received, length=5 1:main ├─ DEBUG basic_verbose_exit response sent, length=8 1:main ├─ DEBUG basic_verbose_exit disconnected 1:main ┌┘basic_verbose_exit::conn peer_addr="8.8.8.8", port=18230 1:main ┌┘basic_verbose_exit::server host="localhost", port=8080 1:main ├─ WARN basic_verbose_exit internal error 1:main ├─ ERROR basic_verbose_exit this is a log message 1:main ├─ INFO basic_verbose_exit exit 1:main ┌┘basic_verbose_exit::server host="localhost", port=8080 1:main┌┘basic_verbose_exit::hierarchical-example version=0.1 1:main┘basic_verbose_exit::hierarchical-example version=0.1 tracing-tree-0.4.0/examples/concurrent.rs000064400000000000000000000054641046102023000165700ustar 00000000000000use std::{ future::Future, pin::Pin, task::{Context, Poll}, }; use futures::FutureExt; use tracing::{debug, debug_span, info, span, warn, Instrument, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(4) .with_thread_names(true) .with_thread_ids(true) .with_span_retrace(true) .with_deferred_spans(true) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); #[cfg(feature = "tracing-log")] tracing_log::LogTracer::init().unwrap(); let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::TRACE, "server", host = "localhost", port = 8080); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(1000)); info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); span!(Level::INFO, "empty-span").in_scope(|| { // empty span }); debug!("starting countdowns"); debug_span!("countdowns").in_scope(|| { let mut countdown_a = CountdownFuture { label: "a", count: 3, } .instrument(span!(Level::DEBUG, "countdown_a")) .fuse(); let mut countdown_b = CountdownFuture { label: "b", count: 5, } .instrument(span!(Level::DEBUG, "countdown_b")) .fuse(); // We don't care if the futures are ready, as we poll manually let waker = futures::task::noop_waker(); let mut cx = Context::from_waker(&waker); let _ = countdown_a.poll_unpin(&mut cx); let _ = countdown_b.poll_unpin(&mut cx); std::thread::sleep(std::time::Duration::from_millis(300)); let _ = countdown_b.poll_unpin(&mut cx); let _ = countdown_a.poll_unpin(&mut cx); peer1.in_scope(|| { warn!("peer1 warning"); }); tracing::info!("finished polling countdowns"); }); drop(peer1); tracing::info!("all done!"); info!("exit") } struct CountdownFuture { label: &'static str, count: u32, } impl Future for CountdownFuture { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { debug!(label=?self.label, count=?self.count, "polling countdown"); self.count -= 1; if self.count == 0 { Poll::Ready(()) } else { cx.waker().wake_by_ref(); Poll::Pending } } } tracing-tree-0.4.0/examples/concurrent.stdout000064400000000000000000000024531046102023000174610ustar 000000000000001:main┐concurrent::hierarchical-example version=0.1 1:main└───┐concurrent::server host="localhost", port=8080 1:main ├─── INFO concurrent starting 1:main ├─── INFO concurrent listening 1:main ├─── DEBUG concurrent starting countdowns 1:main └───┐concurrent::countdowns 1:main └───┐concurrent::countdown_a 1:main ├─── DEBUG concurrent polling countdown, label="a", count=3 1:main └───┐concurrent::countdown_b 1:main ├─── DEBUG concurrent polling countdown, label="b", count=5 1:main ├─── DEBUG concurrent polling countdown, label="b", count=4 1:main └───┐concurrent::countdown_a 1:main ├─── DEBUG concurrent polling countdown, label="a", count=2 1:main └───┐concurrent::conn peer_addr="82.9.9.9", port=42381 1:main ├─── WARN concurrent peer1 warning 1:main └───┐concurrent::countdowns 1:main ├─── INFO concurrent finished polling countdowns 1:main ┌───┘ 1:main ┌───┘ 1:main ┌───┘ 1:main ┌───┘ 1:main ├─── INFO concurrent all done! 1:main ├─── INFO concurrent exit 1:main┌───┘ 1:main┘ tracing-tree-0.4.0/examples/concurrent_eager.rs000064400000000000000000000045771046102023000177370ustar 00000000000000use std::{ future::Future, pin::Pin, task::{Context, Poll}, }; use futures::{pin_mut, FutureExt}; use tracing::Instrument; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(4) .with_thread_names(true) .with_thread_ids(true) .with_span_retrace(true) .with_deferred_spans(false) .with_verbose_entry(true) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); #[cfg(feature = "tracing-log")] tracing_log::LogTracer::init().unwrap(); let fut_a = spawn_fut("a", a); pin_mut!(fut_a); let waker = futures::task::noop_waker(); let mut cx = Context::from_waker(&waker); assert!(fut_a.poll_unpin(&mut cx).is_pending()); let fut_b = spawn_fut("b", b); pin_mut!(fut_b); assert!(fut_b.poll_unpin(&mut cx).is_pending()); assert!(fut_a.poll_unpin(&mut cx).is_pending()); assert!(fut_b.poll_unpin(&mut cx).is_pending()); assert!(fut_a.poll_unpin(&mut cx).is_ready()); assert!(fut_b.poll_unpin(&mut cx).is_ready()); } fn spawn_fut Fut, Fut: Future>( key: &'static str, inner: F, ) -> impl Future { let span = tracing::info_span!("spawn_fut", key); async move { countdown(1).await; inner().await; } .instrument(span) } fn a() -> impl Future { let span = tracing::info_span!("a"); async move { countdown(1).await; tracing::info!("a"); } .instrument(span) } fn b() -> impl Future { let span = tracing::info_span!("b"); async move { countdown(1).await; tracing::info!("b"); } .instrument(span) } fn countdown(count: u32) -> impl Future { CountdownFuture { count } } struct CountdownFuture { count: u32, } impl Future for CountdownFuture { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if self.count == 0 { Poll::Ready(()) } else { self.count -= 1; cx.waker().wake_by_ref(); Poll::Pending } } } tracing-tree-0.4.0/examples/concurrent_eager.stdout000064400000000000000000000011241046102023000206160ustar 000000000000001:main┐concurrent_eager::spawn_fut key="a" 1:main┐concurrent_eager::spawn_fut key="b" 1:main┐concurrent_eager::spawn_fut key="a" 1:main└───┐concurrent_eager::a 1:main┐concurrent_eager::spawn_fut key="b" 1:main└───┐concurrent_eager::b 1:main┐concurrent_eager::spawn_fut key="a" 1:main└───┐concurrent_eager::a 1:main ├─── INFO concurrent_eager a 1:main┌───┘ 1:main┐concurrent_eager::spawn_fut key="b" 1:main└───┐concurrent_eager::b 1:main ├─── INFO concurrent_eager b 1:main┌───┘ 1:main┘ 1:main┘ tracing-tree-0.4.0/examples/concurrent_verbose.rs000064400000000000000000000055671046102023000203210ustar 00000000000000use std::{ future::Future, pin::Pin, task::{Context, Poll}, }; use futures::FutureExt; use tracing::{debug, debug_span, info, span, warn, Instrument, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(4) .with_thread_names(true) .with_thread_ids(true) .with_verbose_exit(true) .with_verbose_entry(true) .with_span_retrace(true) .with_deferred_spans(true) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); #[cfg(feature = "tracing-log")] tracing_log::LogTracer::init().unwrap(); let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::TRACE, "server", host = "localhost", port = 8080); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(1000)); info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); span!(Level::INFO, "empty-span").in_scope(|| { // empty span }); debug!("starting countdowns"); debug_span!("countdowns").in_scope(|| { let mut countdown_a = CountdownFuture { label: "a", count: 3, } .instrument(span!(Level::DEBUG, "countdown_a")) .fuse(); let mut countdown_b = CountdownFuture { label: "b", count: 5, } .instrument(span!(Level::DEBUG, "countdown_b")) .fuse(); // We don't care if the futures are ready, as we poll manually let waker = futures::task::noop_waker(); let mut cx = Context::from_waker(&waker); let _ = countdown_a.poll_unpin(&mut cx); let _ = countdown_b.poll_unpin(&mut cx); std::thread::sleep(std::time::Duration::from_millis(300)); let _ = countdown_b.poll_unpin(&mut cx); let _ = countdown_a.poll_unpin(&mut cx); peer1.in_scope(|| { warn!("peer1 warning"); }); tracing::info!("finished polling countdowns"); }); drop(peer1); tracing::info!("all done!"); info!("exit") } struct CountdownFuture { label: &'static str, count: u32, } impl Future for CountdownFuture { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { debug!(label=?self.label, count=?self.count, "polling countdown"); self.count -= 1; if self.count == 0 { Poll::Ready(()) } else { cx.waker().wake_by_ref(); Poll::Pending } } } tracing-tree-0.4.0/examples/concurrent_verbose.stdout000064400000000000000000000044571046102023000212140ustar 000000000000001:main┐concurrent_verbose::hierarchical-example version=0.1 1:main└───┐concurrent_verbose::server host="localhost", port=8080 1:main ├─── INFO concurrent_verbose starting 1:main ├─── INFO concurrent_verbose listening 1:main ├─── DEBUG concurrent_verbose starting countdowns 1:main └─┐concurrent_verbose::server host="localhost", port=8080 1:main └─┐concurrent_verbose::countdowns 1:main └───┐concurrent_verbose::countdown_a 1:main ├─── DEBUG concurrent_verbose polling countdown, label="a", count=3 1:main └─┐concurrent_verbose::countdowns 1:main └─┐concurrent_verbose::countdown_b 1:main ├─── DEBUG concurrent_verbose polling countdown, label="b", count=5 1:main ├─── DEBUG concurrent_verbose polling countdown, label="b", count=4 1:main └─┐concurrent_verbose::countdowns 1:main └─┐concurrent_verbose::countdown_a 1:main ├─── DEBUG concurrent_verbose polling countdown, label="a", count=2 1:main └─┐concurrent_verbose::server host="localhost", port=8080 1:main └─┐concurrent_verbose::conn peer_addr="82.9.9.9", port=42381 1:main ├─── WARN concurrent_verbose peer1 warning 1:main └─┐concurrent_verbose::server host="localhost", port=8080 1:main └─┐concurrent_verbose::countdowns 1:main ├─── INFO concurrent_verbose finished polling countdowns 1:main ┌─┘concurrent_verbose::countdown_b 1:main ┌─┘concurrent_verbose::countdowns 1:main ┌─┘concurrent_verbose::countdown_a 1:main ┌─┘concurrent_verbose::countdowns 1:main ┌─┘concurrent_verbose::countdowns 1:main ┌─┘concurrent_verbose::server host="localhost", port=8080 1:main ┌─┘concurrent_verbose::conn peer_addr="82.9.9.9", port=42381 1:main ┌─┘concurrent_verbose::server host="localhost", port=8080 1:main ├─── INFO concurrent_verbose all done! 1:main ├─── INFO concurrent_verbose exit 1:main ┌─┘concurrent_verbose::server host="localhost", port=8080 1:main┌─┘concurrent_verbose::hierarchical-example version=0.1 1:main┘concurrent_verbose::hierarchical-example version=0.1 tracing-tree-0.4.0/examples/deferred.rs000064400000000000000000000071771046102023000161710ustar 00000000000000use tracing::{ debug, error, info, instrument, level_filters::LevelFilter, span, trace, warn, Level, }; use tracing_subscriber::{layer::SubscriberExt, registry::Registry, Layer}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) .with_verbose_exit(true) .with_verbose_entry(true) .with_deferred_spans(true) .with_targets(true) .with_span_modes(true) .with_filter(LevelFilter::DEBUG); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); #[cfg(feature = "tracing-log")] tracing_log::LogTracer::init().unwrap(); let app_span = span!(Level::DEBUG, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::DEBUG, "server", host = "localhost", port = 8080); println!("-> This prints before the span open message"); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(1000)); span!(Level::INFO, "empty-span").in_scope(|| { // empty span }); info!("listening"); // Defer two levels of spans println!("-> Deferring two levels of spans"); span!(Level::INFO, "connections").in_scope(|| { let peer1 = span!(Level::DEBUG, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { debug!(peer = "peer1", "connected"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 2, "message received"); }); drop(peer1); let peer2 = span!(Level::DEBUG, "conn", peer_addr = "82.9.9.9", port = 61548); // This span will not be printed at all since no event in it will pass the filter peer2.in_scope(|| { trace!(peer = "peer2", "connected"); std::thread::sleep(std::time::Duration::from_millis(300)); trace!(length = 2, "message received"); }); drop(peer2); let peer3 = span!(Level::DEBUG, "conn", peer_addr = "8.8.8.8", port = 18230); peer3.in_scope(|| { std::thread::sleep(std::time::Duration::from_millis(300)); debug!(peer = "peer3", "connected"); }); drop(peer3); let peer4 = span!( Level::DEBUG, "foomp", normal_var = 43, "{} <- format string", 42 ); peer4.in_scope(|| { error!("hello"); }); drop(peer4); let peer1 = span!(Level::DEBUG, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { warn!(algo = "xor", "weak encryption requested"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer1); let peer2 = span!(Level::DEBUG, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { debug!(length = 5, "message received"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer2); }); warn!("internal error"); log::error!("this is a log message"); info!("exit"); } #[instrument] fn call_a(name: &str) { info!(name, "got a name"); call_b(name) } #[instrument] fn call_b(name: &str) { info!(name, "got a name"); } tracing-tree-0.4.0/examples/deferred.stdout000064400000000000000000000050441046102023000170560ustar 00000000000000-> This prints before the span open message 1:main┐open: deferred::hierarchical-example version=0.1 1:main└─┐open: deferred::server host="localhost", port=8080 1:main ├─ INFO deferred starting 1:main ├─ INFO deferred listening -> Deferring two levels of spans 1:main └┐pre_open: deferred::server host="localhost", port=8080 1:main └┐open(v): deferred::connections 1:main └─┐open: deferred::conn peer_addr="82.9.9.9", port=42381 1:main ├─ DEBUG deferred connected, peer="peer1" 1:main ├─ DEBUG deferred message received, length=2 1:main ┌┘close(v): deferred::conn peer_addr="82.9.9.9", port=42381 1:main ┌┘post_close: deferred::connections 1:main └┐pre_open: deferred::connections 1:main └┐open(v): deferred::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG deferred connected, peer="peer3" 1:main ┌┘close(v): deferred::conn peer_addr="8.8.8.8", port=18230 1:main ┌┘post_close: deferred::connections 1:main └┐pre_open: deferred::connections 1:main └┐open(v): deferred::foomp 42 <- format string, normal_var=43 1:main ├─ ERROR deferred hello 1:main ┌┘close(v): deferred::foomp 42 <- format string, normal_var=43 1:main ┌┘post_close: deferred::connections 1:main └┐pre_open: deferred::connections 1:main └┐open(v): deferred::conn peer_addr="82.9.9.9", port=42381 1:main ├─ WARN deferred weak encryption requested, algo="xor" 1:main ├─ DEBUG deferred response sent, length=8 1:main ├─ DEBUG deferred disconnected 1:main ┌┘close(v): deferred::conn peer_addr="82.9.9.9", port=42381 1:main ┌┘post_close: deferred::connections 1:main └┐pre_open: deferred::connections 1:main └┐open(v): deferred::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG deferred message received, length=5 1:main ├─ DEBUG deferred response sent, length=8 1:main ├─ DEBUG deferred disconnected 1:main ┌┘close(v): deferred::conn peer_addr="8.8.8.8", port=18230 1:main ┌┘post_close: deferred::connections 1:main ┌┘close(v): deferred::connections 1:main ┌┘post_close: deferred::server host="localhost", port=8080 1:main ├─ WARN deferred internal error 1:main ├─ ERROR deferred this is a log message 1:main ├─ INFO deferred exit 1:main ┌┘close(v): deferred::server host="localhost", port=8080 1:main┌┘post_close: deferred::hierarchical-example version=0.1 1:main┘close(v): deferred::hierarchical-example version=0.1 tracing-tree-0.4.0/examples/no-indent.rs000064400000000000000000000047121046102023000162740ustar 00000000000000use tracing::{debug, error, info, instrument, span, warn, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) .with_verbose_exit(false) .with_verbose_entry(false) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::TRACE, "server", host = "localhost", port = 8080); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(300)); info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { debug!("connected"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 2, "message received"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { std::thread::sleep(std::time::Duration::from_millis(300)); debug!("connected"); }); drop(peer2); let peer3 = span!( Level::TRACE, "foomp", normal_var = 43, "{} <- format string", 42 ); peer3.in_scope(|| { error!("hello"); }); drop(peer3); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { warn!(algo = "xor", "weak encryption requested"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { debug!(length = 5, "message received"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer2); warn!("internal error"); info!("exit"); } #[instrument] fn call_a(name: &str) { info!(name, "got a name"); call_b(name) } #[instrument] fn call_b(name: &str) { info!(name, "got a name"); } tracing-tree-0.4.0/examples/no-indent.stdout000064400000000000000000000017711046102023000171740ustar 000000000000001:main no_indent::hierarchical-example version=0.1 1:main no_indent::server host="localhost", port=8080 1:main INFO no_indent starting 1:main INFO no_indent listening 1:main no_indent::conn peer_addr="82.9.9.9", port=42381 1:main DEBUG no_indent connected 1:main DEBUG no_indent message received, length=2 1:main no_indent::conn peer_addr="8.8.8.8", port=18230 1:main DEBUG no_indent connected 1:main no_indent::foomp 42 <- format string, normal_var=43 1:main ERROR no_indent hello 1:main no_indent::conn peer_addr="82.9.9.9", port=42381 1:main WARN no_indent weak encryption requested, algo="xor" 1:main DEBUG no_indent response sent, length=8 1:main DEBUG no_indent disconnected 1:main no_indent::conn peer_addr="8.8.8.8", port=18230 1:main DEBUG no_indent message received, length=5 1:main DEBUG no_indent response sent, length=8 1:main DEBUG no_indent disconnected 1:main WARN no_indent internal error 1:main INFO no_indent exit tracing-tree-0.4.0/examples/quiet.rs000064400000000000000000000047531046102023000155350ustar 00000000000000use tracing::{debug, error, info, instrument, span, warn, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) .with_verbose_exit(false) .with_verbose_entry(false) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1); let _e = app_span.enter(); let server_span = span!(Level::TRACE, "server", host = "localhost", port = 8080); let _e2 = server_span.enter(); info!("starting"); std::thread::sleep(std::time::Duration::from_millis(300)); info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { debug!("connected"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 2, "message received"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { std::thread::sleep(std::time::Duration::from_millis(300)); debug!("connected"); }); drop(peer2); let peer3 = span!( Level::TRACE, "foomp", normal_var = 43, "{} <- format string", 42 ); peer3.in_scope(|| { error!("hello"); }); drop(peer3); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| { warn!(algo = "xor", "weak encryption requested"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer1); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| { debug!(length = 5, "message received"); std::thread::sleep(std::time::Duration::from_millis(300)); debug!(length = 8, "response sent"); debug!("disconnected"); }); drop(peer2); warn!("internal error"); info!("exit"); } #[instrument] fn call_a(name: &str) { info!(name, "got a name"); call_b(name) } #[instrument] fn call_b(name: &str) { info!(name, "got a name"); } tracing-tree-0.4.0/examples/quiet.stdout000064400000000000000000000022051046102023000164210ustar 000000000000001:main┐quiet::hierarchical-example version=0.1 1:main└─┐quiet::server host="localhost", port=8080 1:main ├─ INFO quiet starting 1:main ├─ INFO quiet listening 1:main └─┐quiet::conn peer_addr="82.9.9.9", port=42381 1:main ├─ DEBUG quiet connected 1:main ├─ DEBUG quiet message received, length=2 1:main ┌─┘ 1:main └─┐quiet::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG quiet connected 1:main ┌─┘ 1:main └─┐quiet::foomp 42 <- format string, normal_var=43 1:main ├─ ERROR quiet hello 1:main ┌─┘ 1:main └─┐quiet::conn peer_addr="82.9.9.9", port=42381 1:main ├─ WARN quiet weak encryption requested, algo="xor" 1:main ├─ DEBUG quiet response sent, length=8 1:main ├─ DEBUG quiet disconnected 1:main ┌─┘ 1:main └─┐quiet::conn peer_addr="8.8.8.8", port=18230 1:main ├─ DEBUG quiet message received, length=5 1:main ├─ DEBUG quiet response sent, length=8 1:main ├─ DEBUG quiet disconnected 1:main ┌─┘ 1:main ├─ WARN quiet internal error 1:main ├─ INFO quiet exit 1:main┌─┘ 1:main┘ tracing-tree-0.4.0/examples/stderr.rs000064400000000000000000000017301046102023000157010ustar 00000000000000use tracing::{debug, info, instrument}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; #[instrument] fn nth_fibonacci(n: u64) -> u64 { if n == 0 || n == 1 { debug!("Base case"); 1 } else { debug!("Recursing"); nth_fibonacci(n - 1) + nth_fibonacci(n - 2) } } #[instrument] fn fibonacci_seq(to: u64) -> Vec { let mut sequence = vec![]; for n in 0..=to { debug!("Pushing {n} fibonacci", n = n); sequence.push(nth_fibonacci(n)); } sequence } fn main() { let layer = HierarchicalLayer::default() .with_indent_lines(true) .with_indent_amount(2) .with_bracketed_fields(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); let n = 5; let sequence = fibonacci_seq(n); info!("The first {} fibonacci numbers are {:?}", n, sequence); } tracing-tree-0.4.0/examples/stderr.stderr000064400000000000000000000053411046102023000165620ustar 00000000000000┐fibonacci_seq{to=5} ├─ DEBUG Pushing 0 fibonacci └─┐nth_fibonacci{n=0} ├─ DEBUG Base case ┌─┘ ├─ DEBUG Pushing 1 fibonacci └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ ├─ DEBUG Pushing 2 fibonacci └─┐nth_fibonacci{n=2} ├─ DEBUG Recursing └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ └─┐nth_fibonacci{n=0} ├─ DEBUG Base case ┌─┘ ┌─┘ ├─ DEBUG Pushing 3 fibonacci └─┐nth_fibonacci{n=3} ├─ DEBUG Recursing └─┐nth_fibonacci{n=2} ├─ DEBUG Recursing └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ └─┐nth_fibonacci{n=0} ├─ DEBUG Base case ┌─┘ ┌─┘ └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ ┌─┘ ├─ DEBUG Pushing 4 fibonacci └─┐nth_fibonacci{n=4} ├─ DEBUG Recursing └─┐nth_fibonacci{n=3} ├─ DEBUG Recursing └─┐nth_fibonacci{n=2} ├─ DEBUG Recursing └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ └─┐nth_fibonacci{n=0} ├─ DEBUG Base case ┌─┘ ┌─┘ └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ ┌─┘ └─┐nth_fibonacci{n=2} ├─ DEBUG Recursing └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ └─┐nth_fibonacci{n=0} ├─ DEBUG Base case ┌─┘ ┌─┘ ┌─┘ ├─ DEBUG Pushing 5 fibonacci └─┐nth_fibonacci{n=5} ├─ DEBUG Recursing └─┐nth_fibonacci{n=4} ├─ DEBUG Recursing └─┐nth_fibonacci{n=3} ├─ DEBUG Recursing └─┐nth_fibonacci{n=2} ├─ DEBUG Recursing └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ └─┐nth_fibonacci{n=0} ├─ DEBUG Base case ┌─┘ ┌─┘ └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ ┌─┘ └─┐nth_fibonacci{n=2} ├─ DEBUG Recursing └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ └─┐nth_fibonacci{n=0} ├─ DEBUG Base case ┌─┘ ┌─┘ ┌─┘ └─┐nth_fibonacci{n=3} ├─ DEBUG Recursing └─┐nth_fibonacci{n=2} ├─ DEBUG Recursing └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ └─┐nth_fibonacci{n=0} ├─ DEBUG Base case ┌─┘ ┌─┘ └─┐nth_fibonacci{n=1} ├─ DEBUG Base case ┌─┘ ┌─┘ ┌─┘ ┘ INFO The first 5 fibonacci numbers are [1, 1, 2, 3, 5, 8] tracing-tree-0.4.0/examples/wraparound.rs000064400000000000000000000013351046102023000165610ustar 00000000000000use tracing::{instrument, warn}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; fn main() { let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) .with_targets(true) .with_wraparound(5); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); recurse(0); } #[instrument] fn recurse(i: usize) { warn!("boop"); if i > 20 { warn!("bop"); return; } else { recurse(i + 1); } warn!("bop"); } tracing-tree-0.4.0/examples/wraparound.stdout000064400000000000000000000057601046102023000174650ustar 000000000000001:main┐wraparound::recurse i=0 1:main├─ WARN wraparound boop 1:main└─┐wraparound::recurse i=1 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=2 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=3 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=4 1:main────────┘ 1:main WARN wraparound boop 1:main┐wraparound::recurse i=5 1:main├─ WARN wraparound boop 1:main└─┐wraparound::recurse i=6 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=7 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=8 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=9 1:main────────┘ 1:main WARN wraparound boop 1:main┐wraparound::recurse i=10 1:main├─ WARN wraparound boop 1:main└─┐wraparound::recurse i=11 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=12 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=13 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=14 1:main────────┘ 1:main WARN wraparound boop 1:main┐wraparound::recurse i=15 1:main├─ WARN wraparound boop 1:main└─┐wraparound::recurse i=16 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=17 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=18 1:main ├─ WARN wraparound boop 1:main └─┐wraparound::recurse i=19 1:main────────┘ 1:main WARN wraparound boop 1:main┐wraparound::recurse i=20 1:main├─ WARN wraparound boop 1:main└─┐wraparound::recurse i=21 1:main ├─ WARN wraparound boop 1:main ├─ WARN wraparound bop 1:main┌─┘ 1:main├─ WARN wraparound bop 1:main┘ 1:main WARN wraparound bop 1:main────────┐ 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main┌─┘ 1:main├─ WARN wraparound bop 1:main┘ 1:main WARN wraparound bop 1:main────────┐ 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main┌─┘ 1:main├─ WARN wraparound bop 1:main┘ 1:main WARN wraparound bop 1:main────────┐ 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main┌─┘ 1:main├─ WARN wraparound bop 1:main┘ 1:main WARN wraparound bop 1:main────────┐ 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main ┌─┘ 1:main ├─ WARN wraparound bop 1:main┌─┘ 1:main├─ WARN wraparound bop 1:main┘ tracing-tree-0.4.0/src/format.rs000064400000000000000000000353561046102023000146520ustar 00000000000000use nu_ansi_term::Color; use std::{ fmt::{self, Write as _}, io, }; use tracing_core::{ field::{Field, Visit}, span, Level, }; pub(crate) const LINE_VERT: &str = "│"; const LINE_HORIZ: &str = "─"; pub(crate) const LINE_BRANCH: &str = "├"; pub(crate) const LINE_CLOSE: &str = "┘"; pub(crate) const LINE_CLOSE2: char = '┌'; pub(crate) const LINE_OPEN: &str = "┐"; pub(crate) const LINE_OPEN2: char = '└'; #[derive(Debug, Copy, Clone)] pub(crate) enum SpanMode { /// Executed on the parent before entering a child span PreOpen, Open { verbose: bool, }, Close { verbose: bool, }, /// A span has been entered but another *different* span has been entered in the meantime. Retrace { verbose: bool, }, PostClose, Event, } #[derive(Debug)] pub struct Config { /// Whether to use colors. pub ansi: bool, /// Whether an ascii art tree is used or (if false) whether to just use whitespace indent pub indent_lines: bool, /// The amount of chars to indent. pub indent_amount: usize, /// Whether to show the module paths. pub targets: bool, /// Whether to show thread ids. pub render_thread_ids: bool, /// Whether to show thread names. pub render_thread_names: bool, /// Specifies after how many indentation levels we will wrap back around to zero pub wraparound: usize, /// Whether to print the current span before activating a new one pub verbose_entry: bool, /// Whether to print the current span before exiting it. pub verbose_exit: bool, /// Print the path leading up to a span if a different span was entered concurrently pub span_retrace: bool, /// Whether to print squiggly brackets (`{}`) around the list of fields in a span. pub bracketed_fields: bool, /// Defer printing a span until an event is generated inside of it pub deferred_spans: bool, /// Print a label of the span mode (open/close etc). pub span_modes: bool, } impl Config { pub fn with_ansi(self, ansi: bool) -> Self { Self { ansi, ..self } } pub fn with_indent_lines(self, indent_lines: bool) -> Self { Self { indent_lines, ..self } } pub fn with_targets(self, targets: bool) -> Self { Self { targets, ..self } } pub fn with_thread_ids(self, render_thread_ids: bool) -> Self { Self { render_thread_ids, ..self } } pub fn with_thread_names(self, render_thread_names: bool) -> Self { Self { render_thread_names, ..self } } pub fn with_wraparound(self, wraparound: usize) -> Self { Self { wraparound, ..self } } pub fn with_verbose_entry(self, verbose_entry: bool) -> Self { Self { verbose_entry, ..self } } pub fn with_verbose_exit(self, verbose_exit: bool) -> Self { Self { verbose_exit, ..self } } pub fn with_span_retrace(self, enabled: bool) -> Self { Self { span_retrace: enabled, ..self } } pub fn with_deferred_spans(self, enable: bool) -> Self { Self { deferred_spans: enable, ..self } } pub fn with_span_modes(self, enable: bool) -> Self { Self { span_modes: enable, ..self } } pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self { Self { bracketed_fields, ..self } } pub(crate) fn prefix(&self) -> String { let mut buf = String::new(); if self.render_thread_ids { write!(buf, "{:?}", std::thread::current().id()).unwrap(); if buf.ends_with(')') { buf.truncate(buf.len() - 1); } if buf.starts_with("ThreadId(") { buf.drain(0.."ThreadId(".len()); } } if self.render_thread_names { if let Some(name) = std::thread::current().name() { if self.render_thread_ids { buf.push(':'); } buf.push_str(name); } } buf } } impl Default for Config { fn default() -> Self { Self { ansi: true, indent_lines: false, indent_amount: 2, targets: false, render_thread_ids: false, render_thread_names: false, wraparound: usize::max_value(), verbose_entry: false, verbose_exit: false, span_retrace: false, bracketed_fields: false, deferred_spans: false, span_modes: false, } } } #[derive(Debug)] pub struct Buffers { pub current_buf: String, pub indent_buf: String, /// The last seen span of this layer /// /// This serves to serialize spans as two events can be generated in different spans /// without the spans entering and exiting beforehand. This happens for multithreaded code /// and instrumented futures pub current_span: Option, } impl Buffers { pub fn new() -> Self { Self { current_buf: String::new(), indent_buf: String::new(), current_span: None, } } pub fn flush_current_buf(&mut self, mut writer: impl io::Write) { write!(writer, "{}", &self.current_buf).unwrap(); self.current_buf.clear(); } pub fn flush_indent_buf(&mut self) { self.current_buf.push_str(&self.indent_buf); self.indent_buf.clear(); } pub(crate) fn indent_current(&mut self, indent: usize, config: &Config, style: SpanMode) { let prefix = config.prefix(); // Render something when wraparound occurs so the user is aware of it if config.indent_lines { self.current_buf.push('\n'); match style { SpanMode::Close { .. } | SpanMode::PostClose => { if indent > 0 && (indent + 1) % config.wraparound == 0 { self.indent_buf.push_str(&prefix); for _ in 0..(indent % config.wraparound * config.indent_amount) { self.indent_buf.push_str(LINE_HORIZ); } self.indent_buf.push_str(LINE_OPEN); self.indent_buf.push('\n'); } } _ => {} } } indent_block( &self.current_buf, &mut self.indent_buf, indent % config.wraparound, config.indent_amount, config.indent_lines, &prefix, style, ); self.current_buf.clear(); self.flush_indent_buf(); // Render something when wraparound occurs so the user is aware of it if config.indent_lines { match style { SpanMode::PreOpen { .. } | SpanMode::Open { .. } => { if indent > 0 && (indent + 1) % config.wraparound == 0 { self.current_buf.push_str(&prefix); for _ in 0..(indent % config.wraparound * config.indent_amount) { self.current_buf.push_str(LINE_HORIZ); } self.current_buf.push_str(LINE_CLOSE); self.current_buf.push('\n'); } } _ => {} } } } } pub struct FmtEvent<'a> { pub bufs: &'a mut Buffers, pub comma: bool, } impl<'a> Visit for FmtEvent<'a> { fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { let buf = &mut self.bufs.current_buf; let comma = if self.comma { "," } else { "" }; match field.name() { "message" => { write!(buf, "{} {:?}", comma, value).unwrap(); self.comma = true; } // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => {} name => { write!(buf, "{} {}={:?}", comma, name, value).unwrap(); self.comma = true; } } } } pub struct ColorLevel<'a>(pub &'a Level); impl<'a> fmt::Display for ColorLevel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self.0 { Level::TRACE => Color::Purple.bold().paint("TRACE"), Level::DEBUG => Color::Blue.bold().paint("DEBUG"), Level::INFO => Color::Green.bold().paint(" INFO"), Level::WARN => Color::Rgb(252, 234, 160).bold().paint(" WARN"), // orange Level::ERROR => Color::Red.bold().paint("ERROR"), } .fmt(f) } } pub(crate) fn write_span_mode(buf: &mut String, style: SpanMode) { match style { SpanMode::Open { verbose: true } => buf.push_str("open(v)"), SpanMode::Open { verbose: false } => buf.push_str("open"), SpanMode::Retrace { verbose: false } => buf.push_str("retrace"), SpanMode::Retrace { verbose: true } => buf.push_str("retrace(v)"), SpanMode::Close { verbose: true } => buf.push_str("close(v)"), SpanMode::Close { verbose: false } => buf.push_str("close"), SpanMode::PreOpen => buf.push_str("pre_open"), SpanMode::PostClose => buf.push_str("post_close"), SpanMode::Event => buf.push_str("event"), } buf.push_str(": ") } fn indent_block_with_lines( lines: &[&str], buf: &mut String, indent: usize, // width of one level of indent indent_amount: usize, prefix: &str, style: SpanMode, ) { let indent_spaces = indent * indent_amount; if lines.is_empty() { return; } else if indent_spaces == 0 { for line in lines { buf.push_str(prefix); // The first indent is special, we only need to print open/close and nothing else if indent == 0 { match style { SpanMode::Open { .. } => buf.push_str(LINE_OPEN), SpanMode::Retrace { .. } => buf.push_str(LINE_OPEN), SpanMode::Close { .. } => buf.push_str(LINE_CLOSE), SpanMode::PreOpen { .. } | SpanMode::PostClose => {} SpanMode::Event => {} } } buf.push_str(line); buf.push('\n'); } return; } let mut s = String::with_capacity(indent_spaces + prefix.len()); s.push_str(prefix); for _ in 0..(indent_spaces - indent_amount) { s.push(' '); } // draw branch buf.push_str(&s); match style { SpanMode::PreOpen => { buf.push(LINE_OPEN2); for _ in 1..(indent_amount / 2) { buf.push_str(LINE_HORIZ); } buf.push_str(LINE_OPEN); } SpanMode::Open { verbose: false } | SpanMode::Retrace { verbose: false } => { buf.push(LINE_OPEN2); for _ in 1..indent_amount { buf.push_str(LINE_HORIZ); } buf.push_str(LINE_OPEN); } SpanMode::Open { verbose: true } | SpanMode::Retrace { verbose: true } => { buf.push(' '); for _ in 1..(indent_amount / 2) { buf.push(' '); } // We don't have the space for fancy rendering at single space indent. if indent_amount > 1 { buf.push(LINE_OPEN2); } for _ in (indent_amount / 2)..(indent_amount - 1) { buf.push_str(LINE_HORIZ); } // We don't have the space for fancy rendering at single space indent. if indent_amount > 1 { buf.push_str(LINE_OPEN); } else { buf.push(' '); } } SpanMode::Close { verbose: false } => { buf.push(LINE_CLOSE2); for _ in 1..indent_amount { buf.push_str(LINE_HORIZ); } buf.push_str(LINE_CLOSE); } SpanMode::Close { verbose: true } => { buf.push(' '); for _ in 1..(indent_amount / 2) { buf.push(' '); } // We don't have the space for fancy rendering at single space indent. if indent_amount > 1 { buf.push(LINE_CLOSE2); } for _ in (indent_amount / 2)..(indent_amount - 1) { buf.push_str(LINE_HORIZ); } // We don't have the space for fancy rendering at single space indent. if indent_amount > 1 { buf.push_str(LINE_CLOSE); } else { buf.push(' '); } } SpanMode::PostClose => { buf.push(LINE_CLOSE2); for _ in 1..(indent_amount / 2) { buf.push_str(LINE_HORIZ); } buf.push_str(LINE_CLOSE); } SpanMode::Event => { buf.push_str(LINE_BRANCH); // add `indent_amount - 1` horizontal lines before the span/event for _ in 0..(indent_amount - 1) { buf.push_str(LINE_HORIZ); } } } buf.push_str(lines[0]); buf.push('\n'); // add the rest of the indentation, since we don't want to draw horizontal lines // for subsequent lines for i in 0..indent_amount { if i % indent_amount == 0 { s.push_str(LINE_VERT); } else { s.push(' '); } } // add all of the actual content, with each line preceded by the indent string for line in &lines[1..] { buf.push_str(&s); buf.push_str(line); buf.push('\n'); } } fn indent_block( block: &str, buf: &mut String, mut indent: usize, indent_amount: usize, indent_lines: bool, prefix: &str, style: SpanMode, ) { let lines: Vec<&str> = block.lines().collect(); let indent_spaces = indent * indent_amount; buf.reserve(block.len() + (lines.len() * indent_spaces)); // The PreOpen and PostClose need to match up with the indent of the entered child span one more indent // deep match style { SpanMode::PreOpen | SpanMode::PostClose => { indent += 1; } _ => (), } if indent_lines { indent_block_with_lines(&lines, buf, indent, indent_amount, prefix, style); } else { let indent_str = String::from(" ").repeat(indent_spaces); for line in lines { buf.push_str(prefix); buf.push(' '); buf.push_str(&indent_str); buf.push_str(line); buf.push('\n'); } } } tracing-tree-0.4.0/src/lib.rs000064400000000000000000000472571046102023000141330ustar 00000000000000pub(crate) mod format; pub mod time; use crate::time::FormatTime; use format::{write_span_mode, Buffers, ColorLevel, Config, FmtEvent, SpanMode}; use nu_ansi_term::{Color, Style}; use std::{ fmt::{self, Write}, io::{self, IsTerminal}, iter::Fuse, mem, sync::{ atomic::{AtomicBool, Ordering}, Mutex, }, thread::LocalKey, time::Instant, }; use tracing_core::{ field::{Field, Visit}, span::{Attributes, Id}, Event, Subscriber, }; #[cfg(feature = "tracing-log")] use tracing_log::NormalizeEvent; use tracing_subscriber::{ fmt::MakeWriter, layer::{Context, Layer}, registry::{LookupSpan, ScopeFromRoot, SpanRef}, }; // Span extension data pub(crate) struct Data { start: Instant, kvs: Vec<(&'static str, String)>, written: bool, } impl Data { pub fn new(attrs: &Attributes<'_>, written: bool) -> Self { let mut span = Self { start: Instant::now(), kvs: Vec::new(), written, }; attrs.record(&mut span); span } } impl Visit for Data { fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { self.kvs.push((field.name(), format!("{:?}", value))) } } #[derive(Debug)] pub struct HierarchicalLayer io::Stderr, FT = ()> where W: for<'writer> MakeWriter<'writer> + 'static, FT: FormatTime, { make_writer: W, bufs: Mutex, config: Config, timer: FT, } impl Default for HierarchicalLayer { fn default() -> Self { Self::new(2) } } impl HierarchicalLayer io::Stderr> { pub fn new(indent_amount: usize) -> Self { let ansi = io::stderr().is_terminal(); let config = Config { ansi, indent_amount, ..Default::default() }; Self { make_writer: io::stderr, bufs: Mutex::new(Buffers::new()), config, timer: (), } } } impl HierarchicalLayer where W: for<'writer> MakeWriter<'writer> + 'static, FT: FormatTime, { /// Enables terminal colors, boldness and italics. pub fn with_ansi(self, ansi: bool) -> Self { Self { config: self.config.with_ansi(ansi), ..self } } pub fn with_writer(self, make_writer: W2) -> HierarchicalLayer where W2: for<'writer> MakeWriter<'writer>, { HierarchicalLayer { make_writer, config: self.config, bufs: self.bufs, timer: self.timer, } } pub fn with_indent_amount(self, indent_amount: usize) -> Self { let config = Config { indent_amount, ..self.config }; Self { config, ..self } } /// Renders an ascii art tree instead of just using whitespace indentation. pub fn with_indent_lines(self, indent_lines: bool) -> Self { Self { config: self.config.with_indent_lines(indent_lines), ..self } } /// Specifies how to measure and format time at which event has occurred. pub fn with_timer(self, timer: FT2) -> HierarchicalLayer { HierarchicalLayer { make_writer: self.make_writer, config: self.config, bufs: self.bufs, timer, } } /// Whether to render the event and span targets. Usually targets are the module path to the /// event/span macro invocation. pub fn with_targets(self, targets: bool) -> Self { Self { config: self.config.with_targets(targets), ..self } } /// Whether to render the thread id in the beginning of every line. This is helpful to /// untangle the tracing statements emitted by each thread. pub fn with_thread_ids(self, thread_ids: bool) -> Self { Self { config: self.config.with_thread_ids(thread_ids), ..self } } /// Whether to render the thread name in the beginning of every line. Not all threads have /// names, but if they do, this may be more helpful than the generic thread ids. pub fn with_thread_names(self, thread_names: bool) -> Self { Self { config: self.config.with_thread_names(thread_names), ..self } } /// Resets the indentation to zero after `wraparound` indentation levels. /// This is helpful if you expect very deeply nested spans as otherwise the indentation /// just runs out of your screen. pub fn with_wraparound(self, wraparound: usize) -> Self { Self { config: self.config.with_wraparound(wraparound), ..self } } /// Whether to print the currently active span's message again before entering a new span. /// This helps if the entry to the current span was quite a while back (and with scrolling /// upwards in logs). pub fn with_verbose_entry(self, verbose_entry: bool) -> Self { Self { config: self.config.with_verbose_entry(verbose_entry), ..self } } /// Whether to print the currently active span's message again before dropping it. /// This helps if the entry to the current span was quite a while back (and with scrolling /// upwards in logs). pub fn with_verbose_exit(self, verbose_exit: bool) -> Self { Self { config: self.config.with_verbose_exit(verbose_exit), ..self } } /// Whether to print the currently active span's message again if another span was entered in /// the meantime /// This helps during concurrent or multi-threaded events where threads are entered, but not /// necessarily *exited* before other *divergent* spans are entered and generating events. pub fn with_span_retrace(self, enabled: bool) -> Self { Self { config: self.config.with_span_retrace(enabled), ..self } } /// Defers printing span opening until an event is generated within the span. /// /// Avoids printing empty spans with no generated events. pub fn with_deferred_spans(self, enabled: bool) -> Self { Self { config: self.config.with_deferred_spans(enabled), ..self } } /// Prefixes each branch with the event mode, such as `open`, or `close` pub fn with_span_modes(self, enabled: bool) -> Self { Self { config: self.config.with_span_modes(enabled), ..self } } /// Whether to print `{}` around the fields when printing a span. /// This can help visually distinguish fields from the rest of the message. pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self { Self { config: self.config.with_bracketed_fields(bracketed_fields), ..self } } fn styled(&self, style: Style, text: impl AsRef) -> String { styled(self.config.ansi, style, text) } fn print_kvs<'a, I, V>(&self, buf: &mut impl fmt::Write, kvs: I) -> fmt::Result where I: IntoIterator, V: fmt::Display + 'a, { let mut kvs = kvs.into_iter(); if let Some((k, v)) = kvs.next() { if k == "message" { write!(buf, "{}", v)?; } else { write!(buf, "{}={}", k, v)?; } } for (k, v) in kvs { write!(buf, ", {}={}", k, v)?; } Ok(()) } /// Ensures that `new_span` and all its ancestors are properly printed before an event fn write_retrace_span<'a, S>( &self, new_span: &SpanRef<'a, S>, bufs: &mut Buffers, ctx: &'a Context, pre_open: bool, ) where S: Subscriber + for<'new_span> LookupSpan<'new_span>, { // Also handle deferred spans along with retrace since deferred spans may need to print // multiple spans at once as a whole tree can be deferred // // If a another event occurs right after a previous event in the same span, this will // simply print nothing since the path to the common lowest ancestor is empty // if self.config.span_retrace || self.config.deferred_spans { let old_span_id = bufs.current_span.replace((new_span.id()).clone()); let old_span_id = old_span_id.as_ref(); let new_span_id = new_span.id(); if Some(&new_span_id) != old_span_id { let old_span = old_span_id.as_ref().and_then(|v| ctx.span(v)); let old_path = old_span.as_ref().map(scope_path).into_iter().flatten(); let new_path = scope_path(new_span); // Print the path from the common base of the two spans let new_path = DifferenceIter::new(old_path, new_path, |v| v.id()); for (i, span) in new_path.enumerate() { // Mark traversed spans as *written* let was_written = if let Some(data) = span.extensions_mut().get_mut::() { mem::replace(&mut data.written, true) } else { // `on_new_span` was not called, before // Consider if this should panic instead, which is *technically* correct but is // bad behavior for a logging layer in production. false }; // Print the parent of the first span let mut verbose = false; if i == 0 && pre_open { if let Some(span) = span.parent() { verbose = true; self.write_span_info(&span, bufs, SpanMode::PreOpen); } } self.write_span_info( &span, bufs, if was_written { SpanMode::Retrace { verbose } } else { SpanMode::Open { verbose } }, ) } } } fn write_span_info(&self, span: &SpanRef, bufs: &mut Buffers, style: SpanMode) where S: Subscriber + for<'span> LookupSpan<'span>, { let ext = span.extensions(); let data = ext.get::().expect("span does not have data"); let mut current_buf = &mut bufs.current_buf; if self.config.span_modes { write_span_mode(current_buf, style) } let indent = scope_path(span).skip(1).count(); let should_write = match style { SpanMode::Open { .. } | SpanMode::Event => true, // Print the parent of a new span again before entering the child SpanMode::PreOpen { .. } if self.config.verbose_entry => true, SpanMode::Close { verbose } => verbose, // Generated if `span_retrace` is enabled SpanMode::Retrace { .. } => true, // Generated if `verbose_exit` is enabled SpanMode::PostClose => true, _ => false, }; if should_write { if self.config.targets { let target = span.metadata().target(); write!( &mut current_buf, "{}::", self.styled(Style::new().dimmed(), target,), ) .expect("Unable to write to buffer"); } write!( current_buf, "{name}", name = self.styled(Style::new().fg(Color::Green).bold(), span.metadata().name()) ) .unwrap(); if self.config.bracketed_fields { write!( current_buf, "{}", self.styled(Style::new().fg(Color::Green).bold(), "{") // Style::new().fg(Color::Green).dimmed().paint("{") ) .unwrap(); } else { write!(current_buf, " ").unwrap(); } self.print_kvs(&mut current_buf, data.kvs.iter().map(|(k, v)| (*k, v))) .unwrap(); if self.config.bracketed_fields { write!( current_buf, "{}", self.styled(Style::new().fg(Color::Green).bold(), "}") // Style::new().dimmed().paint("}") ) .unwrap(); } } bufs.indent_current(indent, &self.config, style); let writer = self.make_writer.make_writer(); bufs.flush_current_buf(writer) } fn write_timestamp(&self, span: SpanRef, buf: &mut String) where S: Subscriber + for<'span> LookupSpan<'span>, { let ext = span.extensions(); let data = ext .get::() .expect("Data cannot be found in extensions"); self.timer .style_timestamp(self.config.ansi, data.start.elapsed(), buf) .unwrap() } fn is_recursive() -> Option { thread_local! { pub static IS_EMPTY: AtomicBool = const { AtomicBool::new(true) }; } IS_EMPTY.with(|is_empty| { is_empty .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed) .ok() .map(|_| RecursiveGuard(&IS_EMPTY)) }) } } fn styled(ansi: bool, style: Style, text: impl AsRef) -> String { if ansi { style.paint(text.as_ref()).to_string() } else { text.as_ref().to_string() } } struct RecursiveGuard(&'static LocalKey); impl Drop for RecursiveGuard { fn drop(&mut self) { self.0 .with(|is_empty| is_empty.store(true, Ordering::Relaxed)); } } impl Layer for HierarchicalLayer where S: Subscriber + for<'span> LookupSpan<'span>, W: for<'writer> MakeWriter<'writer> + 'static, FT: FormatTime + 'static, { fn on_new_span(&self, attrs: &Attributes, id: &Id, ctx: Context) { let Some(_guard) = Self::is_recursive() else { return; }; let span = ctx.span(id).expect("in new_span but span does not exist"); if span.extensions().get::().is_none() { let data = Data::new(attrs, !self.config.deferred_spans); span.extensions_mut().insert(data); } // Entry will be printed in on_event along with retrace if self.config.deferred_spans { return; } let bufs = &mut *self.bufs.lock().unwrap(); if self.config.span_retrace { self.write_retrace_span(&span, bufs, &ctx, self.config.verbose_entry); } else { if self.config.verbose_entry { if let Some(span) = span.parent() { self.write_span_info(&span, bufs, SpanMode::PreOpen); } } // Store the most recently entered span bufs.current_span = Some(span.id()); self.write_span_info( &span, bufs, SpanMode::Open { verbose: self.config.verbose_entry, }, ); } } fn on_event(&self, event: &Event<'_>, ctx: Context) { let Some(_guard) = Self::is_recursive() else { return; }; let span = ctx.current_span(); let span_id = span.id(); let span = span_id.and_then(|id| ctx.span(id)); let mut guard = self.bufs.lock().unwrap(); let bufs = &mut *guard; if let Some(new_span) = &span { if self.config.span_retrace || self.config.deferred_spans { self.write_retrace_span(new_span, bufs, &ctx, self.config.verbose_entry); } } let mut event_buf = &mut bufs.current_buf; // Time. { let prev_buffer_len = event_buf.len(); self.timer .format_time(&mut event_buf) .expect("Unable to write time to buffer"); // Something was written to the buffer, pad it with a space. if prev_buffer_len < event_buf.len() { write!(event_buf, " ").expect("Unable to write to buffer"); } } let deindent = if self.config.indent_lines { 0 } else { 1 }; // printing the indentation let indent = ctx .event_scope(event) .map(|scope| scope.count() - deindent) .unwrap_or(0); // check if this event occurred in the context of a span. // if it has, get the start time of this span. if let Some(span) = span { self.write_timestamp(span, event_buf); event_buf.push(' '); } #[cfg(feature = "tracing-log")] let normalized_meta = event.normalized_metadata(); #[cfg(feature = "tracing-log")] let metadata = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); #[cfg(not(feature = "tracing-log"))] let metadata = event.metadata(); let level = metadata.level(); let level = if self.config.ansi { ColorLevel(level).to_string() } else { level.to_string() }; write!(&mut event_buf, "{level}", level = level).expect("Unable to write to buffer"); if self.config.targets { let target = metadata.target(); write!( &mut event_buf, " {}", self.styled(Style::new().dimmed(), target,), ) .expect("Unable to write to buffer"); } let mut visitor = FmtEvent { comma: false, bufs }; event.record(&mut visitor); visitor .bufs .indent_current(indent, &self.config, SpanMode::Event); let writer = self.make_writer.make_writer(); bufs.flush_current_buf(writer) } fn on_close(&self, id: Id, ctx: Context) { let Some(_guard) = Self::is_recursive() else { return; }; let bufs = &mut *self.bufs.lock().unwrap(); let span = ctx.span(&id).expect("invalid span in on_close"); // Span was not printed, so don't print an exit if self.config.deferred_spans && span.extensions().get::().map(|v| v.written) != Some(true) { return; } // self.write_retrace_span(&span, bufs, &ctx); self.write_span_info( &span, bufs, SpanMode::Close { verbose: self.config.verbose_exit, }, ); if let Some(parent_span) = span.parent() { bufs.current_span = Some(parent_span.id()); if self.config.verbose_exit { // Consider parent as entered self.write_span_info(&parent_span, bufs, SpanMode::PostClose); } } } } fn scope_path<'a, R: LookupSpan<'a>>(span: &SpanRef<'a, R>) -> ScopeFromRoot<'a, R> { span.scope().from_root() } /// Runs `A` and `B` side by side and only yields items present in `B` struct DifferenceIter { left: Fuse, right: R, compare: F, } impl, R: Iterator, T, U: PartialEq, F: Fn(&T) -> U> DifferenceIter { fn new(left: L, right: R, compare: F) -> Self { Self { left: left.fuse(), right, compare, } } } impl, R: Iterator, T, U: PartialEq, F: Fn(&T) -> U> Iterator for DifferenceIter { type Item = T; fn next(&mut self) -> Option { loop { let left = self.left.next(); let right = self.right.next()?; if left.as_ref().map(&self.compare) != Some((self.compare)(&right)) { return Some(right); } } } } tracing-tree-0.4.0/src/time.rs000064400000000000000000000162621046102023000143130ustar 00000000000000use std::{fmt::Write, time::Duration}; use nu_ansi_term::Style; use crate::styled; /// A type that can measure and format the current time. /// /// This trait is used by [HierarchicalLayer] to include a timestamp with each /// [Event] when it is logged. /// /// Notable default implementations of this trait are [LocalDateTime] and `()`. /// The former prints the current time as reported by [time's OffsetDateTime] /// (note that it requires a `time` feature to be enabled and may panic! /// make sure to check out the docs for the [LocalDateTime]), /// and the latter does not print the current time at all. /// /// Inspired by the [FormatTime] trait from [tracing-subscriber]. /// /// [HierarchicalLayer]: crate::HierarchicalLayer /// [Event]: tracing_core::Event /// [time's OffsetDateTime]: time::OffsetDateTime /// [FormatTime]: tracing_subscriber::fmt::time::FormatTime /// [tracing-subscriber]: tracing_subscriber // NB: // We can't use `tracing_subscriber::fmt::format::Writer` // since it doesn't have a public constructor. pub trait FormatTime { fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result; fn style_timestamp( &self, ansi: bool, elapsed: Duration, w: &mut impl std::fmt::Write, ) -> std::fmt::Result; } //////////////////////////////////////////////////////////////////////////////////////////////////// /// Default do-nothing time formatter. impl FormatTime for () { fn format_time(&self, _w: &mut impl std::fmt::Write) -> std::fmt::Result { Ok(()) } fn style_timestamp( &self, _ansi: bool, _elapsed: Duration, _w: &mut impl std::fmt::Write, ) -> std::fmt::Result { Ok(()) } } //////////////////////////////////////////////////////////////////////////////////////////////////// /// Retrieve and print the current wall-clock time in UTC timezone. #[cfg(feature = "time")] #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct UtcDateTime { /// Whether to print the time with higher precision. pub higher_precision: bool, } #[cfg(feature = "time")] impl FormatTime for UtcDateTime { fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result { let time = time::OffsetDateTime::now_utc(); write!(w, "{} {}", time.date(), time.time()) } fn style_timestamp( &self, ansi: bool, elapsed: Duration, w: &mut impl std::fmt::Write, ) -> std::fmt::Result { style_timestamp(ansi, self.higher_precision, elapsed, w) } } //////////////////////////////////////////////////////////////////////////////////////////////////// /// Retrieve and print the current wall-clock time. /// /// # Panics /// /// Panics if [time crate] cannot determine the local UTC offset. /// /// [time crate]: time // NB: // Can't use `tracing_subscriber::fmt::time::SystemTime` since it uses // private `datetime` module to format the actual time. #[cfg(feature = "time")] #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct LocalDateTime { /// Whether to print the time with higher precision. pub higher_precision: bool, } #[cfg(feature = "time")] impl FormatTime for LocalDateTime { fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result { let time = time::OffsetDateTime::now_local().expect("time offset cannot be determined"); write!(w, "{}", time) } fn style_timestamp( &self, ansi: bool, elapsed: Duration, w: &mut impl std::fmt::Write, ) -> std::fmt::Result { style_timestamp(ansi, self.higher_precision, elapsed, w) } } //////////////////////////////////////////////////////////////////////////////////////////////////// /// Retrieve and print the relative elapsed wall-clock time since an epoch. /// /// The `Default` implementation for `Uptime` makes the epoch the current time. // NB: Copy-pasted from `tracing-subscriber::fmt::time::Uptime`. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct Uptime { epoch: std::time::Instant, /// Whether to print the time with higher precision. pub higher_precision: bool, } impl Default for Uptime { fn default() -> Self { Uptime::from(std::time::Instant::now()) } } impl From for Uptime { fn from(epoch: std::time::Instant) -> Self { Uptime { epoch, higher_precision: false, } } } impl FormatTime for Uptime { fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result { let e = self.epoch.elapsed(); write!(w, "{:4}.{:06}s", e.as_secs(), e.subsec_micros()) } fn style_timestamp( &self, ansi: bool, elapsed: Duration, w: &mut impl std::fmt::Write, ) -> std::fmt::Result { style_timestamp(ansi, self.higher_precision, elapsed, w) } } fn style_timestamp( ansi: bool, higher_precision: bool, elapsed: Duration, w: &mut impl Write, ) -> std::fmt::Result { if higher_precision { format_timestamp_with_decimals(ansi, elapsed, w) } else { format_timestamp(ansi, elapsed, w) } } fn format_timestamp(ansi: bool, elapsed: Duration, w: &mut impl Write) -> std::fmt::Result { let millis = elapsed.as_millis(); let secs = elapsed.as_secs(); // Convert elapsed time to appropriate units: ms, s, or m. // - Less than 1s : use ms // - Less than 1m : use s // - 1m and above : use m let (n, unit) = if millis < 1000 { (millis as _, "ms") } else if secs < 60 { (secs, "s ") } else { (secs / 60, "m ") }; let timestamp = format!("{n:>3}"); write_style_timestamp(ansi, timestamp, unit, w) } fn format_timestamp_with_decimals( ansi: bool, elapsed: Duration, w: &mut impl Write, ) -> std::fmt::Result { let secs = elapsed.as_secs_f64(); // Convert elapsed time to appropriate units: μs, ms, or s. // - Less than 1ms: use μs // - Less than 1s : use ms // - 1s and above : use s let (n, unit) = if secs < 0.001 { (secs * 1_000_000.0, "μs") } else if secs < 1.0 { (secs * 1_000.0, "ms") } else { (secs, "s ") }; let timestamp = format!(" {n:.2}"); write_style_timestamp(ansi, timestamp, unit, w) } fn write_style_timestamp( ansi: bool, timestamp: String, unit: &str, w: &mut impl Write, ) -> std::fmt::Result { write!( w, "{timestamp}{unit}", timestamp = styled(ansi, Style::new().dimmed(), timestamp), unit = styled(ansi, Style::new().dimmed(), unit), ) } //////////////////////////////////////////////////////////////////////////////////////////////////// impl<'a, F> FormatTime for &'a F where F: FormatTime, { fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result { F::format_time(self, w) } fn style_timestamp( &self, ansi: bool, duration: Duration, w: &mut impl std::fmt::Write, ) -> std::fmt::Result { F::style_timestamp(self, ansi, duration, w) } } // NB: // Can't impl for `fn(&mut impl std::fmt::Write)` since impl trait is not allowed // outside of function and inherent method return types for now. tracing-tree-0.4.0/tests/format_time.rs000064400000000000000000000045721046102023000162370ustar 00000000000000use std::{ fmt::Write, sync::{ atomic::{AtomicU64, Ordering}, Arc, }, }; use tracing::{span, Level}; use tracing_subscriber::{layer::SubscriberExt, Registry}; use tracing_tree::{time::FormatTime, HierarchicalLayer}; #[derive(Debug)] struct FormatTimeCounter(Arc); impl FormatTime for FormatTimeCounter { fn format_time(&self, _w: &mut impl Write) -> std::fmt::Result { self.0.fetch_add(1, Ordering::Relaxed); Ok(()) } fn style_timestamp( &self, _ansi: bool, _elapsed: std::time::Duration, _w: &mut impl std::fmt::Write, ) -> std::fmt::Result { Ok(()) } } #[test] fn format_time_num_calls() { let num_called = Arc::new(AtomicU64::new(0)); let format_time_counter = FormatTimeCounter(Arc::clone(&num_called)); let layer = HierarchicalLayer::default() .with_writer(std::io::stdout) .with_indent_lines(true) .with_indent_amount(2) .with_timer(format_time_counter) .with_thread_names(true) .with_thread_ids(true) .with_verbose_exit(true) .with_verbose_entry(true) .with_targets(true); let subscriber = Registry::default().with(layer); tracing::subscriber::set_global_default(subscriber).unwrap(); let test_span = span!(Level::TRACE, "format-time-num-calls-test", version = %0.1); let _e = test_span.enter(); tracing::info!("first event"); assert_eq!(num_called.load(Ordering::Relaxed), 1); std::thread::sleep(std::time::Duration::from_millis(100)); tracing::info!("second event"); assert_eq!(num_called.load(Ordering::Relaxed), 2); let nested_span = span!(Level::TRACE, "nested-span"); nested_span.in_scope(|| { tracing::debug!("nested event"); assert_eq!(num_called.load(Ordering::Relaxed), 3); tracing::info!("important nested event"); assert_eq!(num_called.load(Ordering::Relaxed), 4); }); drop(nested_span); instrumented_function(); assert_eq!(num_called.load(Ordering::Relaxed), 6); tracing::info!("exiting"); assert_eq!(num_called.load(Ordering::Relaxed), 7); } #[tracing::instrument] fn instrumented_function() { tracing::info!("instrumented function"); nested_instrumented_function(); } #[tracing::instrument] fn nested_instrumented_function() { tracing::warn!("nested instrumented function"); } tracing-tree-0.4.0/tests/recursive_event.rs000064400000000000000000000031061046102023000171310ustar 00000000000000use std::{io, str, sync::Mutex}; use tracing::subscriber::set_global_default; use tracing_subscriber::{layer::SubscriberExt, registry}; use tracing_tree::HierarchicalLayer; struct RecursiveWriter(Mutex>); impl io::Write for &RecursiveWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.lock().unwrap().extend(buf); tracing::error!("Nobody expects the Spanish Inquisition"); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { tracing::error!("Nobody expects the Spanish Inquisition"); Ok(()) } } /// This test checks that if `tracing` events happen during processing of /// `on_event`, the library does not deadlock. #[test] fn recursive_event() { static WRITER: RecursiveWriter = RecursiveWriter(Mutex::new(Vec::new())); let subscriber = registry().with(HierarchicalLayer::new(2).with_writer(|| &WRITER)); // This has to be its own integration test because we can't just set a // global default like this otherwise and not expect everything else to // break. set_global_default(subscriber).unwrap(); tracing::error!("We can never expect the unexpected."); let output = WRITER.0.lock().unwrap(); let output = str::from_utf8(&output).unwrap(); // If this test finished we're happy. Let's just also check that we did // in fact log _something_ and that the logs from within the writer did // not actually go through. assert!(output.contains("We can never expect the unexpected.")); assert!(!output.contains("Nobody expects the Spanish Inquisition")); } tracing-tree-0.4.0/tests/ui.rs000064400000000000000000000012531046102023000143370ustar 00000000000000use ui_test::{color_eyre::Result, run_tests, Config, Mode, OutputConflictHandling}; fn main() -> Result<()> { let mut config = Config::default(); config.root_dir = "examples".into(); config.dependencies_crate_manifest_path = Some("test_dependencies/Cargo.toml".into()); config.args.push("--cfg".into()); config.args.push("feature=\"tracing-log\"".into()); config.out_dir = Some("target/ui_test".into()); config.mode = Mode::Run { exit_code: 0 }; config.output_conflict_handling = if std::env::args().any(|arg| arg == "--bless") { OutputConflictHandling::Bless } else { OutputConflictHandling::Error }; run_tests(config) }