【日志系列】日志框架Log4j2源码解析

初始化

LoggerFactory

    private static final Logger logger = LoggerFactory.getLogger(LogFilter.class);

LoggerFactory#getLogger()

    public static Logger getLogger(Class<?> clazz) {
        Logger logger = getLogger(clazz.getName());
        if (DETECT_LOGGER_NAME_MISMATCH) {
            Class<?> autoComputedCallingClass = Util.getCallingClass();
            if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
                Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
                Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
            }
        }

        return logger;
    }

    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }

AbstractLoggerAdapter#getLogger,抽象方法,getContext由子类重写。

    @Override
    public L getLogger(final String name) {
        final LoggerContext context = getContext();
        final ConcurrentMap<String, L> loggers = getLoggersInContext(context);
        final L logger = loggers.get(name);
        if (logger != null) {
            return logger;
        }
        loggers.putIfAbsent(name, newLogger(name, context));
        return loggers.get(name);
    }

    protected abstract LoggerContext getContext();

Log4jLoggerFactory#getContext,调用LogManager.getContext()

    protected LoggerContext getContext() {
        Class<?> anchor = StackLocatorUtil.getCallerClass(FQCN, "org.slf4j");
        return anchor == null ? LogManager.getContext() : this.getContext(StackLocatorUtil.getCallerClass(anchor));
    }

LogManager

LogManager初始化会调用静态方法块

  • 根据特定配置文件的配置信息获取loggerContextFactory
  • 没有找到对应的Factory的实现类,则通过ProviderUtil中的getProviders()方法载入providers。
  • 如果provider中没有获取到LoggerContextFactory的实现类或provider为空,则使用SimpleLoggerContextFactory作为LoggerContextFactory
    static {
        // Shortcut binding to force a specific logging implementation.
        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
        final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
        if (factoryClassName != null) {
            try {
                factory = LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class);
            } catch (final ClassNotFoundException cnfe) {
                LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
            } catch (final Exception ex) {
                LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex);
            }
        }

        if (factory == null) {
            final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>();
            // note that the following initial call to ProviderUtil may block until a Provider has been installed when
            // running in an OSGi environment
            if (ProviderUtil.hasProviders()) {
                for (final Provider provider : ProviderUtil.getProviders()) {
                    final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
                    if (factoryClass != null) {
                        try {
                            factories.put(provider.getPriority(), factoryClass.newInstance());
                        } catch (final Exception e) {
                            LOGGER.error("Unable to create class {} specified in provider URL {}", factoryClass.getName(), provider
                                    .getUrl(), e);
                        }
                    }
                }

                if (factories.isEmpty()) {
                    LOGGER.error("Log4j2 could not find a logging implementation. "
                            + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
                    factory = new SimpleLoggerContextFactory();
                } else if (factories.size() == 1) {
                    factory = factories.get(factories.lastKey());
                } else {
                    final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
                    for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
                        sb.append("Factory: ").append(entry.getValue().getClass().getName());
                        sb.append(", Weighting: ").append(entry.getKey()).append('\n');
                    }
                    factory = factories.get(factories.lastKey());
                    sb.append("Using factory: ").append(factory.getClass().getName());
                    LOGGER.warn(sb.toString());

                }
            } else {
                LOGGER.error("Log4j2 could not find a logging implementation. "
                        + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
                factory = new SimpleLoggerContextFactory();
            }
        }
    }

ProviderUtil,通过SPI加载Provider,得到Log4jProvider

    private ProviderUtil() {
        for (final ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
            try {
                loadProviders(classLoader);
            } catch (final Throwable ex) {
                LOGGER.debug("Unable to retrieve provider from ClassLoader {}", classLoader, ex);
            }
        }
        for (final LoaderUtil.UrlResource resource : LoaderUtil.findUrlResources(PROVIDER_RESOURCE)) {
            loadProvider(resource.getUrl(), resource.getClassLoader());
        }
    }

	protected static void loadProviders(final ClassLoader classLoader) {
		final ServiceLoader<Provider> serviceLoader = ServiceLoader.load(Provider.class, classLoader);
		for (final Provider provider : serviceLoader) {
			if (validVersion(provider.getVersions()) && !PROVIDERS.contains(provider)) {
				PROVIDERS.add(provider);
			}
		}
	}

Log4jProvider对应的工厂类Log4jContextFactory

public class Log4jProvider extends Provider {
    public Log4jProvider() {
        super(10, "2.6.0", Log4jContextFactory.class);
    }
}

LoggerContext

加载好LogManager,初始化得到Log4jContextFactory,会执行Log4jContextFactory#getContext,得到LoggerContext对象,并启动。

    public LoggerContext getContext(String fqcn, ClassLoader loader, Object externalContext, boolean currentContext) {
        LoggerContext ctx = this.selector.getContext(fqcn, loader, currentContext);
        if (externalContext != null && ctx.getExternalContext() == null) {
            ctx.setExternalContext(externalContext);
        }

        if (ctx.getState() == State.INITIALIZED) {
            ctx.start();
        }

        return ctx;
    }

LoggerContext#start()

    public void start() {
        LOGGER.debug("Starting LoggerContext[name={}, {}]...", this.getName(), this);
        if (PropertiesUtil.getProperties().getBooleanProperty("log4j.LoggerContext.stacktrace.on.start", false)) {
            LOGGER.debug("Stack trace to locate invoker", new Exception("Not a real error, showing stack trace to locate invoker"));
        }

        if (this.configLock.tryLock()) {
            try {
                if (this.isInitialized() || this.isStopped()) {
                    this.setStarting();
                    this.reconfigure();
                    if (this.configuration.isShutdownHookEnabled()) {
                        this.setUpShutdownHook();
                    }

                    this.setStarted();
                }
            } finally {
                this.configLock.unlock();
            }
        }

        LOGGER.debug("LoggerContext[name={}, {}] started OK.", this.getName(), this);
    }

核心方法是reconfigure

  • Configuration instance = ConfigurationFactory.getInstance().getConfiguration();,加载了一堆ConfigurationFactory,通过ConfigurationFactory去获取`Configuration``
  • LoggerContext#setConfiguration会执行config.start();
    private void reconfigure(URI configURI) {
        Object externalContext = this.externalMap.get("__EXTERNAL_CONTEXT_KEY__");
        ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? (ClassLoader)externalContext : null;
        LOGGER.debug("Reconfiguration started for context[name={}] at URI {} ({}) with optional ClassLoader: {}", this.contextName, configURI, this, cl);
        Configuration instance = ConfigurationFactory.getInstance().getConfiguration(this, this.contextName, configURI, cl);
        if (instance == null) {
            LOGGER.error("Reconfiguration failed: No configuration found for '{}' at '{}' in '{}'", this.contextName, configURI, cl);
        } else {
            this.setConfiguration(instance);
            String location = this.configuration == null ? "?" : String.valueOf(this.configuration.getConfigurationSource());
            LOGGER.debug("Reconfiguration complete for context[name={}] at URI {} ({}) with optional ClassLoader: {}", this.contextName, location, this, cl);
        }

    }

AbstractConfiguration#start,进行启动

    @Override
    public void start() {
        // Preserve the prior behavior of initializing during start if not initialized.
        if (getState().equals(State.INITIALIZING)) {
            initialize();
        }
        LOGGER.debug("Starting configuration {}", this);
        this.setStarting();
        if (watchManager.getIntervalSeconds() >= 0) {
            watchManager.start();
        }
        if (hasAsyncLoggers()) {
            asyncLoggerConfigDisruptor.start();
        }
        final Set<LoggerConfig> alreadyStarted = new HashSet<>();
        for (final LoggerConfig logger : loggerConfigs.values()) {
            logger.start();
            alreadyStarted.add(logger);
        }
        for (final Appender appender : appenders.values()) {
            appender.start();
        }
        if (!alreadyStarted.contains(root)) { // LOG4J2-392
            root.start(); // LOG4J2-336
        }
        super.start();
        LOGGER.debug("Started configuration {} OK.", this);
    }

AbstractConfiguration#initialize,进行初始化。

    @Override
    public void initialize() {
        LOGGER.debug(Version.getProductString() + " initializing configuration {}", this);
        subst.setConfiguration(this);
        try {
            scriptManager = new ScriptManager(this, watchManager);
        } catch (final LinkageError | Exception e) {
            // LOG4J2-1920 ScriptEngineManager is not available in Android
            LOGGER.info("Cannot initialize scripting support because this JRE does not support it.", e);
        }
        pluginManager.collectPlugins(pluginPackages);
        final PluginManager levelPlugins = new PluginManager(Level.CATEGORY);
        levelPlugins.collectPlugins(pluginPackages);
        final Map<String, PluginType<?>> plugins = levelPlugins.getPlugins();
        if (plugins != null) {
            for (final PluginType<?> type : plugins.values()) {
                try {
                    // Cause the class to be initialized if it isn't already.
                    Loader.initializeClass(type.getPluginClass().getName(), type.getPluginClass().getClassLoader());
                } catch (final Exception e) {
                    LOGGER.error("Unable to initialize {} due to {}", type.getPluginClass().getName(), e.getClass()
                            .getSimpleName(), e);
                }
            }
        }
        setup();
        setupAdvertisement();
        doConfigure();
        setState(State.INITIALIZED);
        LOGGER.debug("Configuration {} initialized", this);
    }

XmlConfiguration#setup,对xml进行解析,constructHierarchy是一个递归方法。

    @Override
    public void setup() {
        if (rootElement == null) {
            LOGGER.error("No logging configuration");
            return;
        }
        constructHierarchy(rootNode, rootElement);
        if (status.size() > 0) {
            for (final Status s : status) {
                LOGGER.error("Error processing element {} ({}): {}", s.name, s.element, s.errorType);
            }
            return;
        }
        rootElement = null;
    }

XmlConfiguration#constructHierarchy,使用w3cNode对xml进行解析

    private void constructHierarchy(final Node node, final Element element) {
        processAttributes(node, element);
        final StringBuilder buffer = new StringBuilder();
        final NodeList list = element.getChildNodes();
        final List<Node> children = node.getChildren();
        for (int i = 0; i < list.getLength(); i++) {
            final org.w3c.dom.Node w3cNode = list.item(i);
            if (w3cNode instanceof Element) {
                final Element child = (Element) w3cNode;
                final String name = getType(child);
                final PluginType<?> type = pluginManager.getPluginType(name);
                final Node childNode = new Node(node, name, type);
                constructHierarchy(childNode, child);
                if (type == null) {
                    final String value = childNode.getValue();
                    if (!childNode.hasChildren() && value != null) {
                        node.getAttributes().put(name, value);
                    } else {
                        status.add(new Status(name, element, ErrorType.CLASS_NOT_FOUND));
                    }
                } else {
                    children.add(childNode);
                }
            } else if (w3cNode instanceof Text) {
                final Text data = (Text) w3cNode;
                buffer.append(data.getData());
            }
        }

        final String text = buffer.toString().trim();
        if (text.length() > 0 || (!node.hasChildren() && !node.isRoot())) {
            node.setValue(text);
        }
    }

PluginManager

PluginManager#collectPlugins(),根据包名加载插件

    public void collectPlugins(List<String> packages) {
        String categoryLowerCase = this.category.toLowerCase();
        Map<String, PluginType<?>> newPlugins = new LinkedHashMap();
        Map<String, List<PluginType<?>>> builtInPlugins = PluginRegistry.getInstance().loadFromMainClassLoader();
        if (builtInPlugins.isEmpty()) {
            builtInPlugins = PluginRegistry.getInstance().loadFromPackage("org.apache.logging.log4j.core");
        }

        mergeByName(newPlugins, (List)builtInPlugins.get(categoryLowerCase));
        Iterator var5 = PluginRegistry.getInstance().getPluginsByCategoryByBundleId().values().iterator();

        while(var5.hasNext()) {
            Map<String, List<PluginType<?>>> pluginsByCategory = (Map)var5.next();
            mergeByName(newPlugins, (List)pluginsByCategory.get(categoryLowerCase));
        }

        var5 = PACKAGES.iterator();

        String pkg;
        while(var5.hasNext()) {
            pkg = (String)var5.next();
            mergeByName(newPlugins, (List)PluginRegistry.getInstance().loadFromPackage(pkg).get(categoryLowerCase));
        }

        if (packages != null) {
            var5 = packages.iterator();

            while(var5.hasNext()) {
                pkg = (String)var5.next();
                mergeByName(newPlugins, (List)PluginRegistry.getInstance().loadFromPackage(pkg).get(categoryLowerCase));
            }
        }

        LOGGER.debug("PluginManager '{}' found {} plugins", this.category, newPlugins.size());
        this.plugins = newPlugins;
    }

打印日志

Log4jLogger#info()

    public void info(String format) {
        this.logger.logIfEnabled(FQCN, Level.INFO, (org.apache.logging.log4j.Marker)null, format);
    }

AbstractLogger#logIfEnabled(),判断是否有权限。

    public void logIfEnabled(final String fqcn, final Level level, final Marker marker, final String message) {
        if (isEnabled(level, marker, message)) {
            logMessage(fqcn, level, marker, message);
        }
    }

Logger#isEnabled(),判断是否有权限

    @Override
    public boolean isEnabled(final Level level, final Marker marker, final String message) {
        return privateConfig.filter(level, marker, message);
    }

PrivateConfig#filter(),判断当前的Logger的日志级别是否大于输出的日志级别。

        boolean filter(final Level level, final Marker marker, final String msg) {
            final Filter filter = config.getFilter();
            if (filter != null) {
                final Filter.Result r = filter.filter(logger, level, marker, msg);
                if (r != Filter.Result.NEUTRAL) {
                    return r == Filter.Result.ACCEPT;
                }
            }
            return level != null && intLevel >= level.intLevel();
        }

AbstractLogger#tryLogMessage,输出日志

    private void tryLogMessage(final String fqcn,
                               final StackTraceElement location,
                               final Level level,
                               final Marker marker,
                               final Message msg,
                               final Throwable throwable) {
        try {
            log(level, marker, fqcn, location, msg, throwable);
        } catch (final Exception e) {
            // LOG4J2-1990 Log4j2 suppresses all exceptions that occur once application called the logger
            handleLogMessageException(e, fqcn, msg);
        }
    }

Logger#log,获取到ReliabilityStrategy,进行日志输出。

    @Override
    protected void log(final Level level, final Marker marker, final String fqcn, final StackTraceElement location,
        final Message message, final Throwable throwable) {
        final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
        if (strategy instanceof LocationAwareReliabilityStrategy) {
            ((LocationAwareReliabilityStrategy) strategy).log(this, getName(), fqcn, location, marker, level,
                message, throwable);
        } else {
            strategy.log(this, getName(), fqcn, marker, level, message, throwable);
        }
    }

AwaitCompletionReliabilityStrategy#log(),获取到LoggerConfig,执行log方法。

    @Override
    public void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn,
        final StackTraceElement location, final Marker marker, final Level level, final Message data,
        final Throwable t) {
        final LoggerConfig config = getActiveLoggerConfig(reconfigured);
        try {
            config.log(loggerName, fqcn, location, marker, level, data, t);
        } finally {
            config.getReliabilityStrategy().afterLogEvent();
        }
    }

LoggerConfig#log(),创建LogEvent

    public void log(final String loggerName, final String fqcn, final StackTraceElement location, final Marker marker,
        final Level level, final Message data, final Throwable t) {
        List<Property> props = null;
        if (!propertiesRequireLookup) {
            props = properties;
        } else {
            if (properties != null) {
                props = new ArrayList<>(properties.size());
                final LogEvent event = Log4jLogEvent.newBuilder()
                    .setMessage(data)
                    .setMarker(marker)
                    .setLevel(level)
                    .setLoggerName(loggerName)
                    .setLoggerFqcn(fqcn)
                    .setThrown(t)
                    .build();
                for (int i = 0; i < properties.size(); i++) {
                    final Property prop = properties.get(i);
                    final String value = prop.isValueNeedsLookup() // since LOG4J2-1575
                        ? config.getStrSubstitutor().replace(event, prop.getValue()) //
                        : prop.getValue();
                    props.add(Property.createProperty(prop.getName(), value));
                }
            }
        }
        final LogEvent logEvent = logEventFactory instanceof LocationAwareLogEventFactory ?
            ((LocationAwareLogEventFactory) logEventFactory).createEvent(loggerName, marker, fqcn, location, level,
                data, props, t) : logEventFactory.createEvent(loggerName, marker, fqcn, level, data, props, t);
        try {
            log(logEvent, LoggerConfigPredicate.ALL);
        } finally {
            // LOG4J2-1583 prevent scrambled logs when logging calls are nested (logging in toString())
            ReusableLogEventFactory.release(logEvent);
        }
    }

    protected void log(final LogEvent event, final LoggerConfigPredicate predicate) {
        if (!isFiltered(event)) {
            processLogEvent(event, predicate);
        }
    }

LoggerConfig#processLogEvent,调用appender

    private void processLogEvent(final LogEvent event, final LoggerConfigPredicate predicate) {
        event.setIncludeLocation(isIncludeLocation());
        if (predicate.allow(this)) {
            callAppenders(event);
        }
        logParent(event, predicate);
    }

ConsoleAppender

ConsoleAppender,控制台输出器,获取Layout,执行PatternLayout#encode

    public void append(final LogEvent event) {
        try {
            tryAppend(event);
        } catch (final AppenderLoggingException ex) {
            error("Unable to write to stream " + manager.getName() + " for appender " + getName(), event, ex);
            throw ex;
        }
    }

    private void tryAppend(final LogEvent event) {
        if (Constants.ENABLE_DIRECT_ENCODERS) {
            directEncodeEvent(event);
        } else {
            writeByteArrayToManager(event);
        }
    }

    protected void directEncodeEvent(final LogEvent event) {
        getLayout().encode(event, manager);
        if (this.immediateFlush || event.isEndOfBatch()) {
            manager.flush();
        }
    }

PatternLayout#encode,布局器进行输出。

    @Override
    public void encode(final LogEvent event, final ByteBufferDestination destination) {
        if (!(eventSerializer instanceof Serializer2)) {
            super.encode(event, destination);
            return;
        }
        final StringBuilder text = toText((Serializer2) eventSerializer, event, getStringBuilder());
        final Encoder<StringBuilder> encoder = getStringBuilderEncoder();
        encoder.encode(text, destination);
        trimToMaxSize(text);
    }

    private StringBuilder toText(final Serializer2 serializer, final LogEvent event,
            final StringBuilder destination) {
        return serializer.toSerializable(event, destination);
    }

PatternLayout.PatternSerializer#toSerializable(),获取格式化器的集合,挨个处理

        @Override
        public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {
            final int len = formatters.length;
            for (int i = 0; i < len; i++) {
                formatters[i].format(event, buffer);
            }
            if (replace != null) { // creates temporary objects
                String str = buffer.toString();
                str = replace.format(str);
                buffer.setLength(0);
                buffer.append(str);
            }
            return buffer;
        }

LoggerPatternConverter就是把pattern中的%c转换为当前类名。

@Plugin(name = "LoggerPatternConverter", category = PatternConverter.CATEGORY)
@ConverterKeys({ "c", "logger" })
@PerformanceSensitive("allocation")
public final class LoggerPatternConverter extends NamePatternConverter {
    /**
     * Singleton.
     */
    private static final LoggerPatternConverter INSTANCE =
        new LoggerPatternConverter(null);

    /**
     * Private constructor.
     *
     * @param options options, may be null.
     */
    private LoggerPatternConverter(final String[] options) {
        super("Logger", "logger", options);
    }

    /**
     * Obtains an instance of pattern converter.
     *
     * @param options options, may be null.
     * @return instance of pattern converter.
     */
    public static LoggerPatternConverter newInstance(
        final String[] options) {
        if (options == null || options.length == 0) {
            return INSTANCE;
        }

        return new LoggerPatternConverter(options);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void format(final LogEvent event, final StringBuilder toAppendTo) {
        abbreviate(event.getLoggerName(), toAppendTo);
    }
}

AsyncAppender

AsyncAppender#append,添加到队列中。

    @Override
    public void append(final LogEvent logEvent) {
        if (!isStarted()) {
            throw new IllegalStateException("AsyncAppender " + getName() + " is not active");
        }
        final Log4jLogEvent memento = Log4jLogEvent.createMemento(logEvent, includeLocation);
        InternalAsyncUtil.makeMessageImmutable(logEvent.getMessage());
        if (!transfer(memento)) {
            if (blocking) {
                if (AbstractLogger.getRecursionDepth() > 1) { // LOG4J2-1518, LOG4J2-2031
                    // If queue is full AND we are in a recursive call, call appender directly to prevent deadlock
                    AsyncQueueFullMessageUtil.logWarningToStatusLogger();
                    logMessageInCurrentThread(logEvent);
                } else {
                    // delegate to the event router (which may discard, enqueue and block, or log in current thread)
                    final EventRoute route = asyncQueueFullPolicy.getRoute(thread.getId(), memento.getLevel());
                    route.logMessage(this, memento);
                }
            } else {
                error("Appender " + getName() + " is unable to write primary appenders. queue is full");
                logToErrorAppenderIfNecessary(false, memento);
            }
        }
    }

    private boolean transfer(final LogEvent memento) {
        return queue instanceof TransferQueue
            ? ((TransferQueue<LogEvent>) queue).tryTransfer(memento)
            : queue.offer(memento);
    }

AsyncThread,会不断的从队列中获取LogEvent,进行消费

        @Override
        public void run() {
            while (!shutdown) {
                LogEvent event;
                try {
                    event = queue.take();
                    if (event == SHUTDOWN_LOG_EVENT) {
                        shutdown = true;
                        continue;
                    }
                } catch (final InterruptedException ex) {
                    break; // LOG4J2-830
                }
                event.setEndOfBatch(queue.isEmpty());
                final boolean success = callAppenders(event);
                if (!success && errorAppender != null) {
                    try {
                        errorAppender.callAppender(event);
                    } catch (final Exception ex) {
                        // Silently accept the error.
                    }
                }
            }
        }

异步日志

AsyncLoggerContext

AsyncLoggerContext#start(),启动loggerDisruptor

    @Override
    public void start() {
        loggerDisruptor.start();
        super.start();
    }

AsyncLoggerDisruptor#start,启动disruptor,设置处理器RingBufferLogEventHandler

    public synchronized void start() {
        if (disruptor != null) {
            LOGGER.trace(
                    "[{}] AsyncLoggerDisruptor not starting new disruptor for this context, using existing object.",
                    contextName);
            return;
        }
        LOGGER.trace("[{}] AsyncLoggerDisruptor creating new disruptor for this context.", contextName);
        ringBufferSize = DisruptorUtil.calculateRingBufferSize("AsyncLogger.RingBufferSize");
        final WaitStrategy waitStrategy = DisruptorUtil.createWaitStrategy("AsyncLogger.WaitStrategy");

        final ThreadFactory threadFactory = new Log4jThreadFactory("AsyncLogger[" + contextName + "]", true, Thread.NORM_PRIORITY) {
            @Override
            public Thread newThread(final Runnable r) {
                final Thread result = super.newThread(r);
                backgroundThreadId = result.getId();
                return result;
            }
        };
        asyncQueueFullPolicy = AsyncQueueFullPolicyFactory.create();

        disruptor = new Disruptor<>(RingBufferLogEvent.FACTORY, ringBufferSize, threadFactory, ProducerType.MULTI,
                waitStrategy);

        final ExceptionHandler<RingBufferLogEvent> errorHandler = DisruptorUtil.getAsyncLoggerExceptionHandler();
        disruptor.setDefaultExceptionHandler(errorHandler);

        final RingBufferLogEventHandler[] handlers = {new RingBufferLogEventHandler()};
        disruptor.handleEventsWith(handlers);

        LOGGER.debug("[{}] Starting AsyncLogger disruptor for this context with ringbufferSize={}, waitStrategy={}, "
                + "exceptionHandler={}...", contextName, disruptor.getRingBuffer().getBufferSize(), waitStrategy
                .getClass().getSimpleName(), errorHandler);
        disruptor.start();

        LOGGER.trace("[{}] AsyncLoggers use a {} translator", contextName, useThreadLocalTranslator ? "threadlocal"
                : "vararg");
        super.start();
    }

AsyncLogger

AsyncLogger#log,异步日志输出。

    @Override
    public void log(final Level level, final Marker marker, final String fqcn, final StackTraceElement location,
        final Message message, final Throwable throwable) {
        getTranslatorType().log(fqcn, location, level, marker, message, throwable);
    }

AsyncLogger#logWithThreadLocalTranslator(),将日志相关的信息转换成RingBufferLogEvent(RingBuffer是Disruptor的无所队列),然后将其发布到RingBuffer中。

    private void logWithThreadLocalTranslator(final String fqcn, final StackTraceElement location, final Level level,
        final Marker marker, final Message message, final Throwable thrown) {
        // Implementation note: this method is tuned for performance. MODIFY WITH CARE!

        final RingBufferLogEventTranslator translator = getCachedTranslator();
        initTranslator(translator, fqcn, location, level, marker, message, thrown);
        initTranslatorThreadValues(translator);
        publish(translator);
    }

    private void publish(final RingBufferLogEventTranslator translator) {
        if (!loggerDisruptor.tryPublish(translator)) {
            handleRingBufferFull(translator);
        }
    }

    private void handleRingBufferFull(final RingBufferLogEventTranslator translator) {
        if (AbstractLogger.getRecursionDepth() > 1) { // LOG4J2-1518, LOG4J2-2031
            // If queue is full AND we are in a recursive call, call appender directly to prevent deadlock
            AsyncQueueFullMessageUtil.logWarningToStatusLogger();
            logMessageInCurrentThread(translator.fqcn, translator.level, translator.marker, translator.message,
                    translator.thrown);
            translator.clear();
            return;
        }
        final EventRoute eventRoute = loggerDisruptor.getEventRoute(translator.level);
        switch (eventRoute) {
            case ENQUEUE:
                loggerDisruptor.enqueueLogMessageWhenQueueFull(translator);
                break;
            case SYNCHRONOUS:
                logMessageInCurrentThread(translator.fqcn, translator.level, translator.marker, translator.message,
                        translator.thrown);
                translator.clear();
                break;
            case DISCARD:
                translator.clear();
                break;
            default:
                throw new IllegalStateException("Unknown EventRoute " + eventRoute);
        }
    }

RingBufferLogEventHandler

日志处理器,调用RingBufferLogEventexecute方法

    @Override
    public void onEvent(final RingBufferLogEvent event, final long sequence,
            final boolean endOfBatch) throws Exception {
        try {
            event.execute(endOfBatch);
        }
        finally {
            event.clear();
            // notify the BatchEventProcessor that the sequence has progressed.
            // Without this callback the sequence would not be progressed
            // until the batch has completely finished.
            notifyCallback(sequence);
        }
    }

RingBufferLogEvent#execute,输出日志。

    public void execute(final boolean endOfBatch) {
        this.endOfBatch = endOfBatch;
        asyncLogger.actualAsyncLog(this);
    }

AsyncLogger#actualAsyncLog

    public void actualAsyncLog(final RingBufferLogEvent event) {
        final LoggerConfig privateConfigLoggerConfig = privateConfig.loggerConfig;
        final List<Property> properties = privateConfigLoggerConfig.getPropertyList();

        if (properties != null) {
            onPropertiesPresent(event, properties);
        }

        privateConfigLoggerConfig.getReliabilityStrategy().log(this, event);
    }

Log4j使用disruptor实现异步日志

引入依赖

        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.0</version>
        </dependency>

JVM配置启动参数

-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

log4j2.xml使用AsyncRoot/AsyncLogger替代Root/Logger

    <Loggers>
        <AsyncRoot level="info">
            <AppenderRef ref="STDOUT" />
            <AppenderRef ref="CatAppender" />
            <AppenderRef ref="rabbitmq" level="all"/>
        </AsyncRoot>
    </Loggers>

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个集成目前Android主流优秀第三方组件、优秀好用的自定义控件、实用工具类封装、以及一些APP共通模块(比如:版本更新、意见反馈、引导界面等等)的开发包,帮助程序员快速开发自己的APP 已集成第三方开源组件: 网络请求库android-async-http 图片异步加载库universal-image-loader Sqlite数据库操作ormlite-android 社会化分享ShareSDK 短信验证码 Zxing二维码库 百度地图定位SDK 谷歌依赖注入库RoboGuice WebService调用库ksoap2 XML解析XStream 动画开源库nineoldandroids 表单验证库android-validation-komensky 更多优秀开源库等待集成... 已封装工具类: HTTP网络通信工具类(ToolHTTP.java),get/post请求,支持多种接口回调 SOAP协议通信工具类(ToolSOAP.java),基于异步回调请求WebService接口 Sqlite数据库操作工具类(ToolDatabase.java),获取DAO、创建表、删除表等API 提示信息对话框工具类(ToolAlert.java),已集成泡泡、土司、对话框三种提示 文件操作工具类(ToolFile.java),assets/raw/xml/shrePerface/等文件读写API 地图定位工具类(ToolLocation.java),读取GPS状态、请求定位、获取经纬度等方法 社会化分享工具类(ToolShareSDK.java),各大开发平台分享API操作 短信验证码工具类(ToolMSM.java),移动/联通/电信三网发送手机短信验证码、异步回调验证结果 字符串操作工具类(ToolString.java),生成UUID、非空非NULL逻辑判断、生成MD5等常用共通方法 数据操作工具类(ToolData.java),自动递归获取表单数据封装成Map、本地数据分页共通方法等 图片操作工具类(ToolPicture.java),生成二维码、验证码、灰度、合成、圆角、水印等操作 读取本地资源工具类(ToolResource.java),反射本地资源文件API,避免依赖R文件,方便jar形式发布 Android单位转换工具类(ToolUnit.java),sp/dp/px互转 自定义Toast工具类(ToolToast.java),自定义背景色、字体颜色、大小、边框等 Properties操作工具类(ToolProperties.java),读写Properties文件操作 网络操作工具类(ToolNetwork.java),获取网络信息、更改切换网络等相关操作 日期操作工具类(ToolDateTime.java),获取日期、日期加减、格式化日期、日期转换等操作 XML操作工具类(ToolXml.java),基于DOM/XMLPullPaser模式解析、生成XML操作 XMPP操作工具类(ToolXMPP.java),基于XMPP协议的相关API操作 适配字体工具类(ToolAutoFit.java),代码根据设备密度自动缩放View的字体大小 LOG相关工具类(ToolLog.java) 功能待续-->切入记录异常日志,并存储文件或上传至服务器 已封装/收集自定义控件: 兼容低版本的SwitchButton 追加自定义属性Value的CheckBox/RadioButton/RadioGroup/SingleSpinner 圆角提示信息TipsView 圆角图片RoundImageView 自定义样式风格ProgressDialog 自定义样式WebView 圆形进度条(RoundProgressBar)、垂直进度条(VerticalProgressBar) 开源优秀自定义控件:imageindicator(图片轮播/引导界面)、pulltorefresh、XListView、TableFixHeaders(水平 垂直滚动 首行 首列冻结表格)、ViewPager
注意:本项目使用android studio开发,eclipse可能无法直接使用。 本项目是PHPHub 社区非官方 Android 客户端, App UI 风格遵循了 Google Material Design 设计风格, 项目架构使用了 MVP 模式, 数据处理使用了 RxJava Retrofit 技术。 项目特点: 界面设计遵循 Google 设计规范 沉浸式状态栏 数据处理使用了 RxJava Retrofit 二维码扫码登录 WebView 图片点击事件 JS 注入 项目架构使用 MVP 模式 发布帖子支持使用 Markdown 语法编辑器 多主题切换 运行环境: Min Android SDK version 4.0 Android Studio 2.2.1 Gradle version 2.14.1 Gradle plugin version 2.2.1 Build tools version 24.0.2 使用到开源库: 项目名称 简介 Logger 一个强大漂亮的Log输出日志,支持json格式化输出 Material-Dialogs 一个强大漂亮的Material Dialog BGABadgeView-Android Android徽章控件 MultiStateView 通用显示各种状态 View FloatingActionButton 悬浮操作按钮库, 支持监听滑滚动事件 glide-transformations 快速实现毛玻璃效果 writeily-pro Markdown 文本编辑 Demo RxJava RxJava RxAndroid RxAndroid Retrofit Retrofit Gson Json 解析库 Prettytime 格式化日期时间 barcodescanner 二维码扫描 fresco Facebook 开源的图片缓存库 butterknife 注解框架 materialish-progress Material Design 风格进度条 PhotoDraweeView 基于 Fresco 的图片缩放控件 cwac-anddown Markdown 渲染 gm-mkdroid 一个所见即所得的 Markdown 编辑器 android-Ultra-Pull-To-Refresh-With-Load-More 这个是基于 Ultra-Pull-to-Refresh 库修改增加支持加载更多的刷新控件 T-MVP 泛型深度解耦下的MVP大瘦身 本项目git地址:https://github.com/Freelander/Elephant
SuperSocket是一个轻量级的可扩展的Socket开发框架,可用来构建一个服务器端Socket程序,而无需了解如何使用Socket,如何维护Socket连接,Socket是如何工作的。该项目使用纯C#开发,易于扩展和集成到已有的项目。只要你的已有系统是使用.NET开发的,你都能够使用SuperSocket来轻易的开发出你需要的Socket应用程序来集成到你的现有系统之中。 SuperSocket具有如下特点: 简单易用,只需要几个类就能创建出健壮的Socket服务器端程序 性能优良, 稳定可靠 支持各种协议, 内置的协议解析工具让你把实现通信协议这种复杂的工作变得很简单 自动支持SSL/TLS传输层加密 强大,灵活而且可扩展的配置让你开发Socket服务器省时省力 支持多个socket服务器实例运行, 而且支持多个服务器实例的隔离 SuperSocket能以控制台或者Windows服务形式运行。一个脚本就能将SuperSocket安装成服务 灵活的日志策略能够记录大部分socket活动 支持UDP 支持IPv6 支持Windows Azure 支持Linux/Unix操作系统(通过Mono 2.10或以上版本) 内置可直接使用的Flash/Silverlight Socket策略服务器 SuperSocket v1.6.5 更新日志: 修复NuGet包中错误的log4net引用版本号; 增加高版本TLS加密的支持; 增加配置热更新功能; 改进服务器实例停止顺序; 支持指定Active Connect本地端口的的功能; 支持Active Connect的本地端口复用; 改进进程级别隔离模式下的配置文件的读取,使其支持读取SuperSocket之外的配置节点,如appSetting和connectionString等等; 修复了性能计数器在bootstrap停止时抛出的异常; 修复了发送队列关于"索引超出范围"的异常; 为ServerManager做出一些改进;
这是一个集成目前Android主流优秀第三方组件、优秀好用的自定义控件、实用工具类封装、以及一些APP共通模块(比如:版本更新、意见反馈、引导界面等等)的开发包,帮助程序员快速开发自己的APP   已集成第三方开源组件: 网络请求库android-async-http 图片异步加载库universal-image-loader Sqlite数据库操作ormlite-android 社会化分享ShareSDK 短信验证码 Zxing二维码库 百度地图定位SDK 谷歌依赖注入库RoboGuice WebService调用库ksoap2 XML解析XStream 动画开源库nineoldandroids 表单验证库android-validation-komensky 更多优秀开源库等待集成... 已封装工具类: HTTP网络通信工具类(ToolHTTP.java),get/post请求,支持多种接口回调 SOAP协议通信工具类(ToolSOAP.java),基于异步回调请求WebService接口 Sqlite数据库操作工具类(ToolDatabase.java),获取DAO、创建表、删除表等API 提示信息对话框工具类(ToolAlert.java),已集成泡泡、土司、对话框三种提示 文件操作工具类(ToolFile.java),assets/raw/xml/shrePerface/等文件读写API 地图定位工具类(ToolLocation.java),读取GPS状态、请求定位、获取经纬度等方法 社会化分享工具类(ToolShareSDK.java),各大开发平台分享API操作 短信验证码工具类(ToolMSM.java),移动/联通/电信三网发送手机短信验证码、异步回调验证结果 字符串操作工具类(ToolString.java),生成UUID、非空非NULL逻辑判断、生成MD5等常用共通方法 数据操作工具类(ToolData.java),自动递归获取表单数据封装成Map、本地数据分页共通方法等 图片操作工具类(ToolPicture.java),生成二维码、验证码、灰度、合成、圆角、水印等操作 读取本地资源工具类(ToolResource.java),反射本地资源文件API,避免依赖R文件,方便jar形式发布 Android单位转换工具类(ToolUnit.java),sp/dp/px互转 自定义Toast工具类(ToolToast.java),自定义背景色、字体颜色、大小、边框等 Properties操作工具类(ToolProperties.java),读写Properties文件操作 网络操作工具类(ToolNetwork.java),获取网络信息、更改切换网络等相关操作 日期操作工具类(ToolDateTime.java),获取日期、日期加减、格式化日期、日期转换等操作 XML操作工具类(ToolXml.java),基于DOM/XMLPullPaser模式解析、生成XML操作 XMPP操作工具类(ToolXMPP.java),基于XMPP协议的相关API操作 适配字体工具类(ToolAutoFit.java),代码根据设备密度自动缩放View的字体大小 LOG相关工具类(ToolLog.java) 功能待续-->切入记录异常日志,并存储文件或上传至服务器 已封装/收集自定义控件: 兼容低版本的SwitchButton 追加自定义属性Value的CheckBox/RadioButton/RadioGroup/SingleSpinner 圆角提示信息TipsView 圆角图片RoundImageView 自定义样式风格ProgressDialog 自定义样式WebView 圆形进度条(RoundProgressBar)、垂直进度条(VerticalProgressBar) 开源优秀自定义控件:imageindicator(图片轮播/引导界面)、pulltorefresh、XListView、TableFixHeaders(水平 垂直滚动 首行 首列冻结表格)、ViewPager 项目源码的git主页:http://git.oschina.net/zftlive/zftlive javaapk.com之前也介绍过这类的项目源码,可以点击javaapk.com首页的开发框架分类下载。
Hasor开发框架是一款基于Java语言的应用程序开发框架,它的核心设计目标是提供一个简单、切必要的环境给开发者。开发者可以在此基础上,通过Hasor强有力的粘合机制,构建出更加完善的应用场景。同时Hasor的各种内置插件也会帮助开发者进行快速开发。 基于目前Hasor内核上已经发展出DB、Web、Restful三大独立的基础框架。 特征: 1、IoC/Aop编程模型,设计精巧,使用简单 2、COC原则的最佳实践,‘零’配置文件 3、微内核 + 扩展,基于内核已发展出DB、Web、Restful三大独立的基础框架 4、真正的零开发解析项目特有的自定义Xml配置 5、支持模板化配置文件,程序打包之后一套配置通吃(日常、预发、线上)以及其它各种环境 6、完备的JDBC操作接口,支持Result -> Object映射 7、提供三种途径控制事务,支持七种事务传播属性,标准的事务隔离级别 8、支持多数据源、及多数据源下的事务控制(非JPA) 9、内置事件机制,方便进行业务深度解耦,使业务逻辑更佳清晰 10、支持Web类型项目开发,提供restful风格的mvc开发方式 11、支持Form表单验证、支持场景化验证 12、提供开放的模版渲染接口,支持各种类型的模版引擎 13、提供丰富的工具箱,帮助您快速开发,有了它您甚至不需要 apache-commons 14、支持log4j、logback等多种主流日志框架 15、体积小,无第三方依赖 最低要求: 1、jdk6 2、servlet 2.3
好的,我可以为您提供一些关于Spring Boot整合Log4j2的指导。 首先,您需要在Spring Boot项目的pom.xml文件中添加Log4j2相关依赖。以下是一个示例: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> </dependencies> ``` 接下来,您需要在项目中创建log4j2.xml文件。以下是一个示例: ```xml <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="console"/> </Root> </Loggers> </Configuration> ``` 在这个示例中,我们定义了一个名为“console”的Appender,用于将日志输出到控制台。我们还定义了一个名为“Root”的Logger,用于将所有日志消息发送到“console”Appender,并将日志级别设置为“info”。 最后,在项目中使用Log4j2日志记录。您可以使用Spring的内置日志记录注解@Log4j2来注释您的类。以下是一个示例: ```java import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller @Log4j2 public class HomeController { @GetMapping("/") public String home() { log.info("Home page accessed"); return "home"; } } ``` 这个示例中,我们使用了Lombok提供的@Log4j2注解来注释HomeController类。然后,我们在home()方法中使用log.info()方法来记录日志消息。 希望这些指导对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值