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      private static final long serialVersionUID = 1L;
40  
41      private final ReadWriteLock lock = new ReentrantReadWriteLock();
42      private final Lock readLock = lock.readLock();
43      private final Lock writeLock = lock.writeLock();
44  
45      private T manager;
46  
47      /**
48       * Instantiates the base appender.
49       *
50       * @param name The appender name.
51       * @param filter The filter, if any, to use.
52       * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
53       *                         they are propagated to the caller.
54       * @param manager The matching {@link AbstractDatabaseManager} implementation.
55       */
56      protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean ignoreExceptions,
57                                         final T manager) {
58          super(name, filter, null, ignoreExceptions);
59          this.manager = manager;
60      }
61  
62      /**
63       * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders
64       * do not use a layout at all. The JDBC appender has a layout-per-column pattern.
65       *
66       * @return {@code null}.
67       */
68      @Override
69      public final Layout<LogEvent> getLayout() {
70          return null;
71      }
72  
73      /**
74       * Returns the underlying manager in use within this appender.
75       *
76       * @return the manager.
77       */
78      public final T getManager() {
79          return this.manager;
80      }
81  
82      @Override
83      public final void start() {
84          if (this.getManager() == null) {
85              LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName());
86          }
87          super.start();
88          if (this.getManager() != null) {
89              this.getManager().startup();
90          }
91      }
92  
93      @Override
94      public final void stop() {
95          super.stop();
96          if (this.getManager() != null) {
97              this.getManager().release();
98          }
99      }
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 }