Hệ thống ghi nhật ký Android hướng đến khả năng hỗ trợ tiếp cận phổ quát và dễ sử dụng, giả sử rằng tất cả dữ liệu nhật ký đều có thể được biểu thị dưới dạng một chuỗi ký tự. Giả định này phù hợp với hầu hết các trường hợp sử dụng, đặc biệt là khi khả năng đọc nhật ký là yếu tố quan trọng mà không cần đến các công cụ chuyên dụng. Tuy nhiên, trong những môi trường đòi hỏi hiệu suất ghi nhật ký cao và kích thước nhật ký bị hạn chế, thì việc ghi nhật ký dựa trên văn bản có thể không phải là lựa chọn tối ưu. Một trường hợp như vậy là WindowManager, đòi hỏi một hệ thống ghi nhật ký mạnh mẽ có khả năng xử lý nhật ký chuyển đổi cửa sổ theo thời gian thực với tác động tối thiểu đến hệ thống.
ProtoLog là giải pháp thay thế để đáp ứng nhu cầu ghi nhật ký của WindowManager và các dịch vụ tương tự. ProtoLog có những lợi ích chính sau so với logcat:
- Lượng tài nguyên dùng để ghi nhật ký sẽ ít hơn.
- Theo quan điểm của nhà phát triển, việc này cũng giống như khi bạn sử dụng khung ghi nhật ký Android mặc định.
- Hỗ trợ bật hoặc tắt các câu lệnh nhật ký trong thời gian chạy.
- Vẫn có thể ghi vào logcat nếu cần.
Để tối ưu hoá mức sử dụng bộ nhớ, ProtoLog sử dụng cơ chế lưu trữ chuỗi liên tục liên quan đến việc tính toán và lưu một hàm băm đã biên dịch của thông báo. Để cải thiện hiệu suất, ProtoLog thực hiện việc lưu trữ chuỗi trong quá trình biên dịch (đối với các dịch vụ hệ thống), chỉ ghi lại mã nhận dạng thông báo và các đối số trong thời gian chạy. Ngoài ra, khi tạo dấu vết ProtoLog hoặc nhận báo cáo lỗi, ProtoLog sẽ tự động kết hợp từ điển thông báo được tạo tại thời gian biên dịch, cho phép giải mã thông báo từ mọi bản dựng.
Khi sử dụng ProtoLog, thông báo sẽ được lưu trữ ở định dạng nhị phân (proto) trong dấu vết Perfetto. Quá trình giải mã thông báo diễn ra bên trong trace_processor
của Perfetto. Quy trình này bao gồm việc giải mã các thông báo proto nhị phân, dịch mã nhận dạng thông báo thành chuỗi bằng cách sử dụng từ điển thông báo được nhúng và định dạng chuỗi bằng cách sử dụng các đối số động.
ProtoLog hỗ trợ các cấp độ nhật ký giống như android.utils.Log
, cụ thể là: d
, v
, i
, w
, e
, wtf
.
ProtoLog phía máy khách
Ban đầu, ProtoLog chỉ dành cho phía máy chủ của WindowManager, hoạt động trong một quy trình và thành phần duy nhất. Sau đó, nó được mở rộng để bao gồm mã shell WindowManager trong quy trình Giao diện người dùng hệ thống, nhưng việc sử dụng ProtoLog đòi hỏi phải có mã thiết lập nguyên mẫu phức tạp. Hơn nữa, tính năng ghi nhật ký Proto bị hạn chế đối với các quy trình máy chủ hệ thống và Giao diện người dùng hệ thống, khiến việc kết hợp tính năng này vào các quy trình khác trở nên khó khăn và cần phải thiết lập một vùng đệm bộ nhớ riêng cho từng quy trình. Tuy nhiên, ProtoLog hiện đã được cung cấp cho mã phía máy khách, giúp bạn không cần thêm mã khởi tạo.
Không giống như mã dịch vụ hệ thống, mã phía máy khách thường bỏ qua quá trình chuyển đổi chuỗi tại thời gian biên dịch. Thay vào đó, quá trình nội suy chuỗi sẽ diễn ra linh hoạt trong một luồng nền. Do đó, mặc dù ProtoLog ở phía máy khách mang lại những lợi ích tương đương về việc sử dụng bộ nhớ so với ProtoLog trên các dịch vụ hệ thống, nhưng nó có hiệu suất cao hơn một chút và không có lợi thế về việc giảm bộ nhớ của bộ nhớ được ghim của đối tác phía máy chủ.
Nhóm ProtoLog
Thông báo ProtoLog được sắp xếp thành các nhóm gọi là ProtoLogGroups
, tương tự như cách thông báo Logcat được sắp xếp theo TAG
. Các ProtoLogGroups
này đóng vai trò là các cụm thông báo có thể được bật hoặc tắt cùng lúc trong thời gian chạy.
Ngoài ra, chúng kiểm soát việc có nên loại bỏ các thông báo trong quá trình biên dịch hay không và vị trí mà các thông báo đó sẽ được ghi lại (proto, logcat hoặc cả hai). Mỗi ProtoLogGroup
bao gồm các thuộc tính sau:
enabled
: Khi được đặt thànhfalse
, các thông báo trong nhóm này sẽ bị loại trừ trong quá trình biên dịch và không có sẵn trong thời gian chạy.logToProto
: Xác định xem nhóm này có ghi nhật ký bằng định dạng nhị phân hay không.logToLogcat
: Xác định xem nhóm này có ghi nhật ký vào logcat hay không.tag
: Tên của nguồn thông báo được ghi nhật ký.
Mỗi quy trình sử dụng ProtoLog đều phải có một phiên bản ProtoLogGroup
được định cấu hình.
Loại đối số được hỗ trợ
Về nội bộ, các chuỗi định dạng ProtoLog sử dụng android.text.TextUtils#formatSimple(String, Object...)
, nên cú pháp của nó cũng giống như vậy.
ProtoLog hỗ trợ các loại đối số sau:
%b
– boolean%d
,%x
– kiểu số nguyên (ngắn, số nguyên hoặc dài)%f
– loại dấu phẩy động (float hoặc double)%s
– chuỗi%%
– ký tự phần trăm theo nghĩa đen
Các đối tượng sửa đổi về độ rộng và độ chính xác như %04d
và %10b
được hỗ trợ, nhưng argument_index
và flags
thì không.
Sử dụng ProtoLog trong một dịch vụ mới
Cách sử dụng ProtoLog trong một quy trình mới:
Tạo một định nghĩa
ProtoLogGroup
cho dịch vụ này.Khởi tạo định nghĩa trước khi sử dụng lần đầu (ví dụ: khi tạo quy trình):
Protolog.init(ProtologGroup.values());
Sử dụng
Protolog
theo cách tương tự nhưandroid.util.Log
:ProtoLog.v(WM_SHELL_STARTING_WINDOW, "create taskSnapshot surface for task: %d", taskId);
Bật tính năng tối ưu hoá thời gian biên dịch
Để bật ProtoLog trong thời gian biên dịch trong một quy trình, bạn phải thay đổi các quy tắc xây dựng của quy trình đó và gọi tệp nhị phân protologtool
.
ProtoLogTool
là một tệp nhị phân chuyển đổi mã thực hiện việc lưu trữ chuỗi và cập nhật lệnh gọi ProtoLog. Tệp nhị phân này chuyển đổi mọi lệnh gọi ghi nhật ký ProtoLog
như minh hoạ trong ví dụ sau:
ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);
thành:
if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
int protoLogParam0 = value1;
String protoLogParam1 = String.valueOf(value2);
ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 1234560b0100, protoLogParam0, protoLogParam1);
}
Trong ví dụ này, ProtoLog
, ProtoLogImpl
và ProtoLogGroup
là các lớp được cung cấp dưới dạng đối số (có thể được nhập, nhập tĩnh hoặc đường dẫn đầy đủ, không được phép nhập ký tự đại diện) và x
là phương thức ghi nhật ký.
Quá trình chuyển đổi được thực hiện ở cấp nguồn. Một hàm băm được tạo từ chuỗi định dạng, cấp nhật ký và tên nhóm nhật ký, rồi được chèn sau đối số ProtoLogGroup. Mã được tạo thực tế được nội tuyến và một số ký tự dòng mới được thêm vào để giữ nguyên việc đánh số dòng trong tệp.
Ví dụ:
genrule {
name: "wm_shell_protolog_src",
srcs: [
":protolog-impl", // protolog lib
":wm_shell_protolog-groups", // protolog groups declaration
":wm_shell-sources", // source code
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
"--protolog-class com.android.internal.protolog.ProtoLog " +
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
"--viewer-config-file-path /system_ext/etc/wmshell.protolog.pb " +
"--legacy-viewer-config-file-path /system_ext/etc/wmshell.protolog.json.gz " +
"--legacy-output-file-path /data/misc/wmtrace/shell_log.winscope " +
"--output-srcjar $(out) " +
"$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.srcjar"],
}
Tuỳ chọn dòng lệnh
Một trong những ưu điểm chính của ProtoLog là bạn có thể bật hoặc tắt tính năng này trong thời gian chạy. Ví dụ: bạn có thể có tính năng ghi nhật ký chi tiết hơn trong một bản dựng, bị tắt theo mặc định và bật tính năng này trong quá trình phát triển cục bộ để gỡ lỗi một vấn đề cụ thể. Ví dụ: mẫu này được dùng trong WindowManager với các nhóm WM_DEBUG_WINDOW_TRANSITIONS
và WM_DEBUG_WINDOW_TRANSITIONS_MIN
, cho phép ghi nhật ký các loại hiệu ứng chuyển đổi khác nhau, trong đó nhóm trước được bật theo mặc định.
Bạn có thể định cấu hình ProtoLog bằng Perfetto khi bắt đầu một dấu vết.
Bạn cũng có thể định cấu hình ProtoLog cục bộ bằng dòng lệnh adb
.
Lệnh adb shell cmd protolog_configuration
hỗ trợ các đối số sau:
help
Print this help text.
groups (list | status)
list - lists all ProtoLog groups registered with ProtoLog service"
status <group> - print the status of a ProtoLog group"
logcat (enable | disable) <group>"
enable or disable ProtoLog to logcat
Mẹo sử dụng hiệu quả
ProtoLog sử dụng tính năng lưu trữ chuỗi cho cả thông báo và mọi đối số chuỗi được truyền. Điều này có nghĩa là để khai thác nhiều lợi ích hơn từ ProtoLog, các thông báo nên tách các giá trị lặp lại thành các biến.
Ví dụ: hãy xem xét câu lệnh sau:
Protolog.v(MY_GROUP, "%s", "The argument value is " + argument);
Khi được tối ưu hoá tại thời điểm biên dịch, nó sẽ chuyển thành:
ProtologImpl.v(MY_GROUP, 0x123, "The argument value is " + argument);
Nếu ProtoLog được dùng trong mã có đối số A,B,C
:
Protolog.v(MY_GROUP, "%s", "The argument value is A");
Protolog.v(MY_GROUP, "%s", "The argument value is B");
Protolog.v(MY_GROUP, "%s", "The argument value is C");
Protolog.v(MY_GROUP, "%s", "The argument value is A");
Điều này dẫn đến các thông báo sau trong bộ nhớ:
Dict:
0x123: "%s"
0x111: "The argument value is A"
0x222: "The argument value is B"
0x333: "The argument value is C"
Message1 (Hash: 0x123, Arg1: 0x111)
Message2 (Hash: 0x123, Arg2: 0x222)
Message3 (Hash: 0x123, Arg3: 0x333)
Message4 (Hash: 0x123, Arg1: 0x111)
Nếu thay vào đó, câu lệnh ProtoLog được viết như sau:
Protolog.v(MY_GROUP, "The argument value is %s", argument);
Bộ nhớ đệm sẽ kết thúc như sau:
Dict:
0x123: "The argument value is %s" (24 b)
0x111: "A" (1 b)
0x222: "B" (1 b)
0x333: "C" (1 b)
Message1 (Hash: 0x123, Arg1: 0x111)
Message2 (Hash: 0x123, Arg2: 0x222)
Message3 (Hash: 0x123, Arg3: 0x333)
Message4 (Hash: 0x123, Arg1: 0x111)
Trình tự này giúp giảm 35% mức sử dụng bộ nhớ.
Trình xem Winscope
Thẻ trình xem ProtoLog của Winscope cho thấy các dấu vết ProtoLog được sắp xếp theo định dạng bảng. Bạn có thể lọc dấu vết theo cấp nhật ký, thẻ, tệp nguồn (nơi có câu lệnh ProtoLog) và nội dung thông báo. Bạn có thể lọc tất cả các cột. Khi bạn nhấp vào dấu thời gian ở cột đầu tiên, dòng thời gian sẽ chuyển đến dấu thời gian của tin nhắn. Ngoài ra, khi bạn nhấp vào Chuyển đến thời gian hiện tại, bảng ProtoLog sẽ quay lại dấu thời gian được chọn trong dòng thời gian:
Hình 1. Trình xem ProtoLog