| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache license, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the license for the specific language governing permissions and |
| * limitations under the license. |
| */ |
| package org.apache.logging.log4j.core.appender.db; |
| |
| import java.io.Serializable; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReadWriteLock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| import org.apache.logging.log4j.LoggingException; |
| import org.apache.logging.log4j.core.Filter; |
| import org.apache.logging.log4j.core.Layout; |
| import org.apache.logging.log4j.core.LogEvent; |
| import org.apache.logging.log4j.core.appender.AbstractAppender; |
| import org.apache.logging.log4j.core.appender.AppenderLoggingException; |
| import org.apache.logging.log4j.core.config.Property; |
| |
| /** |
| * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders |
| * should inherit from this base appender. Three implementations are currently provided: |
| * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa |
| * JPA}, and {@link org.apache.logging.log4j.core.appender.nosql NoSQL}. |
| * |
| * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires. |
| */ |
| public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender { |
| |
| public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B> { |
| // empty for now. |
| } |
| |
| public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000; |
| |
| private final ReadWriteLock lock = new ReentrantReadWriteLock(); |
| private final Lock readLock = lock.readLock(); |
| private final Lock writeLock = lock.writeLock(); |
| private T manager; |
| |
| /** |
| * Instantiates the base appender. |
| * |
| * @param name The appender name. |
| * @param filter The filter, if any, to use. |
| * @param layout The layout to use to format the event. |
| * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise |
| * they are propagated to the caller. |
| * @param properties TODO |
| * @param manager The matching {@link AbstractDatabaseManager} implementation. |
| */ |
| protected AbstractDatabaseAppender(final String name, final Filter filter, |
| final Layout<? extends Serializable> layout, final boolean ignoreExceptions, Property[] properties, final T manager) { |
| super(name, filter, layout, ignoreExceptions, properties); |
| this.manager = manager; |
| } |
| |
| @Override |
| public final void append(final LogEvent event) { |
| this.readLock.lock(); |
| try { |
| this.getManager().write(event, toSerializable(event)); |
| } catch (final LoggingException e) { |
| LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(), |
| this.getName(), e); |
| throw e; |
| } catch (final Exception e) { |
| LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(), |
| this.getName(), e); |
| throw new AppenderLoggingException("Unable to write to database in appender: " + e.getMessage(), e); |
| } finally { |
| this.readLock.unlock(); |
| } |
| } |
| |
| /** |
| * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders |
| * do not use a layout at all. The JDBC appender has a layout-per-column pattern. |
| * |
| * @return {@code null}. |
| */ |
| @Override |
| public final Layout<LogEvent> getLayout() { |
| return null; |
| } |
| |
| /** |
| * Returns the underlying manager in use within this appender. |
| * |
| * @return the manager. |
| */ |
| public final T getManager() { |
| return this.manager; |
| } |
| |
| /** |
| * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log |
| * events are written to the database without losing buffered or in-progress events. The existing manager is |
| * released only after the new manager has been installed. This method is thread-safe. |
| * |
| * @param manager The new manager to install. |
| */ |
| protected final void replaceManager(final T manager) { |
| this.writeLock.lock(); |
| try { |
| final T old = this.getManager(); |
| if (!manager.isRunning()) { |
| manager.startup(); |
| } |
| this.manager = manager; |
| old.close(); |
| } finally { |
| this.writeLock.unlock(); |
| } |
| } |
| |
| @Override |
| public final void start() { |
| if (this.getManager() == null) { |
| LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName()); |
| } |
| super.start(); |
| if (this.getManager() != null) { |
| this.getManager().startup(); |
| } |
| } |
| |
| @Override |
| public boolean stop(final long timeout, final TimeUnit timeUnit) { |
| setStopping(); |
| boolean stopped = super.stop(timeout, timeUnit, false); |
| if (this.getManager() != null) { |
| stopped &= this.getManager().stop(timeout, timeUnit); |
| } |
| setStopped(); |
| return stopped; |
| } |
| } |