18 điểm bởi hongminhee 2025-06-23 | 7 bình luận | Chia sẻ qua WhatsApp

Thư viện vs ứng dụng: yêu cầu ghi log khác nhau về bản chất

  • Ghi log cho ứng dụng: được cấu hình và quản lý tường minh trong môi trường do nhà phát triển trực tiếp kiểm soát
  • Ghi log cho thư viện: được đưa vào dự án của người khác nên cần tôn trọng môi trường và quyền lựa chọn của người dùng
  • Giới hạn của cách làm hiện có: khi áp dụng các logger thiên về ứng dụng như winston, Pino cho thư viện sẽ phát sinh vấn đề mang tính ép buộc
  • Thế tiến thoái lưỡng nan của tác giả thư viện: cung cấp thông tin debug nhưng không gây gánh nặng cho người dùng

Những vấn đề của việc ghi log thư viện hiện nay

  • Hệ sinh thái ghi log phân mảnh: Express dùng DEBUG=express:*, Mongoose dùng mongoose.set('debug', true) và nhiều cách khác nhau
  • Thế tiến thoái lưỡng nan về phụ thuộc: khi dùng các thư viện thiên về ứng dụng như winston, Pino thì sẽ áp đặt lên người dùng những phụ thuộc và cấu hình họ không mong muốn
  • Im lặng vs áp đặt: либо từ bỏ logging hoàn toàn, либо ép người dùng theo một cách logging nhất định
  • Độ phức tạp của dependency injection: là cách tiếp cận tinh vi hơn nhưng làm tăng độ phức tạp của API và gánh nặng cho người dùng

Triết lý "ưu tiên thư viện" của LogTape

  • Kích hoạt có điều kiện: nếu logging chưa được cấu hình thì hoàn toàn không hoạt động, còn khi đã cấu hình thì được quản lý thống nhất
  • Đảm bảo quyền lựa chọn của người dùng: thư viện không ép buộc cách ghi log mà chỉ kích hoạt khi người dùng muốn
  • Zero dependency: dung lượng 5.3KB, loại bỏ rủi ro bảo mật chuỗi cung ứng và tránh xung đột phiên bản
  • Hỗ trợ đầy đủ ESM/CJS: giải quyết vấn đề trong chuỗi tương thích và tối ưu bundle thông qua tree shaking

Những ưu điểm thực tiễn

  • Tối ưu hiệu năng: khi tắt gần như không có overhead, khi bật cho hiệu năng xuất console rất tốt
  • Tách biệt namespace: tránh xung đột với các danh mục phân cấp dạng ["my-lib", "feature"]
  • Thiết kế ưu tiên TypeScript: cung cấp đầy đủ type safety mà không cần thêm gói kiểu dữ liệu
  • Kết nối với hệ thống hiện có: hỗ trợ áp dụng dần dần thông qua adapter cho winston và Pino

Những điểm cần cân nhắc trong thực tế

  • Ý nghĩa của adapter: thừa nhận thực tế rằng đây chưa phải là tiêu chuẩn của hệ sinh thái và là một sự thỏa hiệp thực dụng
  • Cảm hứng từ hệ sinh thái Python: tham chiếu trường hợp thành công của Python với thư viện chuẩn logging được tích hợp thống nhất
  • Cách tiếp cận hướng tới tương lai: được đưa ra như một lựa chọn để cải thiện dần hệ sinh thái thư viện

7 bình luận

 
sunrabbit 2025-06-25

Tôi vẫn chưa hiểu rõ vì sao lại được xem là không hoạt động khi chưa cấu hình.
Khi gọi getLogger thì logger đã được tạo sẵn, và nếu ghi debug thì nó vẫn hoạt động mà.

Theo như tôi xem code thì nó chỉ khiến mọi thứ trông như là không hoạt động thôi,
mà cũng không hề trì hoãn phép xử lý chuỗi,
nên tôi vẫn chưa hiểu “không hoạt động” ở đây khác gì so với các thư viện khác vốn chỉ không in ra khi thiết lập mức log.

 
hongminhee 2025-06-25

Ơ, dù chưa gọi configure()/configureSync() mà log vẫn được ghi à? Nó được ghi ở đâu? Có được in ra màn hình console không?

 
sunrabbit 2025-06-26

À, ý tôi nói ở đây về việc "hoạt động" không phải là log được lưu vào console hay file, mà là liệu hàm có thực sự được thực thi và phát sinh overhead đáng kể hay không.

Có vẻ cũng dễ gây hiểu nhầm thật.

 
sunrabbit 2025-06-26

Tất nhiên, xét đến việc phần overhead chính của logger là system call, thì có thể nói là không phải không có overhead.
Nhưng nếu hỏi đó có phải là điểm khác biệt so với các logger khác hay không, thì cũng khó nói vậy, vì các logger khác cũng hoạt động theo cách tương tự.

 
hongminhee 2025-06-26

À, ra là ý bạn là vậy. Trước hết, khi chạy benchmark với tiêu chuẩn null output thì có vẻ có thể xem như gần như không có overhead. Tuy nhiên, điều tôi cho là quan trọng hơn overhead về hiệu năng là hành vi mặc định có phải là no-op hay không. Từ góc nhìn của tác giả thư viện, dù có ghi log bên trong thư viện đi nữa, nếu khi ứng dụng sử dụng thư viện đó chạy mà log tự ý bị xuất ra console hoặc file thì sẽ rất phiền.

 
sunrabbit 2025-06-26

À, ra là SHOW GN.
Dạo này trong hệ sinh thái, nhiều nơi chọn kiểu logger được inject từ bên ngoài nên có lẽ đó cũng là lý do tôi không thấy đồng cảm lắm.
Vì nếu không được cấu hình thì đương nhiên nó sẽ không hoạt động.
Dù vậy, đây cũng là một interface logger trước giờ chưa có trong hệ sinh thái đó, và vì độ tự do cao nên có vẻ tốt hơn.

Với tiêu chuẩn benchmark mà bạn đưa ra, do nó xuất null output sau khi loại trừ system call
nên tôi nghĩ phần này chắc chắn có thể khác đi tùy theo dạng logger nội bộ.

Ở điểm này thì nó tạo ra khác biệt tới gấp ba lần so với Pino nhỉ.


FYI: Ngoài ra, về kiểu logger được inject từ bên ngoài mà tôi có nhắc tới, chỉ cần nhìn vào OpenAI Node SDK là bạn cũng có thể dễ dàng thấy kiểu nó nhận logger từ bên ngoài rồi xuất ra.