logust 0.3.2__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. {logust-0.3.2 → logust-0.4.0}/.github/workflows/CI.yml +1 -1
  2. logust-0.4.0/.github/workflows/docs.yml +85 -0
  3. {logust-0.3.2 → logust-0.4.0}/.github/workflows/freethreaded-release.yml +1 -1
  4. {logust-0.3.2 → logust-0.4.0}/.github/workflows/test.yml +29 -3
  5. {logust-0.3.2 → logust-0.4.0}/CHANGELOG.md +10 -1
  6. {logust-0.3.2 → logust-0.4.0}/Cargo.lock +41 -43
  7. {logust-0.3.2 → logust-0.4.0}/Cargo.toml +2 -2
  8. {logust-0.3.2 → logust-0.4.0}/PKG-INFO +49 -1
  9. {logust-0.3.2 → logust-0.4.0}/README.md +48 -0
  10. {logust-0.3.2 → logust-0.4.0}/docs/api.md +89 -0
  11. {logust-0.3.2 → logust-0.4.0}/docs/getting-started/installation.md +10 -0
  12. logust-0.4.0/docs/guide/canonical-events.md +191 -0
  13. {logust-0.3.2 → logust-0.4.0}/docs/guide/formatting.md +15 -0
  14. {logust-0.3.2 → logust-0.4.0}/docs/guide/integrations.md +93 -3
  15. {logust-0.3.2 → logust-0.4.0}/docs/index.md +3 -0
  16. logust-0.4.0/examples/08_fastapi_integration.py +167 -0
  17. logust-0.4.0/examples/README.md +38 -0
  18. {logust-0.3.2 → logust-0.4.0}/logust/__init__.py +6 -2
  19. {logust-0.3.2 → logust-0.4.0}/logust/_logger.py +18 -11
  20. {logust-0.3.2 → logust-0.4.0}/logust/_logust.pyi +8 -0
  21. {logust-0.3.2 → logust-0.4.0}/logust/contrib/__init__.py +15 -0
  22. {logust-0.3.2 → logust-0.4.0}/logust/contrib/decorators.py +3 -3
  23. logust-0.4.0/logust/contrib/events.py +137 -0
  24. logust-0.4.0/logust/contrib/starlette.py +770 -0
  25. {logust-0.3.2 → logust-0.4.0}/mkdocs.yml +4 -0
  26. {logust-0.3.2 → logust-0.4.0}/src/format.rs +10 -10
  27. logust-0.4.0/src/handler.rs +762 -0
  28. {logust-0.3.2 → logust-0.4.0}/src/lib.rs +119 -34
  29. logust-0.4.0/tests/test_canonical_events.py +280 -0
  30. {logust-0.3.2 → logust-0.4.0}/tests/test_kwargs_extra.py +2 -2
  31. {logust-0.3.2 → logust-0.4.0}/tests/test_multiprocessing.py +2 -0
  32. logust-0.4.0/tests/test_starlette_middleware.py +386 -0
  33. {logust-0.3.2 → logust-0.4.0}/tests/test_starlette_security.py +4 -4
  34. logust-0.4.0/tests/test_typed_json_extra.py +670 -0
  35. logust-0.4.0/tests/test_web_integration.py +153 -0
  36. logust-0.3.2/.github/workflows/docs.yml +0 -44
  37. logust-0.3.2/examples/08_fastapi_integration.py +0 -179
  38. logust-0.3.2/logust/contrib/starlette.py +0 -381
  39. logust-0.3.2/src/handler.rs +0 -404
  40. logust-0.3.2/tests/test_starlette_middleware.py +0 -128
  41. {logust-0.3.2 → logust-0.4.0}/.github/CODEOWNERS +0 -0
  42. {logust-0.3.2 → logust-0.4.0}/.github/FUNDING.yml +0 -0
  43. {logust-0.3.2 → logust-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  44. {logust-0.3.2 → logust-0.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  45. {logust-0.3.2 → logust-0.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  46. {logust-0.3.2 → logust-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  47. {logust-0.3.2 → logust-0.4.0}/.github/release-drafter.yml +0 -0
  48. {logust-0.3.2 → logust-0.4.0}/.github/workflows/release-drafter.yml +0 -0
  49. {logust-0.3.2 → logust-0.4.0}/.github/workflows/security.yml +0 -0
  50. {logust-0.3.2 → logust-0.4.0}/.gitignore +0 -0
  51. {logust-0.3.2 → logust-0.4.0}/CODE_OF_CONDUCT.md +0 -0
  52. {logust-0.3.2 → logust-0.4.0}/CONTRIBUTING.md +0 -0
  53. {logust-0.3.2 → logust-0.4.0}/LICENSE +0 -0
  54. {logust-0.3.2 → logust-0.4.0}/SECURITY.md +0 -0
  55. {logust-0.3.2 → logust-0.4.0}/benchmarks/README.md +0 -0
  56. {logust-0.3.2 → logust-0.4.0}/benchmarks/__init__.py +0 -0
  57. {logust-0.3.2 → logust-0.4.0}/benchmarks/bench_filter_mixed.py +0 -0
  58. {logust-0.3.2 → logust-0.4.0}/benchmarks/bench_format_record.py +0 -0
  59. {logust-0.3.2 → logust-0.4.0}/benchmarks/bench_lazy_is_level.py +0 -0
  60. {logust-0.3.2 → logust-0.4.0}/benchmarks/bench_throughput.py +0 -0
  61. {logust-0.3.2 → logust-0.4.0}/benchmarks/conftest.py +0 -0
  62. {logust-0.3.2 → logust-0.4.0}/docs/assets/favicon.svg +0 -0
  63. {logust-0.3.2 → logust-0.4.0}/docs/assets/logo.svg +0 -0
  64. {logust-0.3.2 → logust-0.4.0}/docs/comparison.md +0 -0
  65. {logust-0.3.2 → logust-0.4.0}/docs/getting-started/quick-start.md +0 -0
  66. {logust-0.3.2 → logust-0.4.0}/docs/guide/context.md +0 -0
  67. {logust-0.3.2 → logust-0.4.0}/docs/guide/exceptions.md +0 -0
  68. {logust-0.3.2 → logust-0.4.0}/docs/guide/file-output.md +0 -0
  69. {logust-0.3.2 → logust-0.4.0}/docs/guide/levels.md +0 -0
  70. {logust-0.3.2 → logust-0.4.0}/docs/stylesheets/extra.css +0 -0
  71. {logust-0.3.2 → logust-0.4.0}/examples/01_basic_logging.py +0 -0
  72. {logust-0.3.2 → logust-0.4.0}/examples/02_file_output.py +0 -0
  73. {logust-0.3.2 → logust-0.4.0}/examples/03_json_serialization.py +0 -0
  74. {logust-0.3.2 → logust-0.4.0}/examples/04_context_binding.py +0 -0
  75. {logust-0.3.2 → logust-0.4.0}/examples/05_exception_handling.py +0 -0
  76. {logust-0.3.2 → logust-0.4.0}/examples/06_custom_levels.py +0 -0
  77. {logust-0.3.2 → logust-0.4.0}/examples/07_callbacks.py +0 -0
  78. {logust-0.3.2 → logust-0.4.0}/logust/_opt.py +0 -0
  79. {logust-0.3.2 → logust-0.4.0}/logust/_parse.py +0 -0
  80. {logust-0.3.2 → logust-0.4.0}/logust/_template.py +0 -0
  81. {logust-0.3.2 → logust-0.4.0}/logust/_traceback.py +0 -0
  82. {logust-0.3.2 → logust-0.4.0}/logust/_types.py +0 -0
  83. {logust-0.3.2 → logust-0.4.0}/logust/contrib/logging_handler.py +0 -0
  84. {logust-0.3.2 → logust-0.4.0}/logust/py.typed +0 -0
  85. {logust-0.3.2 → logust-0.4.0}/prek.toml +0 -0
  86. {logust-0.3.2 → logust-0.4.0}/pyproject.toml +0 -0
  87. {logust-0.3.2 → logust-0.4.0}/renovate.json +0 -0
  88. {logust-0.3.2 → logust-0.4.0}/src/level.rs +0 -0
  89. {logust-0.3.2 → logust-0.4.0}/src/sink.rs +0 -0
  90. {logust-0.3.2 → logust-0.4.0}/tests/__init__.py +0 -0
  91. {logust-0.3.2 → logust-0.4.0}/tests/conftest.py +0 -0
  92. {logust-0.3.2 → logust-0.4.0}/tests/test_callable_sink.py +0 -0
  93. {logust-0.3.2 → logust-0.4.0}/tests/test_callable_template.py +0 -0
  94. {logust-0.3.2 → logust-0.4.0}/tests/test_callbacks.py +0 -0
  95. {logust-0.3.2 → logust-0.4.0}/tests/test_caller_info.py +0 -0
  96. {logust-0.3.2 → logust-0.4.0}/tests/test_collect_options.py +0 -0
  97. {logust-0.3.2 → logust-0.4.0}/tests/test_configure.py +0 -0
  98. {logust-0.3.2 → logust-0.4.0}/tests/test_context.py +0 -0
  99. {logust-0.3.2 → logust-0.4.0}/tests/test_filter_fast_path.py +0 -0
  100. {logust-0.3.2 → logust-0.4.0}/tests/test_format_optimization.py +0 -0
  101. {logust-0.3.2 → logust-0.4.0}/tests/test_format_tokens.py +0 -0
  102. {logust-0.3.2 → logust-0.4.0}/tests/test_handlers.py +0 -0
  103. {logust-0.3.2 → logust-0.4.0}/tests/test_levels.py +0 -0
  104. {logust-0.3.2 → logust-0.4.0}/tests/test_logger.py +0 -0
  105. {logust-0.3.2 → logust-0.4.0}/tests/test_opt.py +0 -0
  106. {logust-0.3.2 → logust-0.4.0}/tests/test_parsing.py +0 -0
  107. {logust-0.3.2 → logust-0.4.0}/tests/test_token_registry.py +0 -0
@@ -172,7 +172,7 @@ jobs:
172
172
  subject-path: 'wheels-*/*'
173
173
  - name: Install uv
174
174
  if: ${{ startsWith(github.ref, 'refs/tags/') }}
175
- uses: astral-sh/setup-uv@v8.1.0
175
+ uses: astral-sh/setup-uv@v8.2.0
176
176
  - name: Publish to PyPI
177
177
  if: ${{ startsWith(github.ref, 'refs/tags/') }}
178
178
  run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/ 'wheels-*/*'
@@ -0,0 +1,85 @@
1
+ name: Deploy Docs
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ version:
9
+ description: 'Version label (e.g. 0.3.2). Leave empty to deploy "dev".'
10
+ required: false
11
+ default: ''
12
+ set_latest:
13
+ description: 'Update the "latest" alias to this version.'
14
+ type: boolean
15
+ default: false
16
+
17
+ permissions:
18
+ contents: write
19
+
20
+ jobs:
21
+ deploy:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v6
25
+ with:
26
+ fetch-depth: 0
27
+
28
+ - name: Configure Git
29
+ run: |
30
+ git config user.name github-actions[bot]
31
+ git config user.email github-actions[bot]@users.noreply.github.com
32
+
33
+ - name: Set up Python
34
+ uses: actions/setup-python@v6
35
+ with:
36
+ python-version: '3.14'
37
+
38
+ - name: Cache pip
39
+ uses: actions/cache@v5
40
+ with:
41
+ path: ~/.cache/pip
42
+ key: ${{ runner.os }}-pip-mkdocs-material-mike
43
+ restore-keys: |
44
+ ${{ runner.os }}-pip-
45
+
46
+ - name: Install dependencies
47
+ run: pip install mkdocs-material mkdocstrings[python] mkdocs-minify-plugin mike
48
+
49
+ - name: Resolve version
50
+ id: version
51
+ env:
52
+ EVENT_NAME: ${{ github.event_name }}
53
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
54
+ INPUT_VERSION: ${{ inputs.version }}
55
+ INPUT_SET_LATEST: ${{ inputs.set_latest }}
56
+ run: |
57
+ if [ "$EVENT_NAME" = "release" ]; then
58
+ tag="$RELEASE_TAG"
59
+ version="${tag#v}"
60
+ printf 'version=%s\n' "$version" >> "$GITHUB_OUTPUT"
61
+ printf 'alias=latest\n' >> "$GITHUB_OUTPUT"
62
+ else
63
+ version="$INPUT_VERSION"
64
+ if [ -z "$version" ]; then
65
+ version="dev"
66
+ fi
67
+ printf 'version=%s\n' "$version" >> "$GITHUB_OUTPUT"
68
+ if [ "$INPUT_SET_LATEST" = "true" ]; then
69
+ printf 'alias=latest\n' >> "$GITHUB_OUTPUT"
70
+ else
71
+ printf 'alias=\n' >> "$GITHUB_OUTPUT"
72
+ fi
73
+ fi
74
+
75
+ - name: Deploy with mike
76
+ env:
77
+ MIKE_VERSION: ${{ steps.version.outputs.version }}
78
+ MIKE_ALIAS: ${{ steps.version.outputs.alias }}
79
+ run: |
80
+ if [ -n "$MIKE_ALIAS" ]; then
81
+ mike deploy --push --update-aliases "$MIKE_VERSION" "$MIKE_ALIAS"
82
+ mike set-default --push "$MIKE_ALIAS"
83
+ else
84
+ mike deploy --push "$MIKE_VERSION"
85
+ fi
@@ -84,7 +84,7 @@ jobs:
84
84
  subject-path: 'wheels-*/*'
85
85
  - name: Install uv
86
86
  if: ${{ startsWith(github.ref, 'refs/tags/') }}
87
- uses: astral-sh/setup-uv@v8.1.0
87
+ uses: astral-sh/setup-uv@v8.2.0
88
88
  - name: Publish to PyPI
89
89
  if: ${{ startsWith(github.ref, 'refs/tags/') }}
90
90
  run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/ 'wheels-*/*'
@@ -70,7 +70,7 @@ jobs:
70
70
  uses: dtolnay/rust-toolchain@stable
71
71
 
72
72
  - name: Install uv
73
- uses: astral-sh/setup-uv@v8.1.0
73
+ uses: astral-sh/setup-uv@v8.2.0
74
74
 
75
75
  - name: Install dependencies and build
76
76
  run: |
@@ -83,11 +83,37 @@ jobs:
83
83
 
84
84
  - name: Upload coverage
85
85
  if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
86
- uses: codecov/codecov-action@v6
86
+ uses: codecov/codecov-action@v7
87
87
  with:
88
88
  files: ./coverage.xml
89
89
  fail_ci_if_error: false
90
90
 
91
+ web-integration-test:
92
+ name: Web Integration Tests
93
+ runs-on: ubuntu-latest
94
+ steps:
95
+ - uses: actions/checkout@v6
96
+
97
+ - name: Set up Python
98
+ uses: actions/setup-python@v6
99
+ with:
100
+ python-version: '3.14'
101
+
102
+ - name: Install Rust
103
+ uses: dtolnay/rust-toolchain@stable
104
+
105
+ - name: Install uv
106
+ uses: astral-sh/setup-uv@v8.2.0
107
+
108
+ - name: Install dependencies and build
109
+ run: |
110
+ uv venv
111
+ uv pip install maturin pytest httpx fastapi starlette uuid-utils
112
+ uv run maturin develop
113
+
114
+ - name: Run web integration tests
115
+ run: uv run pytest tests/test_web_integration.py -v
116
+
91
117
  lint:
92
118
  name: Python Lint
93
119
  runs-on: ubuntu-latest
@@ -103,7 +129,7 @@ jobs:
103
129
  uses: dtolnay/rust-toolchain@stable
104
130
 
105
131
  - name: Install uv
106
- uses: astral-sh/setup-uv@v8.1.0
132
+ uses: astral-sh/setup-uv@v8.2.0
107
133
 
108
134
  - name: Install dependencies and build
109
135
  run: |
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-06-14
11
+
12
+ ### Added
13
+ - **Canonical request events for web integrations**: `logust.contrib` now provides `canonical_event()`, `add_event_fields()`, `clear_event_fields()`, `get_event_fields()`, `get_current_event()`, and `TailSampler`, and Starlette/FastAPI middleware can emit one `http.request` event per request with request/response fields, endpoint-enriched extras, and tail sampling for errors and slow requests. (#38)
14
+
15
+ ### Changed
16
+ - **JSON extra values handle more Python types**: `serialize=True` now emits `bytes` / `bytearray` extras as UTF-8 strings with replacement for invalid bytes, `set` / `frozenset` extras as arrays, `datetime` / `date` / `time` extras as `.isoformat()` strings, and `Enum` extras via their `.value` converted with the same rules. Text formatting (`{extra[key]}`) and Python callback records still use the existing `str(value)` view. Other object types such as `Decimal`, `UUID`, `Path`, and `complex` continue to fall back to `str(value)` in JSON.
17
+
10
18
  ## [0.3.2] - 2026-05-07
11
19
 
12
20
  ### Added
@@ -131,7 +139,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
131
139
  - 1.3x faster than Python standard logging
132
140
  - Lock-free fast path for filtered messages
133
141
 
134
- [Unreleased]: https://github.com/yamaaaaaa31/logust/compare/v0.3.2...HEAD
142
+ [Unreleased]: https://github.com/yamaaaaaa31/logust/compare/v0.4.0...HEAD
143
+ [0.4.0]: https://github.com/yamaaaaaa31/logust/compare/v0.3.2...v0.4.0
135
144
  [0.3.2]: https://github.com/yamaaaaaa31/logust/compare/v0.3.1...v0.3.2
136
145
  [0.3.1]: https://github.com/yamaaaaaa31/logust/compare/v0.3.0...v0.3.1
137
146
  [0.3.0]: https://github.com/yamaaaaaa31/logust/compare/v0.2.1...v0.3.0
@@ -19,27 +19,27 @@ dependencies = [
19
19
 
20
20
  [[package]]
21
21
  name = "autocfg"
22
- version = "1.5.0"
22
+ version = "1.5.1"
23
23
  source = "registry+https://github.com/rust-lang/crates.io-index"
24
- checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
24
+ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
25
25
 
26
26
  [[package]]
27
27
  name = "bitflags"
28
- version = "2.11.1"
28
+ version = "2.13.0"
29
29
  source = "registry+https://github.com/rust-lang/crates.io-index"
30
- checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
30
+ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
31
31
 
32
32
  [[package]]
33
33
  name = "bumpalo"
34
- version = "3.20.2"
34
+ version = "3.20.3"
35
35
  source = "registry+https://github.com/rust-lang/crates.io-index"
36
- checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
36
+ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
37
37
 
38
38
  [[package]]
39
39
  name = "cc"
40
- version = "1.2.61"
40
+ version = "1.2.64"
41
41
  source = "registry+https://github.com/rust-lang/crates.io-index"
42
- checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
42
+ checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"
43
43
  dependencies = [
44
44
  "find-msvc-tools",
45
45
  "shlex",
@@ -53,9 +53,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
53
53
 
54
54
  [[package]]
55
55
  name = "chrono"
56
- version = "0.4.44"
56
+ version = "0.4.45"
57
57
  source = "registry+https://github.com/rust-lang/crates.io-index"
58
- checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
58
+ checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
59
59
  dependencies = [
60
60
  "iana-time-zone",
61
61
  "js-sys",
@@ -182,13 +182,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
182
182
 
183
183
  [[package]]
184
184
  name = "js-sys"
185
- version = "0.3.98"
185
+ version = "0.3.102"
186
186
  source = "registry+https://github.com/rust-lang/crates.io-index"
187
- checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
187
+ checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
188
188
  dependencies = [
189
189
  "cfg-if",
190
190
  "futures-util",
191
- "once_cell",
192
191
  "wasm-bindgen",
193
192
  ]
194
193
 
@@ -209,13 +208,13 @@ dependencies = [
209
208
 
210
209
  [[package]]
211
210
  name = "log"
212
- version = "0.4.29"
211
+ version = "0.4.32"
213
212
  source = "registry+https://github.com/rust-lang/crates.io-index"
214
- checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
213
+ checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
215
214
 
216
215
  [[package]]
217
216
  name = "logust"
218
- version = "0.3.2"
217
+ version = "0.4.0"
219
218
  dependencies = [
220
219
  "chrono",
221
220
  "colored",
@@ -231,9 +230,9 @@ dependencies = [
231
230
 
232
231
  [[package]]
233
232
  name = "memchr"
234
- version = "2.8.0"
233
+ version = "2.8.2"
235
234
  source = "registry+https://github.com/rust-lang/crates.io-index"
236
- checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
235
+ checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
237
236
 
238
237
  [[package]]
239
238
  name = "miniz_oxide"
@@ -306,9 +305,9 @@ dependencies = [
306
305
 
307
306
  [[package]]
308
307
  name = "pyo3"
309
- version = "0.28.3"
308
+ version = "0.29.0"
310
309
  source = "registry+https://github.com/rust-lang/crates.io-index"
311
- checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
310
+ checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
312
311
  dependencies = [
313
312
  "libc",
314
313
  "once_cell",
@@ -320,18 +319,18 @@ dependencies = [
320
319
 
321
320
  [[package]]
322
321
  name = "pyo3-build-config"
323
- version = "0.28.3"
322
+ version = "0.29.0"
324
323
  source = "registry+https://github.com/rust-lang/crates.io-index"
325
- checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e"
324
+ checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
326
325
  dependencies = [
327
326
  "target-lexicon",
328
327
  ]
329
328
 
330
329
  [[package]]
331
330
  name = "pyo3-ffi"
332
- version = "0.28.3"
331
+ version = "0.29.0"
333
332
  source = "registry+https://github.com/rust-lang/crates.io-index"
334
- checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e"
333
+ checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
335
334
  dependencies = [
336
335
  "libc",
337
336
  "pyo3-build-config",
@@ -339,9 +338,9 @@ dependencies = [
339
338
 
340
339
  [[package]]
341
340
  name = "pyo3-macros"
342
- version = "0.28.3"
341
+ version = "0.29.0"
343
342
  source = "registry+https://github.com/rust-lang/crates.io-index"
344
- checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813"
343
+ checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
345
344
  dependencies = [
346
345
  "proc-macro2",
347
346
  "pyo3-macros-backend",
@@ -351,13 +350,12 @@ dependencies = [
351
350
 
352
351
  [[package]]
353
352
  name = "pyo3-macros-backend"
354
- version = "0.28.3"
353
+ version = "0.29.0"
355
354
  source = "registry+https://github.com/rust-lang/crates.io-index"
356
- checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb"
355
+ checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
357
356
  dependencies = [
358
357
  "heck",
359
358
  "proc-macro2",
360
- "pyo3-build-config",
361
359
  "quote",
362
360
  "syn",
363
361
  ]
@@ -424,9 +422,9 @@ dependencies = [
424
422
 
425
423
  [[package]]
426
424
  name = "serde_json"
427
- version = "1.0.149"
425
+ version = "1.0.150"
428
426
  source = "registry+https://github.com/rust-lang/crates.io-index"
429
- checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
427
+ checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
430
428
  dependencies = [
431
429
  "itoa",
432
430
  "memchr",
@@ -437,9 +435,9 @@ dependencies = [
437
435
 
438
436
  [[package]]
439
437
  name = "shlex"
440
- version = "1.3.0"
438
+ version = "2.0.1"
441
439
  source = "registry+https://github.com/rust-lang/crates.io-index"
442
- checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
440
+ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
443
441
 
444
442
  [[package]]
445
443
  name = "simd-adler32"
@@ -455,9 +453,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
455
453
 
456
454
  [[package]]
457
455
  name = "smallvec"
458
- version = "1.15.1"
456
+ version = "1.15.2"
459
457
  source = "registry+https://github.com/rust-lang/crates.io-index"
460
- checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
458
+ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
461
459
 
462
460
  [[package]]
463
461
  name = "syn"
@@ -484,9 +482,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
484
482
 
485
483
  [[package]]
486
484
  name = "wasm-bindgen"
487
- version = "0.2.121"
485
+ version = "0.2.125"
488
486
  source = "registry+https://github.com/rust-lang/crates.io-index"
489
- checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
487
+ checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
490
488
  dependencies = [
491
489
  "cfg-if",
492
490
  "once_cell",
@@ -497,9 +495,9 @@ dependencies = [
497
495
 
498
496
  [[package]]
499
497
  name = "wasm-bindgen-macro"
500
- version = "0.2.121"
498
+ version = "0.2.125"
501
499
  source = "registry+https://github.com/rust-lang/crates.io-index"
502
- checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
500
+ checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
503
501
  dependencies = [
504
502
  "quote",
505
503
  "wasm-bindgen-macro-support",
@@ -507,9 +505,9 @@ dependencies = [
507
505
 
508
506
  [[package]]
509
507
  name = "wasm-bindgen-macro-support"
510
- version = "0.2.121"
508
+ version = "0.2.125"
511
509
  source = "registry+https://github.com/rust-lang/crates.io-index"
512
- checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
510
+ checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
513
511
  dependencies = [
514
512
  "bumpalo",
515
513
  "proc-macro2",
@@ -520,9 +518,9 @@ dependencies = [
520
518
 
521
519
  [[package]]
522
520
  name = "wasm-bindgen-shared"
523
- version = "0.2.121"
521
+ version = "0.2.125"
524
522
  source = "registry+https://github.com/rust-lang/crates.io-index"
525
- checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
523
+ checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
526
524
  dependencies = [
527
525
  "unicode-ident",
528
526
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "logust"
3
- version = "0.3.2"
3
+ version = "0.4.0"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -9,7 +9,7 @@ name = "_logust"
9
9
  crate-type = ["cdylib"]
10
10
 
11
11
  [dependencies]
12
- pyo3 = { version = "0.28", features = ["extension-module"] }
12
+ pyo3 = { version = "0.29.0", features = ["extension-module"] }
13
13
  chrono = { version = "0.4", features = ["serde"] }
14
14
  colored = "3.0"
15
15
  flate2 = "1.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logust
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -68,6 +68,7 @@ A fast, Rust-powered Python logging library inspired by [loguru](https://github.
68
68
  - **Custom Levels** - Define your own log levels with colors and icons
69
69
  - **Color Markup** - Inline `<red>color</red>` tags in log messages
70
70
  - **Async Writing** - Optional thread-safe async file writes with `enqueue=True`
71
+ - **Canonical Events** - Request-scoped wide events with built-in tail sampling
71
72
  - **Free-Threaded Python** - Release automation builds wheels for CPython 3.13t and 3.14t on Linux and macOS
72
73
  - **Zero-Config Integrations** - Built-in support for standard logging, FastAPI, and function timing
73
74
 
@@ -503,6 +504,14 @@ async def fetch_user(user_id):
503
504
 
504
505
  ### FastAPI / Starlette Middleware
505
506
 
507
+ Install the matching extra for web middleware:
508
+
509
+ ```bash
510
+ pip install "logust[fastapi]"
511
+ # or
512
+ pip install "logust[starlette]"
513
+ ```
514
+
506
515
  ```python
507
516
  from fastapi import FastAPI
508
517
  from logust.contrib.starlette import setup_fastapi
@@ -516,6 +525,45 @@ setup_fastapi(app, skip_routes=["/health"])
516
525
  # - Standard logging redirected to logust
517
526
  ```
518
527
 
528
+ ### Canonical Request Events
529
+
530
+ Emit one wide event at request completion, then keep high-value events with tail sampling:
531
+
532
+ ```python
533
+ from fastapi import FastAPI
534
+ from logust.contrib import add_event_fields
535
+ from logust.contrib.starlette import setup_fastapi
536
+
537
+ app = FastAPI()
538
+
539
+ setup_fastapi(
540
+ app,
541
+ canonical=True,
542
+ sample_rate=0.02,
543
+ slow_ms=1000,
544
+ skip_routes=["/health"],
545
+ )
546
+
547
+ @app.post("/checkout")
548
+ async def checkout(user_id: str):
549
+ add_event_fields({"user.id": user_id}, feature_checkout_v2=True)
550
+ return {"ok": True}
551
+ ```
552
+
553
+ The middleware logs a single `http.request` event with request, response, timing,
554
+ trace context, and any fields added during the request. Errors and slow requests
555
+ are kept even when normal successful traffic is sampled. JSON sinks preserve
556
+ numeric, boolean, list, dict, and null extra values as native JSON types. Incoming
557
+ `x-request-id` values are preserved; otherwise Logust generates a short
558
+ `uuid4`-based request ID.
559
+ `sample_rate` must be between `0.0` and `1.0`, and `slow_ms` must be non-negative.
560
+
561
+ See [docs/guide/canonical-events.md](docs/guide/canonical-events.md) and
562
+ [examples/08_fastapi_integration.py](examples/08_fastapi_integration.py) for the
563
+ full contract and a runnable FastAPI app.
564
+
565
+ More runnable examples are listed in [examples/README.md](examples/README.md).
566
+
519
567
  ## Complete API Reference
520
568
 
521
569
  ### Logger Methods
@@ -22,6 +22,7 @@ A fast, Rust-powered Python logging library inspired by [loguru](https://github.
22
22
  - **Custom Levels** - Define your own log levels with colors and icons
23
23
  - **Color Markup** - Inline `<red>color</red>` tags in log messages
24
24
  - **Async Writing** - Optional thread-safe async file writes with `enqueue=True`
25
+ - **Canonical Events** - Request-scoped wide events with built-in tail sampling
25
26
  - **Free-Threaded Python** - Release automation builds wheels for CPython 3.13t and 3.14t on Linux and macOS
26
27
  - **Zero-Config Integrations** - Built-in support for standard logging, FastAPI, and function timing
27
28
 
@@ -457,6 +458,14 @@ async def fetch_user(user_id):
457
458
 
458
459
  ### FastAPI / Starlette Middleware
459
460
 
461
+ Install the matching extra for web middleware:
462
+
463
+ ```bash
464
+ pip install "logust[fastapi]"
465
+ # or
466
+ pip install "logust[starlette]"
467
+ ```
468
+
460
469
  ```python
461
470
  from fastapi import FastAPI
462
471
  from logust.contrib.starlette import setup_fastapi
@@ -470,6 +479,45 @@ setup_fastapi(app, skip_routes=["/health"])
470
479
  # - Standard logging redirected to logust
471
480
  ```
472
481
 
482
+ ### Canonical Request Events
483
+
484
+ Emit one wide event at request completion, then keep high-value events with tail sampling:
485
+
486
+ ```python
487
+ from fastapi import FastAPI
488
+ from logust.contrib import add_event_fields
489
+ from logust.contrib.starlette import setup_fastapi
490
+
491
+ app = FastAPI()
492
+
493
+ setup_fastapi(
494
+ app,
495
+ canonical=True,
496
+ sample_rate=0.02,
497
+ slow_ms=1000,
498
+ skip_routes=["/health"],
499
+ )
500
+
501
+ @app.post("/checkout")
502
+ async def checkout(user_id: str):
503
+ add_event_fields({"user.id": user_id}, feature_checkout_v2=True)
504
+ return {"ok": True}
505
+ ```
506
+
507
+ The middleware logs a single `http.request` event with request, response, timing,
508
+ trace context, and any fields added during the request. Errors and slow requests
509
+ are kept even when normal successful traffic is sampled. JSON sinks preserve
510
+ numeric, boolean, list, dict, and null extra values as native JSON types. Incoming
511
+ `x-request-id` values are preserved; otherwise Logust generates a short
512
+ `uuid4`-based request ID.
513
+ `sample_rate` must be between `0.0` and `1.0`, and `slow_ms` must be non-negative.
514
+
515
+ See [docs/guide/canonical-events.md](docs/guide/canonical-events.md) and
516
+ [examples/08_fastapi_integration.py](examples/08_fastapi_integration.py) for the
517
+ full contract and a runnable FastAPI app.
518
+
519
+ More runnable examples are listed in [examples/README.md](examples/README.md).
520
+
473
521
  ## Complete API Reference
474
522
 
475
523
  ### Logger Methods
@@ -7,6 +7,8 @@ Quick reference for the public API. For examples, see the Guide.
7
7
  - **Logger**: Log methods, handlers, levels, context, options, callbacks
8
8
  - **LogLevel**: Enum values for severity
9
9
  - **Parsing**: `parse()` and `parse_json()` helpers
10
+ - **Contrib**: standard logging interception, decorators, canonical events,
11
+ FastAPI/Starlette middleware
10
12
 
11
13
  ## Logger
12
14
 
@@ -176,6 +178,93 @@ LogLevel.Critical # 50
176
178
 
177
179
  ---
178
180
 
181
+ ## Contrib
182
+
183
+ Utilities in `logust.contrib` are optional helpers built on top of the core
184
+ logger.
185
+
186
+ ### Standard logging interception
187
+
188
+ ```python
189
+ from logust.contrib import InterceptHandler, intercept_logging
190
+
191
+ intercept_logging()
192
+ ```
193
+
194
+ ### Function timing
195
+
196
+ ```python
197
+ from logust.contrib import debug_fn, log_fn
198
+
199
+ @log_fn
200
+ def work():
201
+ pass
202
+
203
+ @debug_fn
204
+ async def async_work():
205
+ pass
206
+ ```
207
+
208
+ ### Canonical event helpers
209
+
210
+ ```python
211
+ from logust.contrib import (
212
+ TailSampler,
213
+ add_event_fields,
214
+ canonical_event,
215
+ clear_event_fields,
216
+ get_current_event,
217
+ get_event_fields,
218
+ )
219
+
220
+ with canonical_event({"event": "job"}) as event:
221
+ add_event_fields(job_id="job_123")
222
+ ```
223
+
224
+ `add_event_fields(fields=None, **kwargs)` returns `True` when an event is active
225
+ and `False` outside a canonical event context.
226
+
227
+ `TailSampler` accepts:
228
+
229
+ ```python
230
+ TailSampler(
231
+ rate=1.0, # 0.0 to 1.0
232
+ always_keep_errors=True,
233
+ slow_ms=None, # non-negative milliseconds
234
+ keep_if=None, # predicate(event) -> bool
235
+ )
236
+ ```
237
+
238
+ ### FastAPI / Starlette
239
+
240
+ Install web extras before importing the middleware:
241
+
242
+ ```bash
243
+ pip install "logust[fastapi]"
244
+ # or
245
+ pip install "logust[starlette]"
246
+ ```
247
+
248
+ ```python
249
+ from logust.contrib.starlette import (
250
+ RequestLoggerMiddleware,
251
+ get_request_id,
252
+ setup_fastapi,
253
+ )
254
+
255
+ setup_fastapi(
256
+ app,
257
+ canonical=True,
258
+ sample_rate=0.05,
259
+ slow_ms=1000,
260
+ skip_routes=["/health"],
261
+ )
262
+ ```
263
+
264
+ For the full event contract, see [Canonical Events](guide/canonical-events.md).
265
+
266
+ ---
267
+
179
268
  ## Type definitions
180
269
 
181
270
  ### LogRecord