20 个版本
0.1.19 | 2021 年 9 月 23 日 |
---|---|
0.1.18 | 2021 年 1 月 4 日 |
0.1.15 | 2020 年 12 月 31 日 |
0.1.11 | 2020 年 1 月 22 日 |
0.1.9 | 2019 年 8 月 22 日 |
#1693 在 命令行工具 中
每月 56 次下载
315KB
7K SLoC
logq - 使用 Rust 实现的命令行工具分析 PartiQL 中的日志文件
此项目处于 alpha 阶段,欢迎 PR。
logq 是一个命令行工具,可轻松通过 PartiQL(兼容 SQL-92)接口分析、查询和聚合 Web 服务器日志文件。目前支持以下格式:
- AWS 经典弹性负载均衡器
- AWS 应用负载均衡器
- AWS S3 访问日志(初步支持)
- Squid 原生格式(初步支持)
未来将支持更多日志格式,并理想情况下可以通过配置进行自定义,如 GoAccess 所做的那样。
安装
cargo install logq
查询平面日志的示例
从日志文件中投影 timestamp
和 backend_and_port
字段,并打印前三个记录。
> logq query 'select timestamp, backend_processing_time from it order by timestamp asc limit 3' --table it:elb=data/AWSELB.log
+-----------------------------------+----------+
| 2015-11-07 18:45:33.007671 +00:00 | 0.618779 |
+-----------------------------------+----------+
| 2015-11-07 18:45:33.054086 +00:00 | 0.654135 |
+-----------------------------------+----------+
| 2015-11-07 18:45:33.094266 +00:00 | 0.506634 |
+-----------------------------------+----------+
在 5 秒时间内汇总发送的字节数。
> logq query 'select t, sum(sent_bytes) as s from it group by time_bucket("5 seconds", timestamp) as t' --table it:elb=data/AWSELB.log
+----------------------------+----------+
| 2015-11-07 18:45:30 +00:00 | 12256229 |
+----------------------------+----------+
| 2015-11-07 18:45:35 +00:00 | 33148328 |
+----------------------------+----------+
以 5 秒为时间范围选择 90% 分位数后端处理时间。
> logq query 'select t, percentile_disc(0.9) within group (order by backend_processing_time asc) as bps from it group by time_bucket("5 seconds", timestamp) as t' --table it:elb=data/AWSELB.log
+----------------------------+----------+
| 2015-11-07 18:45:30 +00:00 | 0.112312 |
+----------------------------+----------+
| 2015-11-07 18:45:35 +00:00 | 0.088791 |
+----------------------------+----------+
要折叠 URL 路径部分,以便它们映射到相同的 Restful 处理器,可以使用 url_path_bucket
> logq query 'select time_bucket("5 seconds", timestamp) as t, url_path_bucket(request, 1, "_") as s from it limit 10' --table it:elb=data/AWSELB.log
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | / |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | /img/_/000000000000000000000000 |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | /favicons/_ |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | /images/_/devices.png |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | /stylesheets/_/font-awesome.css |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | /favicons/_ |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | /mobile/_/register-push |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | /img/_/205/2r1/562e37d9208bee5b70f56836.anim |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | /img/_/300/2r0/54558148eab71c6c2517f1d9.jpg |
+----------------------------+----------------------------------------------+
| 2015-11-07 18:45:30 +00:00 | / |
+----------------------------+----------------------------------------------+
以不同的格式输出,您可以通过 --output
指定格式,目前支持 json
和 csv
。
> logq query --output csv 'select t, sum(sent_bytes) as s from it group by time_bucket("5 seconds", timestamp) as t' --table it:elb=data/AWSELB.log
2015-11-07 18:45:35 +00:00,33148328
2015-11-07 18:45:30 +00:00,12256229
> logq query --output json 'select t, sum(sent_bytes) as s from it group by time_bucket("5 seconds", timestamp) as t' --table it:elb=data/AWSELB.log
[{"t":"2015-11-07 18:45:30 +00:00","s":12256229},{"t":"2015-11-07 18:45:35 +00:00","s":33148328}]
您可以使用图形命令行工具在终端中图形化数据集。例如,termgraph 是柱状图的不错选择
> logq query --output csv 'select backend_and_port, sum(sent_bytes) from it group by backend_and_port' --table it:elb=data/AWSELB.log | termgraph
10.0.2.143:80: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 20014156.00
10.0.0.215:80: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 25390392.00
或者您可以使用 spark 绘制随时间变化的处理时间
> logq query --output csv 'select host_name(backend_and_port) as h, backend_processing_time from it where h = "10.0.2.143"' --table it:elb=data/AWSELB.log | cut -d, -f2 | spark
▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
如果您不清楚执行过程,可以解释查询计划。
> logq explain 'select t, sum(sent_bytes) as s from it group by time_bucket("5 seconds", timestamp) as t'
Query Plan:
GroupBy(["t"], [NamedAggregate { aggregate: Sum(SumAggregate { sums: {} }, Expression(Variable("sent_bytes"), Some("sent_bytes"))), name_opt: Some("s") }], Map([Expression(Function("time_bucket", [Expression(Variable("const_000000000"), None), Expression(Variable("timestamp"), Some("timestamp"))]), Some("t")), Expression(Variable("sent_bytes"), Some("sent_bytes"))], DataSource(Stdin)))
要了解字段,请参阅表模式。
> logq schema elb
+--------------------------+-------------+
| timestamp | DateTime |
+--------------------------+-------------+
| elbname | String |
+--------------------------+-------------+
| client_and_port | Host |
+--------------------------+-------------+
| backend_and_port | Host |
+--------------------------+-------------+
| request_processing_time | Float |
+--------------------------+-------------+
| backend_processing_time | Float |
+--------------------------+-------------+
| response_processing_time | Float |
+--------------------------+-------------+
| elb_status_code | String |
+--------------------------+-------------+
| backend_status_code | String |
+--------------------------+-------------+
| received_bytes | Integral |
+--------------------------+-------------+
| sent_bytes | Integral |
+--------------------------+-------------+
| request | HttpRequest |
+--------------------------+-------------+
| user_agent | String |
+--------------------------+-------------+
| ssl_cipher | String |
+--------------------------+-------------+
| ssl_protocol | String |
+--------------------------+-------------+
| target_group_arn | String |
+--------------------------+-------------+
| trace_id | String |
+--------------------------+-------------+
要了解当前支持的日志格式。
> logq schema
The supported log format
* elb
查询嵌套 jsonl
日志的示例
对于这种 jsonl
格式
{"a": 1, "b": "123", "c": 456.1, "d": [0, 1, 2], "e": {"f": {"g": 1}}}
{"a": 1, "b": "123", "d": [1, 2, 3], "e": {"f": {"g": 2}}}
{"a": 1, "b": "456", "d": [4, 5, 6], "e": {"f": {"g": 3}}}
我们可以像这样查询日志
logq run query 'select x, count(*) as x from it group by d[0] as x' --table it:jsonl=data/structured.log --output=json
[{"x":1},{"x":1},{"x":1}]
logq run query 'select b, e.f.g from it' --table it:jsonl=data/structured.log --output=json
[{"b":"123","g":1},{"b":"123","g":2},{"b":"456","g":3}]
可用函数
函数名称 | 描述 | 输入类型 | 输出类型 |
---|---|---|---|
url_host | 从请求中检索主机 | 请求 | 字符串 |
url_port | 从请求中检索端口 | 请求 | 字符串 |
url_path | 从请求中检索路径 | 请求 | 字符串 |
url_fragment | 从请求中检索片段 | 请求 | 字符串 |
url_query | 从请求中检索查询字符串 | 请求 | 字符串 |
url_path_segments | 从请求中检索路径段 | 请求 | 字符串 |
url_path_bucket | 将路径段映射到给定的字符串 | 请求,整数,字符串 | 字符串 |
time_bucket | 将时间戳放入给定的间隔中 | 字符串,日期时间 | 日期时间 |
date_part | 获取具有给定单位的日期时间的部分 | 字符串,日期时间 | 浮点数 |
host_name | 从主机中检索主机名 | 主机 | 字符串 |
host_port | 从主机中检索端口 | 主机 | 字符串 |
聚合函数
函数名称 | 描述 | 输入类型 |
---|---|---|
avg | 计算数字的平均值 | 整数或浮点数 |
count | 计算记录的数量 | 任何 |
first | 获取记录中的第一个 | 任何 |
last | 获取记录中的最后一个 | 任何 |
min | 获取记录中的最小值 | 任何 |
max | 获取记录中的最大值 | 任何 |
sum | 获取数字的总和 | 整数或浮点数 |
percentile_disc | 计算百分位数的记录 | 浮点数 |
approx_percentile | 计算近似百分位数的记录 | 浮点数 |
动机
通常在日常工作中,当您在解决生产问题时,AWS CloudWatch或内部ELK可能没有提供某些度量标准。然后您会从公司的存档中下载原始访问日志,并编写一个一次性脚本来分析它。然而,这种方法有几个缺点。
- 您花费大量时间解析日志格式,但并未专注于计算帮助解决您生产问题的度量标准。
- 大多数日志格式都很常见,我们最好将其抽象出来,让每个人都能从共享抽象中受益。
- 对于Web服务器日志案例,日志量通常很大,可能是几百MB,甚至几GB。在脚本语言中做这个会让你不耐烦地等待它在本地的运行。
当然,您可以对AWS Athena或ELK这样的分析工具进行微调以分析大量数据,但很多时候您只是想临时分析日志,而不必设置东西或额外付费。此外,现代笔记本电脑/PC实际上足够强大,可以分析GB级的日志量,只是实现不够高效。在Rust中实现logq的目的是为了解决这些不便和担忧。
为什么不用TextQL,或者将空格分隔的字段插入到sqlite中?
TextQL是用Python实现的,对于较小的案例来说还不错。在AWS ELB高流量日志文件的情况下,它通常可以达到GB级的体积,并且速度较慢。此外,TextQL和sqlite都有限制它们的SQL函数和数据类型,这使得像URL、HTTP请求行和User-Agents这样的领域处理变得繁琐。
另一个重要原因是,我们希望支持jsonl
格式(json
的行),这是一种嵌套的半结构化数据,需要扩展SQL以便能够有效地查询。
此外,在Web流量分析的使用案例中,您想要回答的问题可能类似于“在给定的时间段内,对于忽略URL路径段中的user_id的这个Restful端点,第99个百分位数是多少?”拥有提供方便功能的软件来提取或规范化日志中的信息会更容易。
路线图
依赖
~12MB
~225K SLoC