plain.cloud 0.1.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.
- plain_cloud-0.1.0/.gitignore +23 -0
- plain_cloud-0.1.0/LICENSE +28 -0
- plain_cloud-0.1.0/PKG-INFO +93 -0
- plain_cloud-0.1.0/README.md +1 -0
- plain_cloud-0.1.0/plain/cloud/CHANGELOG.md +7 -0
- plain_cloud-0.1.0/plain/cloud/README.md +80 -0
- plain_cloud-0.1.0/plain/cloud/__init__.py +0 -0
- plain_cloud-0.1.0/plain/cloud/config.py +57 -0
- plain_cloud-0.1.0/plain/cloud/default_settings.py +5 -0
- plain_cloud-0.1.0/pyproject.toml +23 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.venv
|
|
2
|
+
/.env
|
|
3
|
+
*.egg-info
|
|
4
|
+
*.py[co]
|
|
5
|
+
__pycache__
|
|
6
|
+
*.DS_Store
|
|
7
|
+
|
|
8
|
+
/*.code-workspace
|
|
9
|
+
|
|
10
|
+
# Test apps
|
|
11
|
+
plain*/tests/.plain
|
|
12
|
+
|
|
13
|
+
# Agent scratch files
|
|
14
|
+
/scratch
|
|
15
|
+
|
|
16
|
+
# Plain temp dirs
|
|
17
|
+
.plain
|
|
18
|
+
|
|
19
|
+
.vscode
|
|
20
|
+
/.claude/settings.local.json
|
|
21
|
+
/CLAUDE.local.md
|
|
22
|
+
/.benchmarks
|
|
23
|
+
.claude/worktrees
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Dropseed, LLC
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: plain.cloud
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Production observability via OTLP export to Plain Cloud.
|
|
5
|
+
Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.13
|
|
9
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.34.1
|
|
10
|
+
Requires-Dist: opentelemetry-sdk>=1.34.1
|
|
11
|
+
Requires-Dist: plain<1.0.0,>=0.113.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# plain.cloud
|
|
15
|
+
|
|
16
|
+
**Production observability via OTLP export to Plain Cloud.**
|
|
17
|
+
|
|
18
|
+
- [Overview](#overview)
|
|
19
|
+
- [Settings](#settings)
|
|
20
|
+
- [Sampling](#sampling)
|
|
21
|
+
- [What gets exported](#what-gets-exported)
|
|
22
|
+
- [Observer coexistence](#observer-coexistence)
|
|
23
|
+
- [FAQs](#faqs)
|
|
24
|
+
- [Installation](#installation)
|
|
25
|
+
|
|
26
|
+
## Overview
|
|
27
|
+
|
|
28
|
+
You can use plain.cloud to export traces and metrics from your Plain app to Plain Cloud. The framework already instruments itself with OpenTelemetry spans and histograms — plain.cloud activates them by providing the OTLP exporters.
|
|
29
|
+
|
|
30
|
+
Set two environment variables and your app starts pushing telemetry:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
PLAIN_CLOUD_EXPORT_URL=https://ingest.plaincloud.com
|
|
34
|
+
PLAIN_CLOUD_EXPORT_TOKEN=your-token
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If `CLOUD_EXPORT_URL` is not set, the package is a no-op — safe to install without configuration.
|
|
38
|
+
|
|
39
|
+
## Settings
|
|
40
|
+
|
|
41
|
+
| Setting | Default | Description |
|
|
42
|
+
| ------------------------- | ------- | ----------------------------------------------------------- |
|
|
43
|
+
| `CLOUD_EXPORT_URL` | `""` | OTLP ingest endpoint (e.g. `https://ingest.plaincloud.com`) |
|
|
44
|
+
| `CLOUD_EXPORT_TOKEN` | `""` | Auth token for the export endpoint |
|
|
45
|
+
| `CLOUD_TRACE_SAMPLE_RATE` | `1.0` | Probability of exporting a trace (0.0–1.0) |
|
|
46
|
+
|
|
47
|
+
All settings can be set via `PLAIN_`-prefixed environment variables or in `app/settings.py`.
|
|
48
|
+
|
|
49
|
+
## Sampling
|
|
50
|
+
|
|
51
|
+
By default, all traces are exported. To reduce volume, set a sample rate:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
CLOUD_TRACE_SAMPLE_RATE = 0.1 # Export 10% of traces
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Metrics are not affected by sampling — histograms aggregate in-process and export periodically regardless of the trace sample rate.
|
|
58
|
+
|
|
59
|
+
## What gets exported
|
|
60
|
+
|
|
61
|
+
**Traces** — HTTP request spans and database query spans instrumented by the framework.
|
|
62
|
+
|
|
63
|
+
**Metrics** — OTel histograms like `db.client.query.duration`, aggregated and pushed every 60 seconds.
|
|
64
|
+
|
|
65
|
+
## Observer coexistence
|
|
66
|
+
|
|
67
|
+
If [plain.observer](../../plain-observer/plain/observer/README.md) is also installed, both work simultaneously. plain.cloud handles production export while observer provides the local dev toolbar and admin trace viewer. Observer detects the existing TracerProvider and layers its sampler and span processor on top.
|
|
68
|
+
|
|
69
|
+
## FAQs
|
|
70
|
+
|
|
71
|
+
#### Do I need plain.observer to use plain.cloud?
|
|
72
|
+
|
|
73
|
+
No. plain.cloud works independently. Observer is for local dev tooling; plain.cloud is for production export.
|
|
74
|
+
|
|
75
|
+
#### What happens if the export endpoint is unreachable?
|
|
76
|
+
|
|
77
|
+
The OTLP exporters batch and retry automatically. If the endpoint is down, telemetry is dropped after retries — it does not block your application.
|
|
78
|
+
|
|
79
|
+
#### Does this add latency to requests?
|
|
80
|
+
|
|
81
|
+
No. Trace spans are exported in a background thread via `BatchSpanProcessor`. Metrics are flushed periodically by a background thread. Neither blocks request handling.
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# app/settings.py
|
|
87
|
+
INSTALLED_PACKAGES = [
|
|
88
|
+
"plain.cloud",
|
|
89
|
+
# ...
|
|
90
|
+
]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Place `plain.cloud` **before** `plain.observer` in `INSTALLED_PACKAGES` so it sets up the TracerProvider first.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
plain/cloud/README.md
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# plain-cloud changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0](https://github.com/dropseed/plain/releases/plain-cloud@0.1.0) (2026-04-01)
|
|
4
|
+
|
|
5
|
+
### What's changed
|
|
6
|
+
|
|
7
|
+
- **Initial release.** Sets up OpenTelemetry TracerProvider and MeterProvider with OTLP HTTP exporters, pushing traces and metrics to Plain Cloud. Configure with `CLOUD_EXPORT_URL` and `CLOUD_EXPORT_TOKEN` settings. Includes head-based trace sampling via `CLOUD_TRACE_SAMPLE_RATE`. Inactive when `CLOUD_EXPORT_URL` is not set. Coexists with plain-observer — observer layers its sampler and span processor on top. ([e3971506cb](https://github.com/dropseed/plain/commit/e3971506cb))
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# plain.cloud
|
|
2
|
+
|
|
3
|
+
**Production observability via OTLP export to Plain Cloud.**
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [Settings](#settings)
|
|
7
|
+
- [Sampling](#sampling)
|
|
8
|
+
- [What gets exported](#what-gets-exported)
|
|
9
|
+
- [Observer coexistence](#observer-coexistence)
|
|
10
|
+
- [FAQs](#faqs)
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
You can use plain.cloud to export traces and metrics from your Plain app to Plain Cloud. The framework already instruments itself with OpenTelemetry spans and histograms — plain.cloud activates them by providing the OTLP exporters.
|
|
16
|
+
|
|
17
|
+
Set two environment variables and your app starts pushing telemetry:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
PLAIN_CLOUD_EXPORT_URL=https://ingest.plaincloud.com
|
|
21
|
+
PLAIN_CLOUD_EXPORT_TOKEN=your-token
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If `CLOUD_EXPORT_URL` is not set, the package is a no-op — safe to install without configuration.
|
|
25
|
+
|
|
26
|
+
## Settings
|
|
27
|
+
|
|
28
|
+
| Setting | Default | Description |
|
|
29
|
+
| ------------------------- | ------- | ----------------------------------------------------------- |
|
|
30
|
+
| `CLOUD_EXPORT_URL` | `""` | OTLP ingest endpoint (e.g. `https://ingest.plaincloud.com`) |
|
|
31
|
+
| `CLOUD_EXPORT_TOKEN` | `""` | Auth token for the export endpoint |
|
|
32
|
+
| `CLOUD_TRACE_SAMPLE_RATE` | `1.0` | Probability of exporting a trace (0.0–1.0) |
|
|
33
|
+
|
|
34
|
+
All settings can be set via `PLAIN_`-prefixed environment variables or in `app/settings.py`.
|
|
35
|
+
|
|
36
|
+
## Sampling
|
|
37
|
+
|
|
38
|
+
By default, all traces are exported. To reduce volume, set a sample rate:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
CLOUD_TRACE_SAMPLE_RATE = 0.1 # Export 10% of traces
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Metrics are not affected by sampling — histograms aggregate in-process and export periodically regardless of the trace sample rate.
|
|
45
|
+
|
|
46
|
+
## What gets exported
|
|
47
|
+
|
|
48
|
+
**Traces** — HTTP request spans and database query spans instrumented by the framework.
|
|
49
|
+
|
|
50
|
+
**Metrics** — OTel histograms like `db.client.query.duration`, aggregated and pushed every 60 seconds.
|
|
51
|
+
|
|
52
|
+
## Observer coexistence
|
|
53
|
+
|
|
54
|
+
If [plain.observer](../../plain-observer/plain/observer/README.md) is also installed, both work simultaneously. plain.cloud handles production export while observer provides the local dev toolbar and admin trace viewer. Observer detects the existing TracerProvider and layers its sampler and span processor on top.
|
|
55
|
+
|
|
56
|
+
## FAQs
|
|
57
|
+
|
|
58
|
+
#### Do I need plain.observer to use plain.cloud?
|
|
59
|
+
|
|
60
|
+
No. plain.cloud works independently. Observer is for local dev tooling; plain.cloud is for production export.
|
|
61
|
+
|
|
62
|
+
#### What happens if the export endpoint is unreachable?
|
|
63
|
+
|
|
64
|
+
The OTLP exporters batch and retry automatically. If the endpoint is down, telemetry is dropped after retries — it does not block your application.
|
|
65
|
+
|
|
66
|
+
#### Does this add latency to requests?
|
|
67
|
+
|
|
68
|
+
No. Trace spans are exported in a background thread via `BatchSpanProcessor`. Metrics are flushed periodically by a background thread. Neither blocks request handling.
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# app/settings.py
|
|
74
|
+
INSTALLED_PACKAGES = [
|
|
75
|
+
"plain.cloud",
|
|
76
|
+
# ...
|
|
77
|
+
]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Place `plain.cloud` **before** `plain.observer` in `INSTALLED_PACKAGES` so it sets up the TracerProvider first.
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import atexit
|
|
4
|
+
|
|
5
|
+
from opentelemetry import metrics, trace
|
|
6
|
+
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
|
7
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
8
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
9
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
10
|
+
from opentelemetry.sdk.resources import Resource
|
|
11
|
+
from opentelemetry.sdk.trace import TracerProvider, sampling
|
|
12
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
13
|
+
from opentelemetry.semconv.attributes import service_attributes
|
|
14
|
+
|
|
15
|
+
from plain.packages import PackageConfig, register_config
|
|
16
|
+
from plain.runtime import settings
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@register_config
|
|
20
|
+
class Config(PackageConfig):
|
|
21
|
+
package_label = "plaincloud"
|
|
22
|
+
|
|
23
|
+
def ready(self) -> None:
|
|
24
|
+
if not settings.CLOUD_EXPORT_URL:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
resource = Resource.create(
|
|
28
|
+
{
|
|
29
|
+
service_attributes.SERVICE_NAME: settings.NAME,
|
|
30
|
+
service_attributes.SERVICE_VERSION: settings.VERSION,
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
export_url = str(settings.CLOUD_EXPORT_URL).rstrip("/")
|
|
35
|
+
headers = {"Authorization": f"Bearer {settings.CLOUD_EXPORT_TOKEN}"}
|
|
36
|
+
|
|
37
|
+
# Traces
|
|
38
|
+
span_exporter = OTLPSpanExporter(
|
|
39
|
+
endpoint=f"{export_url}/v1/traces",
|
|
40
|
+
headers=headers,
|
|
41
|
+
)
|
|
42
|
+
sampler = sampling.TraceIdRatioBased(settings.CLOUD_TRACE_SAMPLE_RATE)
|
|
43
|
+
tracer_provider = TracerProvider(sampler=sampler, resource=resource)
|
|
44
|
+
tracer_provider.add_span_processor(BatchSpanProcessor(span_exporter))
|
|
45
|
+
trace.set_tracer_provider(tracer_provider)
|
|
46
|
+
|
|
47
|
+
# Metrics
|
|
48
|
+
metric_exporter = OTLPMetricExporter(
|
|
49
|
+
endpoint=f"{export_url}/v1/metrics",
|
|
50
|
+
headers=headers,
|
|
51
|
+
)
|
|
52
|
+
reader = PeriodicExportingMetricReader(metric_exporter)
|
|
53
|
+
meter_provider = MeterProvider(metric_readers=[reader], resource=resource)
|
|
54
|
+
metrics.set_meter_provider(meter_provider)
|
|
55
|
+
|
|
56
|
+
atexit.register(tracer_provider.shutdown)
|
|
57
|
+
atexit.register(meter_provider.shutdown)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "plain.cloud"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Production observability via OTLP export to Plain Cloud."
|
|
5
|
+
authors = [{ name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev" }]
|
|
6
|
+
license = "BSD-3-Clause"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
requires-python = ">=3.13"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"plain>=0.113.0,<1.0.0",
|
|
11
|
+
"opentelemetry-sdk>=1.34.1",
|
|
12
|
+
"opentelemetry-exporter-otlp-proto-http>=1.34.1",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[dependency-groups]
|
|
16
|
+
dev = ["plain.pytest<1.0.0"]
|
|
17
|
+
|
|
18
|
+
[tool.hatch.build.targets.wheel]
|
|
19
|
+
packages = ["plain"]
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["hatchling"]
|
|
23
|
+
build-backend = "hatchling.build"
|