+1

Ghi Log vào File trong Node.js với LogLayer

Ghi log vào file là một phần quan trọng trong việc giám sát và debug ứng dụng trong môi trường production. Trong bài viết này, chúng ta sẽ tìm hiểu cách quản lý log trong ứng dụng Node.js một cách hiệu quả bằng cách sử dụng LogLayer cùng với Log File Rotation Transport, một thư viện logging mạnh mẽ và linh hoạt.

LogLayer là gì?

LogLayer là một thư viện logging hiện đại, được thiết kế ưu tiên cho TypeScript, cung cấp một API rõ ràng và trực quan để ghi log có cấu trúc. Nó hoạt động như một lớp trung gian trên các thư viện logging phổ biến như winston hoặc pino.

Tại sao nên sử dụng LogLayer để ghi log vào file?

  • Tự động xoay vòng log theo dung lượng hoặc thời gian để tránh log file quá lớn.
  • Hỗ trợ nén log để tiết kiệm dung lượng lưu trữ.
  • Cơ chế batching giúp cải thiện hiệu suất ghi log.
  • Định dạng log linh hoạt, dễ đọc và phân tích.
  • Tự động dọn dẹp log cũ, giúp duy trì hệ thống logging sạch sẽ và tối ưu.

Để bắt đầu

Đầu tiên, cài đặt các gói cần thiết:

npm install loglayer @loglayer/transport-log-file-rotation serialize-error

Thiết lập ghi tệp Log cơ bản

Sau đây là một ví dụ cơ bản về cách thiết lập ghi tệp Log:

import { LogLayer } from "loglayer";
import { LogFileRotationTransport } from "@loglayer/transport-log-file-rotation";
import { serializeError } from "serialize-error";

const logger = new LogLayer({
  errorSerializer: serializeError,
  transport: [
    new LogFileRotationTransport({
      filename: "./logs/app.log"
    }),
  ],
});

// Start logging!
logger.withMetadata({
  port: 3000,
}).info("Application started");

logger
  .withError(new Error("Something went wrong"))
  .error("An error occurred");

Thêm ngữ cảnh và siêu dữ liệu

LogLayer cung cấp một số cách để làm phong phú thêm thông tin cho nhật ký của bạn:

1. Sử dụng ngữ cảnh

Bối cảnh là dữ liệu liên tục áp dụng cho tất cả các mục nhật ký tiếp theo:

import { hostname } from "node:os";

const logger = new LogLayer({
  transport: [
    new LogFileRotationTransport({
      filename: "./logs/app.log"
    }),
  ],
});

// Add context that will be included in all subsequent logs
const contextLogger = logger.withContext({
  hostname: hostname(),
  environment: process.env.NODE_ENV,
  version: process.env.npm_package_version
});

contextLogger.info("Server starting");  // Will include context
contextLogger.error("Connection failed");  // Will include same context

2. Sử dụng siêu dữ liệu

Siêu dữ liệu là dữ liệu một lần chỉ áp dụng cho mục nhật ký hiện tại:

// Add metadata for a single log entry
logger.withMetadata({
  requestId: "123",
  duration: 45,
  statusCode: 200
}).info("Request processed");

// Different metadata for different log entries
logger.withMetadata({
  userId: "user-456",
  action: "login"
}).info("User logged in");

// Combine context and metadata
const userLogger = logger.withContext({ service: "user-service" });

userLogger.withMetadata({
  duration: 123,
  status: "success"
}).info("Operation completed");

3. Dữ liệu tĩnh trong vận chuyển

Bạn cũng có thể cấu hình dữ liệu tĩnh ở cấp độ truyền tải:

const logger = new LogLayer({
  transport: [
    new LogFileRotationTransport({
      filename: "./logs/app.log",
      staticData: {
        hostname: hostname(),
        environment: process.env.NODE_ENV,
        version: process.env.npm_package_version
      }
    }),
  ],
});

Điều này sẽ tạo ra các bản ghi như sau:

{
  "level": "info",
  "message": "Request processed",
  "timestamp": "2024-01-17T12:34:56.789Z",
  "hostname": "prod-server-1",
  "environment": "production",
  "version": "1.0.0",
  "requestId": "123",
  "duration": 45,
  "statusCode": 200
}

Cấu hình nâng cao

1. Nhật ký luân phiên hàng ngày

Đối với các ứng dụng cần xoay vòng nhật ký hàng ngày:

const logger = new LogLayer({
  transport: [
    new LogFileRotationTransport({
      filename: "./logs/app-%DATE%.log",
      frequency: "daily",
      dateFormat: "YMD",
      compressOnRotate: true,
      maxLogs: "7d" // Keep logs for 7 days
    }),
  ],
});

2. Xoay dựa trên kích thước

Đối với các ứng dụng có khối lượng lớn, xoay vòng dựa trên kích thước tệp:

const logger = new LogLayer({
  transport: [
    new LogFileRotationTransport({
      filename: "./logs/app.log",
      size: "10M", // Rotate when file reaches 10 MB
      maxLogs: 5, // Keep last 5 log files
      compressOnRotate: true
    }),
  ],
});

3. Tối ưu hóa hiệu suất với việc xử lý hàng loạt

Đối với các ứng dụng có thông lượng cao, hãy bật chế độ xử lý theo đợt để giảm I/O đĩa:

const logger = new LogLayer({
  transport: [
    new LogFileRotationTransport({
      filename: "./logs/app.log",
      batch: {
        size: 1000,    // Write after 1000 logs are queued
        timeout: 5000  // Or after 5 seconds, whichever comes first
      }
    }),
  ],
});

Best pratice tốt nhất

1. Sử dụng tính năng xoay vòng

Luôn cấu hình xoay vòng nhật ký để tránh các vấn đề về dung lượng đĩa:

   new LogFileRotationTransport({
     filename: "./logs/app-%DATE%.log",
     frequency: "daily",
     maxLogs: "30d",
     compressOnRotate: true
   })

2. Bật tính năng xử lý theo lô cho khối lượng lớn

Đối với các ứng dụng có khối lượng nhật ký lớn:

   new LogFileRotationTransport({
     filename: "./logs/app.log",
     batch: {
       size: 1000,
       timeout: 5000
     }
   })

3. Phân tách các bản ghi theo mối quan tâm

Sử dụng các phương thức vận chuyển khác nhau cho các loại bản ghi khác nhau:

   const logger = new LogLayer({
     transport: [
       new LogFileRotationTransport({
         filename: "./logs/app.log"
       }),
       new LogFileRotationTransport({
         filename: "./logs/errors.log",
         levelMap: {
           error: "ERROR",
           fatal: "FATAL"
         }
       })
     ],
   });

4. Sử dụng ngữ cảnh và siêu dữ liệu một cách thích hợp

   // Use context for values that remain constant
   const serviceLogger = logger.withContext({
     service: "user-service",
     version: "1.0.0"
   });

   // Use metadata for request-specific information
   serviceLogger.withMetadata({
     requestId: "req-123",
     duration: 45,
     statusCode: 200
   }).info("Request completed");

Xử lý lỗi

Việc tuần tự hóa lỗi đúng cách rất quan trọng khi gỡ lỗi:

import { serializeError } from "serialize-error";

const logger = new LogLayer({
  errorSerializer: serializeError,
  transport: [
    new LogFileRotationTransport({
      filename: "./logs/app.log"
    }),
  ],
});

try {
  throw new Error("Something went wrong");
} catch (error) {
  logger.withError(error).error("Operation failed");
}

Kết luận

LogLayer cung cấp giải pháp mạnh mẽ và linh hoạt cho việc ghi nhật ký dựa trên tệp trong các ứng dụng Node.js. Với các tính năng như tự động xoay, nén và xử lý hàng loạt, giải pháp này giúp bạn duy trì nhật ký sạch và hiệu quả đồng thời đảm bảo bạn không bỏ lỡ thông tin quan trọng để gỡ lỗi và giám sát.

Hy vọng các bạn thấy thông tin trong bài hữu ích!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí