我们在应用程序中使用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 Server不可用的情况下包含一个回退RollingFile Appender,为了实现这一点,我们认为我们将通过以下方式修改Async 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,488错误无法在类类org.apache.log4.c..penpen.logpen.log4j.core.appender.SocketAppender中调用元素Socket的工厂方法。 2015-04-24 09:55:43,402 ERROR Null对象为Appenders中的Socket返回。 2015-04-24 09:55:43,407错误没有配置名为logstash的appender 2015年4月24日上午9:55:43 org.apache.catalina.core.StandardContext startInternal SEVERE:ServletContainerInitializer处理时出错javax.servlet.ServletException:失败实例WebApplicationInitializer类在org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:160)在org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5481)在org.apache.catalina.util.LifecycleBase .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)at org.apache.catalina.startup.HostConfig.deployApps(HostConfig。 java:553)at org.apache.catalina.startup.HostConfig.check(Host Config.java:1668)atg.apache.tomcat上java.lang.reflect.Method.invoke(未知来源)的sun.reflect.DelegatingMethodAccessorImpl.invoke(未知来源)sun.reflect.GeneratedMethodAccessor532.invoke(未知来源)位于org的com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(未知来源)的com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(未知来源)中的.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)。 apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1480)位于org.apache.catalina.manager.ManagerServlet.deploy(ManagerServlet.java:709)org.apache.catalina.manager.ManagerServlet.doPut(ManagerServlet。 java:450)在javax.servlet.http.HttpServlet.service(HttpServlet.java:649)的javax.servlet.http.HttpServlet.service(HttpServlet.java:727)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:303)在org.apache.tomc的org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 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)org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108)org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)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)at org.apache.tomcat.util.net.JIoEndpoint位于org.apache.tomcat.util的java.util.concurrent.ThreadPoolExecutor $ Worker.run(未知来源)的java.util.concurrent.ThreadPoolExecutor.runWorker(未知来源)中的$ SocketProcessor.run(JIoEndpoint.java:314)。 threads.TaskThread $ WrappingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Unknown Source)由sun.reflect的sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)引起的java.lang.ExceptionInInitializerError引起的sun.reflect.DelegatingConstructorAccessorImp中的.NativeConstructorAccessorImpl.newInstance(未知来源) l.newInstance(未知来源)位于org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:157)java.lang.Class.newInstance(未知来源)的java.lang.reflect.Constructor.newInstance(未知来源) ... 42更多引起:org.apache.log中的java.lang.NullPointerException(async.apache.log。) .start(AbstractConfiguration.java:157)org.apache.log4中的org.apache.log4.Core.LoggerContext.setConfiguration(LoggerContext.java:364)。(LoggerContext.java:422) )org.apache.logache.log4j.core.LoggerContext.start(LoggerContext.java:146)org.apache中的org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:75)。 log.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:37)at org.apache.logging.log4j.LogManager.getLogger(LogManager.jav) a:468)atg.apache.logging.log4j.LogManager.getLogger(LogManager.java:403)at com.company.service.config.WebInitialiser。(WebInitialiser.java:21)
我们做错了什么?我们应该使用故障转移Appender吗?
---------更新---------
在进一步测试之后,我可以说上述设置适用于Socket Appender主机可用但不是端口(例如服务不可用)的情况。但它不适用于Socket Appender的主机不可用的情况(例如未知主机)。
现在,为了让fallbackFile Appender在两种情况下工作(服务不可用和未知主机),我在SocketAppender上包含了一个Failover 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>
这种配置似乎给了我们想要的行为。
任何更清洁/更整洁的解决方案甚至对此配置的评论都是最受欢迎的。
干杯, PM
答案 0 :(得分:0)
logstash-gelf比log4j2的SocketAppender
更有弹性。 AsyncAppenders可以在一定程度上保护应用程序,但仍然可能遇到TCP问题,例如服务不可用,速度慢或连接/重新连接耗费时间。
GELF通过UDP工作,因此如果服务已关闭/无法访问,则不会以任何方式影响您的应用程序性能。唯一可能发生的事情是您可能会丢失日志事件,但是您已经获得了该案例的文件回退。
logstash-gelf配置的完整示例如下:
<Configuration>
<Appenders>
<Gelf name="gelf" host="udp:localhost" port="12201" version="1.1" extractStackTrace="true"
filterStackTrace="true" mdcProfiling="true" includeFullMdc="true" maximumMessageSize="8192"
originHost="%host{fqdn}">
<Field name="timestamp" pattern="%d{dd MMM yyyy HH:mm:ss,SSS}" />
<Field name="level" pattern="%level" />
<Field name="simpleClassName" pattern="%C{1}" />
<Field name="className" pattern="%C" />
<Field name="server" pattern="%host" />
<Field name="server.fqdn" pattern="%host{fqdn}" />
<!-- This is a static field -->
<Field name="fieldName2" literal="fieldValue2" />
<!-- This is a field using MDC -->
<Field name="mdcField2" mdc="mdcField2" />
<DynamicMdcFields regex="mdc.*" />
<DynamicMdcFields regex="(mdc|MDC)fields" />
</Gelf>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="gelf" />
</Root>
</Loggers>
</Configuration>
此处提供完整文档:http://logging.paluch.biz/
答案 1 :(得分:0)
更新的解决方案是99%好的。
如log4j async appender中所指定,必须在套接字附加器上将ignoreExceptions设置为false ,以使其与故障转移附加器一起使用。
在将此Appender包装在FailoverAppender中时,必须将其设置为false。