├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── async-log-attributes ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── examples └── trace.rs ├── src ├── backtrace.rs ├── lib.rs ├── logger.rs └── macros.rs └── tests └── test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | target/ 3 | tmp/ 4 | dist/ 5 | npm-debug.log* 6 | Cargo.lock 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | 5 | before_script: | 6 | rustup component add rustfmt-preview && 7 | rustup component add clippy-preview 8 | script: | 9 | cargo fmt -- --check && 10 | cargo clippy -- -D clippy && 11 | cargo build --verbose && 12 | cargo test --verbose 13 | cache: cargo 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2019-08-10, Version 2.0.0 2 | ### Commits 3 | - [[`1392e471de`](https://github.com/rustasync/async-log/commit/1392e471de555c36c4bb37e80b7e7bacd26b45b3)] (cargo-release) version 2.0.0 (Yoshua Wuyts) 4 | - [[`ac19dddb27`](https://github.com/rustasync/async-log/commit/ac19dddb27a4624edb6eed49adf3ad4f9473d988)] add kv-logging to async-log (#5) (Yoshua Wuyts) 5 | - [[`655203077e`](https://github.com/rustasync/async-log/commit/655203077e00b960063c567032d0346cdbb4276a)] Update changelog (Yoshua Wuyts) 6 | 7 | ### Stats 8 | ```diff 9 | CHANGELOG.md | 18 ++++++++++++++++++ 10 | Cargo.toml | 8 ++++---- 11 | README.md | 5 +---- 12 | examples/trace.rs | 13 +++++-------- 13 | src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 14 | src/logger.rs | 34 ++++++++++++++++++++++++++-------- 15 | 6 files changed, 101 insertions(+), 30 deletions(-) 16 | ``` 17 | 18 | 19 | ## 2019-07-09, Version 1.1.0 20 | ### Commits 21 | - [[`b0f08bf5fd`](https://github.com/rustasync/async-log/commit/b0f08bf5fd18681643d1344992023fc44d073242)] (cargo-release) version 1.1.0 (Yoshua Wuyts) 22 | - [[`c3673c4edf`](https://github.com/rustasync/async-log/commit/c3673c4edfff3cf553c6754853d004cce3a7a476)] Named arguments (#1) (Stjepan Glavina) 23 | - [[`3f56908ae6`](https://github.com/rustasync/async-log/commit/3f56908ae609dd1ecc72a017bc6e67fc3fb4f24c)] fix clippy warnings (#2) (Yoshua Wuyts) 24 | - [[`99dab06054`](https://github.com/rustasync/async-log/commit/99dab0605479462a4a88844514c9792b1bad9eda)] Update changelog (Yoshua Wuyts) 25 | 26 | ### Stats 27 | ```diff 28 | CHANGELOG.md | 15 +++++++++++++++ 29 | Cargo.toml | 2 +- 30 | async-log-attributes/src/lib.rs | 39 +++++++++++++++++++++------------------ 31 | examples/trace.rs | 4 ++-- 32 | src/logger.rs | 3 +-- 33 | 5 files changed, 40 insertions(+), 23 deletions(-) 34 | ``` 35 | 36 | 37 | ## 2019-06-29, Version 1.0.4 38 | ### Commits 39 | - [[`ff66e76568`](https://github.com/rustasync/async-log/commit/ff66e7656818fc6ffc1be772eb5db255294831d4)] (cargo-release) version 1.0.4 (Yoshua Wuyts) 40 | - [[`b014d175c9`](https://github.com/rustasync/async-log/commit/b014d175c99b7e1cd7de3945fc746760b4193b8e)] fix generics ordering (Yoshua Wuyts) 41 | - [[`ab084f46f7`](https://github.com/rustasync/async-log/commit/ab084f46f7704b07b700fd50be1873e3f1b17628)] Update changelog (Yoshua Wuyts) 42 | 43 | ### Stats 44 | ```diff 45 | CHANGELOG.md | 16 ++++++++++++++++ 46 | Cargo.toml | 2 +- 47 | async-log-attributes/src/lib.rs | 6 +++--- 48 | 3 files changed, 20 insertions(+), 4 deletions(-) 49 | ``` 50 | 51 | 52 | ## 2019-06-29, Version 1.0.3 53 | ### Commits 54 | - [[`cd3b2e601e`](https://github.com/rustasync/async-log/commit/cd3b2e601e1e5d4c65bb8e55faa2fb51de165ab9)] (cargo-release) version 1.0.3 (Yoshua Wuyts) 55 | - [[`6523ce359d`](https://github.com/rustasync/async-log/commit/6523ce359d2358e0315ed8234614fd9f54546f30)] enable feature (Yoshua Wuyts) 56 | - [[`2fd679c21a`](https://github.com/rustasync/async-log/commit/2fd679c21a35a639e3ec245cb5c8b3591b3aa5d1)] fmt (Yoshua Wuyts) 57 | - [[`0f5960c436`](https://github.com/rustasync/async-log/commit/0f5960c4365b82b080eae356818f71772b8e20e7)] Update changelog (Yoshua Wuyts) 58 | 59 | ### Stats 60 | ```diff 61 | CHANGELOG.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 62 | Cargo.toml | 4 ++-- 63 | src/logger.rs | 2 +- 64 | 3 files changed, 60 insertions(+), 3 deletions(-) 65 | ``` 66 | 67 | 68 | ## 2019-06-29, Version 1.0.2 69 | ### Commits 70 | - [[`e884e89858`](https://github.com/rustasync/async-log/commit/e884e8985827d555a4ac8281c0bc316670d182c3)] (cargo-release) version 1.0.2 (Yoshua Wuyts) 71 | - [[`70cd60f0fc`](https://github.com/rustasync/async-log/commit/70cd60f0fcfb2afcbce59754b76c000ccbb73511)] disable warns (Yoshua Wuyts) 72 | - [[`411a674289`](https://github.com/rustasync/async-log/commit/411a674289c7918fde6aeaa824c89ab049837eaa)] fix doc comment (Yoshua Wuyts) 73 | - [[`50c3820ebf`](https://github.com/rustasync/async-log/commit/50c3820ebf49492a5378b324af8ebe70e5ffca7b)] 1.0.1 (Yoshua Wuyts) 74 | - [[`45fc22a8cf`](https://github.com/rustasync/async-log/commit/45fc22a8cffce019ba12f514b9a75e233992d4d4)] patch attrs (Yoshua Wuyts) 75 | - [[`49b02e8a04`](https://github.com/rustasync/async-log/commit/49b02e8a0490e2c2f206ff8b8fef0570f441a571)] versions (Yoshua Wuyts) 76 | - [[`1926a00567`](https://github.com/rustasync/async-log/commit/1926a005670ca365abc987f0956c932ba820ec75)] instrument (Yoshua Wuyts) 77 | - [[`a0e570a7c7`](https://github.com/rustasync/async-log/commit/a0e570a7c79493836b1e814e691f8e262e04e1bb)] cleanup base use (Yoshua Wuyts) 78 | - [[`268de1b9ab`](https://github.com/rustasync/async-log/commit/268de1b9ab16c63e1b8358f20d86fe78dd0f9520)] async-log (Yoshua Wuyts) 79 | - [[`216dcdec73`](https://github.com/rustasync/async-log/commit/216dcdec73da98044daa76ee429512655e2e8c05)] attr names (Yoshua Wuyts) 80 | - [[`ba1b99e171`](https://github.com/rustasync/async-log/commit/ba1b99e1710ddb3be3f9e83ba1fa803a00579abc)] runtime attributes (Yoshua Wuyts) 81 | - [[`f5f37cfb9c`](https://github.com/rustasync/async-log/commit/f5f37cfb9caa81128c44cc7cd4836737e5f69392)] no parent id (Yoshua Wuyts) 82 | - [[`0bcbc4c04a`](https://github.com/rustasync/async-log/commit/0bcbc4c04a66d10afc0e953733dd0e16504e48fd)] threadid (Yoshua Wuyts) 83 | - [[`cac9c6cb33`](https://github.com/rustasync/async-log/commit/cac9c6cb332dde522bf1d6376c555e14fe997391)] async logger (Yoshua Wuyts) 84 | - [[`03ac5ba330`](https://github.com/rustasync/async-log/commit/03ac5ba3303126e3ab7b9c7cc099f3e2cc8dd3ea)] span docs (Yoshua Wuyts) 85 | - [[`d9d8bed1e8`](https://github.com/rustasync/async-log/commit/d9d8bed1e82448491186cdc2e508d2ea3176c178)] docs (Yoshua Wuyts) 86 | - [[`44e09628a5`](https://github.com/rustasync/async-log/commit/44e09628a50cdb724f108f4334b11f3e685d6d3e)] Logger (Yoshua Wuyts) 87 | - [[`f2c565fc53`](https://github.com/rustasync/async-log/commit/f2c565fc5301279dae39349fb321ae0a0381916b)] be able to build the thing (Yoshua Wuyts) 88 | - [[`6a32c99629`](https://github.com/rustasync/async-log/commit/6a32c9962944bc97d2c9625885561228745d882f)] finalize logger (Yoshua Wuyts) 89 | - [[`7b8281bb48`](https://github.com/rustasync/async-log/commit/7b8281bb48b7b073baaff3005a664351ca4d33be)] start the logger (Yoshua Wuyts) 90 | - [[`566c731f7e`](https://github.com/rustasync/async-log/commit/566c731f7e4cca2c8e6348313e1ce3d232417c39)] better grpahs (Yoshua Wuyts) 91 | - [[`6d1c3a6c08`](https://github.com/rustasync/async-log/commit/6d1c3a6c087054908b849d6156017185309cb5d7)] docs (Yoshua Wuyts) 92 | - [[`05b93190d8`](https://github.com/rustasync/async-log/commit/05b93190d8d3eab18fc299589f0bc4a395bfe3cd)] cleanup (Yoshua Wuyts) 93 | - [[`8337c02fd7`](https://github.com/rustasync/async-log/commit/8337c02fd724cfb51e69e16ac868401f4ef0ca6c)] example logger (Yoshua Wuyts) 94 | - [[`fa9c24a08f`](https://github.com/rustasync/async-log/commit/fa9c24a08f0c781932cd9363a1923719e429b63a)] . (Yoshua Wuyts) 95 | 96 | ### Stats 97 | ```diff 98 | .github/CODE_OF_CONDUCT.md | 75 ++++++++++++- 99 | .github/CONTRIBUTING.md | 55 +++++++++- 100 | .github/ISSUE_TEMPLATE.md | 9 +- 101 | .github/ISSUE_TEMPLATE/bug_report.md | 23 ++++- 102 | .github/ISSUE_TEMPLATE/feature_request.md | 43 +++++++- 103 | .github/ISSUE_TEMPLATE/question.md | 18 +++- 104 | .github/PULL_REQUEST_TEMPLATE.md | 14 ++- 105 | .github/stale.yml | 17 +++- 106 | .gitignore | 7 +- 107 | .travis.yml | 13 ++- 108 | Cargo.toml | 26 ++++- 109 | LICENSE-APACHE | 190 +++++++++++++++++++++++++++++++- 110 | LICENSE-MIT | 21 +++- 111 | README.md | 150 ++++++++++++++++++++++++- 112 | async-log-attributes/Cargo.toml | 21 +++- 113 | async-log-attributes/README.md | 47 ++++++++- 114 | async-log-attributes/src/lib.rs | 60 ++++++++++- 115 | examples/trace.rs | 30 +++++- 116 | src/backtrace.rs | 48 ++++++++- 117 | src/lib.rs | 151 +++++++++++++++++++++++++- 118 | src/logger.rs | 130 +++++++++++++++++++++- 119 | src/macros.rs | 72 ++++++++++++- 120 | tests/test.rs | 6 +- 121 | 23 files changed, 1226 insertions(+) 122 | ``` 123 | 124 | 125 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-log" 3 | version = "2.0.0" 4 | license = "MIT OR Apache-2.0" 5 | repository = "https://github.com/rustasync/async-log" 6 | documentation = "https://docs.rs/async-log" 7 | description = "Async tracing capabilities for the log crate." 8 | keywords = ["async", "log", "trace", "span", "macro"] 9 | categories = ["asynchronous", "command-line-utilities", "development-tools", "text-processing", "web-programming"] 10 | authors = ["Yoshua Wuyts "] 11 | readme = "README.md" 12 | edition = "2018" 13 | 14 | [dependencies] 15 | log = { version = "0.4.8", features = ["std", "kv_unstable"] } 16 | backtrace = "0.3.34" 17 | async-log-attributes = { path = "async-log-attributes", version = "1.0.1" } 18 | 19 | [dev-dependencies] 20 | femme = "1.2.0" 21 | 22 | [workspace] 23 | members = [ 24 | ".", 25 | "async-log-attributes", 26 | ] 27 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2019 Yoshua Wuyts 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Yoshua Wuyts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-log 2 | [![crates.io version][1]][2] [![build status][3]][4] 3 | [![downloads][5]][6] [![docs.rs docs][7]][8] 4 | 5 | Async tracing capabilities for the standard [`log`] crate. 6 | 7 | [`log`]: https://docs.rs/log 8 | 9 | - [Documentation][8] 10 | - [Crates.io][2] 11 | - [Releases][releases] 12 | 13 | This crate provides extension types and hooks to `log` to enable asynchronous logging. 14 | 15 | ## What is Async Logging? 16 | When building a _synchronous_ application, log messages can be relied on to always happen 17 | in sequence. But unfortunately synchronous applications are rarely capable of utilizating 18 | system resources to their full potential. 19 | 20 | In contrast, concurrent applications make a lot better use of system resources. But it also 21 | means we can no longer rely on log messages to strictly happen in sequence. In order to make 22 | sense of logs in asynchronous applications, we need to be able to correlate sequences of events 23 | with each other: 24 | 25 | ```txt 26 | a1 -> b1 -> b2 -> a2 -> b3 # raw log stream 27 | 28 | a1 -------------> a2 # parsed log stream a 29 | b1 -> b2 -------> b3 # parsed log stream b 30 | ``` 31 | _The raw log stream contains items for both "a" and "b". With async logging you want to be able 32 | to distinguish between the items for "a", and the items from "b"._ 33 | 34 | ## How do we correlate messages? 35 | The goal of async logging is to determine which events happened in sequence inside your code. In 36 | practice this means being able to correlate events with each other past _yield points_ (e.g. 37 | `.await`), and _thread bounds_. 38 | 39 | The way we do this is by adding the current task ID, and thread ID from where the log is 40 | occurring. An whenever a _new_ task is spawned we log the following values: 41 | 42 | - The ID of the task from which the new task is spawned (`task_parent_id`) 43 | - The ID of the new task that's spawned (`task_id`) 44 | - The current thread ID (`thread_id`) 45 | - The line from where the task was spawned (`spawn_line`, when `RUST_BACKTRACE=1` enabled) 46 | 47 | With all this information we have all the information to correlate tasks with each other. We 48 | know what the parent task was, what the new task is, and log that information together. On the 49 | receiving side we can then reconstruct that to create correlations. 50 | 51 | ## What is a span? 52 | A span is a pair of messages. One is emitted at the start of an operation, and the other is 53 | emitted at the end of the operation. If we add timestamps to when each message was sent, we're 54 | able to determine how long operations take. Or determine which operations never finished. 55 | 56 | In `async-log` each span is annotated with a `span_mark` message: 57 | - `span_mark=start` marks the start of a span 58 | - `span_mark=end` marks the end of a span 59 | 60 | __example__ 61 | ```txt 62 | runtime::fs::read_to_string, span_mark=start, path=/tmp/foob, task_id=7, thread_id=8 63 | runtime::fs::read_to_string, span_mark=end, path=/tmp/foob, task_id=7, thread_id=8 64 | ``` 65 | 66 | ## Why build on the log crate? 67 | [`log`](https://docs.rs/log/) is Rust's standard log crate. It's incredibly flexible, and was 68 | built with extensibility in mind. Because it's so widely used, being able to extend it allows 69 | us to add tracing data to crates without needing to make any changes to their `log!` calls. 70 | 71 | ## Formatting 72 | Structured logging (key-value logging) is [currently in the 73 | process](https://github.com/rust-lang-nursery/log/issues/328) of being added to `log`. 74 | 75 | At the time of writing there are no published versions available with even the experimental 76 | features available. So until then we have to add key-value pairs using strings. Once key-value 77 | logging is added to `log` we'll publish a breaking change, and move over. 78 | 79 | The syntax we've chosen to use is `foo=bar` pairs. Multiple pairs should be delimited using 80 | commas (`,`). Every pair should come _after_ the first message. An example log looks like this: 81 | 82 | ```txt 83 | a new cat has logged on, name=nori, snacks=always 84 | ``` 85 | 86 | ## Examples 87 | ```rust 88 | use async_log::span; 89 | use log::info; 90 | 91 | fn setup_logger() { 92 | let logger = femme::pretty::Logger::new(); 93 | async_log::Logger::wrap(logger, || 12) 94 | .start(log::LevelFilter::Trace) 95 | .unwrap(); 96 | } 97 | 98 | fn main() { 99 | setup_logger(); 100 | 101 | span!("level I", { 102 | let x = "beep"; 103 | info!("look at this value, x={}", x); 104 | 105 | span!("level II", { 106 | let y = "boop"; 107 | info!("another nice value, y={}", y); 108 | }) 109 | }) 110 | } 111 | ``` 112 | 113 | ## Installation 114 | ```sh 115 | $ cargo add async-log 116 | ``` 117 | 118 | ## Safety 119 | This crate uses ``#![deny(unsafe_code)]`` to ensure everything is implemented in 120 | 100% Safe Rust. 121 | 122 | ## Contributing 123 | Want to join us? Check out our ["Contributing" guide][contributing] and take a 124 | look at some of these issues: 125 | 126 | - [Issues labeled "good first issue"][good-first-issue] 127 | - [Issues labeled "help wanted"][help-wanted] 128 | 129 | ## References 130 | - [log](http://docs.rs/log) 131 | 132 | ## License 133 | [MIT](./LICENSE-MIT) OR [Apache-2.0](./LICENSE-APACHE) 134 | 135 | [1]: https://img.shields.io/crates/v/async-log.svg?style=flat-square 136 | [2]: https://crates.io/crates/async-log 137 | [3]: https://img.shields.io/travis/rustasync/async-log/master.svg?style=flat-square 138 | [4]: https://travis-ci.org/rustasync/async-log 139 | [5]: https://img.shields.io/crates/d/async-log.svg?style=flat-square 140 | [6]: https://crates.io/crates/async-log 141 | [7]: https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square 142 | [8]: https://docs.rs/async-log 143 | 144 | [releases]: https://github.com/rustasync/async-log/releases 145 | [contributing]: https://github.com/rustasync/async-log/blob/master.github/CONTRIBUTING.md 146 | [good-first-issue]: https://github.com/rustasync/async-log/labels/good%20first%20issue 147 | [help-wanted]: https://github.com/rustasync/async-log/labels/help%20wanted 148 | -------------------------------------------------------------------------------- /async-log-attributes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-log-attributes" 3 | description = "Proc Macro attributes for the async-log crate." 4 | version = "1.0.1" 5 | license = "MIT OR Apache-2.0" 6 | readme = "README.md" 7 | repository = "https://github.com/rustasync/runtime" 8 | homepage = "https://github.com/rustasync/runtime" 9 | documentation = "https://docs.rs/runtime-attributes" 10 | authors = ["The Rust Async Ecosystem Working Group"] 11 | keywords = ["async", "log", "trace", "span", "macro"] 12 | categories = ["asynchronous", "command-line-utilities", "development-tools", "text-processing", "web-programming"] 13 | edition = "2018" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | syn = { version = "0.15.33", features = ["full"] } 20 | proc-macro2 = { version = "0.4.29", features = ["nightly"] } 21 | quote = "0.6.12" 22 | -------------------------------------------------------------------------------- /async-log-attributes/README.md: -------------------------------------------------------------------------------- 1 | # runtime-attributes 2 | Proc Macro attributes for the [Runtime](https://github.com/rustasync/runtime) crate. See the 3 | [Runtime](https://docs.rs/runtime) documentation for more details. 4 | 5 | __This macro was designed to be used from the Runtime crate. Using this in any other way is unlikely 6 | to work.__ 7 | 8 | ## Installation 9 | With [cargo-edit](https://crates.io/crates/cargo-edit) do: 10 | ```sh 11 | $ cargo add runtime-attributes 12 | ``` 13 | 14 | ## Safety 15 | This crate uses ``#![deny(unsafe_code)]`` to ensure everything is implemented in 100% Safe Rust. 16 | 17 | ## Contributing 18 | Want to join us? Check out our [The "Contributing" section of the 19 | guide][contributing] and take a look at some of these issues: 20 | 21 | - [Issues labeled "good first issue"][good-first-issue] 22 | - [Issues labeled "help wanted"][help-wanted] 23 | 24 | #### Conduct 25 | 26 | The Runtime project adheres to the [Contributor Covenant Code of 27 | Conduct](https://github.com/rustasync/runtime/blob/master/.github/CODE_OF_CONDUCT.md). This 28 | describes the minimum behavior expected from all contributors. 29 | 30 | ## License 31 | Licensed under either of 32 | 33 | * Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 34 | * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) 35 | 36 | at your option. 37 | 38 | #### Contribution 39 | 40 | Unless you explicitly state otherwise, any contribution intentionally submitted 41 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 42 | dual licensed as above, without any additional terms or conditions. 43 | 44 | [releases]: https://github.com/rustasync/runtime/releases 45 | [contributing]: https://github.com/rustasync/runtime/blob/master/.github/CONTRIBUTING.md 46 | [good-first-issue]: https://github.com/rustasync/runtime/labels/good%20first%20issue 47 | [help-wanted]: https://github.com/rustasync/runtime/labels/help%20wanted 48 | -------------------------------------------------------------------------------- /async-log-attributes/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Proc Macro attributes for the `async-log` crate. 2 | 3 | #![forbid(unsafe_code, future_incompatible, rust_2018_idioms)] 4 | #![deny(missing_debug_implementations, nonstandard_style)] 5 | #![recursion_limit = "512"] 6 | 7 | extern crate proc_macro; 8 | 9 | use proc_macro::TokenStream; 10 | use quote::{quote, quote_spanned}; 11 | use syn::spanned::Spanned; 12 | 13 | /// Defines the `instrument` function. 14 | #[proc_macro_attribute] 15 | pub fn instrument(_attr: TokenStream, item: TokenStream) -> TokenStream { 16 | let input = syn::parse_macro_input!(item as syn::ItemFn); 17 | 18 | let attrs = &input.attrs; 19 | let vis = &input.vis; 20 | let constness = &input.constness; 21 | let unsafety = &input.unsafety; 22 | let asyncness = &input.asyncness; 23 | let abi = &input.abi; 24 | 25 | let generics = &input.decl.generics; 26 | let name = &input.ident; 27 | let inputs = &input.decl.inputs; 28 | let output = &input.decl.output; 29 | let body = &input.block.stmts; 30 | 31 | let mut names = String::new(); 32 | let mut args = Vec::::new(); 33 | 34 | for fn_arg in inputs { 35 | if let syn::FnArg::Captured(arg) = fn_arg { 36 | let pat = arg.pat.clone(); 37 | 38 | if let syn::Pat::Ident(pat_ident) = &pat { 39 | names.push_str(&format!(", {}={{:?}}", pat_ident.ident)); 40 | } else { 41 | let tokens = quote_spanned! { fn_arg.span() => 42 | compile_error!("instrumented functions need to name arguments"); 43 | }; 44 | return TokenStream::from(tokens); 45 | } 46 | 47 | args.push(pat); 48 | } 49 | } 50 | 51 | let result = quote! { 52 | #(#attrs)* 53 | #vis #constness #unsafety #asyncness #abi fn #name #generics (#(#inputs)*) #output { 54 | let __name = format!("{}#{}", file!(), stringify!(#name)); 55 | let __args = format!("{}{}", __name, format_args!(#names, #(#args)*)); 56 | async_log::span!(__args, { 57 | #(#body)* 58 | }) 59 | } 60 | }; 61 | 62 | result.into() 63 | } 64 | -------------------------------------------------------------------------------- /examples/trace.rs: -------------------------------------------------------------------------------- 1 | use async_log::{instrument, span}; 2 | use log::info; 3 | 4 | fn setup_logger() { 5 | let logger = femme::pretty::Logger::new(); 6 | async_log::Logger::wrap(logger, || /* get the task id here */ 0) 7 | .start(log::LevelFilter::Trace) 8 | .unwrap(); 9 | } 10 | 11 | fn main() { 12 | setup_logger(); 13 | 14 | span!("level {}", 1, { 15 | let x = "beep"; 16 | info!("look at this value: {}", x); 17 | 18 | span!("level {}", 2, { 19 | inner("boop"); 20 | }) 21 | }) 22 | } 23 | 24 | #[instrument] 25 | fn inner(y: &str) { 26 | info!("another nice value: {}", y); 27 | } 28 | -------------------------------------------------------------------------------- /src/backtrace.rs: -------------------------------------------------------------------------------- 1 | /// The return type of `async_log_capture_caller` 2 | #[derive(Debug)] 3 | pub(crate) struct Symbol { 4 | pub(crate) name: Option, 5 | pub(crate) lineno: Option, 6 | pub(crate) filename: Option, 7 | } 8 | 9 | /// Find the method that called this. 10 | /// 11 | /// `depth` is how many frames we should look past finding the method where this was called. 12 | /// This might require a bit of wrangling to find. 13 | #[allow(unused_attributes)] 14 | #[no_mangle] 15 | pub(crate) fn async_log_capture_caller(depth: u8) -> Option { 16 | let mut count = 0; 17 | let mut counting = false; 18 | let self_name = "async_log_capture_caller"; 19 | let mut ret_symbol: Option = None; 20 | 21 | backtrace::trace(|frame| { 22 | if ret_symbol.is_some() { 23 | return false; 24 | } 25 | backtrace::resolve_frame(frame, |symbol| { 26 | if let Some(name) = symbol.name() { 27 | if format!("{}", name) == self_name { 28 | counting = true; 29 | } 30 | 31 | if !counting { 32 | return; 33 | } 34 | 35 | count += 1; 36 | if count == depth { 37 | ret_symbol = Some(Symbol { 38 | name: symbol.name().map(|s| format!("{}", s)), 39 | lineno: symbol.lineno(), 40 | filename: symbol.filename().map(|p| p.to_path_buf()), 41 | }) 42 | } 43 | } 44 | }); 45 | true 46 | }); 47 | ret_symbol 48 | } 49 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Async tracing capabilities for the standard [`log`] crate. 2 | //! 3 | //! [`log`]: https://docs.rs/log 4 | //! 5 | //! This crate provides extension types and hooks to `log` to enable asynchronous logging. 6 | //! 7 | //! ## What is Async Logging? 8 | //! When building a _synchronous_ application, log messages can be relied on to always happen 9 | //! in sequence. But unfortunately synchronous applications are rarely capable of utilizating 10 | //! system resources to their full potential. 11 | //! 12 | //! In contrast, concurrent applications make a lot better use of system resources. But it also 13 | //! means we can no longer rely on log messages to strictly happen in sequence. In order to make 14 | //! sense of logs in asynchronous applications, we need to be able to correlate sequences of events 15 | //! with each other: 16 | //! 17 | //! ```txt 18 | //! a1 -> b1 -> b2 -> a2 -> b3 # raw log stream 19 | //! 20 | //! a1 -------------> a2 # parsed log stream a 21 | //! b1 -> b2 -------> b3 # parsed log stream b 22 | //! ``` 23 | //! _The raw log stream contains items for both "a" and "b". With async logging you want to be able 24 | //! to distinguish between the items for "a", and the items from "b"._ 25 | //! 26 | //! ## How do we correlate messages? 27 | //! The goal of async logging is to determine which events happened in sequence inside your code. In 28 | //! practice this means being able to correlate events with each other past _yield points_ (e.g. 29 | //! `.await`), and _thread bounds_. 30 | //! 31 | //! The way we do this is by adding the current task ID, and thread ID from where the log is 32 | //! occurring. An whenever a _new_ task is spawned we log the following values: 33 | //! 34 | //! - The ID of the task from which the new task is spawned (`task_parent_id`) 35 | //! - The ID of the new task that's spawned (`task_id`) 36 | //! - The current thread ID (`thread_id`) 37 | //! - The line from where the task was spawned (`spawn_line`, when `RUST_BACKTRACE=1` enabled) 38 | //! 39 | //! With all this information we have all the information to correlate tasks with each other. We 40 | //! know what the parent task was, what the new task is, and log that information together. On the 41 | //! receiving side we can then reconstruct that to create correlations. 42 | //! 43 | //! ## What is a span? 44 | //! A span is a pair of messages. One is emitted at the start of an operation, and the other is 45 | //! emitted at the end of the operation. If we add timestamps to when each message was sent, we're 46 | //! able to determine how long operations take. Or determine which operations never finished. 47 | //! 48 | //! In `async-log` each span is annotated with a `span_mark` message: 49 | //! - `span_mark=start` marks the start of a span 50 | //! - `span_mark=end` marks the end of a span 51 | //! 52 | //! __example__ 53 | //! ```txt 54 | //! runtime::fs::read_to_string, span_mark=start, path=/tmp/foob, task_id=7, thread_id=8 55 | //! runtime::fs::read_to_string, span_mark=end, path=/tmp/foob, task_id=7, thread_id=8 56 | //! ``` 57 | //! 58 | //! ## Why build on the log crate? 59 | //! [`log`](https://docs.rs/log/) is Rust's standard log crate. It's incredibly flexible, and was 60 | //! built with extensibility in mind. Because it's so widely used, being able to extend it allows 61 | //! us to add tracing data to crates without needing to make any changes to their `log!` calls. 62 | //! 63 | //! ## Formatting 64 | //! Structured logging (key-value logging) is [currently in the 65 | //! process](https://github.com/rust-lang-nursery/log/issues/328) of being added to `log`. 66 | //! 67 | //! At the time of writing there are no published versions available with even the experimental 68 | //! features available. So until then we have to add key-value pairs using strings. Once key-value 69 | //! logging is added to `log` we'll publish a breaking change, and move over. 70 | //! 71 | //! The syntax we've chosen to use is `foo=bar` pairs. Multiple pairs should be delimited using 72 | //! commas (`,`). Every pair should come _after_ the first message. An example log looks like this: 73 | //! 74 | //! ```txt 75 | //! a new cat has logged on, name=nori, snacks=always 76 | //! ``` 77 | //! 78 | //! ## Example 79 | //! 80 | //! ```rust 81 | //! use async_log::span; 82 | //! use log::info; 83 | //! 84 | //! fn setup_logger() { 85 | //! let logger = femme::pretty::Logger::new(); 86 | //! async_log::Logger::wrap(logger, || 12) 87 | //! .start(log::LevelFilter::Trace) 88 | //! .unwrap(); 89 | //! } 90 | //! 91 | //! fn main() { 92 | //! setup_logger(); 93 | //! 94 | //! span!("new level, depth={}", 1, { 95 | //! let x = "beep"; 96 | //! info!("look at this value, x={}", x); 97 | //! 98 | //! span!("new level, depth={}", 2, { 99 | //! let y = "boop"; 100 | //! info!("another nice value, y={}", y); 101 | //! }) 102 | //! }) 103 | //! } 104 | //! ``` 105 | 106 | #![forbid(unsafe_code, future_incompatible, rust_2018_idioms)] 107 | #![deny(missing_debug_implementations, nonstandard_style)] 108 | #![warn(missing_docs, unreachable_pub)] 109 | #![cfg_attr(test, deny(warnings))] 110 | 111 | pub use async_log_attributes::instrument; 112 | 113 | use std::fmt::Arguments; 114 | 115 | mod backtrace; 116 | mod logger; 117 | mod macros; 118 | 119 | pub use logger::Logger; 120 | 121 | /// A new span created by [`span!`]. 122 | /// 123 | /// An `trace!` is emitted when this struct is constructed. And another `trace!` is emitted when 124 | /// this struct is dropped. 125 | /// 126 | /// [`span!`]: macro.span.html 127 | #[must_use] 128 | #[derive(Debug)] 129 | pub struct Span { 130 | args: String, 131 | } 132 | 133 | impl Span { 134 | /// Create a new instance. 135 | /// 136 | /// You should generally prefer to call `span!` instead of constructing this manually. 137 | pub fn new(args: impl AsRef) -> Self { 138 | let args = args.as_ref(); 139 | struct KeyValues; 140 | impl log::kv::Source for KeyValues { 141 | fn visit<'kvs>( 142 | &'kvs self, 143 | visitor: &mut dyn log::kv::Visitor<'kvs>, 144 | ) -> Result<(), log::kv::Error> { 145 | visitor.visit_pair("span_mark".into(), "start".into())?; 146 | Ok(()) 147 | } 148 | } 149 | 150 | print(log::Level::Trace, format_args!("{}", args), KeyValues {}); 151 | Self { 152 | args: args.to_owned(), 153 | } 154 | } 155 | } 156 | 157 | impl Drop for Span { 158 | fn drop(&mut self) { 159 | struct KeyValues; 160 | impl log::kv::Source for KeyValues { 161 | fn visit<'kvs>( 162 | &'kvs self, 163 | visitor: &mut dyn log::kv::Visitor<'kvs>, 164 | ) -> Result<(), log::kv::Error> { 165 | visitor.visit_pair("span_mark".into(), "end".into())?; 166 | Ok(()) 167 | } 168 | } 169 | 170 | print( 171 | log::Level::Trace, 172 | format_args!("{}", self.args), 173 | KeyValues {}, 174 | ); 175 | } 176 | } 177 | 178 | fn print(level: log::Level, msg: Arguments<'_>, key_values: impl log::kv::Source) { 179 | if level <= log::STATIC_MAX_LEVEL && level <= log::max_level() { 180 | log::logger().log( 181 | &log::Record::builder() 182 | .args(msg) 183 | .key_values(&key_values) 184 | .level(level) 185 | .target(module_path!()) 186 | .module_path(Some(module_path!())) 187 | .file(Some(file!())) 188 | .line(Some(line!())) 189 | .build(), 190 | ); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/logger.rs: -------------------------------------------------------------------------------- 1 | use crate::backtrace::async_log_capture_caller; 2 | use log::{set_boxed_logger, LevelFilter, Log, Metadata, Record}; 3 | 4 | use std::thread; 5 | 6 | /// A Logger that wraps other loggers to extend it with async functionality. 7 | #[derive(Debug)] 8 | pub struct Logger 9 | where 10 | F: Fn() -> u64 + Send + Sync + 'static, 11 | { 12 | backtrace: bool, 13 | logger: L, 14 | with: F, 15 | } 16 | 17 | impl Logger 18 | where 19 | F: Fn() -> u64 + Send + Sync + 'static, 20 | { 21 | /// Wrap an existing logger, extending it with async functionality. 22 | pub fn wrap(logger: L, with: F) -> Self { 23 | let backtrace = std::env::var_os("RUST_BACKTRACE") 24 | .map(|x| &x != "0") 25 | .unwrap_or(false); 26 | Self { 27 | logger, 28 | backtrace, 29 | with, 30 | } 31 | } 32 | 33 | /// Start logging. 34 | pub fn start(self, filter: LevelFilter) -> Result<(), log::SetLoggerError> { 35 | let res = set_boxed_logger(Box::new(self)); 36 | if res.is_ok() { 37 | log::set_max_level(filter); 38 | } 39 | res 40 | } 41 | 42 | /// Call the `self.with` closure, and return its results. 43 | fn with(&self) -> u64 { 44 | (self.with)() 45 | } 46 | 47 | /// Compute which stack frame to log based on an offset defined inside the log message. 48 | /// This message is then stripped from the resulting record. 49 | fn compute_stack_depth(&self, _record: &Record<'_>) -> u8 { 50 | 4 51 | } 52 | } 53 | 54 | /// Get the thread id. Useful because ThreadId doesn't implement Display. 55 | fn thread_id() -> u64 { 56 | let mut string = format!("{:?}", thread::current().id()); 57 | string.replace_range(0..9, ""); 58 | string.pop(); 59 | string.parse().unwrap() 60 | } 61 | 62 | impl log::Log for Logger 63 | where 64 | F: Fn() -> u64 + Send + Sync + 'static, 65 | { 66 | fn enabled(&self, metadata: &Metadata<'_>) -> bool { 67 | self.logger.enabled(metadata) 68 | } 69 | 70 | fn log(&self, record: &Record<'_>) { 71 | if self.enabled(record.metadata()) { 72 | let depth = self.compute_stack_depth(&record); 73 | let symbol = async_log_capture_caller(depth); 74 | 75 | let key_values = KeyValues { 76 | thread_id: thread_id(), 77 | task_id: self.with(), 78 | kvs: record.key_values(), 79 | }; 80 | 81 | let (line, filename, fn_name) = if self.backtrace { 82 | match symbol { 83 | Some(symbol) => { 84 | let line = symbol 85 | .lineno 86 | .map(|l| format!(", line={}", l)) 87 | .unwrap_or_else(|| String::from("")); 88 | 89 | let filename = symbol 90 | .filename 91 | .map(|f| format!(", filename={}", f.to_string_lossy())) 92 | .unwrap_or_else(|| String::from("")); 93 | 94 | let fn_name = symbol 95 | .name 96 | .map(|l| format!(", fn_name={}", l)) 97 | .unwrap_or_else(|| String::from("")); 98 | 99 | (line, filename, fn_name) 100 | } 101 | None => (String::from(""), String::from(""), String::from("")), 102 | } 103 | } else { 104 | (String::from(""), String::from(""), String::from("")) 105 | }; 106 | 107 | // This is done this way b/c `Record` + `format_args` needs to be built inline. See: 108 | // https://stackoverflow.com/q/56304313/1541707 109 | self.logger.log( 110 | &log::Record::builder() 111 | .args(format_args!( 112 | "{}{}{}{}", 113 | record.args(), 114 | filename, 115 | line, 116 | fn_name, 117 | )) 118 | .metadata(record.metadata().clone()) 119 | .key_values(&key_values) 120 | .level(record.level()) 121 | .target(record.target()) 122 | .module_path(record.module_path()) 123 | .file(record.file()) 124 | .line(record.line()) 125 | .build(), 126 | ) 127 | } 128 | } 129 | fn flush(&self) {} 130 | } 131 | 132 | struct KeyValues<'a> { 133 | thread_id: u64, 134 | task_id: u64, 135 | kvs: &'a dyn log::kv::Source, 136 | } 137 | impl<'a> log::kv::Source for KeyValues<'a> { 138 | fn visit<'kvs>( 139 | &'kvs self, 140 | visitor: &mut dyn log::kv::Visitor<'kvs>, 141 | ) -> Result<(), log::kv::Error> { 142 | self.kvs.visit(visitor)?; 143 | visitor.visit_pair("thread_id".into(), self.thread_id.into())?; 144 | visitor.visit_pair("task_id".into(), self.task_id.into())?; 145 | Ok(()) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | #[macro_export] 3 | macro_rules! span_inner { 4 | ($args:expr, $block:expr) => {{ 5 | let span = async_log::Span::new($args); 6 | let res = $block; 7 | drop(span); 8 | res 9 | }}; 10 | } 11 | 12 | /// Create a tracing span. 13 | /// 14 | /// Spans are pairs of `trace!` logs. Every `span` wraps a block, and logs a message at the start, 15 | /// of the block and a message after the block has finished. This works in asynchronous contexts 16 | /// too. 17 | /// 18 | /// Each span takes a `name`, a block, and optionally a list of key-value pairs in between those. 19 | /// Once structured logging becomes part of `log` (currently feature gated as `kv_unstable`), we'll 20 | /// move to support arbitrary key-value pairs. 21 | /// 22 | /// Because of the way this macro is constructed, we currently support up to 9 key-value pairs. 23 | /// Which makes a total of 12 arguments. 24 | /// 25 | /// ## Examples 26 | /// ``` 27 | /// use async_log::span; 28 | /// use log::info; 29 | /// 30 | /// span!("main", { 31 | /// let x = "foo"; 32 | /// info!("this {}", x); 33 | /// 34 | /// span!("inner, x={}", x, { 35 | /// info!("we must go deeper {}", x); 36 | /// }); 37 | /// }) 38 | /// ``` 39 | #[macro_export] 40 | macro_rules! span { 41 | ($args:expr, $block:expr) => {{ 42 | async_log::span_inner!($args, $block) 43 | }}; 44 | ($args:expr, $a:expr, $block:expr) => {{ 45 | let args = format!($args, $a); 46 | async_log::span_inner!(args, $block) 47 | }}; 48 | ($args:expr, $a:expr, $b:expr, $block:expr) => {{ 49 | let args = format!($args, $a, $b); 50 | async_log::span_inner!(args, $block) 51 | }}; 52 | ($args:expr, $a:expr, $b:expr, $c:expr, $block:expr) => {{ 53 | let args = format!($args, $a, $b, $c); 54 | async_log::span_inner!(args, $block) 55 | }}; 56 | ($args:expr, $a:expr, $b:expr, $c:expr, $d:expr, $block:expr) => {{ 57 | let args = format!($args, $a, $b, $c, $d); 58 | async_log::span_inner!(args, $block) 59 | }}; 60 | ($args:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $block:expr) => {{ 61 | let args = format!($args, $a, $b, $c, $d, $e); 62 | async_log::span_inner!(args, $block) 63 | }}; 64 | ($args:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $block:expr) => {{ 65 | let args = format!($args, $a, $b, $c, $d, $e, $f); 66 | async_log::span_inner!(args, $block) 67 | }}; 68 | ($args:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $block:expr) => {{ 69 | let args = format!($args, $a, $b, $c, $d, $e, $f, $g); 70 | async_log::span_inner!(args, $block) 71 | }}; 72 | } 73 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | #[test] 4 | fn should_work() -> Result<(), Box> { 5 | Ok(()) 6 | } 7 | --------------------------------------------------------------------------------