【发布时间】:2015-07-02 20:10:29
【问题描述】:
我们在应用程序中使用 log4j2 进行消息记录。目前,我们的 log4j2 配置使用 Async Appender,然后引用 Socket Appender (protocol="tcp") 将日志写入远程 Logstash 服务器:
<Socket name="logstash" host="logging" port="4560" protocol="tcp" >
<LogStashJSONLayout>
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<KeyValuePair key="tomcat.host" value="${env:HOSTNAME}"/>
<KeyValuePair key="tomcat.port" value="${env:CONNECTOR_PORT}"/>
<KeyValuePair key="tomcat.service" value="${web:contextPath}"/>
</LogStashJSONLayout>
</Socket>
<Async name="async">
<AppenderRef ref="logstash"/>
</Async>
我们现在想要做的是,修改我们的 log4j2 配置,在 Logstash 服务器不可用的情况下包含一个备用 RollingFile Appender,为了实现这一点,我们考虑通过以下方式修改 Async Appender:
- 设置 'blocking=false'。
- 设置 'ignoreExceptions="false"'
- 将“errorRef”设置为指向我们的备用 RollingFile Appender。
这是实现此目的的明智方法吗?如果是这样,Async Appender 的 XML 会是什么样子?我们尝试了以下方法:
<RollingFile name="fallbackFile"
fileName="${sys:catalina.base}/logs/${web:contextPath}-fallback.log"
filePattern="${sys:catalina.base}/logs/${web:contextPath}-%d{dd-MMM-yyyy}-%i.log"
append="true">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<SizeBasedTriggeringPolicy size="1 GB" />
</Policies>
</RollingFile>
<Socket name="logstash" host="logging" port="4560" protocol="tcp" >
<LogStashJSONLayout>
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<KeyValuePair key="tomcat.host" value="${env:HOSTNAME}"/>
<KeyValuePair key="tomcat.port" value="${env:CONNECTOR_PORT}"/>
<KeyValuePair key="tomcat.service" value="${web:contextPath}"/>
</LogStashJSONLayout>
</Socket>
<Async name="async" blocking="false" ignoreExceptions="false" errorRef="fallbackFile">
<AppenderRef ref="logstash"/>
</Async>
但是当我们在 Logstash Server 节点故意不可用的环境中部署应用程序时出现错误:
2015-04-24 09:55:43,688 错误无法为元素 Socket 调用类 org.apache.logging.log4j.core.appender.SocketAppender 中的工厂方法。 2015-04-24 09:55:43,702 错误为附加程序中的套接字返回空对象。 2015-04-24 09:55:43,707 错误没有配置名为 logstash 的附加程序 2015 年 4 月 24 日上午 9:55:43 org.apache.catalina.core.StandardContext startInternal 严重:ServletContainerInitializer 处理 javax.servlet.ServletException 期间出错:失败在 org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5481) 在 org.apache.catalina.util.LifecycleBase 的 org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:160) 实例化 WebApplicationInitializer 类.start(LifecycleBase.java:150) 在 org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901) 在 org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877) 在 org. apache.catalina.core.StandardHost.addChild(StandardHost.java:649) 在 org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1081) 在 org.apache.catalina.startup.HostConfig.deployApps(HostConfig. java:553) 在 org.apache.catalina.startup.HostConfig.check(Host Config.java:1668) 在 sun.reflect.GeneratedMethodAccessor532.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.apache.tomcat .util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(Unknown Source) at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(Unknown Source) at org. apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1480) 在 org.apache.catalina.manager.ManagerServlet.deploy(ManagerServlet.java:709) 在 org.apache.catalina.manager.ManagerServlet.doPut(ManagerServlet.deploy(ManagerServlet.java:709) java:450) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:649) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:727) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:303) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 在 org.apache.tomc at.websocket.server.WsFilter.doFilter(WsFilter.java:52) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain. java:208) at org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core .ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 在 org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) 在 org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) 在org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:612) 在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) 在 org.apache.catalina.valves.ErrorReportValve.invoke( ErrorReportValve.java:103) 在 org.apache.catalina.val ves.AccessLogValve.invoke(AccessLogValve.java:950) 在 org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)在 org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070) 在 org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611) 在 org.apache.tomcat.util.net.JIoEndpoint $SocketProcessor.run(JIoEndpoint.java:314) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at org.apache.tomcat.util。 thread.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Unknown Source) 原因:sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect 的 java.lang.ExceptionInInitializerError .NativeConstructorAccessorImpl.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImp l.newInstance(Unknown Source) at java.lang.reflect.Constructor.newInstance(Unknown Source) at java.lang.Class.newInstance(Unknown Source) at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:157) ... 42 更多原因:org.apache.logging.log4j.core.config.AbstractConfiguration 的 org.apache.logging.log4j.core.appender.AsyncAppender.start(AsyncAppender.java:108) 的 java.lang.NullPointerException .start(AbstractConfiguration.java:157) 在 org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:364) 在 org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:422 ) 在 org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:146) 在 org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:75) 在 org.apache。 logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:37) 在 org.apache.logging.log4j.LogManager.getLogger(LogManager.jav a:468) 在 org.apache.logging.log4j.LogManager.getLogger(LogManager.java:403) 在 com.company.service.config.WebInitialiser.(WebInitialiser.java:21)
我们做错了什么?我们是否应该改用 Failover Appender?
--------- 更新 ---------
经过进一步测试,我可以说上述设置适用于 Socket Appender 的主机可用但端口不可用(例如服务不可用)的情况。但它在 Socket Appender 的主机不可用(例如未知主机)的情况下不起作用。
现在,为了让 fallbackFile Appender 在两种情况下(服务不可用和未知主机)都能正常工作,我在我们的 SocketAppender 上包含了一个故障转移 Appender:
<RollingFile name="fallbackFile"
fileName="${sys:catalina.base}/logs/${web:contextPath}-fallback.log"
filePattern="${sys:catalina.base}/logs/${web:contextPath}-%d{dd-MMM-yyyy}-%i.log"
append="true">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<SizeBasedTriggeringPolicy size="1 GB" />
</Policies>
</RollingFile>
<Socket name="logstash" host="logging" port="4560" protocol="tcp" >
<LogStashJSONLayout>
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<KeyValuePair key="tomcat.host" value="${env:HOSTNAME}"/>
<KeyValuePair key="tomcat.port" value="${env:CONNECTOR_PORT}"/>
<KeyValuePair key="tomcat.service" value="${web:contextPath}"/>
</LogStashJSONLayout>
</Socket>
<Failover name="failover" primary="logstash">
<Failovers>
<AppenderRef ref="fallbackFile"/>
</Failovers>
</Failover>
<Async name="async" bufferSize="10" blocking="false" ignoreExceptions="false" errorRef="fallbackFile">
<AppenderRef ref="failover"/>
</Async>
这个配置似乎给了我们想要的行为。
任何更清洁/更整洁的解决方案,甚至此配置上的 cmets 都将受到欢迎。
干杯, 下午
【问题讨论】: