Links#
https://github.com/pinojs/pino
https://getpino.io/
https://kubernetes.io/docs/concepts/cluster-administration/logging/
https://pm2.io/docs/runtime/guide/log-management/
https://man7.org/linux/man-pages/man8/logrotate.8.html
1. Important Points#
logging rules:
production logs should be structured JSON
container app should write to stdout/stderr
include service / env / request_id / trace_id
do not log password / token / cookie / authorization
error log should include stack
log level must be configurable
2. Log Levels#
| Level |
Use Case |
debug |
local troubleshooting |
info |
business/runtime events |
warn |
recoverable abnormal condition |
error |
request/dependency failed |
fatal |
process cannot continue |
3. Logging Library Setup#
npm install pino
npm install -D pino-pretty
import pino from "pino";
const isLocal = process.env.NODE_ENV === "local";
export const logger = pino({
level: process.env.LOG_LEVEL ?? "info",
base: {
service: process.env.SERVICE_NAME ?? "order-api",
env: process.env.NODE_ENV ?? "local"
},
redact: {
paths: [
"req.headers.authorization",
"req.headers.cookie",
"password",
"token",
"*.password",
"*.token"
],
censor: "[REDACTED]"
},
transport: isLocal
? {
target: "pino-pretty",
options: {
colorize: true,
translateTime: "SYS:standard"
}
}
: undefined
});
4. Request / Context Logging#
import { randomUUID } from "node:crypto";
import { logger } from "./logger.js";
export function requestLogger(req, res, next) {
const start = Date.now();
const requestId = req.header("x-request-id") ?? randomUUID();
res.setHeader("x-request-id", requestId);
req.log = logger.child({
request_id: requestId,
method: req.method,
path: req.path
});
res.on("finish", () => {
req.log.info({
status_code: res.statusCode,
duration_ms: Date.now() - start
}, "request completed");
});
next();
}
5. Error Logging#
export function errorHandler(error, req, res, _next) {
if (error.name === "AppError") {
req.log.warn({
error_code: error.code,
status_code: error.statusCode
}, error.message);
res.status(error.statusCode).json({
error: {
code: error.code,
message: error.message
}
});
return;
}
req.log.error({ err: error }, "unexpected error");
res.status(500).json({
error: {
code: "INTERNAL_ERROR",
message: "internal server error"
}
});
}
{
"level": 30,
"time": 1790599200000,
"service": "order-api",
"env": "prod",
"request_id": "req-1001",
"method": "POST",
"path": "/orders",
"status_code": 201,
"duration_ms": 18,
"msg": "request completed"
}
7. Sensitive Data#
never log:
password
token
authorization header
cookie
private key
credit card
8. Docker#
container logging:
write logs to stdout/stderr
Docker logging driver handles collection and rotation
services:
order-api:
image: order-api:1.0.0
logging:
driver: json-file
options:
max-size: "100m"
max-file: "5"
9. Kubernetes#
kubectl logs deploy/order-api
kubectl logs deploy/order-api --previous
collection:
Fluent Bit / Fluentd / Vector / OpenTelemetry Collector
send to Loki / Elasticsearch / OpenSearch / CloudWatch Logs
10. VM / systemd#
[Service]
User=app
WorkingDirectory=/opt/order-api
Environment=NODE_ENV=prod
Environment=LOG_LEVEL=info
ExecStart=/usr/bin/node /opt/order-api/src/main.js
Restart=always
journalctl -u order-api -f
11. Log Rotation#
/var/log/order-api/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
copytruncate
create 0640 app app
}
sudo logrotate -d /etc/logrotate.d/order-api
sudo logrotate -f /etc/logrotate.d/order-api
12. Process Manager#
pm2 start src/main.js --name order-api
pm2 logs order-api
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 100M
pm2 set pm2-logrotate:retain 14
pm2 set pm2-logrotate:compress true
13. Cloud Logging#
| Platform |
Common Target |
| AWS ECS / EKS |
CloudWatch Logs / FireLens |
| GCP GKE / Cloud Run |
Cloud Logging |
| Azure AKS / App Service |
Azure Monitor / Log Analytics |
| Alibaba ACK |
Simple Log Service |
| Self-managed K8S |
Loki / Elasticsearch / OpenSearch |
14. Production Checklist#
application:
structured JSON logs
request_id / trace_id included
log level configurable
secrets redacted
unexpected errors include stack
runtime:
container writes stdout/stderr
VM file logs use logrotate
centralized log backend configured