前段时间,老大叫我在新闻服务上,查看下redis的read time out的异常,我用linux命令查,感觉有点麻烦,所以就自己搭了一个elk的日志收集系统。注意这里只能在本地分析日志文件,如果日志文件在别的主机上,在下一章会进行讲述。
我采用了很笨的方法,我把要分析的日志拷贝到本地主机上,然后在本地虚拟机上创建了一个elk系统。
这里先给出我要分析的日志格式,不过我只要error的日志,其他级别的不分析:
2018-07-16 13:00:08.026 WARN [newsservice,bf045afa588eb7f9,f7ac1088fb74eab3,false] 17864 --- [ XNIO-5 task-12] c.i.n.controller.v3.UniteNewsController : uniteChannelNews V1 cost time is 4466 ms! The request URL is http://172.18.0.2:8182/v3/uniteChannelNews:installChannel=qita&ac=1&cCode=4504&oldestTime=0&romVersion=Meizu&snapshotNumber=0&latitude=23.474561&channelType=0&language=zh&devicePlatform=android&pageNum=1&md5Hash=6b5eca59c0c0376cc0468e6c57e4ff8f&osVersion=6.0.1&pCode=45&aCode=450403&refreshType=0&deviceModel=M3X&deviceBrand=Meizu&channelId=612&longitude=111.301625
2018-07-16 13:00:08.222 INFO [newsservice,,,] 17864 --- [cTaskExecutor-1] c.i.n.s.rabbitmq.NewsRabbitMQReceiver : received news update message: {"action":"publish","targetType":"news","id":9042825}
2018-07-16 13:00:11.620 WARN [newsservice,f98adb8057c64034,9513bd6c84a454d8,false] 17864 --- [ XNIO-5 task-84] c.i.n.controller.v3.UniteNewsController : [checkpoint] checkpoint1 , cost time: 887
2018-07-16 13:00:11.656 ERROR [newsservice,,,] 17864 --- [cTaskExecutor-1] c.i.n.s.rabbitmq.NewsRabbitMQReceiver : Deal with news to redis error sid: 9042825
org.springframework.data.redis.RedisConnectionFailureException: java.net.SocketTimeoutException: Read timed out; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:67)
at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:41)
at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:37)
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:37)
at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:212)
at org.springframework.data.redis.connection.jedis.JedisConnection.sAdd(JedisConnection.java:1874)
at org.springframework.data.redis.connection.DefaultStringRedisConnection.sAdd(DefaultStringRedisConnection.java:692)
at org.springframework.data.redis.core.DefaultSetOperations$1.doInRedis(DefaultSetOperations.java:46)
at org.springframework.data.redis.core.DefaultSetOperations$1.doInRedis(DefaultSetOperations.java:43)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:202)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:164)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:88)
at org.springframework.data.redis.core.DefaultSetOperations.add(DefaultSetOperations.java:43)
at cn.itouchtv.news.service.impl.MediaCacheImp.beautifyMediaInfoIntoSetter(MediaCacheImp.java:676)
at cn.itouchtv.news.service.rabbitmq.NewsRabbitMQReceiver.receive(NewsRabbitMQReceiver.java:119)
at cn.itouchtv.news.service.rabbitmq.NewsRabbitMQReceiver$$FastClassBySpringCGLIB$$bf59a8ef.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
at cn.itouchtv.debug.StopTaskAndMqAspect.aroundMQStop(StopTaskAndMqAspect.java:57)
at sun.reflect.GeneratedMethodAccessor281.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
at cn.itouchtv.news.service.rabbitmq.NewsRabbitMQReceiver$$EnhancerBySpringCGLIB$$63fa09c6.receive(<generated>)
at sun.reflect.GeneratedMethodAccessor550.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:115)
at org.springframework.amqp.rabbit.listener.adapter.DelegatingInvocableHandler.invoke(DelegatingInvocableHandler.java:108)
at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:52)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:125)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:105)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:777)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:700)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:95)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:187)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1187)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:681)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1165)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1149)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1100(SimpleMessageListenerContainer.java:95)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1312)
at java.lang.Thread.run(Thread.java:745)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:202)
at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40)
at redis.clients.jedis.Protocol.process(Protocol.java:151)
at redis.clients.jedis.Protocol.read(Protocol.java:215)
at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:265)
at redis.clients.jedis.BinaryJedis.sadd(BinaryJedis.java:1298)
at org.springframework.data.redis.connection.jedis.JedisConnection.sAdd(JedisConnection.java:1872)
... 46 common frames omitted
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.net.SocketInputStream.read(SocketInputStream.java:127)
at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:196)
... 53 common frames omitted
2018-07-16 13:00:11.657 ERROR [newsservice,,,] 17864 --- [ XNIO-5 task-52] io.undertow.request : UT005023: Exception handling request to /v6/hotNews
用到的elk如下:
elasticsearch-6.3.1.tar.gz
kibana-6.3.1-linux-x86_64.tar.gz
logstash-6.3.1.tar.gz
elasticserach直接解压后启动即可:./bin/elasticserach -d (-d表示后台运行)
kibana直接解压,然后把访问的localhost改成本机的ip地址:
server.host: "192.168.86.128"
然后启动kibana,运行 ./kibana &
注意:我这里主要是想在我本机windows的浏览器上访问kibana来方便查看数据而已
最后就是logstash了,基本大部分工作在这里:
解压logstash,然后进入config目录,创建一个配置文件errorlog.conf
具体配置如下:
# 输入插件
input {
file {
# 代表要分析的日志的位置,这里也可以使用正则的,比如*.log,就会读入所有的.log结尾的日志
path => "/home/itouchtv/log/nohup.log"
# 这里是多行读入,默认情况下,logstash是一行一行读的,刚开始的时候,我返现一个错误信息,分成几十行
# 非常不方便,我想要完整的错误信息。
codec => multiline {
# 正则配置的模式,这里我是自定义的,^代表以什么开头,%{YEAR}-%{MONTHNUM}-%{MONTHDAY}代表yyyy-MM-dd格式的日期
# \s代表是一个空格,*代表前面的空格出现的次数可以为0次或者多次,%{TIME}代表 HH:mm:ss格式的时间
# 2018-07-16 13:00:11会被匹配到,然后在下一个匹配成功的前的所有行,会被当成同一个事件。所以同一个error的信息
# 会被放到一起。
pattern => "^%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s*%{TIME}"
# 是否启用正则
negate => true
# 先前闭合,意思是在下一个成功匹配之前,这些数据不读入
# 比如最后一个2018-07-16 13:00:11被匹配了,然后下面没有再出现这种格式的数据,那么这段数据就不会读入到logstash中
what => "previous"
}
# 文件的读入位置,文件开始的位置读,不过要保证:logstash-6.3.1/data/plugins/inputs/file下没有记录你要分析文件的偏移地址
# 你在该目录下 ls -a 会看到是否有这种类型的文件 .sincedb_2905c14f44d79b0840f736856456d179,一般开始的时候,没有这个文件
# 所以它会读入全部文件,读完之后,会记录读入的位置,下次再发现文件更新的时候,再增量的读入新增的内容。
start_position => "beginning"
# 这个是我读入多种类型的数据,进行区分的时候加入的,我再实际的开发中,除了读入这种异常分析的日志外,还读入了访问的日志,
# 进行访问。
type => "errorlog"
}
}
# 过滤器
filter {
# 固定的语法,你想要引用一个变量的话,用[],比如这里就是引入上面的type=>"errorlog"这个变量
if [type] == "errorlog" {
# 这是个好东西,可以进行很正则分析,然后提取出需要的信息
grok {
# message固定写法,上述input中每个事件的信息都会放入到这里来,然后再通过后面的正则进行匹配
# 2018-07-16 13:00:14.544 WARN [newsservice,7dfc2622d7e5af21,9ab170947a9b7429,false] 17864 --- [ XNIO-5 task-20]
# %{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s*%{TIME}这里和上述一样功能,接下来的.就是点本身,\d是代表数字{3},出现三次,所以
# 这里刚好是.544被匹配到了,同样的再匹配0个或者多个空格,(?<Level>[A-Z]{3,20}),这里是自定义正则匹配,匹配的数据会
# 放入到Level中,比如这里(?<Level>xxx),xxx是正则表达式,[A-Z]{3,20}是说这里可以是长度为3到20的字符串,由A-Z的任意字目
# 组成,比如上述的例子 WARN 是由[A-Z]组成的,切长度为4,也在(3,20)内,所以会被匹配赋给Level这个变量
match => ["message","%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s*%{TIME}.\d{3}\s*(?<Level>[A-Z]{3,20})"]
}
# 获取Level的变量值,如果和右边的字符串相等,就丢弃这个事件,数据就会被忽略
if [Level] == "DEBUG" {
drop {}
}
if [Level] == "WARN" {
drop {}
}
if [Level] == "INFO" {
drop {}
}
}
}
# 输出插件
output {
if [type] == "errorlog" {
# 固定格式,表示输出到es中
elasticsearch {
# es的地址和端口好
hosts => ["localhost:9200"]
# 输入到es中的索引的名字,%{}表示会在字符串中应用变量
# %{type} 输入accesslog这个值,%{+YYYY.MM.dd} 固定写法
# 会读入当前系统的日期
index => "itouchtv-%{type}-%{+YYYY.MM.dd}"
}
}
}
然后启动logstash:
./bin/logstash -f config/errorlog.conf
启动完后,就可以通过kibana查看数据了。
访问 http://192.168.31.98:5601(访问的时候注意下,要关闭防火墙才行),即可,kibana里面对数据的操作,放到下一章,记录远程日志分析的时候再讲。