Skip to content

Commit 7c49f36

Browse files
committed
feat: migrate from Sentry to Grafana Cloud with OpenTelemetry
1 parent 2ba13fc commit 7c49f36

File tree

15 files changed

+1596
-1116
lines changed

15 files changed

+1596
-1116
lines changed

.env.example

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -125,32 +125,43 @@ FREE_TIER_NOTE_LIMIT=100# Note count limit for free users (default: 100)
125125
# LOGGING & MONITORING
126126
# ================================================================
127127

128-
# Sentry.io Error Tracking (OPTIONAL)
129-
# Format: https://<key>@<org-id>.ingest.us.sentry.io/<project-id>
130-
# SENTRY_DSN=https://your-sentry-dsn-here
131-
# Error tracking, performance monitoring, and profiling are enabled by default
132-
# Errors are automatically captured and sent to Sentry with full context
133-
134-
# Sentry Auth Token (REQUIRED for production builds with source maps)
135-
# Get from: https://sentry.io/settings/account/api/auth-tokens/
136-
# Scope needed: project:releases, project:write
137-
# SENTRY_AUTH_TOKEN=your-sentry-auth-token-here
138-
# This is only needed during production builds to upload source maps
139-
140-
# Sentry Organization and Project (REQUIRED for production builds with source maps)
141-
# These are used during the build process to upload source maps
142-
# SENTRY_ORG=your-sentry-organization-slug
143-
# SENTRY_PROJECT=your-sentry-project-slug
128+
# Grafana Cloud with OpenTelemetry (OPTIONAL - for observability)
129+
# Get your Grafana Cloud details from: https://grafana.com/products/cloud/
130+
# 1. Sign up for Grafana Cloud (free tier available)
131+
# 2. Go to Connections > Add new connection > OpenTelemetry
132+
# 3. Copy the OTLP endpoint URL (should look like: https://otlp-gateway-prod-us-central-0.grafana.net/otlp)
133+
# 4. Generate an API key with MetricsPublisher role
134+
# 5. Base64 encode your instance ID and API token: echo -n "instanceID:apiToken" | base64
135+
136+
# OpenTelemetry Configuration
137+
# IMPORTANT: Observability is ONLY enabled in production (NODE_ENV=production) by default
138+
# This prevents development metrics from flooding your logs and consuming quota
139+
# OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp
140+
# GRAFANA_CLOUD_API_KEY=base64encodedstring
141+
# OTEL_SERVICE_NAME=typelets-api
142+
143+
# Optional: Force enable in development (NOT RECOMMENDED - will flood your logs)
144+
# OTEL_ENABLED=true
144145

145146
# Application Logging
146-
# Structured logs are automatically generated for:
147+
# Structured JSON logs are automatically generated for:
147148
# - Authentication events
148149
# - Rate limiting violations
149150
# - Security events (failed auth, suspicious activity)
150151
# - Billing limit violations
151152
# - WebSocket connection events
152153
# - File upload events
153-
# All console logs are also captured by Sentry for centralized monitoring
154+
# - HTTP requests and responses
155+
# - Database queries
156+
# - Cache operations
157+
158+
# When OpenTelemetry is configured, logs, traces, and metrics are sent to Grafana Cloud
159+
# Features include:
160+
# - Distributed tracing across services
161+
# - Automatic instrumentation for HTTP, PostgreSQL, Redis
162+
# - Custom business metrics and events
163+
# - Performance monitoring and profiling
164+
# - Log aggregation and search
154165

155166
# ================================================================
156167
# DEVELOPMENT HELPERS

.github/workflows/release.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ jobs:
3434
- name: Build
3535
env:
3636
NODE_ENV: production
37-
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
38-
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
39-
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
4037
run: NODE_OPTIONS="--max-old-space-size=8192" pnpm run build
4138

4239
- name: Release

.github/workflows/sentry-release.yml

Lines changed: 0 additions & 41 deletions
This file was deleted.

.husky/pre-commit

100644100755
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
#!/usr/bin/env bash
22
set -e # Exit on any command failure
33

4+
# Load NVM if available
5+
export NVM_DIR="$HOME/.nvm"
6+
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
7+
8+
# Add common paths for pnpm
9+
export PATH="$HOME/.nvm/versions/node/$(node -v)/bin:$PATH"
10+
411
echo "🔍 Running pre-commit checks..."
512

613
echo "📝 Running lint..."

README.md

Lines changed: 78 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@ The backend API for the [Typelets Application](https://github.com/typelets/typel
2828
- [Database Schema](#database-schema)
2929
- [Security Features](#security-features)
3030
- [Environment Variables](#environment-variables)
31-
- [Monitoring with Sentry.io](#monitoring-with-sentryio)
31+
- [Monitoring with Grafana Cloud](#monitoring-with-grafana-cloud)
3232
- [Features](#features-1)
3333
- [Configuration](#configuration)
34-
- [Source Maps](#source-maps)
35-
- [Automated Release Tracking](#automated-release-tracking)
34+
- [What Gets Monitored](#what-gets-monitored)
3635
- [Development](#development)
3736
- [Project Structure](#project-structure)
3837
- [Type Safety](#type-safety)
@@ -59,7 +58,7 @@ The backend API for the [Typelets Application](https://github.com/typelets/typel
5958
-**Fast & Type-Safe** with TypeScript and Hono
6059
- 🐘 **PostgreSQL** with Drizzle ORM
6160
- 🚀 **Valkey/Redis Caching** for high-performance data access with cluster support
62-
- 📊 **Error Tracking & Monitoring** with Sentry.io for observability and performance monitoring
61+
- 📊 **Observability** with Grafana Cloud and OpenTelemetry for distributed tracing, metrics, and logging
6362
- 💻 **Code Execution** via secure Judge0 API proxy
6463
- 🛡️ **Comprehensive Rate Limiting** for HTTP, WebSocket, file uploads, and code execution
6564
- 🏥 **Health Checks** with detailed system status and readiness probes
@@ -73,7 +72,7 @@ The backend API for the [Typelets Application](https://github.com/typelets/typel
7372
- **Cache**: Valkey/Redis Cluster for high-performance caching
7473
- **Authentication**: [Clerk](https://clerk.com/)
7574
- **Validation**: [Zod](https://zod.dev/)
76-
- **Monitoring**: [Sentry.io](https://sentry.io/) for error tracking and performance monitoring
75+
- **Observability**: [Grafana Cloud](https://grafana.com/products/cloud/) with [OpenTelemetry](https://opentelemetry.io/) for tracing, metrics, and logging
7776
- **Logging**: Structured JSON logging with automatic error capture
7877
- **TypeScript**: Strict mode enabled for type safety
7978

@@ -84,7 +83,7 @@ The backend API for the [Typelets Application](https://github.com/typelets/typel
8483
- PostgreSQL database (local installation or Docker)
8584
- Clerk account for authentication ([sign up here](https://dashboard.clerk.com))
8685
- Valkey/Redis cluster for caching (optional - improves performance)
87-
- Sentry.io account for monitoring (optional - [sign up here](https://sentry.io/signup/))
86+
- Grafana Cloud account for monitoring (optional - [sign up here](https://grafana.com/products/cloud/))
8887
- Judge0 API key for code execution (optional - [get from RapidAPI](https://rapidapi.com/judge0-official/api/judge0-ce))
8988

9089
## Local Development Setup
@@ -283,7 +282,10 @@ The application uses the following main tables:
283282
| `VALKEY_HOST` | Valkey/Redis cluster hostname | No | - |
284283
| `VALKEY_PORT` | Valkey/Redis cluster port | No | 6379 |
285284
| **Monitoring (Optional)** | | | |
286-
| `SENTRY_DSN` | Sentry.io DSN for error tracking | No | - |
285+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Grafana Cloud OTLP endpoint (prod only) | No | - |
286+
| `GRAFANA_CLOUD_API_KEY` | Base64 encoded Grafana Cloud credentials | No | - |
287+
| `OTEL_SERVICE_NAME` | Service name for OpenTelemetry | No | typelets-api |
288+
| `OTEL_ENABLED` | Force enable OTEL in dev (not recommended) | No | false |
287289
| **Rate Limiting** | | | |
288290
| `HTTP_RATE_LIMIT_WINDOW_MS` | HTTP rate limit window in milliseconds | No | 900000 (15 min) |
289291
| `HTTP_RATE_LIMIT_MAX_REQUESTS` | Max HTTP requests per window | No | 1000 |
@@ -306,101 +308,96 @@ The application uses the following main tables:
306308

307309
\*Required only for code execution features
308310

309-
## Monitoring with Sentry.io
311+
## Monitoring with Grafana Cloud
310312

311313
⚠️ **Monitoring is completely optional** - The API works perfectly without it.
312314

313-
The API integrates with [Sentry.io](https://sentry.io/) for comprehensive error tracking, performance monitoring, and logging.
315+
The API integrates with [Grafana Cloud](https://grafana.com/products/cloud/) using [OpenTelemetry](https://opentelemetry.io/) for comprehensive observability with distributed tracing, metrics collection, and log aggregation.
314316

315317
### Features
316318

317-
- **Error Tracking**: Automatic exception capture with full stack traces and context
318-
- **Source Maps**: Production builds automatically upload source maps for readable stack traces
319-
- **Performance Monitoring**: 100% transaction sampling for performance analysis
320-
- **Database Monitoring**: Automatic PostgreSQL query tracking and performance analysis
321-
- **Profiling**: CPU and memory profiling during active traces
322-
- **Structured Logging**: Automatic capture of console.log, console.warn, and console.error
323-
- **User Context**: Errors are automatically associated with authenticated users
324-
- **Environment Tracking**: Separate error tracking for development and production
325-
- **Release Tracking**: Errors automatically linked to code releases via GitHub Actions
319+
- **Distributed Tracing**: Automatic instrumentation for HTTP requests, database queries, and cache operations
320+
- **Metrics Collection**: Real-time metrics exported every 60 seconds
321+
- **Log Aggregation**: Structured JSON logs sent to Grafana Loki
322+
- **Automatic Instrumentation**: Zero-code instrumentation for:
323+
- HTTP/HTTPS requests (Hono framework)
324+
- PostgreSQL database queries
325+
- Redis/Upstash cache operations
326+
- **Performance Monitoring**: Request duration, latency, and throughput tracking
327+
- **Error Tracking**: Automatic error capture with full context and stack traces
328+
- **User Context**: Requests are automatically tagged with user IDs
329+
- **Environment Tracking**: Separate monitoring for development and production
326330

327331
### Configuration
328332

329-
Sentry is configured in the application with:
330-
331-
- Profiling integration enabled
332-
- Console logging integration
333-
- 100% trace sampling rate
334-
- PII data collection for better debugging
335-
- Environment-based configuration
336-
337-
**Setup**: Add your Sentry DSN to `.env`:
333+
**Setup**: Add your Grafana Cloud credentials to `.env`:
338334

339335
```env
340-
SENTRY_DSN=https://your-key@your-org-id.ingest.us.sentry.io/your-project-id
336+
# OpenTelemetry Configuration for Grafana Cloud
337+
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp
338+
GRAFANA_CLOUD_API_KEY=base64encodedstring
339+
OTEL_SERVICE_NAME=typelets-api
341340
```
342341

343-
Once configured, all errors are automatically captured and sent to Sentry with contextual information including:
344-
345-
- Error ID for tracking
346-
- User ID (if authenticated)
347-
- Request URL and method
348-
- Stack traces
349-
350-
If `SENTRY_DSN` is not set, the application will run normally with error tracking disabled.
351-
352-
### Source Maps
342+
**Step-by-step setup:**
353343

354-
Source maps are automatically generated during builds and uploaded to Sentry in production:
344+
1. **Sign up for Grafana Cloud** (free tier available):
345+
- Visit https://grafana.com/products/cloud/
355346

356-
**Development builds:**
347+
2. **Get your OTLP endpoint:**
348+
- Go to **Connections****Add new connection****OpenTelemetry**
349+
- Copy the OTLP endpoint URL (e.g., `https://otlp-gateway-prod-us-central-0.grafana.net/otlp`)
357350

358-
- Source maps are generated locally for debugging
359-
- Not uploaded to Sentry (saves bandwidth and quota)
351+
3. **Generate API credentials:**
352+
- Create an API key with **MetricsPublisher** role
353+
- Note your Grafana instance ID and API token
360354

361-
**Production builds:**
362-
363-
- Source maps are generated and uploaded to Sentry
364-
- Requires `SENTRY_AUTH_TOKEN` environment variable
365-
- Stack traces in Sentry show your original TypeScript code, not bundled JavaScript
366-
- Source maps are deleted after upload to keep deployments clean
367-
368-
**Setup:**
369-
370-
1. Create a Sentry Auth Token: [Sentry Settings → Auth Tokens](https://sentry.io/settings/account/api/auth-tokens/)
371-
2. Required scopes: `project:releases`, `project:write`
372-
3. Add to your environment:
355+
4. **Encode your credentials:**
373356
```bash
374-
export SENTRY_AUTH_TOKEN=your-token-here
375-
NODE_ENV=production pnpm run build
357+
echo -n "instanceID:apiToken" | base64
376358
```
377359

378-
The build will automatically upload source maps when both `NODE_ENV=production` and `SENTRY_AUTH_TOKEN` are set.
379-
380-
### Automated Release Tracking
360+
5. **Set environment variables:**
361+
- Add the configuration shown above to your `.env` file
381362

382-
The repository includes automated Sentry release tracking via GitHub Actions. When a new release is published:
383-
384-
1. **Automatic Release Creation**: A Sentry release is created with the version tag
385-
2. **Commit Association**: All commits are automatically associated with the release
386-
3. **Error Attribution**: Errors can be traced back to specific releases
387-
388-
**Setup Required (One-time)**:
389-
390-
To enable automated release tracking and source map uploads, add your Sentry Auth Token as a GitHub secret:
391-
392-
1. Go to **Sentry.io****Settings****Auth Tokens**
393-
2. Create a new token with `project:releases` and `project:write` scopes
394-
3. In GitHub, go to **Settings****Secrets and variables****Actions**
395-
4. Create a new secret named `SENTRY_AUTH_TOKEN` with your token value
396-
397-
**Note**: The same token is used for both release tracking and source map uploads during CI/CD builds.
398-
399-
The workflow automatically triggers on every release and:
400-
401-
- Creates a new Sentry release with the version tag
402-
- Associates all commits since the last release
403-
- Finalizes the release for tracking
363+
6. **Start the application in production mode:**
364+
```bash
365+
NODE_ENV=production pnpm start
366+
```
367+
You should see: `✅ OpenTelemetry initialized with Grafana Cloud`
368+
369+
**Important Notes:**
370+
- Observability is **ONLY enabled in production** (`NODE_ENV=production`) by default
371+
- This prevents development metrics from flooding your logs and consuming quota
372+
- In development, the app will show: `⚠️ OpenTelemetry not initialized - Running in development mode`
373+
- To force enable in development (not recommended): Set `OTEL_ENABLED=true`
374+
- If credentials are not set, the application runs normally with observability disabled
375+
376+
### What Gets Monitored
377+
378+
**Automatic Instrumentation:**
379+
- HTTP requests (method, path, status code, duration)
380+
- PostgreSQL queries (operation, table, duration)
381+
- Redis/Upstash operations (get, set, delete with cache hit/miss tracking)
382+
383+
**Structured Logging:**
384+
- Authentication events (login, logout, token refresh)
385+
- Rate limiting violations
386+
- Security events (failed auth, suspicious activity)
387+
- Billing limit violations
388+
- WebSocket connection events
389+
- File upload events and storage operations
390+
- HTTP request/response logs
391+
- Database query performance
392+
- Cache operations and hit rates
393+
- Business events (note creation, folder operations, etc.)
394+
395+
All logs, traces, and metrics are automatically sent to Grafana Cloud where you can:
396+
- Visualize request traces with flame graphs
397+
- Create custom dashboards for metrics
398+
- Set up alerts for errors and performance issues
399+
- Search and analyze logs with LogQL
400+
- Correlate logs, metrics, and traces in one place
404401

405402
## Development
406403

build.mjs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as esbuild from "esbuild";
2-
import { sentryEsbuildPlugin } from "@sentry/esbuild-plugin";
32

43
const isProduction = process.env.NODE_ENV === "production";
54

@@ -16,32 +15,6 @@ const buildOptions = {
1615
minify: isProduction, // Minify in production
1716
};
1817

19-
// Add Sentry plugin only in production and when auth token is available
20-
if (isProduction && process.env.SENTRY_AUTH_TOKEN) {
21-
buildOptions.plugins = [
22-
sentryEsbuildPlugin({
23-
org: process.env.SENTRY_ORG,
24-
project: process.env.SENTRY_PROJECT,
25-
authToken: process.env.SENTRY_AUTH_TOKEN,
26-
27-
// Upload source maps to Sentry
28-
sourcemaps: {
29-
assets: "./dist/**",
30-
filesToDeleteAfterUpload: ["./dist/**/*.map"], // Clean up source map files after upload
31-
},
32-
33-
// Set release version
34-
release: {
35-
name: process.env.npm_package_version || "1.0.0",
36-
},
37-
}),
38-
];
39-
40-
console.log("🔐 Sentry source maps upload enabled");
41-
} else {
42-
console.log("ℹ️ Sentry source maps upload skipped (development or no auth token)");
43-
}
44-
4518
// Run build
4619
try {
4720
await esbuild.build(buildOptions);

jest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const config: Config = {
1010
"src/**/*.{ts,tsx}",
1111
"!src/**/*.d.ts",
1212
"!src/**/__tests__/**",
13-
"!src/instrument.ts", // Exclude Sentry instrumentation
13+
"!src/instrumentation.ts", // Exclude OpenTelemetry instrumentation
1414
],
1515
coverageDirectory: "coverage",
1616
coverageReporters: ["text", "lcov", "html"],

0 commit comments

Comments
 (0)