Structured Logging with slog

Bài tóm tắt Go 2023 3. Quy mô lớn Độ khó: Dễ

Go · 2023 · 3. Quy mô lớn · Độ khó: Dễ — bài gốc

Bài của đội Go về một API thư viện chuẩn, không phải hệ thống production; giữ lại vì kể cách một hệ sinh thái gom các thư viện log rời rạc về một backend chung.

Bối cảnh

  • Log là nơi đầu tiên người ta tìm khi debug server, nên log nhiều và phải lọc, tìm nhanh. Log có cấu trúc (cặp key-value) phân tích được tin cậy hơn log chữ.
  • Thư viện chuẩn log không có cấu trúc. Hệ sinh thái tự lấp: logrus được hơn 100.000 package dùng, cộng zap, logr, go-kit/log... Chương trình lớn thường kéo vào nhiều thư viện log qua dependency và phải cấu hình từng cái để output cùng nơi, cùng định dạng.

Thay đổi chính

  • Tách frontend và backend: Logger là API gọi, Handler là interface backend. Có sẵn TextHandler (key=value) và JSONHandler; ai cũng viết được handler, kể cả handler bọc handler khác. Mục tiêu không phải thay thư viện bên thứ ba mà cho chúng nói chung một backend (đã có handler cho zap, logr, hclog).
  • Hiệu năng theo dữ liệu thật: khảo sát code mã nguồn mở trên module proxy, hơn 95% lời gọi log có từ 5 thuộc tính trở xuống, phần lớn thuộc vài kiểu phổ biến; benchmark cho đúng các ca đó; phần lợi lớn nhất đến từ giảm cấp phát bộ nhớ. Handler.Enabled cho bỏ sớm sự kiện không cần; WithAttrs/WithGroup cho format trước thuộc tính gắn bằng Logger.With một lần thay vì mỗi lần gọi; LogAttrs + Attr cho đường nóng.
  • Context: một bộ hàm nhận context.Context để handler lấy trace ID; hủy context không chặn việc ghi log.
  • Quy trình thiết kế: 4/2022 quyết định thử; 8/2022 công bố bản thử nghiệm và GitHub discussion (khoảng 300 bình luận trong hai tháng); proposal chính thức hơn 800 bình luận; chấp nhận 15/3/2023; phát hành cùng Go 1.21 ngày 8/8/2023. Bỏ tính năng gắn logger vào context vì gây tranh cãi (phụ thuộc ngầm); giữ cú pháp key-value xen kẽ cho nhẹ dù nhiều người phản đối, thêm vet check để bắt lỗi thiếu key/value.

Vì sao

  • Theo đội Go, đưa log có cấu trúc vào thư viện chuẩn tạo một khung chung cho mọi thư viện log, giải bài toán nhiều logger trong một chương trình mà không bắt ai viết lại.

Bài học cho SWE

  • Khi hệ sinh thái đã phân mảnh, chuẩn hoá interface backend thay vì thay mọi frontend: dữ liệu ra một nơi, code cũ không phải đổi.
  • Tối ưu theo phân phối dùng thật (95% lời gọi ≤ 5 thuộc tính), không theo ca xấu nhất.

Liên quan