This version is in beta. Some features may change before release.

Analytics (PostHog)

Fire-and-forget product-analytics event capture for umbral apps via a PostHog backend. Captures never block a request.

Analytics (PostHog)

umbral-analytics wires PostHog product-analytics into your umbral app the idiomatic way: register the plugin, call capture or identify from any handler, and analytics failures are silent - they never block a response or propagate errors to callers. Every HTTP send is fire-and-forget, spawned on a background task.

When no API key is configured the plugin is a clean no-op: capture and identify log at debug level and return immediately. You can develop and run tests without a PostHog account.

Wiring

Add the crate to your app's Cargo.toml:

Code
toml
umbral-analytics = { path = "../../plugins/umbral-analytics", version = "0.0.1" }

Register the plugin in your main.rs:

Code
rust
use umbral::prelude::*;
use umbral_analytics::AnalyticsPlugin;
 
#[tokio::main]
async fn main() -> anyhow::Result<()> {
App::builder()
.plugin(
AnalyticsPlugin::new("phc_your_project_api_key")
// Optional: fire a $pageview event on every request
// (path, method, status code in properties). Default OFF.
.capture_requests(),
)
.build()
.await?
.serve()
.await
}

Capturing events

capture and identify are free async functions. Call them from any handler or service - no dependency injection needed, the ambient client is installed at boot.

Code
rust
use umbral_analytics::{capture, identify};
use serde_json::json;
 
async fn signup_handler(/* ... */) -> impl IntoResponse {
// Associate the user with properties in PostHog.
identify("user_42", json!({
"$set": { "email": "alice@example.com", "plan": "pro" }
})).await;
 
// Capture a named event with custom properties.
capture("user_42", "signup", json!({
"plan": "pro",
"referrer": "homepage"
})).await;
 
StatusCode::CREATED
}

Both calls return immediately. The HTTP send to PostHog happens in a background tokio::spawn; a PostHog outage or misconfiguration never surfaces as an error to your users.

Configuration

The API key and host are resolved in this order (first match wins):

SourceKey
Builder arg (AnalyticsPlugin::new(key))-
Environment variableUMBRAL_POSTHOG_API_KEY
umbral.toml extra keyposthog_api_key

The PostHog host defaults to https://us.i.posthog.com. Override for EU data residency or a self-hosted instance:

Code
rust
AnalyticsPlugin::new("phc_your_key")
.host("https://eu.i.posthog.com")

Or via env / umbral.toml:

Code
bash
UMBRAL_POSTHOG_HOST=https://eu.i.posthog.com

Opt-in per-request middleware

.capture_requests() mounts an axum from_fn middleware that fires a PostHog $pageview event for every HTTP request, with path, method, and status in properties. The status is captured after the handler responds so it reflects the real HTTP status code.

This is off by default. Enable it only when you want volume metrics without instrumenting individual handlers.

No-op without a key

When the API key is absent at boot, AnalyticsPlugin logs a one-time warning and registers without installing an ambient client. capture and identify become silent no-ops for the lifetime of the process. No panics, no errors returned to handlers.

See also

analyticsposthogeventsmiddlewareplugin