Skip to main content

plain.observer

Request tracing and debugging tools built on OpenTelemetry.

Overview

You can use Observer to trace requests and debug performance issues in your Plain application. Observer integrates with OpenTelemetry to capture spans, database queries, and logs for individual requests.

When enabled, Observer shows you a real-time summary of each request including query counts, duplicate queries, and total duration. You can also persist traces to the database for later analysis.

# Access the Observer from any request
from plain.observer import Observer

observer = Observer.from_request(request)

# Check the current mode
if observer.is_enabled():
    print("Observer is tracking this request")

if observer.is_persisting():
    print("Traces will be saved to the database")

# Get performance stats for the current trace
stats = observer.get_current_trace_stats()
# Returns a dict like:
# {"query_count": 5, "duplicate_count": 2, "duration_ms": 45.2, "response_body_size": 1024}

The Observer class provides methods to check the current mode and enable/disable tracing via cookies.

Observer modes

Observer has three modes that control how traces are captured.

Summary mode

Summary mode captures spans in memory for real-time monitoring but does not save them to the database. This is useful for debugging during development without filling up your database.

from plain.observer import Observer

def my_view(request):
    observer = Observer.from_request(request)
    response = Response("OK")
    observer.enable_summary_mode(response)
    return response

The summary cookie lasts for 1 week.

Persist mode

Persist mode captures spans and saves them to the database. This includes full trace data, spans, and log entries. Use this when you need to analyze traces after the request completes.

observer.enable_persist_mode(response)

The persist cookie lasts for 1 day.

Disabled mode

You can explicitly disable Observer to prevent any tracing, even if a parent trace exists.

observer.disable(response)

Toolbar integration

If you have plain.toolbar installed, Observer automatically adds a panel showing the current mode and trace summary. You can toggle between modes directly from the toolbar.

The toolbar panel displays:

  • Current observer mode (Summary, Persist, or Disabled)
  • Query count with duplicate detection
  • Total request duration
  • Link to view persisted traces

Settings

Setting Default Env var
OBSERVER_IGNORE_URL_PATTERNS [...] PLAIN_OBSERVER_IGNORE_URL_PATTERNS (JSON)
OBSERVER_TRACE_LIMIT 100 PLAIN_OBSERVER_TRACE_LIMIT

See default_settings.py for more details.

FAQs

How do I enable Observer in production?

Observer is controlled by a signed cookie, so you can enable it for specific users or sessions. The toolbar provides an easy way to toggle modes, or you can set the cookie programmatically in a view.

Can I use Observer with an external OpenTelemetry collector?

Yes. Observer uses the ObserverSampler and ObserverSpanProcessor which integrate with OpenTelemetry's standard APIs. You can combine Observer with other samplers using ObserverCombinedSampler.

Why are some URLs not being traced?

Observer ignores certain URL patterns by default (assets, observer routes, etc.) to reduce noise. You can customize this with the OBSERVER_IGNORE_URL_PATTERNS setting.

How do I get the trace stats in a template?

In persist or summary mode, you can access the stats from the Observer instance:

# In your view
context["trace_stats"] = Observer.from_request(request).get_current_trace_stats()

What data is stored when persisting traces?

The Trace model stores trace ID, timing, request ID, user ID, and session ID. Each trace has related Span records with full OpenTelemetry span data (including SQL queries and attributes) and Log entries captured during the request.

Installation

Install the plain.observer package from PyPI:

uv add plain.observer

Add plain.observer to your INSTALLED_PACKAGES:

# app/settings.py
INSTALLED_PACKAGES = [
    # ...
    "plain.observer",
]

Include the observer URLs in your URL configuration:

# app/urls.py
from plain.observer.urls import ObserverRouter
from plain.urls import Router, include

class AppRouter(Router):
    namespace = ""
    urls = [
        # ...
        include("observer/", ObserverRouter),
    ]

Sync the database to create the necessary tables:

plain postgres sync

After installation, Observer will automatically integrate with your application's toolbar (if using plain.toolbar). You can access the web interface at /observer/traces/.

Content Security Policy (CSP)

If you're using a Content Security Policy (CSP), the Observer toolbar panel requires frame-ancestors 'self' to display trace information in an iframe.

Without this directive, the toolbar panel will fail to load with a CSP error: "Refused to frame... because an ancestor violates the following Content Security Policy directive: 'frame-ancestors 'none'".

Example CSP configuration:

DEFAULT_RESPONSE_HEADERS = {
    "Content-Security-Policy": (
        "default-src 'self'; "
        "script-src 'self' 'nonce-{request.csp_nonce}'; "
        "style-src 'self' 'nonce-{request.csp_nonce}'; "
        "frame-ancestors 'self'; "  # Required for Observer toolbar
        # ... other directives
    ),
}

Metadata

Release files for plain.observer 0.35.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for plain.observer 0.35.1
File Size Uploaded
plain_observer-0.35.1.tar.gz 44.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for plain.observer 0.35.1
File Interpreter ABI Platform
plain_observer-0.35.1-py3-none-any.whl Python 3 none any Details

Total release size: 103.5 kB

Release files / plain_observer-0.35.1.tar.gz

Download URL plain_observer-0.35.1.tar.gz
Size 44.7 kB
Tags Source
SHA-256 checksum
How to use checksums
1ad0a96702662600f5c63bf660f61b58a603ac67a3ade7e1af27d6ce7bac23a2
BLAKE2b-256 checksum
How to use checksums
a3498791e04fedeb6508787af103c9a04c83c5fa32ded339889005d741f0a9e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / plain_observer-0.35.1-py3-none-any.whl

Download URL plain_observer-0.35.1-py3-none-any.whl
Size 58.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
009f4434cdcc24b33c97558244b98e5c4c9494cac1bfa93b366e7d396dd0a85c
BLAKE2b-256 checksum
How to use checksums
dc0e5a7b81877812d88945b49c211a108c7ba47be2602321c8bc9493c00a87ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.35.1 This release

2 release files

0.35.0

2 release files

0.34.8

2 release files

0.34.7

2 release files

0.34.6

2 release files

0.34.5

2 release files

0.34.4

2 release files

0.34.3

2 release files

0.34.2

2 release files

0.33.1

2 release files

0.33.0

2 release files

0.32.1

2 release files

0.32.0

2 release files

0.31.3

2 release files

0.31.2

2 release files

0.31.1

2 release files

0.31.0

2 release files

0.30.0

2 release files

0.29.1

2 release files

0.29.0

2 release files

0.27.5

2 release files

0.27.4

2 release files

0.27.2

2 release files

0.27.1

2 release files

0.27.0

2 release files

0.26.0

2 release files

0.25.0

2 release files

0.24.0

2 release files

0.23.2

2 release files

0.23.1

2 release files

0.21.0

2 release files

0.20.0

2 release files

0.19.1

2 release files

0.19.0

2 release files

0.18.0

2 release files

0.17.0

2 release files

0.16.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.2

2 release files

0.11.1

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page