Giám sát lỗi trong môi trường Production: Vượt xa console.log
Trong môi trường phát triển, lỗi hiển thị rõ ràng, nhưng trên môi trường thực tế (production), chúng lại hoàn toàn im lặng. Bài viết này sẽ hướng dẫn bạn cách xây dựng hệ thống giám sát lỗi hiệu quả bằng Sentry, Source Maps, logging có cấu trúc và các endpoint kiểm tra sức khỏe (health checks) để kịp thời xử lý sự cố.

Trong quá trình phát triển phần mềm, các lỗi (bug) thường hiển thị rõ ràng ngay trên màn hình console của developer. Tuy nhiên, khi ứng dụng đã được triển khai ra môi trường thực tế (production), những lỗi này trở nên "im lặng" hoàn toàn.
Người dùng gặp lỗi, họ tải lại trang, và sau đó bỏ cuộc. Bạn sẽ không bao giờ biết điều gì đã xảy ra cho đến khi chỉ số giữ chân người dùng (retention rate) bắt đầu giảm sút. Giám sát lỗi chính là cầu nối giúp lấp đầy khoảng trống đó.
Sentry: Tiêu chuẩn vàng cho giám sát lỗi
Sentry hiện nay được coi là công cụ tiêu chuẩn để theo dõi lỗi trong các ứng dụng hiện đại. Cài đặt cực kỳ đơn giản với npm:
npm install @sentry/nextjs
# hoặc
npm install @sentry/node
Dưới đây là cấu hình cơ bản để khởi tạo Sentry:
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
// Lọc bỏ các lỗi nhiễu
ignoreErrors: [
'ResizeObserver loop limit exceeded',
/^Network Error/,
],
beforeSend(event, hint) {
// Không gửi lỗi từ các bot crawl
const ua = event.request?.headers?.['user-agent'] ?? '';
if (/bot|crawler|spider/i.test(ua)) return null;
return event;
},
});
Bắt lỗi với ngữ cảnh chi tiết
Một stack trace thô sơ thường khó hiểu. Tuy nhiên, nếu bạn thêm ngữ cảnh (context) vào, nó sẽ trở thành một báo cáo lỗi có thể hành động được ngay lập tức.
try {
await processPayment(userId, amount);
} catch (error) {
Sentry.withScope((scope) => {
scope.setUser({ id: userId, email: user.email });
scope.setTag('payment.provider', 'stripe');
scope.setContext('payment', { amount, currency: 'USD' });
Sentry.captureException(error);
});
throw error; // ném lại lỗi để caller xử lý
}
Nhờ đó, bạn biết chính xác ai bị ảnh hưởng, họ đang làm gì và trạng thái hệ thống tại thời điểm đó là như thế nào.
Sử dụng các lớp Error tùy chỉnh
Việc định nghĩa các lớp Error riêng giúp bạn phân loại rõ ràng giữa các lỗi đã biết và lỗi chưa biết.
class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode: number = 500,
public context?: Record<string, unknown>,
) {
super(message);
this.name = 'AppError';
}
}
class PaymentError extends AppError {
constructor(message: string, public stripeError?: Stripe.StripeError) {
super(message, 'PAYMENT_ERROR', 402);
}
}
// Xử lý lỗi trong middleware
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
if (error instanceof AppError) {
// Lỗi đã biết — log mức cảnh báo, gửi đến Sentry dưới dạng info
Sentry.captureException(error, { level: 'warning' });
return res.status(error.statusCode).json({ error: error.message, code: error.code });
}
// Lỗi chưa biết — cảnh báo đầy đủ
Sentry.captureException(error);
res.status(500).json({ error: 'Internal server error' });
});
Source Maps: Định vị chính xác lỗi
Nếu không có Source Maps, stack traces của bạn sẽ là những dòng code bị rút gọn (minified) vô nghĩa:
TypeError: Cannot read properties of undefined
at e.t.handleRequest (main.8f3a2.js:1:48291)
Nhưng với Source Maps, bạn sẽ thấy rõ ràng nguồn gốc vấn đề:
TypeError: Cannot read properties of undefined
at PaymentService.handleRequest (src/services/payment.ts:83:12)
Đối với Next.js, bạn có thể cấu hình như sau để tự động tải Source Maps lên khi build:
// next.config.js
const { withSentryConfig } = require('@sentry/nextjs');
module.exports = withSentryConfig(nextConfig, {
silent: true,
org: 'your-org',
project: 'your-project',
// Tải source maps khi build
widenClientFileUpload: true,
hideSourceMaps: true, // không hiển thị cho trình duyệt
});
Cảnh báo thông minh mà không bị quá tải
Việc nhận quá nhiều thông báo (alert) sẽ khiến nhà phát triển bị "mù" thông báo. Hãy thiết lập quy tắc trong dashboard của Sentry và kiểm soát lượng alert trong code:
// Quy tắc cảnh báo (cấu hình trong dashboard):
// 1. Vấn đề mới → Thông báo Slack ngay lập tức
// 2. Vấn đề > 100 sự cố/giờ → Gửi PagerDuty
// 3. Tỷ lệ lỗi > 1% phiên → Cảnh báo ngay
// Trong code: kiểm soát khối lượng cảnh báo
Sentry.captureException(error, {
// Không cảnh báo cho các lỗi dự đoán trước
level: error instanceof AppError ? 'warning' : 'error',
fingerprint: ['{{ default }}', error.code], // nhóm các lỗi tương tự
});
Kiểm tra sức khỏe (Health Checks)
Một endpoint kiểm tra sức khỏe là bắt buộc để các công cụ giám sát bên ngoài như UptimeRobot có thể theo dõi trạng thái của hệ thống.
// endpoint /api/health
app.get('/health', async (req, res) => {
const checks = await Promise.allSettled([
db.$queryRaw`SELECT 1`,
redis.ping(),
fetch(process.env.EXTERNAL_API_URL + '/health'),
]);
const status = checks.every(c => c.status === 'fulfilled') ? 'ok' : 'degraded';
const statusCode = status === 'ok' ? 200 : 503;
res.status(statusCode).json({
status,
timestamp: new Date().toISOString(),
checks: {
database: checks[0].status,
redis: checks[1].status,
externalApi: checks[2].status,
},
});
});
Logging có cấu trúc (Structured Logging)
Thay vì dùng console.log thông thường, hãy sử dụng các thư viện như Pino hoặc Winston để tạo ra các bản ghi có cấu trúc, dễ dàng truy vấn trên các hệ thống như Datadog hay CloudWatch.
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL ?? 'info',
transport: process.env.NODE_ENV === 'development'
? { target: 'pino-pretty' }
: undefined,
});
// Log có cấu trúc dễ dàng truy vấn
logger.info({ userId, orderId, amount }, 'Thanh toán thành công');
logger.error({ error: err.message, stack: err.stack, userId }, 'Thanh toán thất bại');
So với console.log('Payment failed for user 123'), logging có cấu trúc cho phép bạn thực hiện truy vấn phức tạp như "tất cả các lần thanh toán thất bại trong giờ qua" chỉ trong vài giây.
Bộ công cụ giám sát tối thiểu (MVP Stack)
Bạn không nhất thiết phải sử dụng Datadog hay PagerDuty ngay từ ngày đầu tiên. Tuy nhiên, bạn cần những yếu tố tối thiểu sau để biết người dùng có đang gặp lỗi hay không:
- Sentry: Để theo dõi lỗi (gói miễn phí thường đủ cho các startup).
- Structured Logging: Sử dụng Pino hoặc Winston.
- Health Check Endpoint: Được giám sát bởi UptimeRobot (miễn phí).
- Source Maps: Tự động tải lên khi triển khai (deploy).
Việc thiết lập tốt các công cụ này sẽ giúp bạn yên tâm phát triển tính năng mới mà không lo ngại các lỗi "tàng hình" trong môi trường production.
Bài viết liên quan

Phần mềm
Anthropic ra mắt Claude Opus 4.7: Nâng cấp mạnh mẽ cho lập trình nhưng vẫn thua Mythos Preview
16 tháng 4, 2026

Công nghệ
Qwen3.6-35B-A3B: Quyền năng Lập trình Agentic, Nay Đã Mở Cửa Cho Tất Cả
16 tháng 4, 2026

Công nghệ
Spotify thắng kiện 322 triệu USD từ nhóm pirate Anna's Archive nhưng đối mặt với bài toán thu hồi
16 tháng 4, 2026
