@quyenpv/logger
v1.0.2
Published
MIDA structured logging + trace-context propagation (Pino + AsyncLocalStorage + W3C traceparent). Shared across api/recorder/hm/search/proxy/mcp.
Maintainers
Readme
@quyenpv/logger
Structured logging + trace-context propagation dùng chung cho toàn bộ service MIDA
(api, recorder, hm, search, proxy, mcp).
- Pino — JSON log ra stdout, nhanh.
- AsyncLocalStorage — mang
traceIdtheo luồng async, không phải truyền tay. - W3C
traceparent— propagate xuyên HTTP + RabbitMQ. OTEL-ready. - Schema log khớp
CanonicalEventcủa quyenpv-rca để RCA correlate ngay.
Cài đặt
Package nội bộ. Cài qua private registry (Verdaccio) hoặc git URL:
npm install @quyenpv/logger
# hoặc: npm install git+ssh://git@<host>/quyenpv/quyenpv-logger.gitBiến môi trường
| Env | Ý nghĩa | Ví dụ |
| -------------- | ------------------------------------------------ | ------------ |
| SERVICE_NAME | tên service | api |
| INSTANCE_ID | instance (khớp inventory.yaml RCA) | api-1 |
| SHARD | shard số | 1 |
| NODE_ENV | môi trường | production |
| LOG_LEVEL | debug(mặc định)/info/warn/error/silent | info |
| LOG_HTTP | 0 để tắt access-log HTTP | 1 |
| LOG_QUEUE | 0 để tắt auto-log mỗi message consume | 1 |
| LOGGER | 0 để tắt hẳn (tương thích logger cũ) | 1 |
Prod khuyến nghị
LOG_LEVEL=info. Local xem đẹp:node app.js | npx pino-pretty.HTTP tự log 1 dòng
kind:"http"mỗi request (level info). RabbitMQ tự log 1 dòngkind:"queue"mỗi message khi consume ở level debug (ẩn ở prod info vì queue volume lớn; bật xem bằngLOG_LEVEL=debug). Lỗi trong consumer → levelerror. traceId luôn được gắn.
API phân nhóm theo chức năng
Export được chia 4 namespace để biết ngay dùng gì cho việc gì (không còn trải phẳng):
const { log, http, queue, trace } = require('@quyenpv/logger');| Namespace | Dùng khi | Thành viên |
|---|---|---|
| log | ghi log | logger (pino gốc), debug/info/warn/error/success (drop-in) |
| http | web server / gọi nội bộ | koaTrace, expressTrace, tracedFetch, traceHeaders, setProxyTraceHeaders |
| queue | RabbitMQ | traceConnection, traceChannel, withTrace, wrapConsumer, domainFromJsonBody |
| trace | context lõi | getContext, runWithContext, setContext, createStore, gen*, parse/buildTraceparent |
Hoặc import đúng concern qua subpath:
const { koaTrace } = require('@quyenpv/logger/http');
const { traceConnection } = require('@quyenpv/logger/amqp');
const { getContext } = require('@quyenpv/logger/context');Drop-in thay logger cũ vẫn riêng:
const logger = require('@quyenpv/logger').compat;Các export phẳng (vdrequire('@quyenpv/logger').koaTrace) vẫn còn để tương thích ngược, nhưng code mới nên dùng namespace/subpath ở trên.
Dùng nhanh (theo namespace)
1. Drop-in thay logger cũ (giữ nguyên call-site)
const logger = require('@quyenpv/logger'); // có .debug/.info/.warn/.error/.success
logger.error(__filename, domain, e); // chữ ký y như logger cũ2. Code mới (pino gốc, structured)
const { logger } = require('@quyenpv/logger');
logger.info({ kind: 'job', name: 'reset-quota', count: 12 }, 'done');3. Middleware (đặt ĐẦU TIÊN trong chain)
// Koa: api, recorder, hm, proxy
const { koaTrace } = require('@quyenpv/logger');
app.use(koaTrace());
// Express: search, mcp
const { expressTrace } = require('@quyenpv/logger');
app.use(expressTrace());4. RabbitMQ — propagate traceId qua queue
const { withTrace, wrapConsumer, domainFromJsonBody } = require('@quyenpv/logger');
// publish: thêm withTrace() vào options
channel.sendToQueue(queue, buf, withTrace({ persistent: true }));
channel.publish(exchange, key, buf, withTrace({ persistent: true }));
// consume: bọc handler
channel.consume(queue, wrapConsumer(handler)); // handler(channel,msg) hoặc (msg)
// nếu domain chỉ có trong body:
channel.consume(queue, wrapConsumer(handler, { getDomain: domainFromJsonBody }));5. HTTP nội bộ (KHÔNG dùng cho Shopify/bên thứ 3)
const { tracedFetch, traceHeaders, setProxyTraceHeaders } = require('@quyenpv/logger');
await tracedFetch(internalUrl, { method: 'POST', body });
// hoặc tự gắn header: fetch(url, { headers: traceHeaders(myHeaders) })
// proxy (koa-proxies/http-proxy):
proxyReq: (proxyReq) => setProxyTraceHeaders(proxyReq);ESM (quyenpv-proxy)
import { Logger, koaTrace, withTrace, setProxyTraceHeaders } from '@quyenpv/logger';Schema log
{
"time": "2026-06-19T07:30:45.123Z",
"level": "error",
"service": "api",
"instance": "api-1",
"shard": 1,
"env": "production",
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"parentSpanId": "...",
"domain": "shop.myshopify.com",
"kind": "http",
"filename": "shopify.service.js",
"caller": "getPlan",
"message": "...",
"err": { "type": "Error", "message": "...", "stack": "..." }
}Test
npm test # node --test