Lợi ích của ghi nhật ký có cấu trúc so với ghi nhật ký cơ bản


110

Chúng tôi đang xây dựng một ứng dụng mới và tôi muốn bao gồm ghi nhật ký có cấu trúc. Thiết lập lý tưởng của tôi sẽ giống như Serilogmã C # Bunyancủa chúng tôi và cho mã JS của chúng tôi. Những thứ này sẽ ăn vào fluentdvà sau đó có thể đi ra bất kỳ số lượng nào, tôi đã suy nghĩ ban đầu elasticsearch + kibana. Chúng tôi đã có cơ sở dữ liệu MySQL, vì vậy trong thời gian ngắn, tôi quan tâm nhiều hơn đến việc thiết lập Serilog + Bunyan và các nhà phát triển sử dụng nó và chúng tôi có thể đăng nhập vào MySQL trong khi chúng tôi mất thêm một chút thời gian để thông thạo và phần còn lại.

Tuy nhiên, một trong những lập trình viên giàu kinh nghiệm hơn của chúng tôi muốn làm điều gì đó như: log.debug("Disk quota {0} exceeded by user {1}", quota, user);sử dụng log4netvà sau đó chỉ chạy các câu lệnh chọn đối với MySQL như:SELECT text FROM logs WHERE text LIKE "Disk quota";

Điều đó đang được nói, cách tiếp cận nào là tốt hơn và / hoặc những điều chúng ta cần xem xét khi chọn loại hệ thống đăng nhập?


Tôi đồng ý với các chỉnh sửa đã được thực hiện. Tôi không cố gắng để chứng minh điều gì đó với ai đó vì tôi đang cố gắng tìm hiểu lợi ích và sự khác biệt trong ghi nhật ký có cấu trúc so với cơ bản. Trong tâm trí tôi, cấu trúc cho phép chúng tôi linh hoạt hơn nhiều, đặc biệt là trong các nguồn của nhật ký và cách chúng tôi có thể hiển thị dữ liệu của họ. Theo quan điểm của tôi, tôi không thể giải thích tại sao đăng nhập cơ bản và tìm kiếm MySQL tốt hơn / tệ hơn so với ghi nhật ký có cấu trúc.
DTI-Matt

2
Ghi nhật ký có cấu trúc của @ DTI-Matt chỉ là ghi nhật ký cơ bản, chỉ có nó định dạng các đối tượng mà bạn in cho nó - điều mà bạn có thể tự làm bằng cách vượt qua ToString rất dễ dàng. Một khía cạnh quan trọng hơn là cấu hình và quản lý các tệp nhật ký, không phải là một cách định dạng chuỗi trên chuỗi khác, khác là hiệu suất. Nếu nhà phát triển muốn sử dụng log4net (đây là một bản ghi nhật ký tốt), thì sự lựa chọn ser seri của bạn (trông rất tuyệt) là một trong những "giải pháp tìm kiếm vấn đề".
gbjbaanb

@ DTI-Matt Nhìn từ serilog, nó trông rất giống với log4net. log4net xử lý việc tạo các bản ghi có cấu trúc trên cấu hình. Bạn không cần tìm kiếm các thông điệp tường trình vì bạn có thể có thông tin bổ sung được cấu hình và ghi vào bảng. Đồng thời định cấu hình log4net cho fluentd tip ware.org/2014/05/ trên
RubberChickenLeader

Xem ra có một số kẻ ngu ngốc không hiểu ý tưởng của các câu hỏi khái niệm ở đây. hỏi về hướng của các ứng dụng cơ sở dữ liệu trong nỗ lực để xử lý các khả năng ETL của họ v. mã sẽ giúp bạn có một số nhược điểm nghiêm trọng. Tôi cho rằng câu hỏi của bạn cũng sẽ nằm trong khối chặt.
dùng3916597

2
@gbjbaanb Serilog hoạt động giống như log4net khi hiển thị các sự kiện dưới dạng văn bản, nhưng nếu bạn sử dụng định dạng có cấu trúc để lưu trữ nhật ký, nó sẽ liên kết các thuộc tính có tên với các đối số được truyền qua (nghĩa là để hỗ trợ tìm kiếm / lọc mà không cần regex, v.v. ) HTH!
Nicholas Blumhardt

Câu trả lời:


140

Có hai tiến bộ cơ bản với cách tiếp cận có cấu trúc không thể được mô phỏng bằng nhật ký văn bản mà không cần (đôi khi mức độ cực đoan).

Các loại sự kiện

Khi bạn viết hai sự kiện với log4net như:

log.Debug("Disk quota {0} exceeded by user {1}", 100, "DTI-Matt");
log.Debug("Disk quota {0} exceeded by user {1}", 150, "nblumhardt");

Chúng sẽ tạo ra văn bản tương tự:

Disk quota 100 exceeded by user DTI-Matt
Disk quota 150 exceeded by user nblumhardt

Nhưng, liên quan đến xử lý máy móc, chúng chỉ là hai dòng văn bản khác nhau.

Bạn có thể muốn tìm tất cả các sự kiện "vượt quá hạn ngạch đĩa", nhưng trường hợp đơn giản tìm kiếm sự kiện like 'Disk quota%'sẽ rơi xuống ngay khi một sự kiện khác xảy ra như sau:

Disk quota 100 set for user DTI-Matt

Ghi nhật ký văn bản sẽ loại bỏ thông tin ban đầu chúng ta có về nguồn gốc của sự kiện và điều này phải được xây dựng lại khi đọc nhật ký thường với các biểu thức khớp ngày càng phức tạp hơn.

Ngược lại, khi bạn viết hai sự kiện Serilog sau:

log.Debug("Disk quota {Quota} exceeded by user {Username}", 100, "DTI-Matt");
log.Debug("Disk quota {Quota} exceeded by user {Username}", 150, "nblumhardt");

Chúng tạo ra đầu ra văn bản tương tự như phiên bản log4net, nhưng đằng sau hậu trường, "Disk quota {Quota} exceeded by user {Username}" mẫu thông báo được thực hiện bởi cả hai sự kiện.

Với một mức chìm phù hợp, sau này bạn có thể viết các truy vấn where MessageTemplate = 'Disk quota {Quota} exceeded by user {Username}'và nhận chính xác các sự kiện vượt quá hạn ngạch đĩa.

Không phải lúc nào cũng thuận tiện để lưu trữ toàn bộ mẫu thông báo với mọi sự kiện nhật ký, do đó, một số chìm băm mẫu tin nhắn thành một EventTypegiá trị số (ví dụ 0x1234abcd), hoặc, bạn có thể thêm một enricher vào đường dẫn ghi nhật ký để tự làm điều này .

Nó tinh tế hơn sự khác biệt tiếp theo bên dưới, nhưng mạnh mẽ hơn khi xử lý khối lượng nhật ký lớn.

Dữ liệu có cấu trúc

Một lần nữa xem xét hai sự kiện về việc sử dụng không gian đĩa, có thể dễ dàng sử dụng nhật ký văn bản để truy vấn cho một người dùng cụ thể like 'Disk quota' and like 'DTI-Matt'.

Nhưng, chẩn đoán sản xuất không phải lúc nào cũng đơn giản. Hãy tưởng tượng bạn cần tìm các sự kiện vượt quá dung lượng đĩa dưới 125 MB?

Với Serilog, điều này có thể xảy ra ở hầu hết các bồn rửa bằng cách sử dụng một biến thể:

Quota < 125

Xây dựng loại truy vấn này từ một biểu thức thông thường có thể, nhưng nó trở nên mệt mỏi nhanh chóng và thường kết thúc là một biện pháp cuối cùng.

Bây giờ thêm vào đây một loại sự kiện:

Quota < 125 and EventType = 0x1234abcd

Bạn bắt đầu thấy ở đây làm thế nào các khả năng này kết hợp một cách đơn giản để làm cho việc gỡ lỗi sản xuất với nhật ký cảm thấy giống như một hoạt động phát triển hạng nhất.

Một lợi ích nữa, có lẽ không dễ gì ngăn chặn được, nhưng một khi việc gỡ lỗi sản xuất đã được gỡ bỏ khỏi vùng đất của regex, các nhà phát triển bắt đầu coi trọng nhật ký hơn rất nhiều và quan tâm và cân nhắc hơn khi viết chúng. Nhật ký tốt hơn -> ứng dụng chất lượng tốt hơn -> hạnh phúc hơn xung quanh.


4
tôi thích câu trả lời này được viết rất tốt và vì một số lý do tôi không thể giải thích, giữ tôi trên mép ghế của tôi.
jokab

16

Khi bạn đang thu thập nhật ký để xử lý, có thể phân tích cú pháp vào một số cơ sở dữ liệu và / hoặc tìm kiếm thông qua nhật ký được xử lý sau đó, sử dụng ghi nhật ký có cấu trúc giúp cho việc xử lý dễ dàng / hiệu quả hơn. Trình phân tích cú pháp có thể tận dụng cấu trúc đã biết ( ví dụ JSON, XML, ASN.1, bất cứ thứ gì) và sử dụng các máy trạng thái để phân tích cú pháp, trái với các biểu thức thông thường (có thể tốn kém về mặt tính toán (tương đối) để biên dịch và thực thi). Phân tích cú pháp văn bản dạng tự do, như được đề xuất bởi đồng nghiệp của bạn, có xu hướng dựa vào các biểu thức thông thường dựa vào văn bản đó không thay đổi . Điều này có thể làm cho phân tích văn bản dạng tự do khá dễ vỡ ( nghĩa là phân tích cú pháp được kết hợp chặt chẽ với văn bản chính xác trong mã).

Cũng xem xét trường hợp tìm kiếm / tra cứu, ví dụ :

SELECT text FROM logs WHERE text LIKE "Disk quota";

LIKEđiều kiện yêu cầu so sánh với mọi textgiá trị hàng; một lần nữa, điều này tương đối tốn kém về mặt tính toán, đặc biệt là khi sử dụng ký tự đại diện:

SELECT text FROM logs WHERE text LIKE "Disk %";

Với ghi nhật ký có cấu trúc, thông báo nhật ký liên quan đến lỗi đĩa của bạn có thể trông như thế này trong JSON:

{ "level": "DEBUG", "user": "username", "error_type": "disk", "text": "Disk quota ... exceeded by user ..." }

Các trường của loại cấu trúc này có thể ánh xạ khá dễ dàng, ví dụ như tên cột của bảng SQL, điều này có nghĩa là việc tra cứu có thể cụ thể hơn / chi tiết hơn:

SELECT user, text FROM logs WHERE error_type = "disk";

Bạn có thể đặt các chỉ mục trên các cột có giá trị bạn muốn tìm kiếm / tra cứu thường xuyên, miễn là bạn không sử dụng LIKEmệnh đề cho các giá trị cột đó . Bạn càng có thể chia nhỏ thông điệp tường trình của mình thành các danh mục cụ thể, bạn càng có thể nhắm mục tiêu nhiều hơn để tìm kiếm. Ví dụ, ngoài error_typetrường / cột trong ví dụ trên, bạn có thể thực hiện ngay cả "error_category": "disk", "error_type": "quota"hoặc somesuch.

Cấu trúc hơn bạn có trong thông điệp đăng nhập của bạn, hệ thống hơn phân tích / tìm kiếm của bạn (ví dụ như fluentd, elasticsearch, kibana) có thể tận dụng lợi thế của cấu trúc đó, và thực hiện các nhiệm vụ của mình với tốc độ lớn hơn và ít CPU / bộ nhớ.

Hi vọng điêu nay co ich!


1
+1 Muốn thêm rằng đó không chỉ là về tốc độ và hiệu quả. Mức độ liên quan của kết quả tìm kiếm sẽ cao hơn rất nhiều khi sử dụng ghi nhật ký có cấu trúc và do đó "truy vấn có cấu trúc". Nếu không tìm kiếm bất kỳ từ nào xảy ra trong các ngữ cảnh khác nhau sẽ cung cấp cho bạn hàng tấn lượt truy cập không liên quan.
Marjan Venema

1
+1 từ tôi quá, tôi nghĩ móng này nó. Đã thêm một công thức hơi khác bên dưới, để mở rộng trong trường hợp các loại sự kiện.
Nicholas Blumhardt

8

Bạn sẽ không tìm thấy nhiều lợi ích từ việc ghi nhật ký có cấu trúc khi ứng dụng của bạn tạo ra vài trăm thông điệp tường trình mỗi ngày. Bạn chắc chắn sẽ làm được khi bạn có vài trăm thông điệp tường trình mỗi giây đến từ nhiều ứng dụng được triển khai khác nhau.

Liên quan, thiết lập nơi thông báo nhật ký kết thúc trong ELK Stack cũng phù hợp với quy mô khi việc đăng nhập vào SQL trở thành nút cổ chai.

Tôi đã thấy thiết lập "ghi nhật ký và tìm kiếm cơ bản" với SQL select .. likevà biểu thức chính được đẩy đến giới hạn của nó khi nó bị tách ra - có những lỗi tích cực, thiếu sót, mã bộ lọc khủng khiếp với các lỗi knwon khó duy trì và không ai muốn chạm vào, thông điệp tường trình mới không tuân theo các giả định của bộ lọc, miễn cưỡng chạm vào các báo cáo ghi nhật ký trong mã vì chúng phá vỡ các báo cáo, v.v.

Vì vậy, một số gói phần mềm đang nổi lên để giải quyết vấn đề này theo cách tốt hơn. Có Serilog, tôi nghe nói rằng nhóm NLog đang xem nó và chúng tôi đã viết StructuredLogging.Jsoncho Nlog , tôi cũng thấy rằng bản tóm tắt ghi nhật ký lõi mới của ASP.Net "giúp các nhà cung cấp đăng nhập thực hiện ... ghi nhật ký có cấu trúc".

Một ví dụ với StructuredLogging. Bạn đăng nhập vào một logger NLog như thế này:

logger.ExtendedError("Order send failed", new { OrderId = 1234, RestaurantId = 4567 } );

Dữ liệu có cấu trúc này đi đến kibana. Giá trị 1234được lưu trữ trong OrderIdtrường của mục nhật ký. Sau đó, bạn có thể tìm kiếm bằng cú pháp truy vấn kibana, ví dụ: tất cả các mục nhật ký trong đó @LogType:nlog AND Level:Error AND OrderId:1234.

MessageOrderIdbây giờ chỉ là các trường có thể được tìm kiếm cho các kết quả khớp chính xác hoặc không chính xác khi bạn cần hoặc tổng hợp để đếm. Đây là mạnh mẽ và linh hoạt.

Từ thực tiễn tốt nhất StructuredLogging :

Thông điệp được ghi lại nên giống nhau mỗi lần. Nó phải là một chuỗi không đổi, không phải là một chuỗi được định dạng để chứa các giá trị dữ liệu như id hoặc số lượng. Sau đó, nó là dễ dàng để tìm kiếm.

Thông điệp được ghi phải khác biệt tức là không giống với thông điệp được tạo bởi một câu lệnh nhật ký không liên quan. Sau đó, tìm kiếm nó cũng không phù hợp với những thứ không liên quan.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.