View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender.db;
18  
19  import java.util.concurrent.locks.Lock;
20  import java.util.concurrent.locks.ReadWriteLock;
21  import java.util.concurrent.locks.ReentrantReadWriteLock;
22  
23  import org.apache.logging.log4j.LoggingException;
24  import org.apache.logging.log4j.core.Filter;
25  import org.apache.logging.log4j.core.Layout;
26  import org.apache.logging.log4j.core.LogEvent;
27  import org.apache.logging.log4j.core.appender.AbstractAppender;
28  import org.apache.logging.log4j.core.appender.AppenderLoggingException;
29  
30  /**
31   * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders
32   * should inherit from this base appender. Three implementations are currently provided:
33   * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa
34   * JPA}, and <a href="/log4j/2.x/log4j-nosql/apidocs/">NoSQL</a>.
35   *
36   * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires.
37   */
38  public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender {
39  
40      private final ReadWriteLock lock = new ReentrantReadWriteLock();
41      private final Lock readLock = lock.readLock();
42      private final Lock writeLock = lock.writeLock();
43  
44      private T manager;
45  
46      /**
47       * Instantiates the base appender.
48       *
49       * @param name The appender name.
50       * @param filter The filter, if any, to use.
51       * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
52       *                         they are propagated to the caller.
53       * @param manager The matching {@link AbstractDatabaseManager} implementation.
54       */
55      protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean ignoreExceptions,
56                                         final T manager) {
57          super(name, filter, null, ignoreExceptions);
58          this.manager = manager;
59      }
60  
61      /**
62       * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders
63       * do not use a layout at all. The JDBC appender has a layout-per-column pattern.
64       *
65       * @return {@code null}.
66       */
67      @Override
68      public final Layout<LogEvent> getLayout() {
69          return null;
70      }
71  
72      /**
73       * Returns the underlying manager in use within this appender.
74       *
75       * @return the manager.
76       */
77      public final T getManager() {
78          return this.manager;
79      }
80  
81      @Override
82      public final void start() {
83          if (this.getManager() == null) {
84              LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName());
85          }
86          super.start();
87          if (this.getManager() != null) {
88              this.getManager().startup();
89          }
90      }
91  
92      @Override
93      public final void stop() {
94          super.stop();
95          if (this.getManager() != null) {
96              this.getManager().release();
97          }
98      }
99  
100     @Override
101     public final void append(final LogEvent event) {
102         this.readLock.lock();
103         try {
104             this.getManager().write(event);
105         } catch (final LoggingException e) {
106             LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
107                     this.getName(), e);
108             throw e;
109         } catch (final Exception e) {
110             LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
111                     this.getName(), e);
112             throw new AppenderLoggingException("Unable to write to database in appender: " + e.getMessage(), e);
113         } finally {
114             this.readLock.unlock();
115         }
116     }
117 
118     /**
119      * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log
120      * events are written to the database without losing buffered or in-progress events. The existing manager is
121      * released only after the new manager has been installed. This method is thread-safe.
122      *
123      * @param manager The new manager to install.
124      */
125     protected final void replaceManager(final T manager) {
126         this.writeLock.lock();
127         try {
128             final T old = this.getManager();
129             if (!manager.isRunning()) {
130                 manager.startup();
131             }
132             this.manager = manager;
133             old.release();
134         } finally {
135             this.writeLock.unlock();
136         }
137     }
138 }