## 13【案例】通过ELK收集Tomcat访问日志和状态日志 ## 1、tomcat日志解析 对tomcat的日志监控和分析主要有有两种类型,分别是访问日志(localhost\_access\_log.Y-M-D.txt)和运行状态日志(catalina.out),其中,访问日志记录访问的时间,IP,访问的资料等相关信息,catalina.out其实记录了tomcat的标准输出(stdout)和标准出错(stderr),这是在tomcat的启动脚本里指定的,默认情况下stdout和stderr都会重定向到这里。所以可以在应用里使用System.out打印的东西都会输出到这里来。此文件对监控tomcat的运行状态非常重要。 在企业具体的运维监控中,对访问日志的监控主要是看访问量,访问来源等信息,因此,这部分日志输出可以入到elasticsearch中,然后在Kibana中展示分析,而对catalina.out文件的监控,主要是监控日志中是否有错误、告警等信息,如果监控到有类似信息,那么就进行告警,因此对catalina.out文件的日志收集,需要过滤关键字,然后输出到zabbix进行告警。 下面就开始对tomcat两种不同类型的日志进行收集、过滤和告警设置。 ## 2、配置tomcat的访问日志和catalina.out文件 ### 2.1、配置访问日志(localhost\_access\_log.Y-M-D.txt)输出为JSON格式 要配置访问日志文件的输出格式,需要修改tomcat的server.xml文件,这里以tomcat8.5.30为例,打开server.xml,找到如下内容: ```python ``` 将此内容修改为如下配置: ```perl ``` 介绍下出现的一些参数的含义: > directory:日志文件存放的目录。通常设置为tomcat下已有的那个logs文件。 > prefix:日志文件的名称前缀。 > suffix :日志文件的名称后缀。 > pattern:比较重要的一个参数。下面列出这个参数常用的选项和含义: > > %a   这是记录访问者的IP,在日志里是127.0.0.1 > %A   这是记录本地服务器的IP。 > %b   发送信息的字节数,不包括http头,如果字节数为0的话,显示为“-” > %B   发送信息的字节数,不包括http头。 > %h   服务器的名称。如果resolveHosts为false的话,这里就是IP地址 > %H   访问者的协议 > %l   记录浏览者进行身份验证时提供的名称 > %m   访问的方式,是GET还是POST > %p   本地接收访问的端口  > %q   比如你访问的是aaa.jsp?bbb=ccc,那么这里就显示?bbb=ccc,表示query string的意思 > %r   请求的方法和URL > %s   http的响应状态码  > %S   用户的session ID,每次都会生成不同的session ID > %t   请求时间 > %u   得到了验证的访问者,否则就是"-" > %U   访问的URI地址 > %v   服务器名称 > %D  请求消耗的时间,以毫秒记 > %T   请求消耗的时间,以秒记 配置完成后,重启tomcat,可以查看tomcat的访问日志是否输出了json格式,如下所示: ```json {"client":"172.16.213.132", "client user":"-", "authenticated":"-", "access time":"[13/Sep/2018:19:01:09 +0800]", "method":"GET /docs/jndi-datasource-examples-howto.html HTTP/1.1", "status":"200", "send bytes":"-", "Query?string":"", "partner":"http://172.16.213.233:8080/", "Agent version":"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"} {"client":"172.16.213.132", "client user":"-", "authenticated":"-", "access time":"[13/Sep/2018:19:01:09 +0800]", "method":"GET /docs/jndi-datasource-examples-howto.html HTTP/1.1", "status":"200", "send bytes":"35230", "Query?string":"", "partner":"http://172.16.213.233:8080/", "Agent version":"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"} {"client":"172.16.213.132", "client user":"-", "authenticated":"-", "access time":"[13/Sep/2018:19:01:10 +0800]", "method":"GET /favicon.ico HTTP/1.1", "status":"200", "send bytes":"21630", "Query?string":"", "partner":"http://172.16.213.233:8080/docs/jndi-datasource-examples-howto.html", "Agent version":"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"} ``` 简单整理一下,输出的json格式如下: ```json { "client": "172.16.213.132", "client user": "-", "authenticated": "-", "access time": "[13/Sep/2018:19:01:09 +0800]", "method": "GET /docs/jndi-datasource-examples-howto.html HTTP/1.1", "status": "200", "send bytes": "-", "Query?string": "", "partner": "http://172.16.213.233:8080/", "Agent version": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" } ``` ### 2.2、配置tomcat的catalina.out文件 默认情况下,tomcat8.5版本中catalina.out文件的输出格式如下: ```css 17-Sep-2018 15:57:10.387 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"] 17-Sep-2018 15:57:10.425 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-nio-8009"] 17-Sep-2018 15:57:10.443 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 31377 ms ``` 在这个日志中,时间字段输出不是很友好,我们需要修改下时间自动的输出为类似“2018-09-17 15:57:10.387”这种格式,要修改catalina.out的时间输出字段,需要修改logging.properties文件,此文件位于tomcat配置文件目录conf下,打开此文件,添加如下内容: ```perl 1catalina.org.apache.juli.AsyncFileHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.SimpleFormatter.format = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL [%4$s] [%3$s] %2$s %5$s %6$s%n ``` 同时删除如下行: ```plain java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter ``` 这样,catalina.out文件输出的时间字段就变成比较友好的格式了,如下图所示: ```css 2018-09-17 16:03:56.540 [INFO] [org.apache.coyote.http11.Http11NioProtocol] org.apache.coyote.AbstractProtocol start Starting ProtocolHandler ["http-nio-8080"] 2018-09-17 16:03:56.582 [INFO] [org.apache.coyote.ajp.AjpNioProtocol] org.apache.coyote.AbstractProtocol start Starting ProtocolHandler ["ajp-nio-8009"] 2018-09-17 16:03:56.602 [INFO] [org.apache.catalina.startup.Catalina] org.apache.catalina.startup.Catalina start Server startup in 41372 ms ``` 对于catalina.out文件的内容,我们只需要过滤出来四个字段即可,分别是时间字段、日志输出级别字段、异常信息字段以及运行状态内容字段,对上面的日志信息,通过logstash的grok插件进行过滤,可以很方便的取出这四个字段,登录http://grokdebug.herokuapp.com/ ,然后今天在线调试,可以很轻松得出过滤规则: ```markdown %{TIMESTAMP_ISO8601:access_time}\s+\[%{LOGLEVEL:loglevel}\]\s+\[%{DATA:exception_info}\](?[\s\S]*) ``` 其中,access\_time取的是时间字段、loglevel取的是日志输出级别字段、exception\_info取的是异常信息字段,tomcatcontent取的是运行状态字段。 ## 3、配置filebeat 在tomcat所在的服务器(172.16.213.233)上安装filebeat,然后配置filebeat,配置好的filebeat.yml文件内容如下: ```plain filebeat.inputs: - type: log enabled: true paths: - /usr/local/tomcat/logs/localhost_access_log.2018-09*.txt #这是tomcat的访问日志文件 fields: log_topic: tomcatlogs #这是新增的字段,用于后面的kafka的topic,其实是对tomcat访问日志文件分类 - type: log enabled: true paths: - /usr/local/tomcat/logs/catalina.out #这是定义的第二个tomcat日志文件catalina.out,注意写的格式。 fields: log_topic: tomcatlogs_catalina #这是新增的字段,用于后面的kafka的topic,专门用于存储catalina.out日志文件 processors: - drop_fields: fields: ["beat", "input", "source", "offset", "prospector"] filebeat.config.modules: path: ${path.config}/modules.d/*.yml reload.enabled: false name: 172.16.213.233 output.kafka: enabled: true hosts: ["172.16.213.51:9092", "172.16.213.75:9092", "172.16.213.109:9092"] version: "0.10" topic: '%{[fields.log_topic]}' #这是指定topic,注意写法,上面新增了两个字段,两个对应的日志文件会分别写入不同的topic partition.round_robin: reachable_only: true worker: 2 required_acks: 1 compression: gzip max_message_bytes: 10000000 logging.level: debug ``` 配置文件编写好后,启动filebeat服务即可: ```coffeescript [root@filebeat232 ~]# cd /usr/local/filebeat [root@filebeat232 filebeat]#nohup ./filebeat -e -c filebeat.yml & ``` ## 4、通过Logstash的grok插件过滤、分析配置tomcat日志 Logstash服务部署在172.16.213.120服务器上,下面给出Logstash事件配置文件,命名为kafka\_tomcat\_into\_es.conf,内容如下: ```php input { #这里定义了两个消费topic,分别读取的是tomcat的访问日志和catalina.out文件 kafka { bootstrap_servers => "172.16.213.51:9092,172.16.213.75:9092,172.16.213.109:9092" topics => ["tomcatlogs"] codec => "json" } kafka { bootstrap_servers => "172.16.213.51:9092,172.16.213.75:9092,172.16.213.109:9092" topics => ["tomcatlogs_catalina"] codec => "json" } } filter { if [fields][log_topic] == "tomcatlogs_catalina" { #判断语句,根据topic不同,对日志做不同的过滤、分析,先分析的是catalina.out文件 mutate { add_field => [ "[zabbix_key]", "tomcatlogs_catalina" ] add_field => [ "[zabbix_host]", "%{[host][name]}" ] } grok { match => { "message" => "%{TIMESTAMP_ISO8601:access_time}\s+\[(?[\s\S]*)\]\s+\[%{DATA:exception_info}\](?[\s\S]*)" } } date { match => [ "access_time","MMM d HH:mm:ss", "MMM dd HH:mm:ss", "ISO8601"] } mutate { remove_field => "@version" remove_field => "message" #remove_field => "[fields][log_topic]" #remove_field => "fields" remove_field => "access_time" } } if [fields][log_topic] == "tomcatlogs" { #判断语句,根据topic不同,对日志做不同的过滤、分析,这里分析的是访问日志文件 json { source => "message" #由于访问日志文件已经是json格式,所以这里解码出来即可 } date { match => [ "access time" , "[dd/MMM/yyyy:HH:mm:ss Z]" ] #时间字段转换,然后赋值给@timestamp字段 } mutate { remove_field => "@version" #删除不需要的字段 remove_field => "message" } } } output { if [fields][log_topic] == "tomcatlogs_catalina" { #输出判断,根据不同的topic,做不同的输出设置 if ([loglevel] =~ "INFO" or [tomcatcontent] =~ /(Exception|error|ERROR|Failed)/ ) { #对catalina.out文件出现前面指定的关键字,就过滤出来,然后告警 zabbix { zabbix_host => "[zabbix_host]" zabbix_key => "[zabbix_key]" zabbix_server_host => "172.16.213.140" zabbix_server_port => "10051" zabbix_value => "tomcatcontent" #这是输出到zabbix的内容配置 } } } if [fields][log_topic] == "tomcatlogs" { #输出判断,根据不同的topic,做不同的输出设置,这是将访问日志输出到elasticsearch集群 elasticsearch { hosts => ["172.16.213.37:9200","172.16.213.77:9200","172.16.213.78:9200"] index => "tomcatlogs-%{+YYYY.MM.dd}" } } stdout { codec => rubydebug } #调试模式,可以方便观看日志输出是否正常,调试完成后,删除即可 } ``` 这个配置文件稍微复杂,将两个topic的日志分别做了不同处理,将catalina.out文件首先进行grok分割,然后进行关键字过滤,如果输出指定关键字,那么将和zabbix进行联动,发出告警; 接着,将tomcat的访问日志进行简单过滤后,直接输出到 elasticsearch集群,最后在Kibana进行展示。 ## 5、配置zabbix输出并告警 登录zabbix web平台,首先创建一个模板logstash-output-zabbix,然后在此模块下创建一个监控项,如下图所示: ![](assets/1593327564-ace4c1a45f184861dfa221cfde7bb92f.png) 最后,要实现监控、告警,还需要创建一个触发器,进入刚刚创建好的模板中,创建一个触发器,如下图所示: ![](assets/1593327564-a72ed742fffa5f73fd7b666e5668d455.png) 这个触发器的含义是:如果收到logstash发送过来的数据就进行告警,或者说接收到的数据大于0就告警。 可以模拟产生一些包含上面关键字的日志信息,然后观察是否会进行告警。 ## 6、Kibana平台创建tomcat访问日志索引 tomcat日志的索引名称为tomcatlogs-%{+YYYY.MM.dd},登录Kibana,选择创建索引,然后添加索引名tomcatlogs-\*,然后按照时间添加排序规则,即可完成访问日志索引创建,然后到discover查看日志,如果日志输出正常,即可看到访问日志,如下图所示: ![](assets/1593327564-4d31b4387b2e974836b19fceb35e7c74.png) 至此,ELK收集tomcat日志完成。