plone.observability 1.0.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 (137) hide show
  1. plone_observability-1.0.0/.editorconfig +56 -0
  2. plone_observability-1.0.0/.github/dependabot.yml +11 -0
  3. plone_observability-1.0.0/.github/workflows/ci.yaml +21 -0
  4. plone_observability-1.0.0/.github/workflows/docs.yaml +58 -0
  5. plone_observability-1.0.0/.github/workflows/qa.yaml +20 -0
  6. plone_observability-1.0.0/.github/workflows/release.yaml +105 -0
  7. plone_observability-1.0.0/.github/workflows/tests.yaml +80 -0
  8. plone_observability-1.0.0/.gitignore +20 -0
  9. plone_observability-1.0.0/.pre-commit-config.yaml +23 -0
  10. plone_observability-1.0.0/CHANGES.md +178 -0
  11. plone_observability-1.0.0/CONTRIBUTORS.md +10 -0
  12. plone_observability-1.0.0/LICENSE +338 -0
  13. plone_observability-1.0.0/PKG-INFO +92 -0
  14. plone_observability-1.0.0/README.md +44 -0
  15. plone_observability-1.0.0/RELEASE.md +70 -0
  16. plone_observability-1.0.0/docs/.gitignore +3 -0
  17. plone_observability-1.0.0/docs/.vale/styles/config/vocabularies/Project/accept.txt +108 -0
  18. plone_observability-1.0.0/docs/.vale.ini +21 -0
  19. plone_observability-1.0.0/docs/Makefile +310 -0
  20. plone_observability-1.0.0/docs/include.mk +14 -0
  21. plone_observability-1.0.0/docs/mx.ini +3 -0
  22. plone_observability-1.0.0/docs/plans/2026-06-30-documentation-plan.md +344 -0
  23. plone_observability-1.0.0/docs/sources/_ext/ecosystem_dashboard.py +146 -0
  24. plone_observability-1.0.0/docs/sources/_static/apple-touch-icon.png +0 -0
  25. plone_observability-1.0.0/docs/sources/_static/dashboard.css +137 -0
  26. plone_observability-1.0.0/docs/sources/_static/dashboard.js +223 -0
  27. plone_observability-1.0.0/docs/sources/_static/favicon.ico +0 -0
  28. plone_observability-1.0.0/docs/sources/_static/logo-400.png +0 -0
  29. plone_observability-1.0.0/docs/sources/_static/logo-web.png +0 -0
  30. plone_observability-1.0.0/docs/sources/_static/logo.png +0 -0
  31. plone_observability-1.0.0/docs/sources/_static/og-image.png +0 -0
  32. plone_observability-1.0.0/docs/sources/_static/site.webmanifest +31 -0
  33. plone_observability-1.0.0/docs/sources/_templates/layout.html +14 -0
  34. plone_observability-1.0.0/docs/sources/_templates/partials/extra-head.html +1 -0
  35. plone_observability-1.0.0/docs/sources/conf.py +111 -0
  36. plone_observability-1.0.0/docs/sources/ecosystem.md +76 -0
  37. plone_observability-1.0.0/docs/sources/explanation/health-probes.md +66 -0
  38. plone_observability-1.0.0/docs/sources/explanation/index.md +13 -0
  39. plone_observability-1.0.0/docs/sources/explanation/metrics-design.md +69 -0
  40. plone_observability-1.0.0/docs/sources/explanation/tracing.md +43 -0
  41. plone_observability-1.0.0/docs/sources/how-to/add-custom-health-check.md +65 -0
  42. plone_observability-1.0.0/docs/sources/how-to/add-custom-metric-formatter.md +47 -0
  43. plone_observability-1.0.0/docs/sources/how-to/add-custom-metric-provider.md +55 -0
  44. plone_observability-1.0.0/docs/sources/how-to/add-custom-spans.md +73 -0
  45. plone_observability-1.0.0/docs/sources/how-to/configure-kubernetes-probes.md +59 -0
  46. plone_observability-1.0.0/docs/sources/how-to/enable-opentelemetry-tracing.md +63 -0
  47. plone_observability-1.0.0/docs/sources/how-to/index.md +18 -0
  48. plone_observability-1.0.0/docs/sources/how-to/install.md +104 -0
  49. plone_observability-1.0.0/docs/sources/how-to/scrape-with-prometheus.md +84 -0
  50. plone_observability-1.0.0/docs/sources/index.md +90 -0
  51. plone_observability-1.0.0/docs/sources/llms.txt +83 -0
  52. plone_observability-1.0.0/docs/sources/reference/configuration.md +72 -0
  53. plone_observability-1.0.0/docs/sources/reference/health-endpoints.md +50 -0
  54. plone_observability-1.0.0/docs/sources/reference/index.md +14 -0
  55. plone_observability-1.0.0/docs/sources/reference/interfaces.md +102 -0
  56. plone_observability-1.0.0/docs/sources/reference/metrics.md +78 -0
  57. plone_observability-1.0.0/docs/sources/reference/tracing.md +67 -0
  58. plone_observability-1.0.0/docs/sources/tutorials/_assets/docker-compose.yml +94 -0
  59. plone_observability-1.0.0/docs/sources/tutorials/getting-started.md +194 -0
  60. plone_observability-1.0.0/docs/sources/tutorials/index.md +12 -0
  61. plone_observability-1.0.0/docs/superpowers/plans/2026-06-30-subrequest-tracing.md +403 -0
  62. plone_observability-1.0.0/docs/superpowers/plans/2026-06-30-zodb-span-counts.md +393 -0
  63. plone_observability-1.0.0/docs/superpowers/plans/2026-07-01-render-tracing.md +559 -0
  64. plone_observability-1.0.0/docs/superpowers/plans/2026-07-01-zodb-load-time.md +361 -0
  65. plone_observability-1.0.0/docs/superpowers/plans/2026-07-02-io-instrumentors.md +365 -0
  66. plone_observability-1.0.0/docs/superpowers/specs/2026-06-30-subrequest-tracing-design.md +144 -0
  67. plone_observability-1.0.0/docs/superpowers/specs/2026-06-30-zodb-span-counts-design.md +127 -0
  68. plone_observability-1.0.0/docs/superpowers/specs/2026-07-01-render-tracing-design.md +131 -0
  69. plone_observability-1.0.0/docs/superpowers/specs/2026-07-01-zodb-load-time-design.md +121 -0
  70. plone_observability-1.0.0/docs/superpowers/specs/2026-07-02-io-instrumentors-design.md +109 -0
  71. plone_observability-1.0.0/pyproject.toml +141 -0
  72. plone_observability-1.0.0/src/plone/observability/__init__.py +0 -0
  73. plone_observability-1.0.0/src/plone/observability/auth.py +28 -0
  74. plone_observability-1.0.0/src/plone/observability/configure.zcml +30 -0
  75. plone_observability-1.0.0/src/plone/observability/health/__init__.py +0 -0
  76. plone_observability-1.0.0/src/plone/observability/health/checks.py +31 -0
  77. plone_observability-1.0.0/src/plone/observability/health/configure.zcml +9 -0
  78. plone_observability-1.0.0/src/plone/observability/health/server.py +146 -0
  79. plone_observability-1.0.0/src/plone/observability/health/wsgi.py +30 -0
  80. plone_observability-1.0.0/src/plone/observability/interfaces.py +63 -0
  81. plone_observability-1.0.0/src/plone/observability/metric.py +14 -0
  82. plone_observability-1.0.0/src/plone/observability/metrics/__init__.py +0 -0
  83. plone_observability-1.0.0/src/plone/observability/metrics/access.py +54 -0
  84. plone_observability-1.0.0/src/plone/observability/metrics/configure.zcml +70 -0
  85. plone_observability-1.0.0/src/plone/observability/metrics/formatters.py +48 -0
  86. plone_observability-1.0.0/src/plone/observability/metrics/providers/__init__.py +0 -0
  87. plone_observability-1.0.0/src/plone/observability/metrics/providers/conflict.py +55 -0
  88. plone_observability-1.0.0/src/plone/observability/metrics/providers/content.py +99 -0
  89. plone_observability-1.0.0/src/plone/observability/metrics/providers/request.py +202 -0
  90. plone_observability-1.0.0/src/plone/observability/metrics/providers/system.py +39 -0
  91. plone_observability-1.0.0/src/plone/observability/metrics/providers/zodb.py +203 -0
  92. plone_observability-1.0.0/src/plone/observability/metrics/providers/zope_runtime.py +68 -0
  93. plone_observability-1.0.0/src/plone/observability/metrics/view.py +61 -0
  94. plone_observability-1.0.0/src/plone/observability/otel/__init__.py +0 -0
  95. plone_observability-1.0.0/src/plone/observability/otel/catalog.py +91 -0
  96. plone_observability-1.0.0/src/plone/observability/otel/configure.zcml +66 -0
  97. plone_observability-1.0.0/src/plone/observability/otel/dbcounts.py +66 -0
  98. plone_observability-1.0.0/src/plone/observability/otel/exclusions.py +79 -0
  99. plone_observability-1.0.0/src/plone/observability/otel/instrumentors.py +72 -0
  100. plone_observability-1.0.0/src/plone/observability/otel/provider.py +47 -0
  101. plone_observability-1.0.0/src/plone/observability/otel/pubevents.py +91 -0
  102. plone_observability-1.0.0/src/plone/observability/otel/rendering.py +160 -0
  103. plone_observability-1.0.0/src/plone/observability/otel/subrequest.py +91 -0
  104. plone_observability-1.0.0/src/plone/observability/otel/transformchain.py +91 -0
  105. plone_observability-1.0.0/src/plone/observability/otel/wsgi.py +35 -0
  106. plone_observability-1.0.0/src/plone/observability/otel/zodb.py +102 -0
  107. plone_observability-1.0.0/src/plone/observability/spans.py +31 -0
  108. plone_observability-1.0.0/tests/conftest.py +64 -0
  109. plone_observability-1.0.0/tests/test_access.py +90 -0
  110. plone_observability-1.0.0/tests/test_auth.py +69 -0
  111. plone_observability-1.0.0/tests/test_autoinclude.py +8 -0
  112. plone_observability-1.0.0/tests/test_checks.py +61 -0
  113. plone_observability-1.0.0/tests/test_formatters.py +117 -0
  114. plone_observability-1.0.0/tests/test_health_server.py +178 -0
  115. plone_observability-1.0.0/tests/test_health_wsgi.py +43 -0
  116. plone_observability-1.0.0/tests/test_integration.py +156 -0
  117. plone_observability-1.0.0/tests/test_metric.py +42 -0
  118. plone_observability-1.0.0/tests/test_metrics_view.py +174 -0
  119. plone_observability-1.0.0/tests/test_otel_catalog.py +90 -0
  120. plone_observability-1.0.0/tests/test_otel_dbcounts.py +267 -0
  121. plone_observability-1.0.0/tests/test_otel_exclusions.py +86 -0
  122. plone_observability-1.0.0/tests/test_otel_instrumentors.py +115 -0
  123. plone_observability-1.0.0/tests/test_otel_integration.py +63 -0
  124. plone_observability-1.0.0/tests/test_otel_provider.py +45 -0
  125. plone_observability-1.0.0/tests/test_otel_pubevents.py +137 -0
  126. plone_observability-1.0.0/tests/test_otel_rendering.py +218 -0
  127. plone_observability-1.0.0/tests/test_otel_subrequest.py +132 -0
  128. plone_observability-1.0.0/tests/test_otel_transformchain.py +140 -0
  129. plone_observability-1.0.0/tests/test_otel_wsgi.py +71 -0
  130. plone_observability-1.0.0/tests/test_otel_zodb.py +106 -0
  131. plone_observability-1.0.0/tests/test_provider_conflict.py +65 -0
  132. plone_observability-1.0.0/tests/test_provider_content.py +207 -0
  133. plone_observability-1.0.0/tests/test_provider_request.py +198 -0
  134. plone_observability-1.0.0/tests/test_provider_system.py +39 -0
  135. plone_observability-1.0.0/tests/test_provider_zodb.py +281 -0
  136. plone_observability-1.0.0/tests/test_provider_zope_runtime.py +38 -0
  137. plone_observability-1.0.0/tests/test_spans.py +18 -0
@@ -0,0 +1,56 @@
1
+ # Generated from:
2
+ # https://github.com/plone/meta/tree/2.x/src/plone/meta/default
3
+ # See the inline comments on how to expand/tweak this configuration file
4
+ #
5
+ # EditorConfig Configuration file, for more details see:
6
+ # http://EditorConfig.org
7
+ # EditorConfig is a convention description, that could be interpreted
8
+ # by multiple editors to enforce common coding conventions for specific
9
+ # file types
10
+
11
+ # top-most EditorConfig file:
12
+ # Will ignore other EditorConfig files in Home directory or upper tree level.
13
+ root = true
14
+
15
+
16
+ [*]
17
+ # Default settings for all files.
18
+ # Unix-style newlines with a newline ending every file
19
+ end_of_line = lf
20
+ insert_final_newline = true
21
+ trim_trailing_whitespace = true
22
+ # Set default charset
23
+ charset = utf-8
24
+ # Indent style default
25
+ indent_style = space
26
+ # Max Line Length - a hard line wrap, should be disabled
27
+ max_line_length = off
28
+
29
+ [*.{py,cfg,ini}]
30
+ # 4 space indentation
31
+ indent_size = 4
32
+
33
+ [*.{yml,zpt,pt,dtml,zcml,html,xml}]
34
+ # 2 space indentation
35
+ indent_size = 2
36
+
37
+ [*.{json,jsonl,js,jsx,ts,tsx,css,less,scss}]
38
+ # Frontend development
39
+ # 2 space indentation
40
+ indent_size = 2
41
+ max_line_length = 80
42
+
43
+ [{Makefile,.gitmodules}]
44
+ # Tab indentation (no size specified, but view as 4 spaces)
45
+ indent_style = tab
46
+ indent_size = unset
47
+ tab_width = unset
48
+
49
+
50
+ ##
51
+ # Add extra configuration options in .meta.toml:
52
+ # [editorconfig]
53
+ # extra_lines = """
54
+ # _your own configuration lines_
55
+ # """
56
+ ##
@@ -0,0 +1,11 @@
1
+ # Generated from:
2
+ # https://github.com/plone/meta/tree/2.x/src/plone/meta/default
3
+ # See the inline comments on how to expand/tweak this configuration file
4
+ version: 2
5
+ updates:
6
+
7
+ - package-ecosystem: "github-actions"
8
+ directory: "/"
9
+ schedule:
10
+ # Check for updates to GitHub Actions every week
11
+ interval: "weekly"
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: CI
3
+
4
+ on:
5
+ push:
6
+ branches-ignore: [main]
7
+ pull_request:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ qa:
18
+ uses: "./.github/workflows/qa.yaml"
19
+
20
+ tests:
21
+ uses: "./.github/workflows/tests.yaml"
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: Documentation
3
+
4
+ on:
5
+ push:
6
+ branches: [main]
7
+ paths:
8
+ - "docs/**"
9
+ - ".github/workflows/docs.yaml"
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: read
14
+ pages: write
15
+ id-token: write
16
+
17
+ concurrency:
18
+ group: pages
19
+ cancel-in-progress: true
20
+
21
+ jobs:
22
+ build:
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@v7
26
+
27
+ - uses: actions/setup-python@v7
28
+ with:
29
+ python-version: "3.13"
30
+
31
+ - uses: astral-sh/setup-uv@v7
32
+
33
+ - name: Install docs dependencies
34
+ working-directory: docs
35
+ run: |
36
+ uv venv .venv
37
+ uv pip install sphinx sphinx-autobuild \
38
+ myst_parser sphinxcontrib.mermaid shibuya \
39
+ sphinx-design sphinx-copybutton
40
+
41
+ - name: Build documentation
42
+ working-directory: docs
43
+ run: .venv/bin/sphinx-build sources html
44
+
45
+ - uses: actions/upload-pages-artifact@v5
46
+ with:
47
+ path: docs/html
48
+
49
+ deploy:
50
+ needs: build
51
+ runs-on: ubuntu-latest
52
+ environment:
53
+ name: github-pages
54
+ url: "${{ steps.deployment.outputs.page_url }}"
55
+ steps:
56
+ - name: Deploy to GitHub Pages
57
+ id: deployment
58
+ uses: actions/deploy-pages@v5
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: QA
3
+
4
+ on:
5
+ workflow_call:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ lint:
12
+ name: pre-commit
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v7
16
+
17
+ - uses: astral-sh/setup-uv@v7
18
+
19
+ - name: Run pre-commit
20
+ run: uvx pre-commit run --all-files --show-diff-on-failure
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: Build & upload PyPI package
3
+
4
+ on:
5
+ push:
6
+ branches: [main]
7
+ release:
8
+ types:
9
+ - published
10
+ workflow_dispatch:
11
+
12
+ concurrency:
13
+ group: release-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ qa:
18
+ uses: "./.github/workflows/qa.yaml"
19
+
20
+ tests:
21
+ uses: "./.github/workflows/tests.yaml"
22
+
23
+ build-package:
24
+ name: Build & verify package
25
+ needs:
26
+ - qa
27
+ - tests
28
+ runs-on: ubuntu-latest
29
+ permissions:
30
+ contents: read
31
+
32
+ steps:
33
+ - uses: actions/checkout@v7
34
+ with:
35
+ fetch-depth: 0
36
+ persist-credentials: false
37
+
38
+ # On main pushes we publish an in-dev package to TestPyPI. TestPyPI
39
+ # rejects re-uploads of an existing version and forbids local version
40
+ # segments (the git-hash part), so we append a monotonic
41
+ # `.dev<run-number>` to make every upload unique and valid, e.g.
42
+ # 1.0.0a1 -> 1.0.0a1.dev42. Tagged releases keep the clean version.
43
+ - name: Set unique in-dev version
44
+ if: github.event_name == 'push'
45
+ env:
46
+ RUN: ${{ github.run_number }}
47
+ run: |
48
+ python - <<'PY'
49
+ import os, re, pathlib
50
+ run = os.environ["RUN"]
51
+ path = pathlib.Path("pyproject.toml")
52
+ text = path.read_text()
53
+ text = re.sub(
54
+ r'(?m)^(version = "[^"]+)"',
55
+ rf'\g<1>.dev{run}"',
56
+ text,
57
+ count=1,
58
+ )
59
+ path.write_text(text)
60
+ print("version ->", re.search(r'(?m)^version = .*', text).group(0))
61
+ PY
62
+
63
+ - uses: hynek/build-and-inspect-python-package@v3.0.1
64
+
65
+ release-test-pypi:
66
+ name: Publish in-dev package to test.pypi.org
67
+ environment: release-test-pypi
68
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
69
+ runs-on: ubuntu-latest
70
+ needs:
71
+ - build-package
72
+ permissions:
73
+ id-token: write
74
+
75
+ steps:
76
+ - name: Download packages built by build-and-inspect-python-package
77
+ uses: actions/download-artifact@v8
78
+ with:
79
+ name: Packages
80
+ path: dist
81
+
82
+ - name: Upload package to Test PyPI
83
+ uses: pypa/gh-action-pypi-publish@release/v1
84
+ with:
85
+ repository-url: https://test.pypi.org/legacy/
86
+
87
+ release-pypi:
88
+ name: Publish released package to pypi.org
89
+ environment: release-pypi
90
+ if: github.event.action == 'published'
91
+ runs-on: ubuntu-latest
92
+ needs:
93
+ - build-package
94
+ permissions:
95
+ id-token: write
96
+
97
+ steps:
98
+ - name: Download packages built by build-and-inspect-python-package
99
+ uses: actions/download-artifact@v8
100
+ with:
101
+ name: Packages
102
+ path: dist
103
+
104
+ - name: Upload package to PyPI
105
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: Tests
3
+
4
+ on:
5
+ workflow_call:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ test:
12
+ name: Python ${{ matrix.python-version }}
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+ with:
22
+ fetch-depth: 0
23
+
24
+ - uses: astral-sh/setup-uv@v7
25
+ with:
26
+ enable-cache: true
27
+ cache-dependency-glob: "pyproject.toml"
28
+
29
+ - name: Set up Python ${{ matrix.python-version }}
30
+ run: uv python install ${{ matrix.python-version }}
31
+
32
+ - name: Install dependencies
33
+ run: |
34
+ uv venv
35
+ uv pip install -c https://dist.plone.org/release/6.2-latest/constraints.txt -e ".[test,opentelemetry]"
36
+
37
+ - name: Run tests with coverage
38
+ run: uv run pytest --cov --cov-report=
39
+
40
+ - name: Rename coverage data
41
+ run: mv .coverage .coverage.${{ matrix.python-version }}
42
+
43
+ - name: Upload coverage data
44
+ uses: actions/upload-artifact@v7
45
+ with:
46
+ name: coverage-data-${{ matrix.python-version }}
47
+ path: .coverage.${{ matrix.python-version }}
48
+ include-hidden-files: true
49
+ if-no-files-found: ignore
50
+
51
+ coverage:
52
+ name: Combine & check coverage
53
+ if: always()
54
+ needs: test
55
+ runs-on: ubuntu-latest
56
+
57
+ steps:
58
+ - uses: actions/checkout@v7
59
+
60
+ - uses: astral-sh/setup-uv@v7
61
+ with:
62
+ enable-cache: true
63
+ cache-dependency-glob: "pyproject.toml"
64
+
65
+ - name: Install dependencies
66
+ run: |
67
+ uv venv
68
+ uv pip install -c https://dist.plone.org/release/6.2-latest/constraints.txt -e ".[test,opentelemetry]"
69
+
70
+ - uses: actions/download-artifact@v8
71
+ with:
72
+ pattern: coverage-data-*
73
+ merge-multiple: true
74
+
75
+ - name: Combine coverage & check threshold
76
+ run: |
77
+ uv run coverage combine
78
+ uv run coverage html --skip-covered --skip-empty
79
+ uv run coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
80
+ uv run coverage report
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ .eggs/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ *.egg
9
+ .tox/
10
+ .coverage
11
+ .coverage.*
12
+ htmlcov/
13
+ coverage.xml
14
+
15
+ # Docs
16
+ docs/html/
17
+ docs/.mxmake/
18
+ docs/.venv/
19
+ docs/.vale/styles/Microsoft/
20
+ docs/.vale/styles/write-good/
@@ -0,0 +1,23 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.15.0
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
8
+
9
+ - repo: https://github.com/pre-commit/pre-commit-hooks
10
+ rev: v5.0.0
11
+ hooks:
12
+ - id: trailing-whitespace
13
+ - id: end-of-file-fixer
14
+ - id: check-yaml
15
+ - id: check-added-large-files
16
+
17
+ - repo: https://github.com/collective/zpretty
18
+ rev: 4.0.3
19
+ hooks:
20
+ - id: zpretty
21
+ # zpretty's default pattern includes *.html; the Sphinx/Jinja templates
22
+ # under docs/ are not ZCML/Zope page templates, so keep zpretty off them.
23
+ exclude: ^docs/
@@ -0,0 +1,178 @@
1
+ # Changes
2
+
3
+ <!-- towncrier release notes start -->
4
+
5
+ ## 1.0.0 (2026-09-14)
6
+
7
+ First stable release. The package graduates from beta: the development status
8
+ classifier is now *Production/Stable*. Functionally identical to 1.0.0b18.
9
+
10
+ ### Internal:
11
+
12
+ - Bump `hynek/build-and-inspect-python-package` from v2 to v3.0.1. Hatchling now emits `Metadata-Version: 2.5`, which the Twine bundled in v2 rejects, failing the release build. v3 ships Twine 7, which supports it.
13
+ - Enable ruff's cyclomatic-complexity check (`C901`, mccabe) with `max-complexity = 15`. The code base passes as-is.
14
+ - Lower ruff's C901 max-complexity threshold from 15 to 13 as part of the ecosystem-wide complexity ratchet. The code base passes as-is.
15
+
16
+ ### Documentation:
17
+
18
+ - Add `cloud-vinyl` to the ecosystem dashboard and to the ecosystem navigation
19
+ dropdown in the docs, and move the `plone.observability` dashboard entry after
20
+ the *Deployment* group so both copies of the dashboard stay in sync.
21
+
22
+
23
+ ## 1.0.0b18 (2026-07-06)
24
+
25
+ ### New features:
26
+
27
+ - Rename the misnamed ``plone.zodb.load_pg_queries`` span attribute (it counted objects) to ``plone.zodb.load_pg_objects``, and add a real ``plone.zodb.load_pg_queries`` reading zodb-pgjsonb's new ``_pg_query_count`` (PostgreSQL round-trips, >= 1.16.0). Their ratio (``load_pg_objects / load_pg_queries``) makes batching/prefetch visible per span. ([#96](https://github.com/plone/plone.observability/issues/96))
28
+
29
+
30
+ ## 1.0.0b17 (2026-07-02)
31
+
32
+ ### New features:
33
+
34
+ - Add `plone.zodb.load_l2_hits` and `plone.zodb.load_pg_queries` span attributes.
35
+ When running on zodb-pgjsonb (>= 1.14.3), these split `plone.zodb.objects_loaded`
36
+ into shared-cache (L2) hits vs actual PostgreSQL fetches, so the shared-cache hit
37
+ ratio (`l2 / (l2 + pg)`) is visible per span — attributing load-time outliers to a
38
+ cold/gated cache (all PG) vs high per-load latency (few PG, but slow). Read
39
+ best-effort via `getattr`, so it is a no-op on other storages (no new dependency).
40
+
41
+
42
+ ## 1.0.0b16 (2026-07-02)
43
+
44
+ ### New features:
45
+
46
+ - Optionally enable the standard OpenTelemetry I/O instrumentors (botocore, requests, urllib3, httpx) via ``PLONE_OBSERVABILITY_OTEL_INSTRUMENTORS=1`` so S3 blob access and outbound HTTP become child spans in the same trace. Install the instrumentation packages via the new ``opentelemetry-io`` extra. ([#50](https://github.com/plone/plone.observability/issues/50))
47
+
48
+
49
+ ## 1.0.0b15 (2026-07-01)
50
+
51
+ ### New features:
52
+
53
+ - Add ``plone.zodb.load_time_ms`` to every span that already carries the ZODB object counts (publish, subrequest, viewlet/portlet render): the time spent materialising objects (round-trip + decode), so per-tile/per-request object-load cost is visible in traces.
54
+ - Trace the ClassicUI render phase: emit spans per viewlet manager, per body viewlet, per portlet column and per portlet (head viewlet managers collapse to a single span), each carrying the per-span ZODB object-load counts. Disable with ``PLONE_OBSERVABILITY_OTEL_RENDER=0``.
55
+
56
+ ### Documentation:
57
+
58
+ - Add a how-to guide, *How to add custom spans in your code*, covering the `start_span` helper for instrumenting add-on and project code with OpenTelemetry spans.
59
+
60
+
61
+ ## 1.0.0b14 (2026-07-01)
62
+
63
+ ### New features:
64
+
65
+ - Record objects loaded/stored per span (``plone.zodb.objects_loaded`` / ``plone.zodb.objects_stored``) on the publish span (request total) and each subrequest span (per-tile delta), making N+1 object-load cost visible in traces. ([#49](https://github.com/plone/plone.observability/issues/49))
66
+
67
+ ### Internal:
68
+
69
+ - Add a `Documentation` GitHub Actions workflow that builds the Sphinx docs with uv and deploys them to GitHub Pages on every push to `main` that touches `docs/**`, matching the convention used across the cloudbrine ecosystem repositories.
70
+ - Exclude ``docs/`` from the zpretty pre-commit hook: its Sphinx/Jinja HTML templates are not ZCML/Zope page templates.
71
+
72
+ ### Documentation:
73
+
74
+ - Replace the single long `README.md` with a structured Sphinx documentation site under `docs/sources/`, organized by the Diataxis framework (tutorials, how-to, reference, explanation) and matching the cloudbrine ecosystem convention (shibuya theme, ecosystem dashboard, `llms.txt`). The `README.md` is now a short pointer to the docs site.
75
+
76
+
77
+ ## 1.0.0b13 (2026-06-30)
78
+
79
+ ### Bug fixes:
80
+
81
+ - Fix the `/startup` probe never turning green under Kubernetes. `/startup` reported success only after `/ready` had been polled and passed at least once, but Kubernetes does not run the readiness probe until the startup probe has already succeeded — a chicken-and-egg that left pods stuck in *starting* for the whole startup-probe budget (CPU idle, since Plone had finished booting). `/startup` now evaluates the readiness checks itself and latches on first success, so it stands on its own. Thanks to Afonso on the Plone community forum for the report and a clear repro. ([#46](https://github.com/plone/plone.observability/issues/46))
82
+
83
+
84
+ ## 1.0.0b12 (2026-06-30)
85
+
86
+ ### New features:
87
+
88
+ - Trace ``plone.subrequest``-rendered tiles: emit one span per subrequest, nested under the active transform span, when ``plone.subrequest`` is installed. ([#43](https://github.com/plone/plone.observability/issues/43))
89
+
90
+
91
+ ## 1.0.0b11 (2026-06-30)
92
+
93
+ ### Bug fixes:
94
+
95
+ - Stop tracing the package's own `@@metrics` scrape and let operators exclude other paths. The bare `OpenTelemetryMiddleware` has no excluded-URL support, so every request — including the Prometheus scrape every ~30s — was traced and dominated trace volume. The OTel filter now honours the standard `OTEL_PYTHON_WSGI_EXCLUDED_URLS` / `OTEL_PYTHON_EXCLUDED_URLS` env vars and excludes `@@metrics` by default (turn defaults off with `PLONE_OBSERVABILITY_OTEL_EXCLUDE_DEFAULTS=0`). Because the publishing/catalog/zodb/transformchain spans are created from Zope events independently of the WSGI middleware, excluded requests are also marked suppressed in the OTel context so those subscribers skip span creation too — an excluded path produces no spans at all, not just a missing outer span. ([#40](https://github.com/plone/plone.observability/issues/40))
96
+
97
+
98
+ ## 1.0.0b10 (2026-06-30)
99
+
100
+ ### New features:
101
+
102
+ - Trace the `plone.transformchain` response-transform phase. When `plone.transformchain` is installed, the package now emits a `transformchain` span with one `transform.<name>` child span per transform (carrying `plone.transform.name`/`plone.transform.handler`, and `plone.transformchain.transform_count` on the parent), nested under the `ZPublisher.publish` span. This surfaces which transform (e.g. the Diazo/theming transform) dominates the post-render time. It is driven by transformchain's own before/after events — no monkeypatching — and is gated by the same OTel activation switch as the other spans.
103
+
104
+
105
+ ## 1.0.0b9 (2026-06-29)
106
+
107
+ ### Bug fixes:
108
+
109
+ - `@@metrics` is no longer slow on Postgres-backed storage (zodb-pgjsonb / RelStorage). The two DB-wide ZODB gauges — `plone_zodb_object_count` and `plone_zodb_db_size_bytes` — were recomputed uncached on every scrape; on FileStorage that is cheap, but on Postgres-backed storage `objectCount()`/`getSize()` become full-table/relation-size queries that took ~seconds each scrape. They are now cached at module level with a TTL (the existing `PLONE_OBSERVABILITY_METRICS_CACHE_TTL`, default 60s), keyed by database name. The cheap in-memory gauges and the load/store counters stay live per scrape. ([#35](https://github.com/plone/plone.observability/issues/35))
110
+
111
+
112
+ ## 1.0.0b8 (2026-06-29)
113
+
114
+ ### Bug fixes:
115
+
116
+ - Health server no longer dumps `BrokenPipeError`/`ConnectionResetError` tracebacks when a probe client disconnects before the response is fully written (common during warmup when readiness returns 503). These connection drops are now logged at debug level instead.
117
+
118
+
119
+ ## 1.0.0b7 (2026-06-25)
120
+
121
+ ### New features:
122
+
123
+ - Add `plone_request_duration_seconds_max{auth="authenticated|anonymous"}` — a per-scrape-window gauge of the worst-case request duration. A histogram can only bound latency to its bucket edges, so the true maximum is now tracked directly and reset on every scrape, giving operators the real max backend response time alongside the `histogram_quantile`-derived p90/p99. ([#17](https://github.com/plone/plone.observability/issues/17))
124
+
125
+
126
+ ## 1.0.0b6 (2026-06-25)
127
+
128
+ ### New features:
129
+
130
+ - Add `plone_zodb_conflicts_total{retry="true|false"}` — a storage-agnostic counter of ZODB ConflictErrors during request publication (including the silently-retried ones), captured via an IPubBeforeAbort subscriber. ([#26](https://github.com/plone/plone.observability/issues/26))
131
+
132
+ ### Bug fixes:
133
+
134
+ - ZODB load/store metrics are now storage-agnostic: `plone_zodb_loads_total`/`plone_zodb_stores_total` counters come from a minimal, O(1) ZODB activity monitor (works on FileStorage, RelStorage, zodb-pgjsonb), replacing the FileStorage-only metrics. Installed by default unless a monitor already exists; disable with `PLONE_OBSERVABILITY_ZODB_ACTIVITY_MONITOR=0`. The content provider now steps aside silently on non-ZCatalog backends (which ship their own provider). ([#25](https://github.com/plone/plone.observability/issues/25))
135
+
136
+ ### Internal:
137
+
138
+ - Adopt the shared ruff ruleset (single-line imports, B/SIM/UP/C4, py310 target), format ZCML with zpretty, run pre-commit as the single source in CI, and gate test coverage at 90%.
139
+
140
+
141
+ ## 1.0.0b5 (2026-06-24)
142
+
143
+ ### Bug fixes:
144
+
145
+ - Declare the `z3c.autoinclude.plugin` entry point (`target = plone`) so the package's ZCML — the `@@metrics` view, metric providers, OpenTelemetry and auth subscribers, and the `zodb` readiness check — loads automatically on a standard Plone instance instead of staying dormant after `pip install`. ([#21](https://github.com/plone/plone.observability/issues/21))
146
+
147
+
148
+ ## 1.0.0b4 (2026-06-24)
149
+
150
+ ### Breaking changes:
151
+
152
+ - The health server and OpenTelemetry activation now start from the WSGI pipeline filters instead of Zope process startup, so `zconsole`/script runs no longer bind the health port or patch the catalog. **Upgrade:** add the `egg:plone.observability#healthserver` filter to your pipeline, otherwise the health server no longer starts. ([#16](https://github.com/plone/plone.observability/issues/16))
153
+
154
+ ### Bug fixes:
155
+
156
+ - Emit `plone_info` as a `gauge` instead of the OpenMetrics-only `info` type. The `@@metrics` endpoint advertises classic Prometheus exposition format (`text/plain; version=0.0.4`), in which `info` is invalid, so Prometheus rejected the **entire** scrape with `invalid metric type "info"`. `plone_info` now follows the conventional `*_info` gauge pattern (constant value 1, version in labels). ([#18](https://github.com/plone/plone.observability/issues/18))
157
+
158
+
159
+ ## 1.0.0b3 (2026-06-22)
160
+
161
+ ### New features:
162
+
163
+ - Add an `auth="authenticated"|"anonymous"` label to all request metrics, and `enduser.authenticated` (plus opt-in `enduser.id` via `PLONE_OBSERVABILITY_OTEL_USER_ID`) to the publish trace span.
164
+
165
+
166
+ ## 1.0.0b2 (2026-06-22)
167
+
168
+ ### Documentation:
169
+
170
+ - Document the recommended way to wire the WSGI filters (request metrics and the OpenTelemetry root request span) via cookiecutter-zope-instance 3.1.0+ `wsgi_filters`, instead of hand-editing `zope.ini`.
171
+
172
+
173
+ ## 1.0.0b1 (2026-06-18)
174
+
175
+ ### New features:
176
+
177
+ - Add optional `[opentelemetry]` extra: distributed tracing (root request, publishing, catalog query, and ZODB commit spans) with OTel-native configuration. Catalog tracing is backend-agnostic, covering both standard ZCatalog-based Plone and plone-pgcatalog. No-op when the extra is not installed. ([#2](https://github.com/plone/plone.observability/issues/2))
178
+ - Initial release: Kubernetes-style health probes (`/live`, `/ready`, `/startup`) on a separate daemon-thread port that survives worker exhaustion, plus a pluggable `@@metrics` endpoint with Prometheus and JSON output and ZCA-extensible health checks, metric providers, and formatters.
@@ -0,0 +1,10 @@
1
+ # Contributors
2
+
3
+ - Jens W. Klein ([@jensens](https://github.com/jensens)) — author and maintainer
4
+
5
+ ## Acknowledgements
6
+
7
+ - David Glick ([@davisagli](https://github.com/davisagli)) — OpenTelemetry /
8
+ tracing groundwork in
9
+ [collective.opentelemetry](https://github.com/collective/collective.opentelemetry),
10
+ which informs the planned `plone.observability[opentelemetry]` tracing extra.