Skip to main content

Performance Monitoring

Ever Works integrates Sentry for application performance monitoring (APM) and PostHog for product analytics. Together they provide end-to-end visibility into API latency, background task performance, user behavior, and business metrics.

Monitoring Architecture

+---------------------+ +-------------------+
| NestJS API | | Next.js Web |
| | | |
| +--Sentry----------+| | +--Sentry--------+|
| | Interceptor || | | Browser SDK ||
| | tracing + errors || | | page load perf ||
| +-------------------+| | +----------------+|
| | | |
| +--PostHog----------+| | +--PostHog-------+|
| | Interceptor || | | JS SDK ||
| | API usage events || | | user events ||
| +--------------------+| | +---------------+|
+----------+-----------+ +--------+---------+
| |
+-----v-----+ +------v------+
| Sentry | | PostHog |
| Cloud | | Cloud |
+-----------+ +-------------+

MonitoringModule Configuration

The MonitoringModule in packages/monitoring/src/monitoring.module.ts is a global NestJS module that initializes both Sentry and PostHog:

interface MonitoringConfig {
sentry?: SentryConfig;
posthog?: PostHogConfig;
}

interface SentryConfig {
dsn?: string;
environment?: string;
tracesSampleRate?: number; // 0.1 prod, 1.0 dev
profilesSampleRate?: number; // 0.1 prod, 1.0 dev
enableLogs?: boolean; // true by default
}

interface PostHogConfig {
apiKey?: string;
host?: string; // default: https://app.posthog.com
flushAt?: number; // batch size, default: 20
flushInterval?: number; // ms, default: 10000
}

Usage in AppModule:

@Module({
imports: [
MonitoringModule.forRoot({
sentry: { tracesSampleRate: 0.1 },
posthog: { flushAt: 20 }
})
]
})
export class AppModule {}

Sentry APM

Transaction Tracing

The SentryInterceptor automatically creates a transaction span for every HTTP request:

  • Tags each transaction with {method} {url} format
  • Attaches user context from JWT authentication
  • Sanitizes sensitive headers (authorization, cookie) and body fields (password, token, secret)
  • Records error metadata including status codes and request bodies

Performance Sampling

// Production: 10% of transactions and profiles are captured
tracesSampleRate: 0.1;
profilesSampleRate: 0.1;

// Development: 100% capture for debugging
tracesSampleRate: 1.0;
profilesSampleRate: 1.0;

Node Profiling

Sentry's nodeProfilingIntegration() is enabled by default, providing:

  • Function-level CPU profiling for slow endpoints
  • Flame graphs in the Sentry Performance dashboard
  • Correlation between slow transactions and CPU-intensive code paths

Key Sentry Dashboards

DashboardPurpose
PerformanceTransaction duration P50/P75/P95, throughput, Apdex
IssuesError grouping, stack traces, affected users
ProfilesCPU flame graphs for slow transactions
LogsStructured log search and filtering
AlertsConfigurable thresholds for errors and latency
Alert: API P95 Latency > 2s
Metric: transaction.duration (p95)
Threshold: > 2000ms over 5 minutes
Action: Slack notification + PagerDuty

Alert: Error Rate Spike
Metric: event.type:error
Threshold: > 50 events / 5 minutes
Action: Slack notification

Alert: Generation Task Failure
Metric: event.type:error, tag:endpoint contains "generation"
Threshold: > 5 events / hour
Action: Email notification

PostHog Analytics

API Usage Tracking

The PostHogInterceptor in packages/monitoring/src/interceptors/posthog.interceptor.ts captures every API request as two events:

  1. api_request: Generic request with method, endpoint, status code, duration, user agent, IP
  2. api_{method}_{endpoint_name}: Specific endpoint event (e.g., api_get_works_:id)
// Event properties captured:
{
method: 'GET',
endpoint: '/api/works/abc-123',
statusCode: 200,
duration: 145, // milliseconds
userAgent: 'Mozilla/5.0...',
timestamp: '2025-01-15T10:30:00.000Z'
}

AnalyticsService API

The AnalyticsService provides typed methods for tracking business and operational events:

interface AnalyticsService {
// Generic event tracking
track(distinctId: string, event: string, properties?: Record<string, any>): void;
trackEvent(event: AnalyticsEvent): void;

// User identification
identify(distinctId: string, properties?: Record<string, any>): void;
setUserProperties(distinctId: string, properties: Record<string, any>): void;

// Specialized tracking
trackApiUsage(distinctId, endpoint, method, statusCode, duration): void;
trackAuth(distinctId, event: 'login' | 'logout' | 'register' | 'password_reset'): void;
trackBusinessEvent(distinctId, event: string, properties?): void;
}

Event Types

InterfaceFieldsUsage
AnalyticsEventdistinctId, event, properties, groupsCustom events
ApiUsageEventdistinctId, endpoint, method, statusCode, durationAPI performance
AuthEventdistinctId, event (login/logout/register/password_reset)Authentication flow
BusinessEventdistinctId, event, propertiesBusiness metrics

PostHog Dashboards

Build the following dashboards in PostHog for operational visibility:

API Performance Dashboard:

  • Average response time by endpoint (trend)
  • Error rate by endpoint (bar chart)
  • Request volume by hour (time series)
  • Slowest endpoints P95 (table)

User Engagement Dashboard:

  • Daily active users (trend)
  • Feature adoption funnel (funnel)
  • Work generation frequency (histogram)
  • Deployment success rate (pie chart)

Business Metrics Dashboard:

  • New work creations per day
  • Generation runs per user
  • Deployment frequency by provider
  • Subscription plan distribution

Custom Metrics

Background Task Metrics

Background tasks (Trigger.dev) emit structured metrics visible in the Trigger.dev dashboard:

// work-schedule-dispatcher.task.ts returns metrics:
return {
dispatched: 5, // number of schedules dispatched
intervalMinutes: 10 // dispatcher interval
};

Generation History Metrics

Each generation run stores metrics in the WorkGenerationHistoryEntry:

MetricDescription
durationInSecondsTotal generation wall-clock time
newItemsCountItems created during this run
updatedItemsCountItems updated during this run
totalItemsCountTotal items in work after run
metrics.total_tokens_usedLLM token consumption
metrics.total_costEstimated LLM cost (USD)

These metrics are displayed in the Generation History UI.

Health Check Endpoints

Both the API and Web services expose health endpoints used by Kubernetes probes:

ServiceEndpointPortPurpose
API/api/health3100Liveness and readiness checks
Web/api/health3000Liveness and readiness checks

Environment Variables

VariableServiceDescription
SENTRY_DSNAPISentry Data Source Name
NODE_ENVAPI/WebEnvironment for sampling rates
POSTHOG_API_KEYAPIPostHog project API key
POSTHOG_HOSTAPIPostHog instance URL

Cross-References