Tùy Biến Log Với TimestampOptions Trong Winston: Hết Thời Nhìn Chuỗi ISO Hại Mắt, Làm Chủ Múi Giờ Hệ Thống
Chào anh em Viblo! 👋
Khi xây dựng một hệ thống Backend Production, việc ghi log (Logging) chuẩn chỉnh được ví như chiếc phao cứu sinh mỗi khi hệ thống xảy ra sự cố. Và một công cụ ghi log "quốc dân" được hầu hết anh em Node.js/TypeScript tin dùng chính là thư viện Winston.
Mặc định, khi anh em bật tính năng thêm thời gian vào log thông qua hàm winston.format.timestamp(), Winston sẽ tự động chèn một chuỗi thời gian trông như thế này: 2026-06-27T09:15:30.123Z. Nhìn thì rất chuẩn ISO đấy, nhưng khi bạn phải ngồi rà soát (trace) hàng vạn dòng log trên Terminal hoặc file .log, chuỗi định dạng này cực kỳ hại mắt và khó đọc nhanh đối với con người. Chưa kể, nếu Server đặt ở nước ngoài chạy múi giờ UTC, bạn lại phải tự ngồi nhẩm cộng thêm 7 tiếng để ra giờ Việt Nam (ICT).
Nếu anh em bấm vào file định nghĩa kiểu mẫu (d.ts) của Winston để tìm cách tối ưu, anh em sẽ bắt gặp một interface chịu trách nhiệm cấu hình việc này:
export interface TimestampOptions {
/**
* Either the format as a string accepted by the [fecha](https://github.com/taylorhakes/fecha)
* module or a function that returns a formatted date. If no format is provided `new
* Date().toISOString()` will be used.
*/
format?: string | (() => string);
/**
* The name of an alias for the timestamp property, that will be added to the `info` object.
*/
alias?: string;
}
Hãy cùng mình mổ xẻ hai thuộc tính tùy chọn (format và alias) cực kỳ quyền lực này để biến những dòng log khô khan trở nên trực quan, thân thiện chuẩn Senior nhé!
1. Bản chất của TimestampOptions trong luồng ghi Log
Trong kiến trúc của Winston, log dữ liệu là một đối tượng info dạng JSON chạy qua một đường ống (Pipeline) gồm nhiều bộ lọc định dạng (Formats). Khi bạn cấu hình timestamp(), Winston sẽ tiêm ngầm một thuộc tính vào object log trước khi in ra màn hình hoặc ghi vào file.
2. Thao tác thuộc tính format: Sức mạnh định dạng thời gian linh hoạt
Thuộc tính format có kiểu dữ liệu là một Union Type khá thú vị: string | (() => string). Điều này mở ra hai cách tiếp cận cấu hình tùy theo mức độ phức tạp của dự án:
Cách A: Truyền vào một chuỗi mẫu (String Pattern)
Winston không tự viết thuật toán parse thời gian mà họ sử dụng thư viện Fecha (một thư viện siêu nhẹ thay thế cho Moment.js). Nếu bạn truyền vào một string, Fecha sẽ biên dịch chuỗi đó thành các mốc thời gian tương ứng:
- YYYY: Năm (4 chữ số)
- MM: Tháng (01 - 12)
- DD: Ngày (01 - 31)
- HH: Giờ (24h)
- mm: Phút
- ss: Giây
// Định dạng gọn gàng, dễ đọc bằng mắt thường
const options: TimestampOptions = {
format: 'YYYY-MM-DD HH:mm:ss'
};
// 👉 Kết quả dòng log sinh ra: 2026-06-27 16:15:30
Cách B: Truyền vào một Hàm khởi tạo (Custom Function)
Đây mới là vũ khí tối thượng giúp giải quyết bài toán lệch múi giờ của máy chủ Cloud. Nếu bạn truyền vào một hàm trả về chuỗi () => string, Winston sẽ thực thi hàm này cho mỗi dòng log sinh ra. Bạn có thể dùng Intl.DateTimeFormat của JavaScript native để ép hệ thống log luôn hiển thị theo giờ Việt Nam (Asia/Ho_Chi_Minh), bất chấp việc server đang đặt ở Mỹ hay Singapore.
const options: TimestampOptions = {
format: () => {
return new Date().toLocaleString('vi-VN', {
timeZone: 'Asia/Ho_Chi_Minh',
hour12: false
});
}
};
// 👉 Kết quả: 16:15:30, 27/06/2026 (Luôn chuẩn giờ Việt Nam)
Nếu bạn bỏ trống không cấu hình thuộc tính format, Winston sẽ tự động thực thi đoạn mã fallback mặc định: new Date().toISOString().
3. Thao tác thuộc tính alias: "Thay tên đổi họ" cho thuộc tính thời gian
Mặc định, khi chèn thời gian vào log, Winston sẽ tạo ra một key tên là "timestamp" nằm trong object dữ liệu: {"message": "Hello", "timestamp": "..."}.
Tuy nhiên, khi làm dự án thực tế, hệ thống log của bạn không chạy độc lập mà thường được đẩy về các công cụ quản lý tập trung (Log Aggregators) như Elasticsearch (ELK Stack), Grafana Loki, hay Datadog. Mỗi công cụ này lại có một quy ước đặt tên trường thời gian khác nhau để tự động nhóm và vẽ biểu đồ. Ví dụ, Elasticsearch thích tên trường là @timestamp, còn một số hệ thống khác lại thích đặt tên ngắn gọn là time hoặc at.
Thuộc tính alias sinh ra để bạn giải quyết xung đột chuẩn hóa dữ liệu đó mà không cần viết thêm Middleware chỉnh sửa Object:
const options: TimestampOptions = {
format: 'YYYY-MM-DD HH:mm:ss',
alias: 'loggedAt' // Đổi tên key từ 'timestamp' thành 'loggedAt'
};
Cấu trúc JSON Log bắn ra hệ thống sẽ thay đổi chớp nhoáng:
{
"level": "info",
"message": "Kết nối thành công Redis bộ đệm!",
"loggedAt": "2026-06-27 16:15:30"
}
4. Cấu hình hoàn chỉnh một Logger chuẩn Senior áp dụng TimestampOptions
Dưới đây là đoạn code thực chiến giúp anh em kết hợp toàn bộ các kiến thức trên vào việc cấu hình một bộ Winston Logger chuẩn chỉ, an toàn và dễ đọc:
import winston from 'winston';
const { combine, timestamp, json, colorize, printf } = winston.format;
// 1. Cấu hình TimestampOptions tối ưu cho mắt nhìn và múi giờ
const timestampConfig: winston.TimestampOptions = {
format: () => {
return new Date().toLocaleString('vi-VN', {
timeZone: 'Asia/Ho_Chi_Minh',
hour12: false,
});
},
alias: 'time', // Đổi tên thành 'time' cho gọn cấu trúc JSON
};
// 2. Khởi tạo Logger
const logger = winston.createLogger({
level: 'info',
format: combine(
timestamp(timestampConfig),
json() // Xuất định dạng JSON phục vụ cho môi trường Production đẩy về ELK/Loki
),
transports: [
// In ra Console phục vụ cho việc debug Local mượt mà
new winston.transports.Console({
format: combine(
colorize(),
printf(({ level, message, time }) => {
// Destructuring lấy trường 'time' đã cấu hình qua alias
return `[${time}] [${level}]: ${message}`;
})
),
}),
],
});
export default logger;
Đúc kết lại
Interface TimestampOptions tuy nhỏ bé nhưng lại thể hiện sự tinh tế của các kỹ sư thiết kế thư viện Winston khi cung cấp khả năng tùy biến định dạng và cấu trúc dữ liệu log rất sâu. Việc làm chủ thuộc tính này giúp bạn kiểm soát hoàn toàn tính nhất quán của mốc thời gian trên hệ thống, giúp đội ngũ DevOps và Developer dễ dàng bắt tay phối hợp trace bug một cách nhanh chóng và chính xác nhất.
Chúc anh em tối ưu cấu hình log thành công! Happy Coding!
All rights reserved