ELK日志采集和查询方法

回顾

前两篇文章简单的介绍了JAVA通过代码如何使用MDC进行日志打印,去查看log4j MDC用户操作日志追踪配置,以及ELK平台的搭建,去查看log4j MDC用户操作日志追踪配置。接下来将结合实际案例,简单介绍生产服务器的日志如何被logstash进行采集,并统一汇总,使得我们能够快速、方便、高效的查询日志,并且可以方便定位到该条日志是哪台服务器产生的,再也不用盲目的翻遍所有的生产服务器,只为找到出问题的那一台机器。

logstash日志采集

因为我们打印的日志各种各样,为了使我们能够方便的通过kibana进行检索,我们需要在logstash配置对应的搜集规则,如果你啥也不做,只是简单的搜集,可能会出现一些意想不到的错误。

  1. 日志收集是按行来收集的,当你的日志发生了换行,这时候换行的那几行就被认为是独立的一行,这样采集到的日志可读性很差
  2. 如果你使用了MDC,配置了log4j的日志格式输出,不配规则的话一整行都会被采集到message字段,此时你要根据某个字段快速搜索的话基本也是不可能的事情了

基于以上可能出现的“错误”,我们需要针对当前项目的日志配置文件制定一套属于自己的收集规则。
其实logstash的配置文件很简单,基本就以下的套路,日志源从哪里来(input输入插件),通过什么样的规则(filter过滤插件),最终将日志输出到什么地方(output输出插件)

# 输入
input {
  ...
}

# 过滤器
filter {
  ...
}

# 输出
output {
  ...
}

因为我们这里介绍的是项目日志的采集,所以input当然是从file文件中来,配置如下:

input {
    file {
      type => "wechat-log"
      path => ["/usr/local/tomcat/logs/wechat/*.log"]
      codec => multiline{
          pattern => "^\[%{TIMESTAMP_ISO8601}\]"
          what => "previous"
          negate => true
      }
      start_position => "beginning"
    }
}

其中path是日志采集的地方,从日志文件的第一行开始采集,定义一个type(一般最后它就是kibana的索引)。

codec插件

这里codec的出场能够解决诸如我们前面提到的日志换行的问题。在logstash读入的时候,通过codec编码解析日志为相应格式,从logstash输出的时候,通过codec解码成相应格式。当我们的应用程序打印出具有换行的日志的时候,比如ERROR日志,一般有错误堆栈消息,各种at开头的一行,我们可以通过multiline来进行处理,让logstash认为这一行是属于上一行的内容,而不是把它作为新的一行进行处理。
一般我们的tomcat日志都是以时间开头的,对于像at那种堆栈的信息都是不存在时间的,所以我们可以配置正则表达式【^[%{TIMESTAMP_ISO8601}]】,只有当以时间开头的一行才算新的一行,不是时间开头的就属于前面【what=>previous】或者后面的【what=>next】,这里我们配置属于前面的。

上述的配置之后解决了换行问题,那么接下来我们还需要处理按字段进行日志切分的。

grok插件

在grok中,支持以正则表达式的方式提取日志信息,其中,正则表达式又分两种,一种是内置的正则表达式,一种是自定义的正则表达式,当内置的正则表达式不能满足我们的需求的时候,我们就要上自定义的正则表达式了,但是内置的基本满足我们的需求了。具体细节可以查看grok介绍

假设我们的日志配置文件是这样配置的:

<appender name="logic" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="${catalina.home}/logs/wechat/logic.log" />
        <param name="DatePattern" value="'.'yyyy-MM-dd" />
        <param name="threshold" value="info"/>
        <param name="append" value="true"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] - %X{merchant} - %X{openid} - %X{queryType} - %X{orderId} - %X{wechatOrderId} - %X{input} - %X{source} - %-6p%c:%L - %m%n" />
        </layout>
</appender>

我们设置了merchant、openid、queryType、orderId、wechatOrderId、input、source7个字段,生产服务器打印的日志将会是以下格式:

[2019-01-27 17:51:22.051] - iPhoneBaoXiu - oisb3smtzToo7jNA4abazKktnECQ - senior - aa4820190127175110 - 4200000283201901277968491434 - 352982093855677 - 1 - INFO  com.apple.wechat.service.Worker:401 - 发送模板消息,查询结果为:me.chanjar.weixin.mp.bean.template.WxMpTemplateData@3813022d
[2019-01-27 17:51:22.230] - iPhoneBaoXiu - oisb3smtzToo7jNA4abazKktnECQ - senior - aa4820190127175110 - 42000002832019277968491434 - 352982093855677 - 1 - ERROR me.chanjar.weixin.mp.api.impl.WxMpServiceImpl:403 - 
[URL]:  https://api.weixin.qq.com/cgi-bin/message/template/send
[PARAMS]: {"touser":"olC5FwLnXjtCbQsW76lkevV57nH0","template_id":"Qt1zyzQs4R1uPrJylGQLSUTS6QcG6UyWB2zDzGt7QGY","url":"http://mp.weixin.qq.com/bizmall/mallshelf?id=&t=mall/list&biz=MjM5OTAxMzk4MQ==&shelf_id=7&showwxpaytitle=1#wechat_redirect","data":{"first":{"value":"查询结果","color":"#B452CD"},"keyword1":{"value":"aa4820190127175110","color":"#FF4040"},"keyword2":{"value":"352982093855677","color":"#FF4040"},"keyword3":{"value":"1.00元","color":"#FF4040"},"remark":********************}
[RESPONSE]: {"errcode":40003,"errmsg":"invalid openid hint: [mLJNpa06824120]"}
[2019-01-27 17:51:22.230] - iPhoneBaoXiu - oisb3smtzToo7jNA4abazKktnECQ - senior - aa4820190127175110 - 4200000283201901277968491434 - 352982093855677 - 1 - ERROR com.apple.wechat.service.Worker:405 - 发送模板消息失败,{"errcode":40003,"errmsg":"invalid openid hint: [mLJNpa06824120]"}
[2019-01-27 17:51:22.231] - iPhoneBaoXiu - oisb3smtzToo7jNA4abazKktnECQ - senior - aa4820190127175110 - 4200000283201901277968491434 - 352982093855677 - 1 - INFO  com.apple.wechat.service.RefundService:57 - 开始发起退款,退款订单id:2056653,微信订单号:4200000283201901277968491434
[2019-01-27 17:51:22.463] -  -  -  -  -  -  -  - INFO  com.apple.wechat.service.Worker:94 - 执行任务:QueryTask{msg='C39XQ4NFKPGN', fromUserId='oOEvtjsGdmAKrZx81zsACqBjjdsA', merchant='MLdress', type='senior', authUserId='olC5FwH40UpZakKBZRls_t_HR9Ew', price='1.00', tradeNo='e50b20190127175115', model='', orderId=2056654, needRefund=false, needRedo=false, sendMsg=false, msgType='1', lat='', lon='', token='e50ba187b2f84297b60fc14699748679', wechatOrderNo='4200000269201901277039023012'}
[2019-01-27 17:51:23.327] - iPhoneBaoXiu - oisb3smtzToo7jNA4abazKktnECQ - senior - aa4820190127175110 - 4200000283201901277968491434 - 352982093855677 - 1 - INFO  com.apple.wechat.service.RefundService:97 - 退款结果:success, 实付金额:1.00,发起退款金额:1.00
[2019-01-27 17:51:26.876] -  -  -  -  -  -  -  - INFO  com.apple.wechat.service.Worker:94 - 执行任务:QueryTask{msg='C39T81JEHG01', fromUserId='oOEvtjotDEF8doO3xVxyJ0-dCqFM', merchant='MLdress', type='normal', authUserId='', price='', tradeNo='', model='', orderId=0, needRefund=false, needRedo=false, sendMsg=false, msgType='1', lat='', lon='', token='', wechatOrderNo=''}
[2019-01-27 17:51:28.003] - MLdress - oOEvtjsGdmAKrZx81zsACqBjjdsA - senior - e50b20190127175115 - 4200000269201901277039023012 - C39XQ4NFKPGN - 1 - INFO  com.apple.wechat.util.HttpUtils:125 - http execute cost total seconds 5540

我们可以通过以下正则表达式将日志进行切割,并把内容赋值给对应的字段

filter {
  grok {
    match => ["message", "\[%{TIMESTAMP_ISO8601:logdate}\] - (?<merchant>[\b\w\s]*) - (?<openid>[\u4e00-\u9fa5\b\w\s]*) - (?<queryType>[\b\w\s]*) - (?<orderId>[\b\w\s]*) - (?<wechatOrderId>[\b\w\s]*) - (?<input>[\b\w\s]*) - (?<source>[\b\w\s]*) - %{WORD:level}\s*%{JAVACLASS:class}:%{NUMBER:lineNumber} - (?<msg>[\W\w\S\s]*)"]
  }
  date {
     match => ["logdate", "yyyy-MM-dd HH:mm:ss.SSS"]
     target => "@timestamp"
  }
}

(? < merchant >[\b\w\s]*) 代表使用[]里面的正则,把识别的结果放到merchant这个字段里面,其他类似。至于这里的match如何输写,跟你的日志配置文件及你想要的效果有很大的关联,所以只能慢慢调试,直到你写的match能够正确切割出你的日志文件。在线测试你的正则表达式是否可以匹配项目输出的日志测试工具传送门

完整的配置文件

input {
    file {
      type => "wechat-log"
      path => ["/usr/local/tomcat/logs/wechat/*.log"]
      codec => multiline{
          pattern => "^\[%{TIMESTAMP_ISO8601}\]"
          what => "previous"
          negate => true
      }
      start_position => "beginning"
    }
}
filter {
  grok {
    match => ["message", "\[%{TIMESTAMP_ISO8601:logdate}\] - (?<merchant>[\b\w\s]*) - (?<openid>[\u4e00-\u9fa5\b\w\s]*) - (?<queryType>[\b\w\s]*) - (?<orderId>[\b\w\s]*) - (?<wechatOrderId>[\b\w\s]*) - (?<input>[\b\w\s]*) - (?<source>[\b\w\s]*) - %{WORD:level}\s*%{JAVACLASS:class}:%{NUMBER:lineNumber} - (?<msg>[\W\w\S\s]*)"]
  }
  date {
     match => ["logdate", "yyyy-MM-dd HH:mm:ss.SSS"]
     target => "@timestamp"
  }
}
output {
    elasticsearch {
      hosts => "**************:9200"
      index => "logstash-%{type}"
      template_overwrite => true
    }
}

保存后启动logstash即可。命令:

nohup ./bin/logstash agent -f config/log.conf &

kibana操作

  1. 首先是创建索引,因为我们的output配置了index为logstash-%{type},所以对于的索引为:logstash-wechat-log
  2. 创建完索引,我们能发现kibana上面列出了我们前面定义的字段,如下图。日志对应的字段信息
  3. 接下来我们就可以通过各种条件来进行日志的搜索了。

假设我们要搜索商户是MLdress,用户输入内容为356110099676675的日志信息,那么我们只需要输入【merchant:MLdress AND input:356110099676675】即可搜索出对应的日志,如下图。在这里插入图片描述

总结

通过在代码使用MDC进行规范的日志打印,在结合logstash提供的强大的日志采集插件,为了解决日志的换行以及我们自定义字段等问题我们需要进行对logstash的配置文件进行配置,以将所有服务器的日志统一上报到es,并通过kibana进行放飞自我的查询操作,只有这样才能极大的提高日常开发效率。

程序员除了要会写代码之外,另外一项加分项那肯定是具有快速发现和定位问题的能力了,如果没有ELK这样的工具,那快速发现及定位其实是比较困难的。

所以,你懂的,赶紧用起来吧。

  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值