转换nginx日志模式_DevOps训练营 - Nginx日志加工分析

f424ea088a37a17185717a4d61add7e7.png

数据加工服务简介

数据加工服务是阿里云SLS推出的面向日志ETL处理的服务,主要解决数据加工过程中转换、过滤、分发、富化等场景。

8f2c8735a0a5c8ab210367d8d07d4a81.png

接下来,我们以nginx日志解析为例, 帮助大家快速入门阿里云日志服务的数据加工。

用于实验的Nginx日志

假设我们通过极简模式采集了Nginx默认日志。默认的nginx 日志format如下

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                     '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

通过使用极简模式采集Nginx日志,样例如下:

5b74faa4d2e5a818d02a73092cd21b76.png

数据加工交互界面

点击 准备数据加工的Logstore,在查询栏的上方,有一个“数据加工”的开关,打开

052dd1f6162385e288cc732d7f6534b7.png

数据加工的代码编辑、预览、发布相关界面如下图:

bae846c7a79339d431a5a82ab14ac1a2.png

一般情况下,我们都可以使用快速预览模式,如果我们的数据加工涉及到使用mysql、oss等维表,可以使用高级预览做实际预览测试。

对Nginx日志进行数据加工 (Step by Step)

Step1. 使用正则抽取基础字段

对于极简模式采集的日志,内容都在一个字段叫content的字段里,不利于我们做分析。可以通过数据加工正则函数,抽取nginx日志里的字段,使用到的加工函数如下:

# 用于将源字段里的内容,通过正则捕获组抽取出对应的字段
e_regex("源字段", "正则或有命名捕获正则", "目标字段名或数组(可选)")

针对Nginx日志,使用以下语句进行正则抽取

# 通用字段抽取
e_regex("content",'(?<remote_addr>[0-9:.]*) - (?<remote_user>[a-zA-Z0-9-_]*) [(?<local_time>[a-zA-Z0-9/ :-+]*)] "(?<request>[^"]*)" (?<status>[0-9]*) (?<body_bytes_sent>[0-9-]*) "(?<refer>[^"]*)" "(?<http_user_agent>[^"]*)"')

通过正则抽取以后,可以看到日志的字段增加了refer、remote_addr、remote_user、request等字段。

14a84684ab06c7654a64cce46e7ced52.png

Step2. 处理时间字段

当前提取到的localtime不易读,我们把它解析成易读的格式,会用到的以下数据加工函数:

# 用于设置字段值
e_set("字段名", "固定值或表达式函数")

# 将时间字符串解析为日期时间对象
dt_strptime('值如v("字段名")', "格式化字符串")

# 将日期时间对象按照指定格式转换为字符串
dt_strftime(日期时间表达式, "格式化字符串")

实现思路,先通过 dt_strptime 将local_time的时间转化为日期时间对象,然后再通过dt_strftime将日期时间对象转化为标准的日期时间字符串。 针对Nginx local_time的转化,使用如下数据加工语句:

e_set("local_time", dt_strftime(dt_strptime(v("local_time"),"%d/%b/%Y:%H:%M:%S %z"),"%Y-%m-%d %H:%M:%S"))

实现效果如下:

1b7cd3886e14d7e054597f8e08f3b951.png

Step3. 解析request uri

可以看到request字段由 METHOD URI VERSION组成,我们希望对 requst字段进行抽取,获取到请求的METHOD、URI以及VERSION,并且将请求URI中的请求的参数变成字段,方便后续进行查询。可以用以下函数来做实现

# 使用正则将request uri抽取
e_regex("源字段名", "正则或有命名捕获正则", "目标字段名或数组(可选)", mode="fill-auto")

# 进行urldecode
url_decoding('值如v("字段名")’)

# 设置字段值
e_set("字段名", "固定值或表达式函数", ..., mode="overwrite")

# 将request_uri中的key=value的组合抽取成字段 值的模式
e_kv("源字段正则或列表", sep="=", prefix="")

实现语句

e_regex("request", "(?<request_method>[^s]*) (?<request_uri>[^s]*) (?<http_version>[^s]*)")
e_set("request_uri", url_decoding(v("request_uri")))
e_kv("request_uri", prefix="uri_")

实现效果

38b50aac74cd6702bdf0f0e3049381e9.png

Step4. http code状态码映射

每一个http状态码都代表了不同的含义,下面是一份http状态码的映射表, 我们可以通过e_table_map 函数来将状态码的信息扩展到我们的日志字段中,方便后续做统计分析。

7a10f1c760aa740b67e52f8646ccbf92.png

涉及到的数据加工函数如下:

# 用来做字段富化,类似sql里join的功能
e_table_map("表格如通过tab_parse_csv(...)解析得到的",
            "源字段列表或映射列表如[('f1', 'f1_new'), ('f2', 'f2_new')]", 
            "目标字段列表")

# 用来把csv文件解析成table对象
tab_parse_csv(CSV文本数据, sep=',', quote='"')

# code的映射关系维度表是一个csv文件,存在oss上,使用res_oss_file
res_oss_file(endpoint="OSS的endpoint", ak_id="OSS的AK_ID", 
             ak_key="OSS的AK_KEY", bucket="OSS的bucket", file="在OSS中存的文件地址", 
             change_detect_interval="定时更新时间,默认为0")

实际使用到的DSL语句如下

# http状态码映射
e_table_map(
      tab_parse_csv(
           res_oss_file(endpoint="oss-cn-shanghai.aliyuncs.com",
              ak_id='',ak_key='',
              bucket="ali-sls-etl-test", 
              file="http_code.csv", format='text')),
              [("status","code")],
              [("alias","http_code_alias"),
               ("description","http_code_desc"),
               ("category","http_code_category")])

看一下映射后的效果

9856fdd1af76ddbea00e11794c8efca5.png

Step5. 通过UserAgent判断客户端操作系统

我们想了解客户用的是什么os版本,可以通过user agent里的字段用正则匹配来判断,用到dsl语句

# 取某个字段的值
v("字段名")

# 获取ua相关信息
ua_parse_all("带useragent信息的内容")

# 展开json字段, 因为ua_parse_all得到的是一个json对象,为了展开到一级字段使用e_json做展开
# 模式有 simple、full、parent、root 参考https://help.aliyun.com/document_detail/125488.html#section-o7x-7rl-2qh
e_json("源字段名", fmt="模式", sep="字段分隔符")

# 丢弃临时产生的字段
e_drop_fields("字段1", "字段2")

用到的dsl语句

# 通过User Agent解析获得客户端信息
e_set("ua",ua_parse_all(v("http_user_agent")))
e_json("ua", fmt='full',sep='_')
e_drop_fields("ua",regex=False)

加工效果

c2c4ceaf95232b98b15dc12592f291f5.png

Step6. 非200的日志投递到指定logstore

可以使用e_output函数来做日志投递,用regex_match做字段匹配

# 条件判断if
e_if("条件1如e_match(...)", "操作1如e_regex(...)", "条件2", "操作2", ....)

# 判断是否相等
op_ne(v("字段名1"), v("字段名2"))

# output发送到目标名称,目标名称在数据加工保存任务的时候配置对应的logstore信息
e_output(name="指定的目标名称")

实际的dsl语句

# 分发非200的日志
e_if(op_ne(v("http_code_alias"),"2xx"), e_output(name="nginx-log-bad"))

在预览里看到这个效果。(保存加工的时候,需要设置好对应project、logstore的ak信息)

19b5a9c00df60f9a492ea92ef83af05e.png

完整的DSL代码以及上线流程

好了,通过一步一步的开发调试,现得到完整的DSL代码如下

# 通用字段抽取
e_regex("content",'(?<remote_addr>[0-9:.]*) - (?<remote_user>[a-zA-Z0-9-_]*) [(?<local_time>[a-zA-Z0-9/ :-]*)] "(?<request>[^"]*)" (?<status>[0-9]*) (?<body_bytes_sent>[0-9-]*) "(?<refer>[^"]*)" "(?<http_user_agent>[^"]*)"')

# 设置localttime
e_set("local_time", dt_strftime(dt_strptime(v("local_time"),"%d/%b/%Y:%H:%M:%S"),"%Y-%m-%d %H:%M:%S"))

# uri字段抽取
e_regex("request", "(?<request_method>[^s]*) (?<request_uri>[^s]*) (?<http_version>[^s]*)")
e_set("request_uri", url_decoding(v("request_uri")))
e_kv("request_uri", prefix="uri_")

# http状态码映射
e_table_map(
      tab_parse_csv(
           res_oss_file(endpoint="oss-cn-shanghai.aliyuncs.com",
              ak_id='',ak_key='',
              bucket="ali-sls-etl-test", 
              file="http_code.csv", format='text')),
              [("status","code")],
              [("alias","http_code_alias"),
               ("description","http_code_desc"),
               ("category","http_code_category")])

# 通过User Agent解析获得客户端信息
e_set("ua",ua_parse_all(v("http_user_agent")))
e_json("ua", fmt='full',sep='_')
e_drop_fields("ua",regex=False)

# 分发非200的日志
e_if(op_ne(v("http_code_alias"),"2xx"), e_coutput(name="nginx-log-bad"))

在页面提交代码以后,保存数据加工

配置目标logstore信息,默认走完加工逻辑的日志都会发送到第一个目标logstore,我们在代码里指定了e_output到指定logstore,因此还需要第二个目标,并且目标的名字和e_output里指定的目标名称一致。

a08ba51635e8271cba180299a75ad7a7.png

保存完即完成上线,可以在数据处理-加工下看到该任务,点击进去可以看到加工延迟等信息。

c7dd2fdb791d7f342836ea0675bc089e.png

Nginx日志查询、可视化、报警

查询

假设,我们想对nginx异常的日志进行查询,以便做问题定位。我们可以针对需要查询的字段开启索引

aa221007c1e03d2c3f7b3a0356bbf364.png

开启索引可以使用“自动生成索引”来自动帮助生成字段索引列表(否则需要手工填写字段)

1dc00f0ff16c1f0e93a11cad0bb9b0a9.png

点击确定后,即完成索引配置。比如我们要查询 uri_item_name参数值为“测试商品_89”的错误情况,可以使用这样的query

* and uri_item_name : 测试商品_89

dedb952a532581f3f4db691e689b143e.png

可视化

我们关心每个请求的http请求是否成功,对于异常状态码,想做相应的统计,以方便我们做问题发现。 可以使用如下query统计 nginx-log-bad(非2xx类请求的logstore)状态码分布

* | select http_code_alias,count(*) as c 
         from log where http_code_alias is not null 
             group by http_code_alias order by c

使用饼图做可视化

f66bd25b9d4d3a336c3c2d9844be57d6.png

点击,“添加到仪表盘”,即可完成仪表盘的创建

063bd7534900237be0dc8918292e32ee.png

添加完成后,在左侧仪表盘就

7e2123346517b089f1e990bb78a74618.png

报警

针对非2xx的状态码,我们不仅要做可视化,还需要对它进行报警。 可以在刚才创建的仪表盘里,点击“告警->创建”,根据提示创建相应的告警规则。

444744b63d8d0bbff3a95d00cf7ff3e5.png

8f29fc12ccbe00a42e8edd96a03f5c63.png

总结

本次训练营,以最常见的Nginx日志作为例,介绍数据加工在日志的转化、处理、富化、转发等场景下相关的算子使用,帮助大家入门SLS的数据加工。除了文中提到的算子,数据加工还有200+的算子以支持更多的日志处理的场景,相关内容可以参考如下:

a) SLS数据加工整体简介:https://help.aliyun.com/document_detail/125384.html

b) SLS数据加工函数总览:https://help.aliyun.com/document_detail/159702.html

在数据加工之后,针对相关字段建立索引,可以方便地对日志内容进行各种场景的分析,并且针对有需要的场景设置对应的报警,提升系统的稳定性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值