作者:京东集团-京东零售-技术与数据中台-共享技术部-流量技术组 王丽。
引言
在程序开发过程中,几乎任何一个应用程序都会通过打印日志来记录跟踪程序运行情况,打印日志信息不仅可以让我们详细的了解程序内部的运行情况,更能在排查问题是提供详细的异常信息,为我们解决问题提供重要的线索。
最近在梳理项目运行情况时发现有个应用每天的日志量较大,在高峰时打印日志较为密集,发现配置日志打印使用的是Apache log4j2同步方式,那果断换成log4j2异步方式啊。但是都说异步方式打印日志性能好,强大且高效,具体性能可以提升多少,和同步方式相比差距有多大,为何大家更偏爱异步日志,今天通过数据对比,揭秘一下真实的原因。
log4j2的三种打印日志方式
Apache log4j2有三种打印日志方式,分别是同步方式、AsyncAppender异步及AsyncLogger异步方式,接下来分别针对这三种方式的用法进行简单的介绍。
1、Sync同步方式
同步记录日志是指当输出日志时,必须等待日志输出语句执行完毕后,才能执行后面的业务逻辑语句。
在输出日志时,会将日志信息转换为日志事件LogEvent对象,然后将LogEvent对象传入对应的Appender中,由对应的Appender调用appender()方法将日志信息写入文件。
下面的代码是在LogConfig类中的log()方法中,创建LogEvent对象,调用processLogEvent()方法将LogEvent对象传入对应的Appender中。
publicvoid log(final String loggerName, final String fqcn, final Marker marker, final Level level, final Message data, final Throwable t) { List<Property> props = null; if (!propertiesRequireLookup) { props = properties; } else { // 省略部分代码 } final LogEvent logEvent = logEventFactoryinstanceof LocationAwareLogEventFactory ? ((LocationAwareLogEventFactory) logEventFactory).createEvent(loggerName, marker, fqcn, requiresLocation() ? StackLocatorUtil.calcLocation(fqcn) : null, level, data, props, t) : logEventFactory.createEvent(loggerName, marker, fqcn, level, data, props, t); try { log(logEvent, LoggerConfigPredicate.ALL); } finally { ReusableLogEventFactory.release(logEvent); } } protectedvoid log(final LogEvent event, final LoggerConfigPredicate predicate) { if (!isFiltered(event)) { processLogEvent(event, predicate); } } privatevoid processLogEvent(final LogEvent event, final LoggerConfigPredicate predicate) { event.setIncludeLocation(isIncludeLocation()); if (predicate.allow(this)) { callAppenders(event); } logParent(event, predicate); } protectedvoid callAppenders(final LogEvent event) { final AppenderControl[] controls = appenders.get(); //noinspection ForLoopReplaceableByForEach for (inti = 0; i < controls.length; i++) { controls[i].callAppender(event); } } |
在抽象类AbstractOutputStreamAppender中的appender()方法,最后调用directEncodeEvent()将日志信息写入文件。
/** * Actual writing occurs here. * <p> * Most subclasses of <code>AbstractOutputStreamAppender</code> will need to override this method. * </p> * * @param event The LogEvent. */ @Override publicvoid append(final LogEvent event) { try { tryAppend(event); } catch (final AppenderLoggingException ex) { error("Unable to write to stream " + manager.getName() + " for appender " + getName(), event, ex); throwex; } } privatevoid tryAppend(final LogEvent event) { if (Constants.ENABLE_DIRECT_ENCODERS) { directEncodeEvent(event); } else { |