#json-log #json #json-format #log-format #log #json-file #format

bin+lib jlf

将 JSON 日志转换为可读格式的 CLI 工具

13 个版本

0.1.13 2024 年 6 月 25 日
0.1.11 2024 年 6 月 25 日
0.1.4 2024 年 2 月 11 日

命令行界面 中排名 107

每月下载量 33

MIT/Apache 许可

85KB
2K SLoC

jlf

Crates.io MIT licensed

cat ./examples/dummy_logs | jlf

给定以下日志文件

./examples/dummy_logs

{"timestamp": "2024-02-09T07:22:41.439284", "level": "DEBUG", "message": "User logged in successfully", "data": {"user_id": 3175, "session_id": "Nsb3P5mZ7971NFIt", "ip_address": "149.215.200.169", "action": "login", "success": false, "error_code": null}}
{"timestamp": "2024-02-09T07:22:42.439284", "level": "ERROR", "message": "Database connection established", "data": {"user_id": 8466, "session_id": "ZMOXKPna3GbzWz2N", "ip_address": "213.135.167.95", "action": "logout", "success": true, "error_code": null}}
...

它将输出更丰富多彩且可读的日志,如下所示

Screenshot 2024-02-09 at 12 23 12 PM

安装

Cargo

cargo install jlf

手动安装

您也可以克隆仓库并手动安装。

git clone https://github.com/PoOnesNerfect/jlf.git
cd jlf
cargo install --path . --locked

CLI 选项

$ jlf -h

JSON Log Formatter CLI

Usage: jlf [OPTIONS] [FORMAT_STRING]

Arguments:
  [FORMAT_STRING]  [default: "{#log}{#if spans|data}\\n{spans|data:json}{/if}"]

Options:
  -n, --no-color     Disable color output. If output is not a terminal, this is always true
  -c, --compact      Display log with data in a compact format
  -s, --strict       If log line is not valid JSON, then report it and exit, instead of printing the line as is
  -t, --take <TAKE>  Take only the first N lines
  -h, --help         Print help
  -V, --version      Print version

自定义格式化

您可以提供自定义的输出行格式。

cat ./examples/dummy_logs | jlf '{#log}{#if spans|data}\n{spans|data}{/if}'

上述提供的格式是默认格式,所以它将以默认格式输出。

{#log} 是一个函数 log,这是一个方便函数,用于打印基本日志格式。目前,函数功能还处于早期阶段,只有 log 函数可用。log 函数等价于格式 {timestamp:dimmed} {level|lvl:level} {message|msg|body}

{#if spans|data}...{/if} 是一个函数 if,这是一个条件函数,如果字段 spansdata 存在,则打印该块内的内容。更多关于该函数行为的详细信息,请参阅 {#if}

\n 将打印换行。

{spans|data:json} 将以 json 格式打印 spansdata 字段。

{timestamp:dimmed} 表示命令行工具会在 JSON 中查找 timestamp 并以 dimmed 淡色打印。

level|lvl 表示命令行工具会在 JSON 中查找 levellvl 并使用找到的第一个。这种样式也称为 level,它是一种特殊样式,会根据级别(调试 = 绿色,信息 = 青色)着色级别。

{message|msg|body} 表示命令行工具会在 JSON 中查找 messagemsgbody 并使用找到的第一个。

打印整个 JSON

如果您想打印整个 JSON 行,只需使用 {}

cat ./examples/dummy_logs | jlf '{}'

您仍然可以提供样式。

cat ./examples/dummy_logs | jlf '{:compact,fg=green}'

样式

您可以通过在 : 后提供样式来为值提供样式。

cat ./examples/dummy_logs | jlf '{timestamp:bright blue,bg=red,bold} {level|lvl:level} {message|msg|body:fg=bright white}'

如果您有多个样式,可以使用 , 分隔它们,例如 fg=red,bg=blue

您可以选择在 = 前提供样式类型。如果您不提供它,则默认为 fg

可用属性

  • dimmed:使文本变暗
  • bold:使文本加粗
  • fg={color}:设置文本颜色
  • {color}:与 fg={color} 相同
  • bg={color}:设置背景颜色
  • indent={n}:通过 n 个空格缩进值
  • key={color}:设置键的颜色
  • value={color}:设置值的颜色
  • str={color}:设置字符串数据类型的颜色
  • syntax={color}:设置语法字符的颜色
  • json:将 JSON 值作为 JSON 打印;这是默认和唯一可用的格式,因此您不需要指定它
  • compact:在一行中打印
  • level:根据级别着色(调试 = 绿色,信息 = 青色)

{color} 是任何颜色值的占位符。

可用颜色

您可以在colors.md中查看所有可用的颜色。

函数

jlf中的函数以#开头,位于{}内部。

log

log是一个便捷函数,用于打印基本日志格式。

cat ./examples/dummy_logs | jlf '{#log}'

equals

cat ./examples/dummy_logs | jlf '{timestamp:dimmed} {level|lvl:level} {message|msg|body}'

并将打印

2024-02-09T07:22:41.439284 DEBUG User logged in successfully

if

您可以使用{#if field}...{/if}来有条件地打印块内的内容,如果该字段存在。

条件仅检查字段是否存在(或为null)或不存在,但不检查字段的真值。

如果字段存在且值为false,它仍会打印块内的内容。

cat ./examples/dummy_logs | jlf '{#if spans|data}data: {spans|data:json}{/if}'

只有在spansdata字段存在时,才会打印data: { ... }

巧妙技巧

  • 如果行不是JSON,它将直接按原样打印该行。
  • 它会在将数据管道输入文件时删除所有ANSI转义码。

这意味着,您可以直接使用jlf将非JSON日志管道输入文件,而不包含所有的ansi转义码。当您直接将其管道输入到终端时,它仍会像以前一样格式化日志。

很酷,对吧?

实现

JSON解析

程序不能假设传入的JSON日志的数据结构。无法保证管道日志的应用程序使用最佳实践进行日志记录,或者保持一致的结构。

因此,它必须能够动态解析任何JSON日志;这使我们不得不使用serde_json::Value

但我们能做得更好吗?答案是肯定的。

尽管我们无法假设日志的数据结构,但我们仍然可以针对JSON日志的常见特性进行优化。因此,我决定创建一个针对JSON日志优化的自定义JSON解析器。

常见JSON日志的一些特性

以下是我想针对每行日志进行优化的常见JSON日志的一些特性

  1. 每行日志通常不是超级巨大
  2. 日志行通常具有相似的结构
  3. 我们不需要转换数据;我们只需重新格式化它们。

优化

以下是针对上述项目实现的优化

  1. JSON对象被解析为键值对的vec,而不是map。
    • 这样,我们不需要为每个键和值分配内存。
  2. 由于每行JSON日志具有相似的结构,我们可以重复使用已分配的现有vecs。
    • 我们不需要为每行分配内存。
  3. 由于我们不需要转换数据,因此不要验证原始值。
  4. 我们使用日志字符串的&str切片,而不是为每个键和值分配新的String

基准测试

那么,它的性能如何?这是唯一重要的事情。

custom parse time: [987.52 ns 993.59 ns 1.0006 µs]
Found 12 outliers among 100 measurements (12.00%)
9 (9.00%) high mild
3 (3.00%) high severe

serde value parse time: [2.8045 µs 2.8357 µs 2.8729 µs]
Found 8 outliers among 100 measurements (8.00%)
4 (4.00%) high mild
4 (4.00%) high severe

serde structured parse time: [712.16 ns 714.93 ns 717.54 ns]

第一部分是自定义解析,第二部分是将解析为 serde_json::Value 解析,第三部分是将它反序列化为结构化的Rust对象。

时间是解析单行JSON日志所需的时间。

正如我们所见,我们的自定义解析器比 serde_json::Value 解析快约3倍。是的,它仍然比结构化解析慢,但我们的解析器在解析动态JSON数据时仍然非常快。

依赖项

~5–13MB
~150K SLoC