001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.appender;
018
019import java.util.concurrent.TimeUnit;
020
021import org.apache.logging.log4j.Logger;
022import org.apache.logging.log4j.core.Appender;
023import org.apache.logging.log4j.core.ErrorHandler;
024import org.apache.logging.log4j.core.LogEvent;
025import org.apache.logging.log4j.status.StatusLogger;
026
027import static java.util.Objects.requireNonNull;
028
029/**
030 * The default {@link ErrorHandler} implementation falling back to {@link StatusLogger}.
031 * <p>
032 * It avoids flooding the {@link StatusLogger} by allowing either the first 3 errors or errors once every 5 minutes.
033 * </p>
034 */
035public class DefaultErrorHandler implements ErrorHandler {
036
037    private static final Logger LOGGER = StatusLogger.getLogger();
038
039    private static final int MAX_EXCEPTION_COUNT = 3;
040
041    private static final long EXCEPTION_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(5);
042
043    private int exceptionCount = 0;
044
045    private long lastExceptionInstantNanos = System.nanoTime() - EXCEPTION_INTERVAL_NANOS - 1;
046
047    private final Appender appender;
048
049    public DefaultErrorHandler(final Appender appender) {
050        this.appender = requireNonNull(appender, "appender");
051    }
052
053    /**
054     * Handle an error with a message.
055     * @param msg a message
056     */
057    @Override
058    public void error(final String msg) {
059        final boolean allowed = acquirePermit();
060        if (allowed) {
061            LOGGER.error(msg);
062        }
063    }
064
065    /**
066     * Handle an error with a message and an exception.
067     *
068     * @param msg a message
069     * @param error a {@link Throwable}
070     */
071    @Override
072    public void error(final String msg, final Throwable error) {
073        final boolean allowed = acquirePermit();
074        if (allowed) {
075            LOGGER.error(msg, error);
076        }
077        if (!appender.ignoreExceptions() && error != null && !(error instanceof AppenderLoggingException)) {
078            throw new AppenderLoggingException(msg, error);
079        }
080    }
081
082    /**
083     * Handle an error with a message, an exception, and a logging event.
084     *
085     * @param msg a message
086     * @param event a {@link LogEvent}
087     * @param error a {@link Throwable}
088     */
089    @Override
090    public void error(final String msg, final LogEvent event, final Throwable error) {
091        final boolean allowed = acquirePermit();
092        if (allowed) {
093            LOGGER.error(msg, error);
094        }
095        if (!appender.ignoreExceptions() && error != null && !(error instanceof AppenderLoggingException)) {
096            throw new AppenderLoggingException(msg, error);
097        }
098    }
099
100    private boolean acquirePermit() {
101        final long currentInstantNanos = System.nanoTime();
102        synchronized (this) {
103            if (currentInstantNanos - lastExceptionInstantNanos > EXCEPTION_INTERVAL_NANOS) {
104                lastExceptionInstantNanos = currentInstantNanos;
105                return true;
106            } else if (exceptionCount < MAX_EXCEPTION_COUNT) {
107                exceptionCount++;
108                lastExceptionInstantNanos = currentInstantNanos;
109                return true;
110            } else {
111                return false;
112            }
113        }
114    }
115
116    public Appender getAppender() {
117        return appender;
118    }
119
120}