Node.js Logging ve Monitoring: Winston ve Pino

Node.js Logging ve Monitoring: Winston ve Pino
Logging, uygulamanızın davranışını anlamanın, hataları teşhis etmenin ve performansı izlemenin en temel yoludur. Node.js ekosisteminde Winston ve Pino, en yaygın kullanılan logging kütüphaneleridir. Bu rehberde her iki kütüphaneyi derinlemesine inceleyecek, yapılandırma, log seviyeleri, transport mekanizmaları ve production-ready monitoring stratejilerini ele alacağız.
1. Neden Profesyonel Logging?
console.log geliştirme ortamında yeterli olabilir ancak production uygulamalarında ciddi eksiklikler yaratır:
- Log seviyeleri yok (debug, info, warn, error ayrımı)
- Yapılandırılmış (structured) log formatı yok
- Dosyaya veya harici servise yazma desteği yok
- Log rotasyonu ve filtreleme imkanı yok
- Performans sorunları yaratabilir
2. Winston ile Logging
Winston, Node.js'in en popüler ve en çok kullanılan logging kütüphanesidir. Zengin özellik seti ve esnekliği ile öne çıkar.
# Kurulum
npm install winston
Temel Yapılandırma
// logger.js
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: 'api-service' },
transports: [
// Hata loglarını ayrı dosyaya yaz
new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
// Tüm logları combined dosyasına yaz
new winston.transports.File({
filename: 'logs/combined.log',
maxsize: 5242880,
maxFiles: 10,
}),
],
});
// Development ortamında konsola da yaz
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
}));
}
module.exports = logger;
Winston Kullanımı
const logger = require('./logger');
// Farklı log seviyeleri
logger.error('Veritabanı bağlantısı başarısız', { host: 'db.example.com', port: 5432 });
logger.warn('API rate limit yaklaşıyor', { remaining: 10 });
logger.info('Kullanıcı giriş yaptı', { userId: 123, ip: '192.168.1.1' });
logger.debug('SQL sorgusu çalıştırıldı', { query: 'SELECT * FROM users', duration: '45ms' });
// Hata objeleri ile
try {
throw new Error('Bir şeyler ters gitti');
} catch (error) {
logger.error('İşlem başarısız', { error: error.message, stack: error.stack });
}
Custom Format ve Transport
const winston = require('winston');
// Custom format
const customFormat = winston.format.printf(({ level, message, timestamp, ...meta }) => {
const metaStr = Object.keys(meta).length ? JSON.stringify(meta) : '';
return `${timestamp} [${level.toUpperCase()}]: ${message} ${metaStr}`;
});
// HTTP transport — harici servise gönderme
const httpTransport = new winston.transports.Http({
host: 'log-server.example.com',
port: 443,
path: '/logs',
ssl: true,
});
// Daily rotate file
const DailyRotateFile = require('winston-daily-rotate-file');
const dailyTransport = new DailyRotateFile({
filename: 'logs/app-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d',
zippedArchive: true,
});
3. Pino ile Logging
Pino, performans odaklı bir logging kütüphanesidir. Winston'a kıyasla 5-10 kat daha hızlıdır ve JSON formatında çıktı üretir.
# Kurulum
npm install pino pino-pretty
Temel Yapılandırma
// logger.js
const pino = require('pino');
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard' } }
: undefined,
base: { service: 'api-service', env: process.env.NODE_ENV },
timestamp: pino.stdTimeFunctions.isoTime,
redact: ['req.headers.authorization', 'password', 'creditCard'],
});
module.exports = logger;
Pino Kullanımı
const logger = require('./logger');
// Temel kullanım
logger.info('Sunucu başlatıldı');
logger.info({ port: 3000 }, 'Sunucu dinleniyor');
logger.error({ err: new Error('DB bağlantı hatası') }, 'Veritabanı hatası');
// Child logger — bağlam ekleme
const requestLogger = logger.child({ requestId: 'req-123', userId: 456 });
requestLogger.info('İstek alındı');
requestLogger.info('Veritabanı sorgusu tamamlandı');
// Her log otomatik olarak requestId ve userId içerir
Express ile Pino
# Kurulum
npm install pino-http
const express = require('express');
const pinoHttp = require('pino-http');
const logger = require('./logger');
const app = express();
app.use(pinoHttp({
logger,
customLogLevel: function (req, res, err) {
if (res.statusCode >= 500) return 'error';
if (res.statusCode >= 400) return 'warn';
return 'info';
},
customSuccessMessage: function (req, res) {
return `${req.method} ${req.url} tamamlandı`;
},
serializers: {
req: (req) => ({
method: req.method,
url: req.url,
query: req.query,
}),
res: (res) => ({
statusCode: res.statusCode,
}),
},
}));
app.get('/api/users', (req, res) => {
req.log.info('Kullanıcı listesi istendi');
res.json([]);
});
4. Winston vs Pino Karşılaştırma
| Özellik | Winston | Pino |
|---|---|---|
| Performans | Orta | Çok yüksek (5-10x) |
| Format | Esnek (JSON, text, custom) | JSON-first |
| Transport | Dahili (file, http, console) | Harici worker thread |
| Öğrenme eğrisi | Kolay | Kolay |
| Ekosistem | Geniş | Büyüyen |
| Redaction | Manuel | Dahili |
5. Structured Logging Best Practices
// KÖTÜ — yapısız log
logger.info('Kullanıcı 123 sipariş 456 oluşturdu, toplam: 99.99 TL');
// İYİ — yapılandırılmış log
logger.info({
event: 'order_created',
userId: 123,
orderId: 456,
total: 99.99,
currency: 'TRY',
}, 'Sipariş oluşturuldu');
// Request context ile middleware
function requestContext(req, res, next) {
const requestId = req.headers['x-request-id'] || crypto.randomUUID();
req.log = logger.child({ requestId, method: req.method, path: req.path });
res.setHeader('x-request-id', requestId);
next();
}
6. Monitoring ve Alerting
Logları toplamak ve analiz etmek için popüler araçlar ve entegrasyonlar:
// Pino ile Elasticsearch entegrasyonu
// npm install pino-elasticsearch
const transport = pino.transport({
target: 'pino-elasticsearch',
options: {
index: 'app-logs',
node: 'http://localhost:9200',
flushBytes: 1000,
},
});
const logger = pino(transport);
// Winston ile Datadog
// npm install winston-datadog
const DatadogTransport = require('winston-datadog');
logger.add(new DatadogTransport({
apiKey: process.env.DATADOG_API_KEY,
hostname: 'my-app',
service: 'api-service',
}));
Healthcheck Endpoint
app.get('/health', async (req, res) => {
const health = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
checks: {},
};
try {
await db.ping();
health.checks.database = 'ok';
} catch (err) {
health.status = 'degraded';
health.checks.database = 'fail';
logger.error({ err }, 'Healthcheck: DB bağlantı hatası');
}
const statusCode = health.status === 'ok' ? 200 : 503;
res.status(statusCode).json(health);
});
redact veya Winston'ın custom format özelliğini kullanarak bu verileri maskeleyeyin.
Sonuç
Profesyonel logging, production uygulamalarının olmazsa olmazıdır. Winston esnek ve zengin özellik setiyle genel amaçlı projelere, Pino ise üstün performansıyla yüksek trafikli uygulamalara uygundur. Yapılandırılmış log formatı, log seviyeleri, context ekleme ve hassas veri maskeleme gibi pratikleri uygulamak hata ayıklama sürecinizi dramatik şekilde hızlandıracaktır. Loglarınızı Elasticsearch, Datadog veya benzeri platformlarla merkezileştirerek monitoring ve alerting altyapınızı kurmayı ihmal etmeyin.