Skip to main content

Monitoring & Observability

Ever Works uses Sentry for error tracking and performance monitoring, and PostHog for product analytics. Both are integrated through the @ever-works/monitoring package located in packages/monitoring/.

Architecture

The monitoring package provides a global NestJS module with a forRoot() configuration pattern:

@Global()
@Module({})
export class MonitoringModule {
static forRoot(config?: MonitoringConfig) {
return {
module: MonitoringModule,
global: true,
imports: [SentryModule.forRoot(config?.sentry), PostHogModule.forRoot(config?.posthog)],
providers: [AnalyticsService, SentryService],
exports: [AnalyticsService, SentryService]
};
}
}

The @Global() decorator makes AnalyticsService and SentryService available throughout the application without explicit imports.

Sentry Integration

Configuration

Sentry is initialized in sentry/sentry.config.ts with environment-aware defaults:

SettingProductionDevelopment
tracesSampleRate0.1 (10%)1.0 (100%)
profilesSampleRate0.1 (10%)1.0 (100%)
enableLogstruetrue

The configuration includes nodeProfilingIntegration() for server-side performance profiling.

Auth Route Filtering

Authentication routes are excluded from Sentry tracking to avoid capturing sensitive data:

beforeSend(event) {
if (event.request?.url?.includes('/auth')) {
return null;
}
return event;
},
beforeSendTransaction(event) {
if (event.request?.url?.includes('/auth')) {
return null;
}
return event;
},

Both beforeSend (errors) and beforeSendTransaction (performance) filters drop any events from /auth endpoints.

Sentry Interceptor

The SentryInterceptor is a NestJS interceptor that enriches error reports with request context:

On every request:

  • Sets the Sentry user context (id, email, username) if authenticated.
  • Sets request context (method, url, sanitized headers, sanitized body).
  • Tags the transaction with the endpoint pattern.

On error:

  • Captures the exception with additional tags (endpoint, statusCode) and extras (requestBody, userAgent).

Data Sanitization

The interceptor sanitizes sensitive data before sending to Sentry:

// Headers: remove auth and cookies
private sanitizeHeaders(headers) {
const sanitized = { ...headers };
delete sanitized.authorization;
delete sanitized.cookie;
return sanitized;
}

// Body: remove credentials
private sanitizeBody(body) {
const sanitized = { ...body };
delete sanitized.password;
delete sanitized.token;
delete sanitized.secret;
return sanitized;
}

SentryService

The SentryService wraps Sentry's logging API with standard log levels:

MethodSentry Level
trace(message)trace
debug(message)debug
info(message)info
warn(message)warning
error(message)error
fatal(message)fatal

PostHog Integration

Configuration

PostHog is initialized in posthog/posthog.config.ts with the POSTHOG_API_KEY and optional POSTHOG_HOST environment variables. The client exposes three functions:

FunctionPurpose
trackEvent(distinctId, event, properties, groups)Track a custom event
identifyUser(distinctId, properties)Identify a user with properties
setUserProperties(distinctId, properties)Update user properties

PostHog Interceptor

The PostHogInterceptor automatically tracks API request metrics:

  • Duration: Measures request processing time.
  • Endpoint normalization: Strips dynamic path segments for consistent grouping.
  • Properties captured: method, endpoint, status code, duration, user agent.

AnalyticsService

The AnalyticsService is the high-level API for product analytics. It wraps PostHog with typed event methods.

Event Categories

CategoryMethodEvents
Generictrack(distinctId, event, properties)Any custom event
API UsagetrackApiUsage(distinctId, endpoint, method, statusCode, duration)Endpoint performance
AuthtrackAuth(distinctId, event, properties)login, logout, register, password_reset
BusinesstrackBusinessEvent(distinctId, event, properties)Business-specific metrics

Typed Event Interfaces

The service supports both positional parameters and typed event objects:

// Positional
analytics.trackApiUsage(userId, '/api/works', 'GET', 200, 150);

// Typed object
analytics.trackApiUsageEvent({
distinctId: userId,
endpoint: '/api/works',
method: 'GET',
statusCode: 200,
duration: 150
});

Availability Check

analytics.isAvailable(): boolean

Returns true if PostHog is configured and available. This allows callers to skip expensive property collection when analytics is disabled.

Environment Variables

VariableServicePurpose
SENTRY_DSNSentryData Source Name for error reporting
NODE_ENVSentryControls sample rates (production vs development)
POSTHOG_API_KEYPostHogAPI key for event tracking
POSTHOG_HOSTPostHogCustom PostHog instance URL (optional)

Module Structure

packages/monitoring/
src/
monitoring.module.ts # Global module with forRoot()
types.ts # MonitoringConfig, event interfaces
sentry/
sentry.module.ts # Sentry NestJS module
sentry.config.ts # Sentry initialization
posthog/
posthog.module.ts # PostHog NestJS module
posthog.config.ts # PostHog client initialization
interceptors/
sentry.interceptor.ts # Request context enrichment
posthog.interceptor.ts # Request duration tracking
services/
analytics.service.ts # High-level analytics API
sentry.service.ts # Sentry logging wrapper