
Instrument Error Tracking
- 126 installs
- 70 repo stars
- Updated August 4, 2026
- posthog/ai-plugin
instrument-error-tracking is a Claude Code skill for ai & agent building.
About
instrument-error-tracking is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- instrument-error-tracking
- AI & Agent Building
- AI-coding skill
Instrument Error Tracking by the numbers
- 126 all-time installs (skills.sh)
- +5 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #3,743 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/posthog/ai-plugin --skill instrument-error-trackingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 126 |
|---|---|
| repo stars | ★ 70 |
| Last updated | August 4, 2026 |
| Repository | posthog/ai-plugin ↗ |
How do I helps with ai & agent building tasks during AI-assisted development.?
Helps with ai & agent building tasks during AI-assisted development.
Who is it for?
Best when you're working on ai & agent building and need structured help with instrument error tracking.
Skip if: Teams with no ai & agent building needs, or anyone wanting a generic chat assistant without this specific workflow.
When should I use this skill?
When you need to helps with ai & agent building tasks during AI-assisted development., or when instrument-error-tracking is a claude code skill for ai & agent building.
What you get
Structured output aligned to instrument-error-tracking: instrument-error-tracking, AI & Agent Building.
Files
Add PostHog error tracking
Use this skill to add PostHog error tracking that captures and monitors exceptions in your application. Use it after implementing features or reviewing PRs to ensure errors are tracked with full stack traces and source maps. If PostHog is not yet installed, this skill also covers initial SDK setup. Supports any platform or language.
Supported platforms: React, Next.js, Web (JavaScript), Node.js, Python, PHP, Ruby, Ruby on Rails, Go, Elixir, Angular, Svelte, Nuxt, React Native, Flutter, iOS, Android, and Hono.
Instructions
Follow these steps IN ORDER:
STEP 1: Analyze the codebase and detect the platform. - Look for dependency files (package.json, pubspec.yaml, Podfile, Package.swift, requirements.txt, go.mod, Gemfile, composer.json, mix.exs, etc.) to determine the language and framework. - Look for lockfiles (pnpm-lock.yaml, package-lock.json, yarn.lock, bun.lockb, go.sum, pubspec.lock, Podfile.lock, Package.resolved, mix.lock) to determine the package manager.
- Check for existing PostHog setup (SDK initialization, env vars, etc.). If PostHog is already installed and initialized, skip to STEP 4.
STEP 2: Research instrumentation. (Skip if PostHog is already set up.) 2.1. Find the reference file below that matches the detected platform — it is the source of truth for SDK initialization, exception autocapture, and framework-specific error tracking patterns. Read it now. 2.2. If no reference matches, fall back to your general knowledge and web search. Use posthog.com/docs as the primary search source.
STEP 3: Install and initialize the PostHog SDK. (Skip if PostHog is already set up.)
- Add the PostHog SDK package for the detected platform. Do not manually edit package.json — use the package manager's install command.
- Always install packages as a background task. Don't await completion; proceed with other work immediately after starting the installation.
- Follow the framework reference for where and how to initialize.
STEP 4: Enable exception autocapture.
- Follow the platform reference to enable exception autocapture. This automatically captures unhandled exceptions without additional code.
STEP 5: Add manual error captures.
- Identify error boundaries, catch blocks, and critical user flows where errors should be explicitly captured.
- Add
posthog.captureException()or the platform-equivalent at these locations. - Do not alter the fundamental architecture of existing error handling. Make additions minimal and targeted.
- You must read a file immediately before attempting to write it.
STEP 6: Upload source maps (frontend/mobile only).
- Configure source map uploads so stack traces resolve to original source code, not minified bundles.
- Follow the platform-specific reference for upload configuration (build plugins, CI scripts, etc.).
STEP 7: Set up environment variables.
- Check if the project already has PostHog environment variables configured (e.g. in
.env,.env.local, or framework-specific env files). If valid values already exist, skip this step. - If the PostHog API key is missing, use the PostHog MCP server's
projects-gettool to retrieve the project'sapi_token. If multiple projects are returned, ask the user which project to use. If the MCP server is not connected or not authenticated, ask the user for their PostHog project API key instead. - For the PostHog host URL, use
https://us.i.posthog.comfor US Cloud orhttps://eu.i.posthog.comfor EU Cloud. - Write these values to the appropriate env file using the framework's naming convention.
- Reference these environment variables in code instead of hardcoding them.
STEP 8: Verify and clean up.
- Check the project for errors. Look for type checking or build scripts in package.json.
- Ensure any components created were actually used.
- Run any linter or prettier-like scripts found in the package.json.
Reference files
references/react.md- React error tracking installation - docsreferences/web.md- Web error tracking installation - docsreferences/nextjs.md- Next.js error tracking installation - docsreferences/node.md- Node.js error tracking installation - docsreferences/python.md- Python error tracking installation - docsreferences/django.md- Django - docsreferences/flask.md- Flask - docsreferences/php.md- Php error tracking installation - docsreferences/laravel.md- Laravel - docsreferences/ruby.md- Ruby error tracking installation - docsreferences/ruby-on-rails.md- Ruby on rails error tracking installation - docsreferences/ruby-on-rails.md- Ruby on rails - docsreferences/go.md- Go error tracking installation - docsreferences/dotnet.md- .net error tracking installation - docsreferences/dotnet.md- .net - docsreferences/elixir.md- Elixir error tracking installation - docsreferences/angular.md- Angular error tracking installation - docsreferences/svelte.md- Sveltekit error tracking installation - docsreferences/nuxt-3-7.md- Nuxt error tracking installation (v3.7 and above) - docsreferences/nuxt-3-6.md- Nuxt error tracking installation (v3.6 and below) - docsreferences/react-native.md- React native error tracking installation - docsreferences/flutter.md- Flutter error tracking installation - docsreferences/ios.md- Ios error tracking installation - docsreferences/android.md- Android error tracking installation - docsreferences/hono.md- Hono error tracking installation - docsreferences/fingerprints.md- Fingerprints - docsreferences/alerts.md- Send error tracking alerts - docsreferences/monitoring.md- Monitor and search issues - docsreferences/assigning-issues.md- Assign issues to teammates - docsreferences/upload-source-maps.md- Upload source maps - docs
Each platform reference contains SDK-specific installation and manual capture patterns. Find the one matching the user's stack.
Key principles
- Environment variables: Always use environment variables for PostHog keys. Never hardcode them.
- Minimal changes: Add error tracking alongside existing error handling. Don't replace or restructure existing code.
- Autocapture first: Enable exception autocapture before adding manual captures.
- Source maps: Upload source maps so stack traces resolve to original source code, not minified bundles.
- Manual capture for boundaries: Use
captureException()at error boundaries and catch blocks for errors that don't propagate to the global handler.
Send error tracking alerts - Docs
To stay on top of issues, you can set up alerts. These enable you to post to Slack, Discord, Teams, or an HTTP Webhook when an issue is created or reopened.
Issue created or reopened
To alert when an issue is created or reopened, go to error tracking's configuration page and click Alerting. This shows you a list of existing alerts. Clicking New notification brings you to a page to create a new one.
!Error tracking alerting!Error tracking alerting
Choosing an option brings you to a page to configure the alert. This may require setting up the Slack integration or pasting in a webhook URL. Once done, you can test the alert by clicking Test function and then finalize by clicking Create & enable.
This will then send alerts to your chosen destination when an issue is created or reopened like this:
Issue properties and assignments
You can filter an alert based on the properties of an issue. This is useful for notifying a specific team when they have been auto assigned an issue using auto assignment rules.
!Error tracking alert assignee filtering!Error tracking alert assignee filtering
Spike alerts
PostHog can also alert you when an existing issue suddenly spikes in volume - for example, after a bad deploy. This works differently from issue-created alerts. Instead of triggering when a new issue is first seen, spike alerts fire when an issue's error rate significantly exceeds its historical baseline.
See the spike detection guide to learn how it works and how to configure it.
Other alerting options
Since error tracking works by capturing $exception events, PostHog features that trigger by events can play a role in alerts too.
Real time destinations
The first way is using real time destinations. This enables you to send events (like $exception) to other tools as soon as they are ingested.
To create a real time destination, go to the data pipelines tab in PostHog, click \+ New, and then select Destination. Choose your destination and press \+ Create.
On the destination creation screen, make sure to add an event matcher for the $exception event, filter for the properties you want, and set the trigger options.
!Real time destination!Real time destination
Check out our real time destinations docs for more information.
Trend alerts
You can also visualize your $exception events using trends. Once you create a trend insight, click the Alerts button at the top of the insight and then New alert.
Here you can set alerts for event volume value, increase, or decrease.
This sends an email notification to the user you choose. Check out our alerts docs for more information.
Can't find your alert?
If you'd like a destination to be added that we don't yet support, let us know in-app.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Android error tracking installation - Docs
1. 1
Install the dependency
Required
Add the PostHog Android SDK to your build.gradle dependencies:
build.gradle
PostHog AI
dependencies {
implementation("com.posthog:posthog-android:3.+")
}2. 2
Configure PostHog
Required
Initialize PostHog in your Application class:
SampleApp.kt
PostHog AI
class SampleApp : Application() {
companion object {
const val POSTHOG_PROJECT_TOKEN = "<ph_project_token>"
const val POSTHOG_HOST = "https://us.i.posthog.com"
}
override fun onCreate() {
super.onCreate()
// Create a PostHog Config with the given project token and host
val config = PostHogAndroidConfig(
apiKey = POSTHOG_PROJECT_TOKEN,
host = POSTHOG_HOST
)
// Setup PostHog with the given Context and Config
PostHogAndroid.setup(this, config)
}
}3. 3
Send events
Recommended
Once installed, PostHog will automatically start capturing events. You can also manually send events to test your integration:
Kotlin
PostHog AI
import com.posthog.PostHog
PostHog.capture(
event = "button_clicked",
properties = mapOf(
"button_name" to "signup"
)
)4. 4
Set up exception autocapture
Recommended
Client-side configuration only
Support for remote configuration in the error tracking settings requires SDK version 3.32.0 or higher.
You can autocapture exceptions by setting the errorTrackingConfig.autoCapture argument to true when initializing the PostHog SDK.
Kotlin
PostHog AI
import com.posthog.android.PostHogAndroidConfig
val config = PostHogAndroidConfig(
apiKey = POSTHOG_PROJECT_TOKEN,
host = POSTHOG_HOST
).apply {
...
errorTrackingConfig.autoCapture = true
}
...When enabled, this automatically captures $exception events when errors are thrown by wrapping the Thread.UncaughtExceptionHandler listener.
Planned features
We currently don't support source code context associated with an exception.
These features will be added in a future release.
5. 5
Manually capture exceptions
Optional
It is also possible to manually capture exceptions using the captureException method:
Kotlin
PostHog AI
PostHog.captureException(
exception,
properties = additionalProperties
)This is helpful if you've built your own error handling logic or want to capture exceptions that are handled by your application code.
6. ## Verify error tracking
Recommended
Confirm events are being sent to PostHog
Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
!Activity feed with events!Activity feed with events
Check for exceptions in PostHog
7. 6
Upload mapping files
Required
Great, you're capturing exceptions! The next step is to upload ProGuard/R8 mapping files so PostHog can deobfuscate your stack traces.
Let's continue to the next section.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Angular error tracking installation - Docs
1. 1
Install the package
Required
Install the PostHog JavaScript library using your package manager:
PostHog AI
npm
npm install posthog-jsyarn
yarn add posthog-jspnpm
pnpm add posthog-js2. 2
Initialize PostHog
Required
In your src/main.ts, initialize PostHog using your project token and instance address:
Angular 17+
For Angular v17 and above, you can set up PostHog as a singleton service. To do this, start by creating and injecting a PosthogService instance.
Create a service by running ng g service services/posthog. The service should look like this:
src/main.ts
PostHog AI
// src/app/services/posthog.service.ts
import { DestroyRef, Injectable, NgZone } from "@angular/core";
import posthog from "posthog-js";
import { environment } from "../../environments/environment";
import { Router } from "@angular/router";
@Injectable({ providedIn: "root" })
export class PosthogService {
constructor(
private ngZone: NgZone,
private router: Router,
private destroyRef: DestroyRef,
) {
this.initPostHog();
}
private initPostHog() {
this.ngZone.runOutsideAngular(() => {
posthog.init(environment.posthogKey, {
api_host: environment.posthogHost,
defaults: '2026-01-30',
});
});
}
}The service is initialized outside of the Angular zone to reduce change detection cycles. This is important to avoid performance issues with session recording. Then, inject the service in your app's root component app.component.ts. This will make sure PostHog is initialized before any other component is rendered.
src/app/app.component.ts
PostHog AI
// src/app/app.component.ts
import { Component } from "@angular/core";
import { RouterOutlet } from "@angular/router";
import { PosthogService } from "./services/posthog.service";
@Component({
selector: "app-root",
styleUrls: ["./app.component.scss"],
template: `
<router-outlet />`,
imports: [RouterOutlet],
})
export class AppComponent {
title = "angular-app";
constructor(posthogService: PosthogService) {}
}Angular 16 and below
In your src/main.ts, initialize PostHog using your project API key and instance address. You can find both in your project settings.
src/main.ts
PostHog AI
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import { environment } from "./environments/environment";
import posthog from 'posthog-js'
posthog.init(environment.posthogKey, {
api_host: environment.posthogHost,
defaults: '2025-11-30'
})
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));3. 3
Send events
Click around and view a couple pages to generate some events. PostHog automatically captures pageviews, clicks, and other interactions for you.
If you'd like, you can also manually capture custom events:
JavaScript
PostHog AI
posthog.capture('my_custom_event', { property: 'value' })4. 4
Setting up exception autocapture
Recommended
Exception autocapture can be enabled during initialization of the PostHog client to automatically capture any exception thrown by your Angular application.
This requires overriding Angular's default ErrorHandler provider:
src/app/posthog-error-handler.ts
PostHog AI
import { ErrorHandler, Injectable, Provider } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import posthog from 'posthog-js';
@Injectable({ providedIn: 'root' })
class PostHogErrorHandler implements ErrorHandler {
public constructor() {}
public handleError(error: unknown): void {
const extractedError = this._extractError(error) || 'Unknown error';
runOutsideAngular(() => posthog.captureException(extractedError));
}
protected _extractError(errorCandidate: unknown): unknown {
const error = tryToUnwrapZonejsError(errorCandidate);
if (error instanceof HttpErrorResponse) {
return extractHttpModuleError(error);
}
if (typeof error === 'string' || isErrorOrErrorLikeObject(error)) {
return error;
}
return null;
}
}
function tryToUnwrapZonejsError(error: unknown): unknown | Error {
return error && (error as { ngOriginalError: Error }).ngOriginalError
? (error as { ngOriginalError: Error }).ngOriginalError
: error;
}
function extractHttpModuleError(error: HttpErrorResponse): string | Error {
if (isErrorOrErrorLikeObject(error.error)) {
return error.error;
}
if (
typeof ErrorEvent !== 'undefined' &&
error.error instanceof ErrorEvent &&
error.error.message
) {
return error.error.message;
}
if (typeof error.error === 'string') {
return `Server returned code ${error.status} with body "${error.error}"`;
}
return error.message;
}
function isErrorOrErrorLikeObject(value: unknown): value is Error {
if (value instanceof Error) {
return true;
}
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
return false;
}
return 'name' in value && 'message' in value && 'stack' in value;
}
declare const Zone: any;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const isNgZoneEnabled = typeof Zone !== 'undefined' && Zone.root?.run;
export function runOutsideAngular<T>(callback: () => T): T {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return isNgZoneEnabled ? Zone.root.run(callback) : callback();
}
export function providePostHogErrorHandler(): Provider {
return {
provide: ErrorHandler,
useValue: new PostHogErrorHandler(),
};
}Then, in your src/app/app.config.ts, import the providePostHogErrorHandler function and add it to the providers array:
src/app/app.config.ts
PostHog AI
// src/app/app.config.ts
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { providePostHogErrorHandler } from './posthog-error-handler';
export const appConfig: ApplicationConfig = {
providers: [
...
providePostHogErrorHandler(),
],
};5. 5
Manually capture exceptions
Optional
If there are more errors you'd like to capture, you can manually call the captureException method:
TypeScript
PostHog AI
posthog.captureException(e, additionalProperties)6. ## Verify error tracking
Recommended
Confirm events are being sent to PostHog
Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
!Activity feed with events!Activity feed with events
Check for exceptions in PostHog
7. 6
Upload source maps
Required
Great, you're capturing exceptions! If you serve minified bundles, the next step is to upload source maps to generate accurate stack traces.
Let's continue to the next section.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Assign issues to teammates - Docs
Error tracking enables you to assign issues to specific PostHog roles or teammates. This helps your team find relevant issues through filtering. You can also set up team-specific alerting to notify them when assigned issues are created or reopened.
Assign issues
You can manually assign issues as you triage them in the UI. This can be done both in the issue list and issue detail pages.
!Error tracking assignment UI!Error tracking assignment UI
1. In your error tracking issue list, click the unassigned selector under each issue to assign it to a role or user.
2. On the detail page of each issue, click the Assignee selector to assign it to a role or user.
Want to assign issues to a team rather than an individual teammate? You can create a role in your project settings.
!Error tracking role assignees!Error tracking role assignees
Automatic issue assignment
You can set up automatic issue assignment through a set of rules. This can be configured in the error tracking settings using auto assignment rules. You can also create assignment rules programmatically using the PostHog MCP server.
!Error tracking auto assignment rules!Error tracking auto assignment rules
Assignment conditions are evaluated against the properties of the exception event that created the issue. Because assignment rules are evaluated during ingestion, the stack trace (if present) will be unminified, which enables filtering on exception properties such as function name and source file.
Issues can be automatically assigned to a role or user by configuring a set of filters. These filters can be configured to match any or all of the criteria.
You can configure automatic assignment to filter on any event property in PostHog. When there are multiple values for a property, the filters return true if it matches any of the values. For example, if you have multiple exception_functions values, the filters returns true if it matches any of the functions.
Here are some common properties you can filter on:
| Property | Event property | Description |
|---|---|---|
| Exception type | $exception_types | The type of exception(s) that occurred |
| Exception message | $exception_values | The message(s) detected on the error |
| Exception function | $exception_functions | The function(s) where the exception occurred |
| Exception source | $exception_sources | The source file(s) where the exception occurred |
| Exception was handled | $exception_handled | Whether the exception was handled by the application |
| Device type | $device_type | The type of device that the error occurred on |
| Browser | $browser | The browser that the error occurred in |
| Current URL | $current_url | The URL that the error occurred on |
| Feature flag | $feature_flag | The feature flag that the error occurred on |
You can also set custom properties on the error tracking event to filter on. For example, setting a custom params_received property to provide more context or debug information.
Order of issue assignment rules
Issue assignment filters are evaluated in the order they are configured. They can also be reordered once created. The first filter that matches is used to assign the issue. This means you should configure the most specific filters first, and then the more general filters later.
Disabled assignment rules
Assignment rules can become disabled if an error occurs during ingestion. When a rule is disabled, a banner displays the original error message. To re-enable the rule, edit it to fix the problem and save your changes. If the issue persists, reach out to support.
Alerting based on assignment
A common use case for automatic issue assignment is to alert assignees of new issues. Once the issues are automatically assigned, you can set up alerts to notify the assignee. See the alerts guide for more information.
Create external issues
You can also create issues in external tracking systems like GitHub Issues, Linear, GitLab, or Jira.
First, set up an integration with your tracking system. Then, from an issue's details page, under External references, click Create issue.
!Error tracking create issue in external tracking system!Error tracking create issue in external tracking system
The new issue will have a partial stack trace and a link to the issue in PostHog.
If you use another issue tracking system and would like to request it, let us know in-app.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Django - Docs
PostHog makes it easy to get data about traffic and usage of your Django app. Integrating PostHog enables analytics, custom events capture, feature flags, error tracking, and more.
This guide walks you through integrating PostHog into your Django app using the Python SDK.
Beta: integration via LLM
Install PostHog for Django in seconds with our wizard by running this prompt with LLM coding agents like Cursor and Bolt, or by running it in your terminal.
npx @posthog/wizard@latest
Or, to integrate manually, continue with the rest of this guide.
Installation
To start, run pip install posthog to install PostHog’s Python SDK.
Note: Version 7.x of the PostHog Python SDK requires Python 3.10 or higher.Then, configure PostHog in your app config so it's initialized when Django starts:
your\_app/apps.py
PostHog AI
from django.apps import AppConfig
import posthog
class YourAppConfig(AppConfig):
name = 'your_app_name'
def ready(self):
posthog.api_key = '<ph_project_token>'
posthog.host = 'https://us.i.posthog.com'Next, if you haven't done so already, add your AppConfig to INSTALLED_APPS in settings.py:
settings.py
PostHog AI
INSTALLED_APPS = [
# ... other apps
'your_app_name.apps.YourAppConfig',
]You can find your project token and instance address in your project settings.
To capture events from any file, import posthog and call the method you need. For example:
Python
PostHog AI
import posthog
from posthog import identify_context
def some_request(request):
with posthog.new_context():
# Django includes request.user for anonymous visitors too. Only identify
# the context when the visitor is logged in.
if request.user.is_authenticated:
identify_context(str(request.user.pk))
posthog.capture('event_name')Events captured without a context or explicit distinct_id are sent as anonymous events with an auto-generated distinct_id. See the Python SDK docs for more details.
Identifying users
Identifying users is required. Backend events need adistinct_idthat matches the ID your frontend uses when callingposthog.identify(). Without this, backend events are orphaned — they can't be linked to frontend event captures, session replays, LLM traces, or error tracking.
>
See our guide on identifying users for how to set this up.
Django contexts middleware
The Python SDK provides a Django middleware that automatically wraps all requests with a context. This middleware extracts session and user information from each request and tags all events captured during that request with relevant metadata.
Basic setup
Add the middleware to your Django settings. If your app uses Django authentication, place it after django.contrib.auth.middleware.AuthenticationMiddleware so the middleware can use the authenticated Django user as a distinct ID fallback and capture the user's email.
Python
PostHog AI
MIDDLEWARE = [
# ... other middleware
'posthog.integrations.django.PosthogContextMiddleware',
# ... other middleware
]The middleware uses the globally configured posthog client by default, so you don't need to create or pass it a separate client instance.
The middleware automatically extracts and uses:
- Session ID from the
X-POSTHOG-SESSION-IDheader, if present - Distinct ID from the
X-POSTHOG-DISTINCT-IDheader, if present, falling back to the authenticated Django user'spk(Django's primary-key alias, which works with custom user models) - User email from the authenticated Django user's
emailasemail - Current URL as
$current_url - Request method as
$request_method - Request path as
$request_path - Forwarded IP address from
X-Forwarded-Foras$ip - User agent from
User-Agentas$user_agent
The session and distinct ID headers are sanitized before use. Empty values are ignored, control characters are removed, values are trimmed, and values are capped at 1000 characters.
All events captured during the request (including exceptions) include these properties and are associated with the extracted session and distinct ID.
If you're using PostHog JS on the frontend, configure `tracing_headers` for your Django backend hostname so browser requests include the session and distinct ID headers.
Exception capture
By default, the middleware captures exceptions and sends them to PostHog's error tracking using the globally configured posthog client. This includes Django view exceptions that Django converts into error responses.
Disable this by setting:
Python
PostHog AI
# settings.py
POSTHOG_MW_CAPTURE_EXCEPTIONS = FalseAdding custom tags
Use POSTHOG_MW_EXTRA_TAGS to add custom properties to all requests:
Python
PostHog AI
# settings.py
def add_user_tags(request):
# type: (HttpRequest) -> Dict[str, Any]
tags = {}
if hasattr(request, 'user') and request.user.is_authenticated:
# Use pk instead of id so this works with custom User primary keys.
tags['user_id'] = str(request.user.pk)
tags['email'] = request.user.email
return tags
POSTHOG_MW_EXTRA_TAGS = add_user_tagsFiltering requests
Skip tracking for certain requests using POSTHOG_MW_REQUEST_FILTER:
Python
PostHog AI
# settings.py
def should_track_request(request):
# type: (HttpRequest) -> bool
# Don't track health checks or admin requests
if request.path.startswith('/health') or request.path.startswith('/admin'):
return False
return True
POSTHOG_MW_REQUEST_FILTER = should_track_requestModifying default tags
Use POSTHOG_MW_TAG_MAP to modify or remove default tags:
Python
PostHog AI
# settings.py
def customize_tags(tags):
# type: (Dict[str, Any]) -> Dict[str, Any]
# Remove URL for privacy
tags.pop('$current_url', None)
# Add custom prefix to method
if '$request_method' in tags:
tags['http_method'] = tags.pop('$request_method')
return tags
POSTHOG_MW_TAG_MAP = customize_tagsComplete configuration example
Python
PostHog AI
# settings.py
def add_request_context(request):
# type: (HttpRequest) -> Dict[str, Any]
tags = {}
if hasattr(request, 'user') and request.user.is_authenticated:
tags['user_type'] = 'authenticated'
# Use pk instead of id so this works with custom User primary keys.
tags['user_id'] = str(request.user.pk)
else:
tags['user_type'] = 'anonymous'
# Add request info
tags['user_agent'] = request.META.get('HTTP_USER_AGENT', '')
return tags
def filter_tracking(request):
# type: (HttpRequest) -> bool
# Skip internal endpoints
return not request.path.startswith(('/health', '/metrics', '/admin'))
def clean_tags(tags):
# type: (Dict[str, Any]) -> Dict[str, Any]
# Remove sensitive data
tags.pop('user_agent', None)
return tags
POSTHOG_MW_EXTRA_TAGS = add_request_context
POSTHOG_MW_REQUEST_FILTER = filter_tracking
POSTHOG_MW_TAG_MAP = clean_tags
POSTHOG_MW_CAPTURE_EXCEPTIONS = TrueAll events captured within the request context automatically include the configured tags and are associated with the session and user identified from the request headers or Django authentication.
The middleware supports both sync (WSGI) and async (ASGI) Django applications. In async mode, it uses Django's request.auser() API when available to avoid synchronous user access.
Next steps
For any technical questions for how to integrate specific PostHog features into Django (such as analytics, feature flags, A/B testing, etc.), have a look at our Python SDK docs.
Alternatively, the following tutorials can help you get started:
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
.NET - Docs
This is an optional library you can install if you're working with .NET Core. It uses an internal queue to make calls fast and non-blocking. It also batches requests and flushes asynchronously, making it perfect to use in any part of your web app or other server side application that needs performance.
Installation
The PostHog package supports any .NET platform that targets .NET Standard 2.1 or .NET 8+, including MAUI, Blazor, and console applications. The PostHog.AspNetCore package provides additional conveniences for ASP.NET Core applications such as streamlined registration, request-scoped caching, and integration with .NET Feature Management.
Note: We actively test with ASP.NET Core. Other platforms should work but haven't been specifically tested. If you encounter issues, please report them on GitHub.
Not supported: Classic UWP (requires .NET Standard 2.0 only). Microsoft has deprecated UWP in favor of the Windows App SDK. For Unity projects, see our dedicated Unity SDK.
Terminal
PostHog AI
dotnet add package PostHog.AspNetCoreIn your Program.cs (or Startup.cs for ASP.NET Core 2.x) file, add the following code:
C#
PostHog AI
using PostHog;
var builder = WebApplication.CreateBuilder(args);
// Add PostHog to the dependency injection container as a singleton.
builder.AddPostHog();Make sure to configure PostHog with your project token, instance address, and optional personal API key. For example, in appsettings.json:
JSON
PostHog AI
{
"PostHog": {
"ProjectToken": "<ph_project_token>",
"HostUrl": "https://us.i.posthog.com"
}
}Note: If the host is not specified, the default host https://us.i.posthog.com is used.Use a secrets manager to store your personal API key. For example, when developing locally you can use the UserSecrets feature of the dotnet CLI:
Terminal
PostHog AI
dotnet user-secrets init
dotnet user-secrets set "PostHog:PersonalApiKey" "phx_..."You can find your project token and instance address in the project settings page in PostHog.
Working with .NET Feature Management
PostHog.AspNetCore supports .NET Feature Management. This enables you to use the <feature /\> tag helper and the FeatureGateAttribute in your ASP.NET Core applications to gate access to certain features using PostHog feature flags.
To use feature flags with the .NET Feature Management library, you'll need to implement the IPostHogFeatureFlagContextProvider interface. The quickest way to do that is to inherit from the PostHogFeatureFlagContextProvider class and override the GetDistinctId and GetFeatureFlagOptionsAsync methods.
C#
PostHog AI
public class MyFeatureFlagContextProvider(IHttpContextAccessor httpContextAccessor)
: PostHogFeatureFlagContextProvider
{
protected override string? GetDistinctId()
=> httpContextAccessor.HttpContext?.User.Identity?.Name;
protected override ValueTask<FeatureFlagOptions> GetFeatureFlagOptionsAsync()
{
// In a real app, you might get this information from a
// database or other source for the current user.
return ValueTask.FromResult(
new FeatureFlagOptions
{
PersonProperties = new Dictionary<string, object?>
{
["email"] = "some-test@example.com"
},
OnlyEvaluateLocally = true
});
}
}Then, register your implementation in Program.cs (or Startup.cs):
C#
PostHog AI
var builder = WebApplication.CreateBuilder(args);
builder.AddPostHog(options => {
options.UseFeatureManagement<MyFeatureFlagContextProvider>();
});With this in place, you can now use feature tag helpers in your Razor views:
HTML
PostHog AI
<feature name="awesome-new-feature">
<p>This is the new feature!</p>
</feature>
<feature name="awesome-new-feature" negate="true">
<p>Sorry, no awesome new feature for you.</p>
</feature>Multivariate feature flags are also supported:
HTML
PostHog AI
<feature name="awesome-new-feature" value="variant-a">
<p>This is the new feature variant A!</p>
</feature>
<feature name="awesome-new-feature" value="variant-b">
<p>This is the new feature variant B!</p>
</feature>You can also use the FeatureGateAttribute to gate access to controllers or actions:
C#
PostHog AI
[FeatureGate("awesome-new-feature")]
public class NewFeatureController : Controller
{
public IActionResult Index()
{
return View();
}
}Using the core package without ASP.NET Core
If you're not using ASP.NET Core (for example, in a console application, MAUI app, or Blazor WebAssembly), install the PostHog package instead of PostHog.AspNetCore. This package has no ASP.NET Core dependencies and can be used in any .NET project targeting .NET Standard 2.1 or .NET 8+.
Terminal
PostHog AI
dotnet add package PostHogThe PostHogClient class must be implemented as a singleton in your project. For PostHog.AspNetCore, this is handled by the builder.AddPostHog(); method. For the PostHog package, you can do the following if you're using dependency injection:
C#
PostHog AI
builder.Services.AddPostHog();If you're not using a builder (such as in a console application), you can do the following:
C#
PostHog AI
using PostHog;
var services = new ServiceCollection();
services.AddPostHog();
var serviceProvider = services.BuildServiceProvider();
var posthog = serviceProvider.GetRequiredService<IPostHogClient>();The AddPostHog methods accept an optional Action<PostHogOptions> parameter that you can use to configure the client.
If you're not using dependency injection, you can create a static instance of the PostHogClient class and use that everywhere in your project:
C#
PostHog AI
using PostHog;
public static readonly PostHogClient PostHog = new(new PostHogOptions {
ProjectToken = "<ph_project_token>",
HostUrl = new Uri("https://us.i.posthog.com"),
PersonalApiKey = Environment.GetEnvironmentVariable(
"PostHog__PersonalApiKey")
});Debug mode
If you're not seeing the expected events being captured, the feature flags being evaluated, or the surveys being shown, you can enable debug mode to see what's happening.
To see detailed logging, set the log level to Debug or Trace in appsettings.json:
JSON
PostHog AI
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"PostHog": "Trace"
}
},
...
}Identifying users
Identifying users is required. Backend events need adistinct_idthat matches the ID your frontend uses when callingposthog.identify(). Without this, backend events are orphaned — they can't be linked to frontend event captures, session replays, LLM traces, or error tracking.
>
See our guide on identifying users for how to set this up.
Capturing events
You can send custom events using capture:
C#
PostHog AI
posthog.Capture("distinct_id_of_the_user", "user_signed_up");Tip: We recommend using a[object] [verb]format for your event names, where[object]is the entity that the behavior relates to, and[verb]is the behavior itself. For example,project created,user signed up, orinvite sent.
Setting event properties
Optionally, you can include additional information with the event by including a properties object:
C#
PostHog AI
posthog.Capture(
"distinct_id_of_the_user",
"user_signed_up",
properties: new() {
["login_type"] = "email",
["is_free_trial"] = "true"
}
);Sending page views
If you're aiming for a backend-only implementation of PostHog and won't be capturing events from your frontend, you can send $pageview events from your backend like so:
C#
PostHog AI
using PostHog;
using Microsoft.AspNetCore.Http.Extensions;
posthog.CapturePageView(
"distinct_id_of_the_user",
HttpContext.Request.GetDisplayUrl());Request context
For ASP.NET Core apps using PostHog.AspNetCore, add request context middleware before routes that call PostHog. This reads incoming PostHog tracing headers and attaches request metadata to captures, exceptions, and feature flag evaluation inside the request.
Program.cs
PostHog AI
using PostHog;
using PostHog.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.AddPostHog();
var app = builder.Build();
app.UsePostHogRequestContext();If you're using PostHog JS on the frontend, configure `tracing_headers` for your ASP.NET Core backend hostname so browser requests include the session and distinct ID headers.
The middleware reads X-PostHog-Distinct-Id and X-PostHog-Session-Id as request-scoped analytics context. It also adds request metadata such as $current_url, $request_method, $request_path, $user_agent, and $ip. Explicit distinct IDs and event properties always override request context.
Tracing headers are client-controlled analytics context, not authentication or authorization. For security-sensitive server-side decisions, pass an authenticated distinct ID explicitly. You can ignore tracing headers while still collecting request metadata:
C#
PostHog AI
app.UsePostHogRequestContext(options =>
{
options.UseTracingHeaders = false;
});Request-context overloads like posthog.Capture("checkout started") and posthog.EvaluateFlagsAsync() use the current request distinct ID when one is available.
Error tracking
You can manually capture exceptions using CaptureException. This sends a $exception event with stack frames, inner exceptions, aggregate exceptions, source context when available, and .NET runtime metadata.
File names, line numbers, and source context depend on debug information already available from the captured .NET stack trace. PostHog doesn't support uploading .NET PDB files yet, so production builds without runtime-accessible debug information may show less detailed stack frames.
C#
PostHog AI
try
{
ProcessOrder(orderId);
}
catch (Exception exception)
{
posthog.CaptureException(exception, "user_distinct_id");
}Add custom properties to include request, tenant, or domain context:
C#
PostHog AI
posthog.CaptureException(
exception,
"user_distinct_id",
new Dictionary<string, object>
{
["order_id"] = orderId,
["environment"] = "production",
}
);For the full setup guide, see the .NET error tracking installation docs.
Automatic exception capture is not available in the .NET SDK yet.
Person profiles and properties
The .NET SDK captures identified events by default. These create person profiles. To set person properties in these profiles, include them when capturing an event:
C#
PostHog AI
posthog.Capture(
"distinct_id",
"event_name",
personPropertiesToSet: new() { ["name"] = "Max Hedgehog" },
personPropertiesToSetOnce: new() { ["initial_url"] = "/blog" }
);For more details on the difference between $set and $set_once, see our person properties docs.
To capture anonymous events without person profiles, set the event's $process_person_profile property to false:
C#
PostHog AI
posthog.Capture(
"distinct_id",
"event_name",
properties: new() {
["$process_person_profile"] = false
}
)Alias
Sometimes, you want to assign multiple distinct IDs to a single user. This is helpful when your primary distinct ID is inaccessible. For example, if a distinct ID used on the frontend is not available in your backend.
In this case, you can use alias to assign another distinct ID to the same user.
C#
PostHog AI
await posthog.AliasAsync("current_distinct_id", "new_distinct_id");We strongly recommend reading our docs on alias to best understand how to correctly use this method.
Group analytics
Group analytics allows you to associate an event with a group (e.g. teams, organizations, etc.). Read the group analytics guide for more information.
Note: This is a paid feature and is not available on the open-source or free cloud plan. Learn more on our pricing page.
To capture an event and associate it with a group, add the groups argument to your Capture call:
C#
PostHog AI
posthog.Capture(
"user_distinct_id",
"some_event",
groups: [new Group("company", "company_id_in_your_db")]);Update properties on a group, use the GroupIdentifyAsync method:
C#
PostHog AI
await posthog.GroupIdentifyAsync(
type: "company",
key: "company_id_in_your_db",
name: "Awesome Inc.",
properties: new()
{
["employees"] = 11
}
);The name is a special property which is used in the PostHog UI for the name of the group. If you don't specify a name property, the group ID will be used instead.
Feature flags
PostHog's feature flags enable you to safely deploy and roll back new features as well as target specific users and groups with them.
There are two steps to implement feature flags in .NET:
Step 1: Evaluate flags once
Call EvaluateFlagsAsync() once for the user, then read values from the returned snapshot.
Boolean feature flags
C#
PostHog AI
var flags = await posthog.EvaluateFlagsAsync("distinct_id_of_your_user");
if (flags.IsEnabled("flag-key"))
{
// Do something differently for this user
// Optional: fetch the payload
var matchedPayload = flags.GetFlagPayload("flag-key");
}Multivariate feature flags
C#
PostHog AI
var flags = await posthog.EvaluateFlagsAsync("distinct_id_of_your_user");
var enabledVariant = flags.GetFlag("flag-key")?.VariantKey;
if (enabledVariant == "variant-key") // replace "variant-key" with the key of your variant
{
// Do something differently for this user
// Optional: fetch the payload
var matchedPayload = flags.GetFlagPayload("flag-key");
}flags.GetFlag() returns a nullable FeatureFlag object. Check VariantKey for multivariate flags and IsEnabled for boolean flags. It returns null when the flag wasn't returned by the evaluation.
Note:posthog.IsFeatureEnabledAsync(),posthog.GetFeatureFlagAsync(), andCapture(..., sendFeatureFlags: true, ...)still work during the migration period, but they're deprecated. PreferEvaluateFlagsAsync()for new code.
Step 2: Include feature flag information when capturing events
If you want use your feature flag to breakdown or filter events in your insights, you'll need to include feature flag information in those events. This ensures that the feature flag value is attributed correctly to the event.
Note: This step is only required for events captured using our server-side SDKs or API.
There are two methods you can use to include feature flag information in your events:
Method 1: Pass the evaluated flags snapshot to Capture()
Pass the same flags object that you used for branching. This attaches the exact flag values from that evaluation and doesn't make another /flags request.
C#
PostHog AI
var flags = await posthog.EvaluateFlagsAsync("distinct_id_of_your_user");
if (flags.IsEnabled("flag-key"))
{
// Do something differently for this user
}
posthog.Capture(
"distinct_id_of_your_user",
"event_name",
properties: null,
groups: null,
flags: flags
);By default, this attaches every flag in the snapshot using $feature/<flag-key> properties and $active_feature_flags.
To reduce event property bloat, pass a filtered snapshot:
C#
PostHog AI
// Attach only flags accessed with IsEnabled() or GetFlag() before this call
posthog.Capture(
"distinct_id_of_your_user",
"event_name",
properties: null,
groups: null,
flags: flags.OnlyAccessed()
);
// Attach only specific flags
posthog.Capture(
"distinct_id_of_your_user",
"event_name",
properties: null,
groups: null,
flags: flags.Only("checkout-flow", "new-dashboard")
);Method 2: Include the $feature/feature_flag_name property manually
In the event properties, include $feature/feature_flag_name: variant_key:
C#
PostHog AI
posthog.Capture(
"distinct_id_of_your_user",
"event_name",
properties: new()
{
// Replace feature-flag-key with your flag key and "variant-key" with the key of your variant
["$feature/feature-flag-key"] = "variant-key",
}
);Evaluating only specific flags
By default, EvaluateFlagsAsync() evaluates every flag for the user. If you only need a few flags, pass FlagKeysToEvaluate to request only those flags:
C#
PostHog AI
var flags = await posthog.EvaluateFlagsAsync(
"distinct_id_of_your_user",
options: new AllFeatureFlagsOptions
{
FlagKeysToEvaluate = new[] { "checkout-flow", "new-dashboard" },
}
);Sending $feature_flag_called events
Capturing $feature_flag_called events enables PostHog to know when a flag was accessed by a user and provide analytics and insights on the flag. With EvaluateFlagsAsync(), the SDK sends this event when you call flags.IsEnabled() or flags.GetFlag() for a flag.
The SDK deduplicates these events per (distinct_id, flag, value) in a local cache. If you reinitialize the PostHog client, the cache resets and $feature_flag_called events may be sent again. PostHog handles duplicates, so duplicate $feature_flag_called events don't affect your analytics.
flags.GetFlagPayload() doesn't send $feature_flag_called events and doesn't count as an access for OnlyAccessed().
Advanced: Overriding server properties
Sometimes, you may want to evaluate feature flags using person properties, groups, or group properties that haven't been ingested yet, or were set incorrectly earlier.
You can provide properties to evaluate the flag with by using the person properties, groups, and group properties arguments. PostHog will then use these values to evaluate the flag, instead of any properties currently stored on your PostHog server.
For example:
C#
PostHog AI
var flags = await posthog.EvaluateFlagsAsync(
"distinct_id_of_the_user",
options: new AllFeatureFlagsOptions
{
PersonProperties = new()
{
["property_name"] = "value",
},
Groups = new()
{
new Group("your_group_type", "your_group_id")
{
["group_property_name"] = "value",
},
new Group("another_group_type", "another_group_id")
{
["group_property_name"] = "another value",
},
},
}
);
if (flags.IsEnabled("flag-key"))
{
// Do something differently for this user
}Overriding GeoIP properties
By default, a user's GeoIP properties are set using the IP address they use to capture events on the frontend. You may want to override the these properties when evaluating feature flags. A common reason to do this is when you're not using PostHog on your frontend, so the user has no GeoIP properties.
You can override GeoIP properties by including them in the person_properties parameter when evaluating feature flags. This is useful when you're evaluating flags on your backend and want to use the client's location instead of your server's location.
The following GeoIP properties can be overridden:
-
$geoip_country_code -
$geoip_country_name -
$geoip_city_name -
$geoip_city_confidence -
$geoip_continent_code -
$geoip_continent_name -
$geoip_latitude -
$geoip_longitude -
$geoip_postal_code -
$geoip_subdivision_1_code -
$geoip_subdivision_1_name -
$geoip_subdivision_2_code -
$geoip_subdivision_2_name -
$geoip_subdivision_3_code -
$geoip_subdivision_3_name -
$geoip_time_zone
Simply include any of these properties in the person_properties parameter alongside your other person properties when calling feature flags.
Evaluation contexts
Configure evaluation contexts so this SDK only evaluates flags intended for the matching application, platform, or product area. For ASP.NET Core apps using PostHog.AspNetCore, add them to the PostHog configuration section:
JSON
PostHog AI
{
"PostHog": {
"ProjectToken": "<ph_project_token>",
"HostUrl": "https://us.i.posthog.com",
"EvaluationContexts": ["main-app", "api", "backend"]
}
}For code-based configuration, set EvaluationContexts on PostHogOptions:
C#
PostHog AI
var posthog = new PostHogClient(new PostHogOptions
{
ProjectToken = "<ph_project_token>",
HostUrl = new Uri("https://us.i.posthog.com"),
EvaluationContexts = ["main-app", "api", "backend"],
});Remote /flags requests from EvaluateFlagsAsync() include evaluation_contexts when configured.
For more details, see the evaluation contexts guide.
Local evaluation
Evaluating feature flags requires making a request to PostHog for each flag. However, you can improve performance by evaluating flags locally. Instead of making a request for each flag, PostHog will periodically request and store feature flag definitions locally, enabling you to evaluate flags without making additional requests.
It is best practice to use local evaluation flags when possible, since this enables you to resolve flags faster and with fewer API calls.
For details on how to implement local evaluation, see our local evaluation guide.
Experiments (A/B tests)
Since experiments use feature flags, the code for running an experiment is very similar to the feature flags code:
C#
PostHog AI
var flags = await posthog.EvaluateFlagsAsync("user_distinct_id");
var variant = flags.GetFlag("experiment-feature-flag-key")?.VariantKey;
if (variant == "variant-name")
{
// Do something
}It's also possible to run experiments without using feature flags.
AI observability
PostHog.AI adds AI observability for .NET applications using OpenAI or Azure OpenAI. It is currently pre-release, so expect breaking changes before a stable release.
For installation instructions, see the OpenAI guide for .NET or the Azure OpenAI guide for .NET.
GeoIP properties
The posthog-dotnet library disregards the server IP, does not add the GeoIP properties, and does not use the values for feature flag evaluations.
Serverless environments (Azure Functions/Render/Lambda/...)
By default, the library buffers events before sending them to the /batch endpoint for better performance. This can lead to lost events in serverless environments if the .NET process is terminated by the platform before the buffer is fully flushed.
To avoid this, call await posthog.FlushAsync() after processing every request by adding it as a middleware to your server. This allows posthog.Capture() to remain asynchronous for better performance.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Elixir Error Tracking installation - Docs
1. 1
Install the Elixir SDK
Required
Add the PostHog Elixir SDK to your list of dependencies in mix.exs:
Elixir
PostHog AI
def deps do
[
{:posthog, "~> 2.5"}
]
endThen run:
Terminal
PostHog AI
mix deps.getSource code context
The Elixir SDK supports displaying the surrounding lines of source code in the Error Tracking UI. Since Elixir is a compiled language, source files must be packaged at build time. See the source context step below for setup instructions.
2. 2
Configure PostHog
Required
Add your project token and host to your config:
config/config.exs
PostHog AI
config :posthog,
api_host: "https://us.i.posthog.com",
api_key: "<ph_project_token>"To get the most out of Error Tracking, set in_app_otp_apps to your application name. This marks stack trace frames from your code as "in-app", making it easier to identify relevant frames in the PostHog UI:
config/config.exs
PostHog AI
config :posthog,
api_host: "https://us.i.posthog.com",
api_key: "<ph_project_token>",
in_app_otp_apps: [:my_app]3. 3
Errors are captured automatically
Required
Error Tracking is enabled by default. The SDK hooks into Elixir's built-in `Logger` handler system, so it automatically captures:
- Unhandled exceptions – crashes in GenServers, Tasks, and other OTP processes
- Logger.error calls – any
Logger.error/1message at or above the configured level
No additional code is needed. Any crash or error log in your application is sent to PostHog as a $exception event with full stack traces.
What gets captured
The handler captures log messages based on two rules:
1. Crash reasons are always captured – any log with a crash_reason metadata (e.g., GenServer/Task crashes) is captured regardless of log level. 2. Log level filtering – other messages at or above the configured capture_level (default: :error) are captured.
4. 4
Add Phoenix/Plug integration (recommended)
Recommended
If you're using Phoenix or Plug, add the PostHog.Integrations.Plug middleware to automatically attach HTTP context (URL, host, path, IP) to error events.
For Phoenix, add it to your endpoint.ex before the router:
lib/my\_app\_web/endpoint.ex
PostHog AI
plug PostHog.Integrations.Plug
plug MyAppWeb.RouterFor Plug apps, add it to your router:
Elixir
PostHog AI
defmodule MyRouter do
use Plug.Router
plug PostHog.Integrations.Plug
plug :match
plug :dispatch
# ... routes
endThis automatically includes $current_url, $host, $pathname, and $ip on every error event that occurs during request processing. It also reads X-PostHog-Distinct-Id and X-PostHog-Session-Id tracing headers, so errors can link back to frontend users and sessions when your client SDK sends those headers.
If you're using PostHog JS on the frontend, configure `tracing_headers` for your Phoenix or Plug backend hostname. For more details, see the Elixir request context docs.
5. 5
Identify users on errors (recommended)
Recommended
By default, errors are attributed to "unknown". To associate errors with specific users, set a context with a distinct_id early in your request lifecycle – for example, in a Plug pipeline after authentication:
Elixir
PostHog AI
PostHog.set_context(%{distinct_id: current_user.id})This is process-scoped, so any error that occurs in the same process (i.e., the same request) will include the user's distinct ID.
For Phoenix apps, a common pattern is to add this in a plug or controller action:
lib/my\_app\_web/plugs/set\_posthog\_context.ex
PostHog AI
defmodule MyAppWeb.Plugs.SetPostHogContext do
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
if user = conn.assigns[:current_user] do
PostHog.set_context(%{distinct_id: user.id})
end
conn
end
endThen add it to your router pipeline:
Elixir
PostHog AI
pipeline :browser do
# ... other plugs
plug MyAppWeb.Plugs.SetPostHogContext
end6. 6
Configure error tracking options (optional)
Optional
The SDK supports several configuration options for Error Tracking:
config/config.exs
PostHog AI
config :posthog,
api_host: "https://us.i.posthog.com",
api_key: "<ph_project_token>",
# Mark your app's stacktrace frames as "in_app"
in_app_otp_apps: [:my_app],
# Minimum log level to capture (default: :error)
# Set to :warning to also capture warnings, or nil to only capture crashes
capture_level: :error,
# Logger metadata keys to include in error events (default: [])
# Set to :all to include all metadata
metadata: [:request_id, :user_id]| Option | Type | Default | Description |
|---|---|---|---|
| in_app_otp_apps | list of atoms | [] | OTP app names whose stacktrace frames are marked as "in_app" in the UI. |
| capture_level | log level or nil | :error | Minimum log level to capture. Crashes with crash_reason are always captured. Set to nil to only capture crashes. |
| metadata | list of atoms or :all | [] | Logger metadata keys to include as event properties. |
| enable_error_tracking | boolean | true | Set to false to disable automatic Error Tracking entirely. |
| global_properties | map | %{} | Properties added to all captured events (not just errors). |
7. 7
Enable source code context (optional)
Optional
Since Elixir is a compiled language, source files aren't available at runtime by default. To display the surrounding lines of code in PostHog's Error Tracking UI, you need to package your source code at build time.
Step 1: Enable source context in your config:
config/config.exs
PostHog AI
config :posthog,
api_host: "https://us.i.posthog.com",
api_key: "<ph_project_token>",
enable_source_code_context: true,
root_source_code_paths: [File.cwd!()],
context_lines: 5Step 2: Package source code before building your release:
Terminal
PostHog AI
mix posthog.package_source_code
mix releaseThis reads all .ex files from your project, compresses them into priv/posthog_source.map, and bundles them with your release. When an error occurs, the SDK matches stack trace frames to the packaged source and includes pre_context, context_line, and post_context in each frame.
Development mode
In development, if root_source_code_paths is set and source files are accessible on disk, the SDK reads them directly at startup – no packaging step needed.
Configuration options
| Option | Type | Default | Description |
|---|---|---|---|
| enable_source_code_context | boolean | false | Enable source code context in stack frames. |
| root_source_code_paths | list of strings | [] | Root paths to scan for source files. |
| source_code_path_pattern | string | "*/.ex" | Glob pattern for files to include. |
| source_code_exclude_patterns | list of regexes | [~r"^_build/", ~r"^priv/", ~r"^test/"] | Patterns to exclude. |
| context_lines | integer | 5 | Number of lines to include before and after the error line. |
| source_code_map_path | string | nil | Custom path to a packaged source map file. |
Mix task options
Terminal
PostHog AI
# Custom output path
mix posthog.package_source_code --output path/to/output.map
# Custom root paths (overrides config)
mix posthog.package_source_code --root-path /app/lib --root-path /app/src8. ## Verify error tracking
Recommended
Trigger a test exception to confirm errors are being sent to PostHog. You should see them appear in the Error Tracking tab.
Elixir
PostHog AI
# In an IEx session or a test route
require Logger
Logger.error("Test error from Elixir")Or raise an exception in a controller or GenServer to test crash capture:
Elixir
PostHog AI
# In a Phoenix controller
def test_error(conn, _params) do
raise "Test exception from Phoenix"
endCommunity questions
Ask a question
Was this page useful?
HelpfulCould be better
Fingerprints - Docs
Every captured exception is assigned a fingerprint. This fingerprint is used to group similar exceptions into issues. This page covers how fingerprints are generated, how they're used, and how you can override them when capturing exceptions.
Fingerprint and issue grouping
Every exception has a fingerprint, whether generated or defined by the user. Each fingerprint links to exactly one issue. Exceptions that share the same fingerprint define an issue.
Multiple different fingerprints can point to the same issue (a many-to-one relationship) if you merge issues.
How are fingerprints generated?
Fingerprints are built iteratively using components of the exception event. The flowchart below shows how fingerprints are generated.
flowchart LR A\[Add exception type to fingerprint\] --> B{Stack trace<br/>available?} B -->|No| C\[Add error message to fingerprint\] C --> D\[Final fingerprint\] B -->|Yes| E{In-app frames<br/>exist?} E -->|No| F\[Add first frame to fingerprint\] E -->|Yes| G\[Add in-app frame to fingerprint<br/>Priority: resolved > unresolved\] F --> D G --> D
The flowchart in text
Fingerprints are generated by considering the following in combination:
1. The exception type 2. If there's no resolved stack trace, add the error message to the fingerprint 3. If there are stack traces but no in-app frames (frames from your code, not a dependency), use the first frame of the stack trace 4. If there are stack traces, in-app frames, and source maps available, use the resolved in-app stack frames 5. If there are stack traces, in-app frames, and source maps not available, use the first in-app stack frame
In some languages, like Python, one error can trigger another, creating a chain of linked exceptions. PostHog records the entire chain in the event and generates a single fingerprint for it.
Ensuring accurate fingerprints
Resolved stack traces are critical for accurate fingerprinting. Without accurate stack traces, PostHog cannot group exceptions consistently. If you have not uploaded source maps, follow the source map guide to do so.
This also means that if the exception type or message changes from one version to the next, the fingerprint will change.
When are generated fingerprints used?
Fingerprints are used to group similar exceptions into issues automatically. Automatic issue grouping is only done when:
- No issue grouping rules are applied
- No issue merging has been configured
- No custom fingerprint is set during capture
You can find details about how issue grouping works in the issues and exceptions guide.
Customizing fingerprints
Fingerprints can be manually set during exception capture. This is a very useful way to group exceptions that are not related to each other. You can find examples of how to do this in the custom issue grouping section.
You can also learn more about grouping issues using rules in the grouping issues guide.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Flask - Docs
PostHog makes it easy to get data about traffic and usage of your Flask app. Integrating PostHog enables analytics, custom events capture, feature flags, error tracking, and more.
This guide walks you through integrating PostHog into your Flask app using the Python SDK.
Installation
To start, run pip install posthog to install PostHog’s Python SDK.
Note: Version 7.x of the PostHog Python SDK requires Python 3.10 or higher.Then, initialize PostHog where you'd like to use it. For example, here's how to capture an event in a simple route:
app.py
PostHog AI
from flask import Flask
from posthog import Posthog
app = Flask(__name__)
posthog = Posthog(
'<ph_project_token>',
host='https://us.i.posthog.com',
)
@app.route('/api/dashboard', methods=['POST'])
def api_dashboard():
posthog.capture(
'dashboard_api_called',
distinct_id='distinct_id_of_your_user',
)
return '', 204You can find your project token and instance address in your project settings.
Identifying users
Identifying users is required. Backend events need adistinct_idthat matches the ID your frontend uses when callingposthog.identify(). Without this, backend events are orphaned — they can't be linked to frontend event captures, session replays, LLM traces, or error tracking.
>
See our guide on identifying users for how to set this up.
Request contexts
Use contexts to share identity, session IDs, and tags across multiple captures during a request.
If you're using PostHog JS on the frontend, configure `tracing_headers` for your Flask backend hostname so browser requests include the session and distinct ID headers.
Then read the incoming headers in your Flask request handler. Tracing headers are client-controlled analytics context, not authentication or authorization, so prefer your authenticated user ID when one is available:
Python
PostHog AI
from flask import request, session
from posthog import identify_context, set_context_session, tag
@app.route('/api/dashboard', methods=['POST'])
def api_dashboard():
with posthog.new_context(fresh=True):
distinct_id = session.get('user_id') or request.headers.get('X-POSTHOG-DISTINCT-ID')
if distinct_id:
identify_context(str(distinct_id))
session_id = request.headers.get('X-POSTHOG-SESSION-ID')
if session_id:
set_context_session(session_id)
tag('$current_url', request.url)
tag('$request_method', request.method)
tag('$request_path', request.path)
posthog.capture('dashboard_api_called')
return '', 204Events captured without a context or explicit distinct_id are sent as anonymous events with an auto-generated distinct_id. See the Python SDK docs for more details.
Error tracking
Flask has built-in error handlers. This means PostHog’s default exception autocapture won’t work and we need to manually capture errors instead using capture_exception():
Python
PostHog AI
from flask import Flask, jsonify
from posthog import Posthog
app = Flask(__name__)
posthog = Posthog('<ph_project_token>', host='https://us.i.posthog.com')
@app.errorhandler(Exception)
def handle_exception(e):
# Capture methods, including capture_exception, return the UUID of the captured event,
# which you can use to find specific errors users encountered
event_id = posthog.capture_exception(e)
# You can show the event ID to your user, and ask them to include it in bug reports
response = jsonify({'message': str(e), 'error_id': event_id})
response.status_code = 500
return responseNext steps
For any technical questions for how to integrate specific PostHog features into Flask (such as analytics, feature flags, A/B testing, etc.), have a look at our Python SDK docs.
Alternatively, the following tutorials can help you get started:
- How to set up analytics in Python and Flask
- How to set up feature flags in Python and Flask
- How to set up A/B tests in Python and Flask
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Flutter error tracking installation - Docs
1. 1
Install the package
Required
Add the PostHog Flutter SDK to your pubspec.yaml:
pubspec.yaml
PostHog AI
posthog_flutter: ^5.24.02. 2
Platform setup
Required
Tab
Add these values to your AndroidManifest.xml:
android/app/src/main/AndroidManifest.xml
PostHog AI
<application>
<activity>
[...]
</activity>
<meta-data android:name="com.posthog.posthog.PROJECT_TOKEN" android:value="<ph_project_token>" />
<meta-data android:name="com.posthog.posthog.POSTHOG_HOST" android:value="https://us.i.posthog.com" />
<meta-data android:name="com.posthog.posthog.TRACK_APPLICATION_LIFECYCLE_EVENTS" android:value="true" />
<meta-data android:name="com.posthog.posthog.DEBUG" android:value="true" />
</application>Update the minimum Android SDK version to 21 in android/app/build.gradle:
android/app/build.gradle
PostHog AI
defaultConfig {
minSdkVersion 23
// rest of your config
}Tab
Add these values to your Info.plist:
ios/Runner/Info.plist
PostHog AI
<dict>
[...]
<key>com.posthog.posthog.PROJECT_TOKEN</key>
<string><ph_project_token></string>
<key>com.posthog.posthog.POSTHOG_HOST</key>
<string>https://us.i.posthog.com</string>
<key>com.posthog.posthog.CAPTURE_APPLICATION_LIFECYCLE_EVENTS</key>
<true/>
<key>com.posthog.posthog.DEBUG</key>
<true/>
</dict>Update the minimum platform version to iOS 13.0 in your Podfile:
Podfile
PostHog AI
platform :ios, '13.0'
# rest of your configTab
Add these values in index.html:
web/index.html
PostHog AI
<!DOCTYPE html>
<html>
<head>
...
<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group identify setPersonProperties setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags resetGroups onFeatureFlags addFeatureFlagsHandler onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('<ph_project_token>', {
api_host: 'https://us.i.posthog.com',
defaults: '2026-01-30',
})
</script>
</head>
<body>
...
</body>
</html>3. 3
Send events
Recommended
Once installed, PostHog will automatically start capturing events. You can also manually send events to test your integration:
Dart
PostHog AI
import 'package:posthog_flutter/posthog_flutter.dart';
await Posthog().capture(
eventName: 'button_clicked',
properties: {
'button_name': 'signup'
}
);4. 4
Set up exception autocapture
Recommended
Client-side configuration only
This configuration is client-side only. Support for remote configuration in the error tracking settings will be added in a future release.
You can autocapture exceptions by configuring the errorTrackingConfig when setting up PostHog:
Dart
PostHog AI
final config = PostHogConfig('<ph_project_token>');
// Enable exception autocapture
config.errorTrackingConfig.captureFlutterErrors = true;
config.errorTrackingConfig.capturePlatformDispatcherErrors = true;
config.errorTrackingConfig.captureIsolateErrors = true;
// Requires SDK version 5.22.0 or higher
config.errorTrackingConfig.captureNativeExceptions = true;
config.errorTrackingConfig.captureSilentFlutterErrors = false;
await Posthog().setup(config);Configuration options:
| Option | Description |
|---|---|
| captureFlutterErrors | Captures Flutter framework errors (FlutterError.onError) |
| capturePlatformDispatcherErrors | Captures Dart runtime errors (PlatformDispatcher.onError). Web not supported. |
| captureIsolateErrors | Captures errors from main isolate. Web not supported. |
| captureNativeExceptions | Captures native exceptions. Android (Java/Kotlin) and Apple platforms (iOS, macOS, tvOS). |
| captureSilentFlutterErrors | Captures Flutter errors that are marked as silent. Default: false. |
5. 5
Manually capture exceptions
Optional
Basic usage
You can manually capture exceptions using the captureException method:
Dart
PostHog AI
try {
// Your awesome code that may throw
await someRiskyOperation();
} catch (exception, stackTrace) {
// Capture the exception with PostHog
await Posthog().captureException(
error: exception,
stackTrace: stackTrace,
properties: {
'user_action': 'button_press',
'feature_name': 'data_sync',
},
);
}This is helpful if you've built your own error handling logic or want to capture exceptions that are handled by your application code.
Error tracking configuration
You can configure error tracking behavior when setting up PostHog:
Flutter web apps use minified stack trace frames
Flutter web apps generate minified stack trace frames by default, which may cause the configurations below to behave differently or not work as expected.
Dart
PostHog AI
final config = PostHogConfig('<ph_project_token>');
// Configure error tracking
config.errorTrackingConfig.inAppIncludes.add('package:your_app');
config.errorTrackingConfig.inAppExcludes.add('package:third_party_lib');
config.errorTrackingConfig.inAppByDefault = true;
await Posthog().setup(config);Configuration options:
| Option | Description |
|---|---|
| inAppIncludes | List of package names to be considered inApp frames (takes precedence over excludes) |
| inAppExcludes | List of package names to be excluded from inApp frames |
| inAppByDefault | Whether frames are considered inApp by default when their origin cannot be determined |
inApp frames are stack trace frames that belong to your application code (as opposed to third-party libraries or system code). These are highlighted in the PostHog error tracking interface to help you focus on the relevant parts of the stack trace.
6. 6
Future features
Optional
We currently don't support the following features:
- No de-obfuscating stacktraces from obfuscated builds (\--obfuscate and \--split-debug-info) for Dart code
- No Source code context associated with an exception (native Android Java/Kotlin errors and Flutter web only)
- No native C/C++ exception capture on Android (Java/Kotlin only)
- No background isolate error capture
For symbolicated stack traces on native platforms, see the Flutter debug symbols guide.
These features will be added in future releases. We recommend you stay up to date with the latest version of the PostHog Flutter SDK.
7. ## Verify error tracking
Recommended
Confirm events are being sent to PostHog
Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
!Activity feed with events!Activity feed with events
Check for exceptions in PostHog
8. 7
Upload source maps
Required
Great, you're capturing exceptions! If you serve minified bundles, the next step is to upload source maps to generate accurate stack traces.
Let's continue to the next section.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Go error tracking installation - Docs
1. 1
Install the Go SDK
Required
Install the PostHog Go SDK:
Terminal
PostHog AI
go get github.com/posthog/posthog-goSource context not yet supported
The Go SDK captures stack traces with file names, line numbers, and function names, but does not yet support source context (displaying the surrounding lines of code in the error tracking UI). Symbol set uploads for Go are not currently available.
2. 2
Initialize the client
Required
Go
PostHog AI
package main
import (
"github.com/posthog/posthog-go"
)
func main() {
client, _ := posthog.NewWithConfig(
"<ph_project_token>",
posthog.Config{
Endpoint: "https://us.i.posthog.com",
},
)
defer client.Close()
}3. 3
Capture exceptions
Required
There are two ways to capture exceptions with the Go SDK:
Option A: Direct capture
Use NewDefaultException to capture errors directly. This automatically generates a UUID and stack trace for you.
Go
PostHog AI
import (
"time"
"github.com/posthog/posthog-go"
)
exception := posthog.NewDefaultException(
time.Now(),
"user_distinct_id",
"DatabaseError", // type - rendered as title in the UI
"connection refused", // value - rendered as description in the UI
)
client.Enqueue(exception)For more control, build the Exception struct manually:
Go
PostHog AI
import (
"time"
"github.com/posthog/posthog-go"
)
handled := true
fingerprint := "my-custom-fingerprint"
exception := posthog.Exception{
DistinctId: "user_distinct_id",
Timestamp: time.Now(),
ExceptionList: []posthog.ExceptionItem{
{
Type: "DatabaseError",
Value: "connection refused",
Mechanism: &posthog.ExceptionMechanism{
Handled: &handled,
},
},
},
ExceptionFingerprint: &fingerprint,
}
client.Enqueue(exception)To see how net/http services can automatically associate backend exceptions with frontend users, view the Go request context documentation.
Option B: Automatic capture with slog
The SDK provides a SlogCaptureHandler that wraps Go's standard log/slog logger and automatically captures log records as exceptions.
By default, it captures logs at Warning level and above.
Go
PostHog AI
import (
"context"
"fmt"
"log/slog"
"os"
"github.com/posthog/posthog-go"
)
client, _ := posthog.NewWithConfig(
"<ph_project_token>",
posthog.Config{
Endpoint: "https://us.i.posthog.com",
},
)
defer client.Close()
baseHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
})
logger := slog.New(posthog.NewSlogCaptureHandler(baseHandler, client,
posthog.WithDistinctIDFn(func(ctx context.Context, r slog.Record) string {
// Return the user ID from context or another source
return "user_distinct_id"
}),
))
// This warning is automatically captured as an exception in PostHog
logger.Warn("Something broke",
"error", fmt.Errorf("connection refused"),
)The handler supports several configuration options:
| Option | Description | Default |
|---|---|---|
| WithMinCaptureLevel(level) | Minimum log level to capture | slog.LevelWarn |
| WithDistinctIDFn(fn) | Function to extract distinct ID from context/record | Returns "" (skips capture) |
| WithFingerprintFn(fn) | Custom fingerprint for error grouping | nil (PostHog assigns) |
| WithSkip(n) | Stack frames to skip | 5 |
| WithStackTraceExtractor(e) | Custom stack trace extractor | DefaultStackTraceExtractor |
| WithDescriptionExtractor(e) | Custom description extractor | ErrorExtractor |
Error extraction
The slog handler automatically extracts error descriptions from log attributes with keys err or error (case-insensitive). It also supports wrapped errors via the Unwrap() interface.
4. 4
Verify error tracking
Recommended
Trigger a test exception to confirm events are being sent to PostHog. You should see them appear in the activity feed.
Go
PostHog AI
exception := posthog.NewDefaultException(
time.Now(),
"test_user",
"TestError",
"This is a test exception from Go",
)
client.Enqueue(exception)
// Flush the queue before exiting
client.Close()Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Hono error tracking installation - Docs
1. 1
Install the package
Required
Install the PostHog Node.js library using your package manager:
PostHog AI
npm
npm install posthog-nodeyarn
yarn add posthog-nodepnpm
pnpm add posthog-node2. 2
Initialize PostHog
Required
Initialize the PostHog client with your project token:
Node.js
PostHog AI
import { PostHog } from 'posthog-node'
const client = new PostHog(
'<ph_project_token>',
{
host: 'https://us.i.posthog.com'
}
)3. 3
Send an event
Recommended
Once installed, you can manually send events to test your integration:
Node.js
PostHog AI
client.capture({
distinctId: 'distinct_id_of_the_user',
event: 'event_name',
properties: {
property1: 'value',
property2: 'value',
},
})4. 4
Exception handling example
Required
Hono uses `app.onError` to handle uncaught exceptions. You can take advantage of this for error tracking.
Remember to export your project token as an environment variable.
index.ts
PostHog AI
import { PostHog } from 'posthog-node'
const posthog = new PostHog(process.env.POSTHOG_TOKEN, { host: 'https://us.i.posthog.com' })
app.onError(async (err, c) => {
posthog.captureException(err, 'user_distinct_id_with_err_rethrow', {
path: c.req.path,
method: c.req.method,
url: c.req.url,
headers: c.req.header(),
// ... other properties
})
await posthog.flush()
// other error handling logic
return c.text('Internal Server Error', 500)
})5. ## Verify error tracking
Recommended
Confirm events are being sent to PostHog
Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
!Activity feed with events!Activity feed with events
Check for exceptions in PostHog
6. 5
Upload source maps
Required
Great, you're capturing exceptions! If you serve minified bundles, the next step is to upload source maps to generate accurate stack traces.
Let's continue to the next section.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
iOS error tracking installation - Docs
1. 1
Install dependency
Required
Install via Swift Package Manager:
Package.swift
PostHog AI
dependencies: [
.package(url: "https://github.com/PostHog/posthog-ios.git", from: "3.56.0")
]Or add PostHog to your Podfile:
Podfile
PostHog AI
pod "PostHog", "~> 3.56"2. 2
Configure PostHog
Required
Initialize PostHog in your AppDelegate:
AppDelegate.swift
PostHog AI
import Foundation
import PostHog
import UIKit
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
let POSTHOG_PROJECT_TOKEN = "<ph_project_token>"
let POSTHOG_HOST = "https://us.i.posthog.com"
let config = PostHogConfig(projectToken: POSTHOG_PROJECT_TOKEN, host: POSTHOG_HOST)
PostHogSDK.shared.setup(config)
return true
}
}3. 3
Send events
Recommended
Once installed, PostHog will automatically start capturing events. You can also manually send events to test your integration:
Swift
PostHog AI
PostHogSDK.shared.capture("button_clicked", properties: ["button_name": "signup"])4. 4
Set up exception autocapture
Recommended
Remote configuration
Exception autocapture can also be managed remotely via the error tracking settings.
Platform support
Exception autocapture is available on iOS, macOS, and tvOS only. It is not available on watchOS or visionOS due to platform limitations.
You can still capture events manually on all platforms, including visionOS.
You can autocapture exceptions by setting the errorTrackingConfig.autoCapture argument to true when initializing the PostHog SDK.
Swift
PostHog AI
import PostHog
let config = PostHogConfig(
projectToken: "<ph_project_token>",
host: "https://us.i.posthog.com"
)
config.errorTrackingConfig.autoCapture = true
PostHogSDK.shared.setup(config)When enabled, this automatically captures $exception events for:
- Mach exceptions (e.g.,
EXC_BAD_ACCESS,EXC_CRASH) - POSIX signals (e.g.,
SIGSEGV,SIGABRT,SIGBUS) - Uncaught NSExceptions
Crashes are persisted to disk and sent as $exception events with level "fatal" on the next app launch.
5. 5
Manually capture exceptions
Optional
Swift Error handling
You can manually capture exceptions using the captureException method:
Swift
PostHog AI
import PostHog
do {
try FileManager.default.removeItem(at: badFileUrl)
} catch {
PostHogSDK.shared.captureException(error)
}Objective-C NSException handling
For Objective-C code that uses NSException:
Objective-C
PostHog AI
@import PostHog;
@try {
[self riskyOperation];
} @catch (NSException *exception) {
[[PostHogSDK shared] captureExceptionWithNSException:exception properties:nil];
}Adding custom properties
You can add custom properties to help with debugging, grouping, and analysis:
Swift
PostHog AI
do {
try performNetworkRequest()
} catch {
PostHogSDK.shared.captureException(error, properties: [
"endpoint": "/api/users",
"retry_count": 3
])
}This is helpful if you've built your own error handling logic or want to capture exceptions that are handled by your application code.
6. 6
Configure in-app frames
Optional
By default, PostHog automatically marks your app's code as "in-app" in stack traces to help you focus on your code rather than system frameworks.
You can customize this behavior with errorTrackingConfig:
Swift
PostHog AI
import PostHog
let config = PostHogConfig(
projectToken: "<ph_project_token>",
host: "https://us.i.posthog.com"
)
// Mark additional packages as in-app
config.errorTrackingConfig.inAppIncludes = [
"MySharedFramework",
"MyUtilityLib"
]
// Exclude specific packages from being marked as in-app
config.errorTrackingConfig.inAppExcludes = [
"Alamofire",
"SDWebImage"
]
// Control default behavior for unknown packages
config.errorTrackingConfig.inAppByDefault = true // default
PostHogSDK.shared.setup(config)Configuration options:
| Option | Description |
|---|---|
| inAppIncludes | List of package/bundle identifiers to mark as in-app (takes precedence over excludes) |
| inAppExcludes | List of package/bundle identifiers to exclude from in-app |
| inAppByDefault | Whether frames are considered in-app by default when origin cannot be determined |
Default behavior:
- Your app's bundle identifier and executable name are automatically included
- System frameworks (Foundation, UIKit, etc.) are automatically excluded
7. ## Verify error tracking
Recommended
Confirm events are being sent to PostHog
Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
!Activity feed with events!Activity feed with events
Check for exceptions in PostHog
8. 7
Upload dSYMs
Required
Great, you're capturing exceptions! The next step is to upload dSYM files so PostHog can symbolicate your crash reports and generate accurate stack traces.
Let's continue to the next section.
Limitations:
- System symbols and frames are not symbolicated (UIKit, Foundation, etc.) (issue).
- Swift crashes appear as
SIGTRAPwithout the actual error message (issue).
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Laravel - Docs
PostHog integrates with Laravel through the PostHog PHP SDK. This page covers Laravel-specific setup. For SDK features such as event capture, identifying users, feature flags, group analytics, and configuration options, see the PHP SDK docs.
Installation
Install the PHP SDK as described in the PHP installation guide, then add your project token and host to .env:
.env
PostHog AI
POSTHOG_API_KEY=<ph_project_token>
POSTHOG_HOST=https://us.i.posthog.comAdd PostHog to Laravel's services config:
config/services.php
PostHog AI
'posthog' => [
'api_key' => env('POSTHOG_API_KEY'),
'host' => env('POSTHOG_HOST', 'https://us.i.posthog.com'),
],Initialize PostHog in the boot method of app/Providers/AppServiceProvider.php:
app/Providers/AppServiceProvider.php
PostHog AI
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use PostHog\PostHog;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
if (! config('services.posthog.api_key')) {
return;
}
PostHog::init(
config('services.posthog.api_key'),
[
'host' => config('services.posthog.host'),
]
);
}
}Request context middleware
Client SDKs such as PostHog JS can send tracing headers to your Laravel backend. Configure `tracing_headers` for your Laravel backend hostname so browser requests include the session and distinct ID headers.
The PHP SDK can read X-PostHog-Distinct-Id and X-PostHog-Session-Id headers and apply them to events captured during the request. Tracing headers are client-controlled analytics context, not authentication or authorization. For security-sensitive server-side events or decisions, pass an authenticated distinctId explicitly, such as auth()->id(). For the lower-level context APIs, see the PHP request context docs.
Add middleware like this:
app/Http/Middleware/PostHogRequestContext.php
PostHog AI
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use PostHog\PostHog;
use Symfony\Component\HttpFoundation\Response;
final class PostHogRequestContext
{
public function handle(Request $request, Closure $next): Response
{
if (! config('services.posthog.api_key')) {
return $next($request);
}
$context = PostHog::contextFromHeaders($request->headers->all());
$context['properties'] = array_merge(
$context['properties'] ?? [],
array_filter([
'$current_url' => $request->fullUrl(),
'$request_method' => $request->method(),
'$request_path' => $request->getPathInfo(),
'$user_agent' => $request->userAgent(),
'$ip' => $request->ip(),
], static fn ($value): bool => $value !== null && $value !== '')
);
return PostHog::withContext(
$context,
static fn (): Response => $next($request),
['fresh' => true]
);
}
}Register this middleware using your Laravel version's normal middleware registration.
Error tracking in Laravel
The PHP SDK supports error tracking, but Laravel handles most request exceptions before they become uncaught PHP exceptions. Capture Laravel-reported exceptions explicitly.
In Laravel 11 and later, add a report callback in bootstrap/app.php:
bootstrap/app.php
PostHog AI
use Illuminate\Foundation\Configuration\Exceptions;
use PostHog\PostHog;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->report(function (Throwable $e): void {
if (! config('services.posthog.api_key')) {
return;
}
PostHog::captureException(
$e,
auth()->id() !== null ? (string) auth()->id() : null,
[
'$current_url' => request()->fullUrl(),
'$request_method' => request()->method(),
]
);
});
})For older Laravel versions, call PostHog::captureException() from your exception handler's report method.
Long-running processes
In normal PHP request lifecycles, queued events flush when the client is destroyed. In long-running Laravel processes such as queue workers, Horizon, or Octane, call PostHog::flush() after capturing important events or at the end of a job/request.
If you prefer immediate delivery in queue workers, configure the PHP SDK with batch_size set to 1 for those workers:
PHP
PostHog AI
PostHog::init(
'<ph_project_token>',
[
'host' => config('services.posthog.host'),
'batch_size' => 1,
]
);Next steps
See the PHP SDK docs for usage examples and the full API reference.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Monitor and search issues - Docs
This guide covers how to find the most relevant, urgent, and impactful issues in your error tracking using the issues page.
Monitoring issues
When you're monitoring issues in your project, there are generally two common workflows:
- You're exploring issues to identify impactful and problematic areas. You should use sorting features.
- You're looking for issues assigned to you to resolve them. You should filter by the
Assigned toproperty.
Sorting issues
Issues can be sorted by the following properties:
| Property | Description |
|---|---|
| Last seen | The issue that has the most recent exception |
| First seen | The issue that has the oldest exception |
| Occurrences | The number of exceptions in the issue |
| Users | The number of unique users affected by the issue |
| Sessions | The number of unique sessions affected by the issue |
Sorting by last seen and occurrences are great ways to get a general sense of issues in your project. Sorting by users and sessions is great to find the most impactful issues if you're using other filters to narrow down your results.
Monitoring issues assigned to you
You can filter issues by the Assigned to property to find issues assigned to you. This is especially useful if you configure automatic issue assignment and configure alerts to notify you when new issues are created.
Finding specific issues
You can use the search bar at the top of the issue page to filter issues based on the properties of the exceptions in that issue.
Search results are matched based on properties of exception events grouped into the issues. For example, if you search for "TypeError", we show you all issues where any exception grouped into the issue has a type of "TypeError".
Unrelated results
You may see seemingly unrelated issues in your search results because your search term matches an exception in the issue group. For example, you may see an issues named RefreshError when searching "schema", because a get_schema method appears on the exception stack traces.
Filtering modes
The search bar provides two modes of filtering:
1\. Exact property filtering
This operates like property filters elsewhere in PostHog, enabling you to add terms like where 'http_referer' is set or where 'library' equals 'web'. You add a property filter by clicking the property name shown here:
!Adding a property to the property filter!Adding a property to the property filter
Added property filters look like this:
!Search bar with property filter!Search bar with property filter
The results of both of these filter types (property filters and freeform search) are combined with AND logic, such that only exceptions that match all filters are included in the search results.
2\. Freeform text search
This does text matching for a subset of the error tracking specific properties of the exception event. It splits the text you give it into tokens. The search matches an exception if each of the tokens in your search term appear in one of the following:
- The exception type
- The exception message
- The function names in the exception stack trace (if known)
- The file paths in the exception stack trace (if known)
For example, imagine you have an exception that looks like this:
PostHog AI
TypeError: Cannot read property 'name' of undefined
at Object.<anonymous> (/path/to/myfile.js:123:45)
at Module._compile (module.js:653:30)
at Object.Module._extensions..js (module.js:664:10)
at Module.load (module.js:566:32)
at tryModuleLoad (module.js:506:12)
at Function.Module._load (module.js:498:3)
at Function.Module.runMain (module.js:694:10)
at startup (bootstrap_node.js:204:16)
at bootstrap_node.js:625:3If you search for the term TypeError myfile.js, the exception matches this search, as it contains TypeError (as the exception type) and myfile.js (as a file path in the stack trace).
If you search for TypeError myfile.js abc, the exception would not match, as the token abc does not appear anywhere in freeform search properties.
If you want to search for longer exact strings, e.g. a particular exception message, you can group tokens into a single term using quotes, e.g. "Cannot read property 'name' of undefined" myfile.js would match, and "Cannot read property of myfile.js" would not.
Note, perhaps unintuitively, Cannot read property of myfile.js would match, because the tokens are ungrouped, and all of them appear somewhere in the exception search properties.
Searching chained exceptions
Exception events can have more than one exception in them, due to language features like exception chaining. For freeform search, we put the types, messages, functions and file paths of all exceptions into one list, and match if the token appears in any of them.
For example, if you had a chained exception with the messages MyCustomError: Failed to load user and Cannot read property 'age' of undefined, searching for cannot read property would match the exception, because it matches one of the exception messages (property appears in the "root" one).
Issue details
When you click on an issue, you'll see the details page of the issue.
This page shows you the following:
- The stack trace, properties, and sessions related to the currently selected exception.
- Name, description, status, assignee, and external tracking links for the issue.
- A filterable list of all exceptions in the issue. Selecting an exception will show you the stack trace, properties, and sessions related to that exception at the top of the page.
!An issue, with an unfiltered exception list!An issue, with an unfiltered exception list
Filtering exception occurrences within an issue
Once you've found and opened the issue you want to investigate, you can use the same search interface to filter the exception list for a particular instance of the issue. This is particularly useful in cases where some exceptions in the issue have information others don't and you want to use that information for debugging.
For example, you can add a property filter on http_referer that shows all exceptions where the http_referer is set:
!An issue, with a filtered exception list!An issue, with a filtered exception list
Alerts
If you have a set of filters that you use often, you can create alerts for them. This way you can be notified when new issues match your filters. Learn more about alerts.
Improving search performance
We try to return results to you within a second, but sometimes if you're querying over large amounts of data, it may take longer. The following can improve the search performance:
- Limit the time range you're searching over: 7 days is usually enough to get a sense for the trends of an issue over time.
- Use freeform search rather than property filters: Our freeform searches are generally faster than property filters, as the total amount of data processed is smaller.
If you find your queries timing out or taking more than 30 seconds, please let us know in-app! We're always looking for benchmarks to improve against.
Suppressing issues
If you find issues that are not useful to you, you can suppress them by changing the status to Suppressed. We recommend that you also implement client-side suppression to not capture these exceptions in the first place, for cost and performance reasons.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Node.js error tracking installation - Docs
1. 1
Install the package
Required
Install the PostHog Node.js library using your package manager:
PostHog AI
npm
npm install posthog-nodeyarn
yarn add posthog-nodepnpm
pnpm add posthog-node2. 2
Initialize PostHog
Required
Initialize the PostHog client with your project token:
Node.js
PostHog AI
import { PostHog } from 'posthog-node'
const client = new PostHog(
'<ph_project_token>',
{
host: 'https://us.i.posthog.com'
}
)3. 3
Send an event
Recommended
Once installed, you can manually send events to test your integration:
Node.js
PostHog AI
client.capture({
distinctId: 'distinct_id_of_the_user',
event: 'event_name',
properties: {
property1: 'value',
property2: 'value',
},
})4. 4
Configure exception autocapture
Recommended
You can enable exception autocapture when initializing the PostHog client to automatically capture uncaught exceptions and unhandled rejections in your Node app.
Node.js
PostHog AI
import { PostHog } from 'posthog-node'
const client = new PostHog(
'<ph_project_token>',
{ host: 'https://us.i.posthog.com', enableExceptionAutocapture: true }
)If you are using the Express framework, you will need to import and call setupExpressErrorHandler with your PostHog client and Express app. This is because Express handles uncaught exceptions internally meaning exception autocapture will not work by default.
server.ts
PostHog AI
import express from 'express'
import { PostHog, setupExpressErrorHandler } from 'posthog-node'
const app = express()
const posthog = new PostHog(POSTHOG_PROJECT_TOKEN)
setupExpressErrorHandler(posthog, app)Note: Error tracking requires access the file system to process stack traces. Some providers, like Cloudflare Workers, do not support Node.js runtime APIs by default and need to be included as per their documentation.
5. 5
Manually capture exceptions
Optional
If you need to manually capture exceptions, you can do so by calling the captureException method:
Node.js
PostHog AI
posthog.captureException(e, 'user_distinct_id', additionalProperties)This is helpful if you've built your own error handling logic or want to capture exceptions normally handled by the framework.
6. ## Verify error tracking
Recommended
Confirm events are being sent to PostHog
Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
!Activity feed with events!Activity feed with events
Check for exceptions in PostHog
7. 6
Upload source maps
Required
Great, you're capturing exceptions! If you serve minified bundles, the next step is to upload source maps to generate accurate stack traces.
Let's continue to the next section.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Nuxt error tracking installation (v3.6 and below) - Docs
1. 1
Install the package
Required
Install the PostHog JavaScript library using your package manager:
PostHog AI
npm
npm install posthog-jsyarn
yarn add posthog-jspnpm
pnpm add posthog-jsNuxt version
This guide is for Nuxt v3.0 and above. For Nuxt v2.16 and below, see our Nuxt docs.
2. 2
Add environment variables
Required
Add your PostHog project token and host to your nuxt.config.js file:
nuxt.config.js
PostHog AI
export default defineNuxtConfig({
runtimeConfig: {
public: {
posthogPublicKey: '<ph_project_token>',
posthogHost: 'https://us.i.posthog.com',
posthogDefaults: '2026-01-30'
}
}
})3. 3
Create a plugin
Required
Create a new plugin by creating a new file posthog.client.js in your plugins directory:
plugins/posthog.client.js
PostHog AI
import { defineNuxtPlugin } from '#app'
import posthog from 'posthog-js'
export default defineNuxtPlugin(nuxtApp => {
const runtimeConfig = useRuntimeConfig();
const posthogClient = posthog.init(runtimeConfig.public.posthogPublicKey, {
api_host: runtimeConfig.public.posthogHost,
defaults: runtimeConfig.public.posthogDefaults,
loaded: (posthog) => {
if (import.meta.env.MODE === 'development') posthog.debug();
}
})
return {
provide: {
posthog: () => posthogClient
}
}
})4. 4
Server-side setup
Optional
To capture events from server routes, install posthog-node and instantiate it directly. You can also use it to evaluate feature flags on the server:
PostHog AI
npm
npm install posthog-nodeyarn
yarn add posthog-nodepnpm
pnpm add posthog-nodeserver/api/example.js
PostHog AI
import { PostHog } from 'posthog-node'
export default defineEventHandler(async (event) => {
const runtimeConfig = useRuntimeConfig()
const posthog = new PostHog(
runtimeConfig.public.posthogPublicKey,
{ host: runtimeConfig.public.posthogHost }
)
posthog.capture({
distinctId: 'distinct_id_of_the_user',
event: 'event_name'
})
await posthog.shutdown()
})5. 5
Send events
Click around and view a couple pages to generate some events. PostHog automatically captures pageviews, clicks, and other interactions for you.
If you'd like, you can also manually capture custom events:
JavaScript
PostHog AI
posthog.capture('my_custom_event', { property: 'value' })6. 6
Manually capturing exceptions
Optional
To send errors directly using the PostHog client, import it and use the captureException method like this:
Vue
PostHog AI
<script>
const { $posthog } = useNuxtApp()
if ($posthog) {
const posthog = $posthog()
posthog.captureException(new Error("Important error message"))
}
</script>On the server side, you can use the posthog object directly.
server/api/example.js
PostHog AI
const runtimeConfig = useRuntimeConfig()
const posthog = new PostHog(
runtimeConfig.public.posthogPublicKey,
{
host: runtimeConfig.public.posthogHost,
}
);
try {
const results = await DB.query.users.findMany()
return results
} catch (error) {
posthog.captureException(error)
}7. 7
Configuring exception autocapture
Recommended
Update your posthog.client.js to add an error hook.
JavaScript
PostHog AI
export default defineNuxtPlugin((nuxtApp) => {
...
nuxtApp.hook('vue:error', (error) => {
posthogClient.captureException(error)
})
...
})8. ## Verify error tracking
Recommended
Confirm events are being sent to PostHog
Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
!Activity feed with events!Activity feed with events
Check for exceptions in PostHog
9. 8
Upload source maps
Required
Great, you're capturing exceptions! If you serve minified bundles, the next step is to upload source maps to generate accurate stack traces.
Let's continue to the next section.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Ruby error tracking installation - Docs
1. 1
Install the gem
Required
Add the PostHog Ruby gem to your Gemfile:
Gemfile
PostHog AI
gem "posthog-ruby"2. 2
Configure PostHog
Required
Initialize the PostHog client with your project token and host:
Ruby
PostHog AI
require 'posthog'
posthog = PostHog::Client.new({
api_key: "<ph_project_token>",
host: "https://us.i.posthog.com",
on_error: Proc.new { |status, msg| print msg }
})3. 3
Send events
Recommended
Once installed, you can manually send events to test your integration:
Ruby
PostHog AI
posthog.capture({
distinct_id: 'user_123',
event: 'button_clicked',
properties: {
button_name: 'signup'
}
})4. 4
Manually capture exceptions
Required
Using Ruby on Rails? The posthog-rails gem provides automatic exception capture for controllers and background jobs. Select "Ruby on Rails" from the SDK list for setup instructions.To capture exceptions in your Ruby application, use the capture_exception method:
Ruby
PostHog AI
begin
# Code that might raise an exception
raise StandardError, "Something went wrong"
rescue => e
posthog.capture_exception(
e,
'user_distinct_id',
{
custom_property: 'custom_value'
}
)
endThe capture_exception method accepts the following parameters:
| Param | Type | Description |
|---|---|---|
| exception | Exception | The exception object to capture (required) |
| distinct_id | String | The distinct ID of the user (optional) |
| additional_properties | Hash | Additional properties to attach to the exception event (optional) |
5. ## Verify error tracking
Recommended
Confirm events are being sent to PostHog
Before proceeding, let's make sure exception events are being captured and sent to PostHog. You should see events appear in the activity feed.
!Activity feed with events!Activity feed with events
Check for exceptions in PostHog
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Related skills
FAQ
What does instrument-error-tracking do?
instrument-error-tracking is a Claude Code skill for ai & agent building.
When should I use instrument-error-tracking?
When you need to helps with ai & agent building tasks during AI-assisted development., or when instrument-error-tracking is a claude code skill for ai & agent building.
What are the main capabilities?
instrument-error-tracking; AI & Agent Building; AI-coding skill.