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 */
017 package org.apache.logging.log4j.core.appender.db;
018
019 import java.util.concurrent.locks.Lock;
020 import java.util.concurrent.locks.ReadWriteLock;
021 import java.util.concurrent.locks.ReentrantReadWriteLock;
022
023 import org.apache.logging.log4j.LoggingException;
024 import org.apache.logging.log4j.core.Filter;
025 import org.apache.logging.log4j.core.Layout;
026 import org.apache.logging.log4j.core.LogEvent;
027 import org.apache.logging.log4j.core.appender.AbstractAppender;
028 import org.apache.logging.log4j.core.appender.AppenderLoggingException;
029
030 /**
031 * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders
032 * should inherit from this base appender. Three implementations are currently provided:
033 * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa
034 * JPA}, and <a href="/log4j/2.x/log4j-nosql/apidocs/">NoSQL</a>.
035 *
036 * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires.
037 */
038 public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender {
039 private static final long serialVersionUID = 1L;
040
041 private final ReadWriteLock lock = new ReentrantReadWriteLock();
042 private final Lock readLock = lock.readLock();
043 private final Lock writeLock = lock.writeLock();
044
045 private T manager;
046
047 /**
048 * Instantiates the base appender.
049 *
050 * @param name The appender name.
051 * @param filter The filter, if any, to use.
052 * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
053 * they are propagated to the caller.
054 * @param manager The matching {@link AbstractDatabaseManager} implementation.
055 */
056 protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean ignoreExceptions,
057 final T manager) {
058 super(name, filter, null, ignoreExceptions);
059 this.manager = manager;
060 }
061
062 /**
063 * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders
064 * do not use a layout at all. The JDBC appender has a layout-per-column pattern.
065 *
066 * @return {@code null}.
067 */
068 @Override
069 public final Layout<LogEvent> getLayout() {
070 return null;
071 }
072
073 /**
074 * Returns the underlying manager in use within this appender.
075 *
076 * @return the manager.
077 */
078 public final T getManager() {
079 return this.manager;
080 }
081
082 @Override
083 public final void start() {
084 if (this.getManager() == null) {
085 LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName());
086 }
087 super.start();
088 if (this.getManager() != null) {
089 this.getManager().startup();
090 }
091 }
092
093 @Override
094 public final void stop() {
095 super.stop();
096 if (this.getManager() != null) {
097 this.getManager().release();
098 }
099 }
100
101 @Override
102 public final void append(final LogEvent event) {
103 this.readLock.lock();
104 try {
105 this.getManager().write(event);
106 } catch (final LoggingException e) {
107 LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
108 this.getName(), e);
109 throw e;
110 } catch (final Exception e) {
111 LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
112 this.getName(), e);
113 throw new AppenderLoggingException("Unable to write to database in appender: " + e.getMessage(), e);
114 } finally {
115 this.readLock.unlock();
116 }
117 }
118
119 /**
120 * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log
121 * events are written to the database without losing buffered or in-progress events. The existing manager is
122 * released only after the new manager has been installed. This method is thread-safe.
123 *
124 * @param manager The new manager to install.
125 */
126 protected final void replaceManager(final T manager) {
127 this.writeLock.lock();
128 try {
129 final T old = this.getManager();
130 if (!manager.isRunning()) {
131 manager.startup();
132 }
133 this.manager = manager;
134 old.release();
135 } finally {
136 this.writeLock.unlock();
137 }
138 }
139 }