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.
- plone_observability-1.0.0/.editorconfig +56 -0
- plone_observability-1.0.0/.github/dependabot.yml +11 -0
- plone_observability-1.0.0/.github/workflows/ci.yaml +21 -0
- plone_observability-1.0.0/.github/workflows/docs.yaml +58 -0
- plone_observability-1.0.0/.github/workflows/qa.yaml +20 -0
- plone_observability-1.0.0/.github/workflows/release.yaml +105 -0
- plone_observability-1.0.0/.github/workflows/tests.yaml +80 -0
- plone_observability-1.0.0/.gitignore +20 -0
- plone_observability-1.0.0/.pre-commit-config.yaml +23 -0
- plone_observability-1.0.0/CHANGES.md +178 -0
- plone_observability-1.0.0/CONTRIBUTORS.md +10 -0
- plone_observability-1.0.0/LICENSE +338 -0
- plone_observability-1.0.0/PKG-INFO +92 -0
- plone_observability-1.0.0/README.md +44 -0
- plone_observability-1.0.0/RELEASE.md +70 -0
- plone_observability-1.0.0/docs/.gitignore +3 -0
- plone_observability-1.0.0/docs/.vale/styles/config/vocabularies/Project/accept.txt +108 -0
- plone_observability-1.0.0/docs/.vale.ini +21 -0
- plone_observability-1.0.0/docs/Makefile +310 -0
- plone_observability-1.0.0/docs/include.mk +14 -0
- plone_observability-1.0.0/docs/mx.ini +3 -0
- plone_observability-1.0.0/docs/plans/2026-06-30-documentation-plan.md +344 -0
- plone_observability-1.0.0/docs/sources/_ext/ecosystem_dashboard.py +146 -0
- plone_observability-1.0.0/docs/sources/_static/apple-touch-icon.png +0 -0
- plone_observability-1.0.0/docs/sources/_static/dashboard.css +137 -0
- plone_observability-1.0.0/docs/sources/_static/dashboard.js +223 -0
- plone_observability-1.0.0/docs/sources/_static/favicon.ico +0 -0
- plone_observability-1.0.0/docs/sources/_static/logo-400.png +0 -0
- plone_observability-1.0.0/docs/sources/_static/logo-web.png +0 -0
- plone_observability-1.0.0/docs/sources/_static/logo.png +0 -0
- plone_observability-1.0.0/docs/sources/_static/og-image.png +0 -0
- plone_observability-1.0.0/docs/sources/_static/site.webmanifest +31 -0
- plone_observability-1.0.0/docs/sources/_templates/layout.html +14 -0
- plone_observability-1.0.0/docs/sources/_templates/partials/extra-head.html +1 -0
- plone_observability-1.0.0/docs/sources/conf.py +111 -0
- plone_observability-1.0.0/docs/sources/ecosystem.md +76 -0
- plone_observability-1.0.0/docs/sources/explanation/health-probes.md +66 -0
- plone_observability-1.0.0/docs/sources/explanation/index.md +13 -0
- plone_observability-1.0.0/docs/sources/explanation/metrics-design.md +69 -0
- plone_observability-1.0.0/docs/sources/explanation/tracing.md +43 -0
- plone_observability-1.0.0/docs/sources/how-to/add-custom-health-check.md +65 -0
- plone_observability-1.0.0/docs/sources/how-to/add-custom-metric-formatter.md +47 -0
- plone_observability-1.0.0/docs/sources/how-to/add-custom-metric-provider.md +55 -0
- plone_observability-1.0.0/docs/sources/how-to/add-custom-spans.md +73 -0
- plone_observability-1.0.0/docs/sources/how-to/configure-kubernetes-probes.md +59 -0
- plone_observability-1.0.0/docs/sources/how-to/enable-opentelemetry-tracing.md +63 -0
- plone_observability-1.0.0/docs/sources/how-to/index.md +18 -0
- plone_observability-1.0.0/docs/sources/how-to/install.md +104 -0
- plone_observability-1.0.0/docs/sources/how-to/scrape-with-prometheus.md +84 -0
- plone_observability-1.0.0/docs/sources/index.md +90 -0
- plone_observability-1.0.0/docs/sources/llms.txt +83 -0
- plone_observability-1.0.0/docs/sources/reference/configuration.md +72 -0
- plone_observability-1.0.0/docs/sources/reference/health-endpoints.md +50 -0
- plone_observability-1.0.0/docs/sources/reference/index.md +14 -0
- plone_observability-1.0.0/docs/sources/reference/interfaces.md +102 -0
- plone_observability-1.0.0/docs/sources/reference/metrics.md +78 -0
- plone_observability-1.0.0/docs/sources/reference/tracing.md +67 -0
- plone_observability-1.0.0/docs/sources/tutorials/_assets/docker-compose.yml +94 -0
- plone_observability-1.0.0/docs/sources/tutorials/getting-started.md +194 -0
- plone_observability-1.0.0/docs/sources/tutorials/index.md +12 -0
- plone_observability-1.0.0/docs/superpowers/plans/2026-06-30-subrequest-tracing.md +403 -0
- plone_observability-1.0.0/docs/superpowers/plans/2026-06-30-zodb-span-counts.md +393 -0
- plone_observability-1.0.0/docs/superpowers/plans/2026-07-01-render-tracing.md +559 -0
- plone_observability-1.0.0/docs/superpowers/plans/2026-07-01-zodb-load-time.md +361 -0
- plone_observability-1.0.0/docs/superpowers/plans/2026-07-02-io-instrumentors.md +365 -0
- plone_observability-1.0.0/docs/superpowers/specs/2026-06-30-subrequest-tracing-design.md +144 -0
- plone_observability-1.0.0/docs/superpowers/specs/2026-06-30-zodb-span-counts-design.md +127 -0
- plone_observability-1.0.0/docs/superpowers/specs/2026-07-01-render-tracing-design.md +131 -0
- plone_observability-1.0.0/docs/superpowers/specs/2026-07-01-zodb-load-time-design.md +121 -0
- plone_observability-1.0.0/docs/superpowers/specs/2026-07-02-io-instrumentors-design.md +109 -0
- plone_observability-1.0.0/pyproject.toml +141 -0
- plone_observability-1.0.0/src/plone/observability/__init__.py +0 -0
- plone_observability-1.0.0/src/plone/observability/auth.py +28 -0
- plone_observability-1.0.0/src/plone/observability/configure.zcml +30 -0
- plone_observability-1.0.0/src/plone/observability/health/__init__.py +0 -0
- plone_observability-1.0.0/src/plone/observability/health/checks.py +31 -0
- plone_observability-1.0.0/src/plone/observability/health/configure.zcml +9 -0
- plone_observability-1.0.0/src/plone/observability/health/server.py +146 -0
- plone_observability-1.0.0/src/plone/observability/health/wsgi.py +30 -0
- plone_observability-1.0.0/src/plone/observability/interfaces.py +63 -0
- plone_observability-1.0.0/src/plone/observability/metric.py +14 -0
- plone_observability-1.0.0/src/plone/observability/metrics/__init__.py +0 -0
- plone_observability-1.0.0/src/plone/observability/metrics/access.py +54 -0
- plone_observability-1.0.0/src/plone/observability/metrics/configure.zcml +70 -0
- plone_observability-1.0.0/src/plone/observability/metrics/formatters.py +48 -0
- plone_observability-1.0.0/src/plone/observability/metrics/providers/__init__.py +0 -0
- plone_observability-1.0.0/src/plone/observability/metrics/providers/conflict.py +55 -0
- plone_observability-1.0.0/src/plone/observability/metrics/providers/content.py +99 -0
- plone_observability-1.0.0/src/plone/observability/metrics/providers/request.py +202 -0
- plone_observability-1.0.0/src/plone/observability/metrics/providers/system.py +39 -0
- plone_observability-1.0.0/src/plone/observability/metrics/providers/zodb.py +203 -0
- plone_observability-1.0.0/src/plone/observability/metrics/providers/zope_runtime.py +68 -0
- plone_observability-1.0.0/src/plone/observability/metrics/view.py +61 -0
- plone_observability-1.0.0/src/plone/observability/otel/__init__.py +0 -0
- plone_observability-1.0.0/src/plone/observability/otel/catalog.py +91 -0
- plone_observability-1.0.0/src/plone/observability/otel/configure.zcml +66 -0
- plone_observability-1.0.0/src/plone/observability/otel/dbcounts.py +66 -0
- plone_observability-1.0.0/src/plone/observability/otel/exclusions.py +79 -0
- plone_observability-1.0.0/src/plone/observability/otel/instrumentors.py +72 -0
- plone_observability-1.0.0/src/plone/observability/otel/provider.py +47 -0
- plone_observability-1.0.0/src/plone/observability/otel/pubevents.py +91 -0
- plone_observability-1.0.0/src/plone/observability/otel/rendering.py +160 -0
- plone_observability-1.0.0/src/plone/observability/otel/subrequest.py +91 -0
- plone_observability-1.0.0/src/plone/observability/otel/transformchain.py +91 -0
- plone_observability-1.0.0/src/plone/observability/otel/wsgi.py +35 -0
- plone_observability-1.0.0/src/plone/observability/otel/zodb.py +102 -0
- plone_observability-1.0.0/src/plone/observability/spans.py +31 -0
- plone_observability-1.0.0/tests/conftest.py +64 -0
- plone_observability-1.0.0/tests/test_access.py +90 -0
- plone_observability-1.0.0/tests/test_auth.py +69 -0
- plone_observability-1.0.0/tests/test_autoinclude.py +8 -0
- plone_observability-1.0.0/tests/test_checks.py +61 -0
- plone_observability-1.0.0/tests/test_formatters.py +117 -0
- plone_observability-1.0.0/tests/test_health_server.py +178 -0
- plone_observability-1.0.0/tests/test_health_wsgi.py +43 -0
- plone_observability-1.0.0/tests/test_integration.py +156 -0
- plone_observability-1.0.0/tests/test_metric.py +42 -0
- plone_observability-1.0.0/tests/test_metrics_view.py +174 -0
- plone_observability-1.0.0/tests/test_otel_catalog.py +90 -0
- plone_observability-1.0.0/tests/test_otel_dbcounts.py +267 -0
- plone_observability-1.0.0/tests/test_otel_exclusions.py +86 -0
- plone_observability-1.0.0/tests/test_otel_instrumentors.py +115 -0
- plone_observability-1.0.0/tests/test_otel_integration.py +63 -0
- plone_observability-1.0.0/tests/test_otel_provider.py +45 -0
- plone_observability-1.0.0/tests/test_otel_pubevents.py +137 -0
- plone_observability-1.0.0/tests/test_otel_rendering.py +218 -0
- plone_observability-1.0.0/tests/test_otel_subrequest.py +132 -0
- plone_observability-1.0.0/tests/test_otel_transformchain.py +140 -0
- plone_observability-1.0.0/tests/test_otel_wsgi.py +71 -0
- plone_observability-1.0.0/tests/test_otel_zodb.py +106 -0
- plone_observability-1.0.0/tests/test_provider_conflict.py +65 -0
- plone_observability-1.0.0/tests/test_provider_content.py +207 -0
- plone_observability-1.0.0/tests/test_provider_request.py +198 -0
- plone_observability-1.0.0/tests/test_provider_system.py +39 -0
- plone_observability-1.0.0/tests/test_provider_zodb.py +281 -0
- plone_observability-1.0.0/tests/test_provider_zope_runtime.py +38 -0
- 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.
|