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.TimeUnit;
20  import java.util.concurrent.locks.Lock;
21  import java.util.concurrent.locks.ReadWriteLock;
22  import java.util.concurrent.locks.ReentrantReadWriteLock;
23  
24  import org.apache.logging.log4j.LoggingException;
25  import org.apache.logging.log4j.core.Filter;
26  import org.apache.logging.log4j.core.Layout;
27  import org.apache.logging.log4j.core.LogEvent;
28  import org.apache.logging.log4j.core.appender.AbstractAppender;
29  import org.apache.logging.log4j.core.appender.AppenderLoggingException;
30  
31  /**
32   * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders
33   * should inherit from this base appender. Three implementations are currently provided:
34   * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa
35   * JPA}, and <a href="/log4j/2.x/log4j-nosql/apidocs/">NoSQL</a>.
36   *
37   * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires.
38   */
39  public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender {
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 boolean stop(final long timeout, final TimeUnit timeUnit) {
95          setStopping();
96          boolean stopped = super.stop(timeout, timeUnit, false);
97          if (this.getManager() != null) {
98              stopped &= this.getManager().stop(timeout, timeUnit);
99          }
100         setStopped();
101         return stopped;
102     }
103 
104     @Override
105     public final void append(final LogEvent event) {
106         this.readLock.lock();
107         try {
108             this.getManager().write(event);
109         } catch (final LoggingException e) {
110             LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
111                     this.getName(), e);
112             throw e;
113         } catch (final Exception e) {
114             LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
115                     this.getName(), e);
116             throw new AppenderLoggingException("Unable to write to database in appender: " + e.getMessage(), e);
117         } finally {
118             this.readLock.unlock();
119         }
120     }
121 
122     /**
123      * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log
124      * events are written to the database without losing buffered or in-progress events. The existing manager is
125      * released only after the new manager has been installed. This method is thread-safe.
126      *
127      * @param manager The new manager to install.
128      */
129     protected final void replaceManager(final T manager) {
130         this.writeLock.lock();
131         try {
132             final T old = this.getManager();
133             if (!manager.isRunning()) {
134                 manager.startup();
135             }
136             this.manager = manager;
137             old.close();
138         } finally {
139             this.writeLock.unlock();
140         }
141     }
142 }