使用log4j发送日志到远程ELK平台
2017-12-07 16:36
1046 查看
最近公司安排我把前期熟悉的ELK日志监控平台应用到线上生产环境上去。期间发现了几个问题:
1、目前线上的项目用的是log4j,开始的时候为了避免前期代码里面已经大面积使用了日志打印语句(这些日志信息不是本次我们监控的重点)发送到ELK平台上,所以决定采用logback去打印、发送我们此次需要监控的接口与方法调用的地方。但是后来试了好久才发现:在同一个项目里面同时使用log4j和logback会出现问题:日志会随机发送到ELK上,很多时候都不会发送,最后才找到原因:web系统在调用打印日志的实例对象时,调用的是slf4j(使用了外观模式Facade),而log4j和logback都实现了slf4j,也就是当系统中同时有log4j和logback两个对象时,系统会随机调用一个对象进行日志打印(我只在logback里面配置了发送到远程ELK上),所以当系统调用的是log4j时,就不会发送,但是调用logback时,就又会发送,这种是随机、不可控的。具体参照:http://blog.csdn.net/lgcjava/article/details/52245255
最后没办法只能把logback给砍掉,还是使用原来的log4j。
2、在使用log4j配置时,有两种方式可以实现向远程发送日志信息:一种是log4j主动去连接ELK中的logstash,然后把日志发送到logstash上,这时log4j使用SocketAppender ,配置如下:
另一种方式是,采用SocketHubAppender :
1、目前线上的项目用的是log4j,开始的时候为了避免前期代码里面已经大面积使用了日志打印语句(这些日志信息不是本次我们监控的重点)发送到ELK平台上,所以决定采用logback去打印、发送我们此次需要监控的接口与方法调用的地方。但是后来试了好久才发现:在同一个项目里面同时使用log4j和logback会出现问题:日志会随机发送到ELK上,很多时候都不会发送,最后才找到原因:web系统在调用打印日志的实例对象时,调用的是slf4j(使用了外观模式Facade),而log4j和logback都实现了slf4j,也就是当系统中同时有log4j和logback两个对象时,系统会随机调用一个对象进行日志打印(我只在logback里面配置了发送到远程ELK上),所以当系统调用的是log4j时,就不会发送,但是调用logback时,就又会发送,这种是随机、不可控的。具体参照:http://blog.csdn.net/lgcjava/article/details/52245255
最后没办法只能把logback给砍掉,还是使用原来的log4j。
2、在使用log4j配置时,有两种方式可以实现向远程发送日志信息:一种是log4j主动去连接ELK中的logstash,然后把日志发送到logstash上,这时log4j使用SocketAppender ,配置如下:
log4j.appender.logstash=org.apache.log4j.net.SocketAppender log4j.appender.logstash.RemoteHost=10.30.11.19 log4j.appender.logstash.port=4570 log4j.appender.logstash.ReconnectionDelay=60000 log4j.appender.logstash.LocationInfo=true log4j.appender.logstash.encoding=UTF-8
另一种方式是,采用SocketHubAppender :
log4j.appender.socket=org.apache.log4j.net.SocketHubAppender log4j.appender.socket.port=9999 log4j.appender.socket.Threshold=INFO log4j.appender.socket.LocationInfo=true
这里在blog里面使用properties配置方式了,毕竟如果用xml的话,一会儿出来的效果又是各种输入框了,大家都懂的。如果项目是用的xml方式配置,相应的修改一下就行,两种方式的原理参看:
http://www.tuicool.com/articles/3U7fumv
http://blog.csdn.net/beer2008cn/article/details/7381760
3、在使用项目中使用xml配置时,用到了占位符:paramname="port"value="${monitor.port}"
,但因为log4j在项目启动时,先于spring的org.springframework.beans.factory.config.PropertyPlaceholderConfigurer这个加载properties文件的bean加载,所以在xml中配置的这个占位符始终都取不到monitor.port的值。找到了两种解决办法:
第一种是直接在tomcat启动参数那加一处monitor.port=9999(端口值自定义),第二种是写一个监听器,在web项目启动后,把monitor.properties文件中的monitor.port=9999值读出来,然后注入到System对象中去,最后使用log4j的API重新加载log4j的配置参数:
具体代码如下:
publicclassLoggerInitializerimplementsApplicationListener{
publicfinalStringlog_locations="xxxx/monitor.xml";
publicfinalStringlog_properties="xxxx/monitor.properties";
publicfinalStringMONITORPORT="monitor.port";
publicfinalStringbasePath=this.getClass().getClassLoader().getResource("/").getPath();;
privateStringloadProperties(){
Propertiesprop=newProperties();
Stringport=null;
InputStreamin=null;
try{
Stringpath=basePath+log_properties;
in=newBufferedInputStream(newFileInputStream(path));
prop.load(in);
port=prop.getProperty(MONITORPORT);
}catch(Exceptione){
e.printStackTrace();
}finally{
if(in!=null){
try{
in.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
returnport;
}
privatevoidreloadConfig(){
Stringport=loadProperties();
if(!StringUtil.isBlank(port)){
System.setProperty(MONITORPORT,port);//把properties文件中的值注入到System中去
}
Stringpath=basePath+log_locations;
path=path.substring(path.indexOf(":")+1,path.length());
//重新加载log4j的配置,重置时会抛出异常,但不影响启动和新配置的应用,重置后的配置也能正常使用,这时因为monitor.properties中的值已经注入到System中,所以monitor.port可以取到值
DOMConfigurator.configure(path);
}
@Override
publicvoidonApplicationEvent(ApplicationEventevent){
if(eventinstanceofContextRefreshedEvent){
if(((ContextRefreshedEvent)event).getApplicationContext().getParent()==null)//容器启动完成之后load
reloadConfig();
}
}
}
这里的原理就在于:log4j在通过占位符读取配置时,从log4j源码可以看出是在System中获取的占位符的值,所以这两种方式都可以实现。ps:如果系统使用的log4j配置是xml方式的,那就是在重新加载log4j.xml的配置时,使用DOMConfigurator.configure(path);这个API,如果是使用的properties文件配置方式,则改用:PropertyConfigurator.configure(path);
参考:http://k1280000.iteye.com/blog/2176541
其中:当重新加载log4j的配置文件时抛出的异常信息为:
java.net.SocketException:Socketoperationonnonsocket:configureBlocking
at
at
at
at
at
at
atorg.apache.log4j.net.SocketHubAppender$ServerMonitor.run(SocketHubAppender.
at
Exceptioninthread"SocketHubAppender-Monitor-4560"java.lang.NullPointerException
atorg.apache.log4j.net.SocketHubAppender$ServerMonitor.run(SocketHubAppender.
at
虽然会抛出异常,但经本人测试,log4j中的占位符还是被替换成了配置的值,而且日志也能正常发送到ELK平台上。但至于解决方案,暂时没有找到,有知道的朋友,可以指导一下,不胜感激。
此外,对于log4j还可以自定义日志级别,我当前项目就没有用到了,主要是项目催得比较紧,要实现自定义的日志级别可以参照:http://blog.csdn.net/seven_cm/article/details/26849821
相关文章推荐
- log4j使用SocketAppender推送日志到远程服务器(包含log4j如何升级到log4j2,并使用SocketAppender发送日志到LogStash)
- CentOS 7 使用ELK套件搭建日志分析和监控平台
- CentOS下使用ELK套件搭建日志分析和监控平台
- 使用elk+redis搭建nginx日志分析平台
- ELK日志处理之使用logstash收集log4J日志
- 使用elk+redis搭建nginx日志分析平台
- 使用elk+redis搭建nginx日志分析平台
- log4j将日志发送到远程日志服务器
- 使用elk+redis搭建nginx日志分析平台
- 使用elk+redis搭建nginx日志分析平台
- 使用ELK(Elasticsearch + Logstash + Kibana) 搭建日志集中分析平台实践--转载
- 使用ELK(Elasticsearch + Logstash + Kibana) 搭建日志集中分析平台实践--转载
- ELK学习3_使用redis+logstash+elasticsearch+kibana快速搭建日志平台
- Centos7下使用ELK(Elasticsearch + Logstash + Kibana)搭建日志集中分析平台
- ELK日志处理之使用logstash收集log4J日志
- 使用log4j将日志写入数据库并发送邮件
- Log4J使用实例---日志进行邮件发送或是存入数据库
- Centos6.5使用ELK(Elasticsearch + Logstash + Kibana) 搭建日志集中分析平台实践
- java日志《三》Log4j日志远程发送
- 使用ELK(Elasticsearch + Logstash + Kibana) 搭建日志集中分析平台实践