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 {@link org.apache.logging.log4j.core.appender.db.nosql NoSQL}.
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 final ReadWriteLock lock = new ReentrantReadWriteLock();
40      private final Lock readLock = lock.readLock();
41      private final Lock writeLock = lock.writeLock();
42  
43      private T manager;
44  
45      /**
46       * Instantiates the base appender.
47       *
48       * @param name The appender name.
49       * @param filter The filter, if any, to use.
50       * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
51       *                         they are propagated to the caller.
52       * @param manager The matching {@link AbstractDatabaseManager} implementation.
53       */
54      protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean ignoreExceptions,
55                                         final T manager) {
56          super(name, filter, null, ignoreExceptions);
57          this.manager = manager;
58      }
59  
60      /**
61       * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders
62       * do not use a layout at all. The JDBC appender has a layout-per-column pattern.
63       *
64       * @return {@code null}.
65       */
66      @Override
67      public final Layout<LogEvent> getLayout() {
68          return null;
69      }
70  
71      /**
72       * Returns the underlying manager in use within this appender.
73       *
74       * @return the manager.
75       */
76      public final T getManager() {
77          return this.manager;
78      }
79  
80      @Override
81      public final void start() {
82          if (this.getManager() == null) {
83              LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName());
84          }
85          super.start();
86          if (this.getManager() != null) {
87              this.getManager().startup();
88          }
89      }
90  
91      @Override
92      public final void stop() {
93          super.stop();
94          if (this.getManager() != null) {
95              this.getManager().release();
96          }
97      }
98  
99      @Override
100     public final void append(final LogEvent event) {
101         this.readLock.lock();
102         try {
103             this.getManager().write(event);
104         } catch (final LoggingException e) {
105             LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
106                     this.getName(), e);
107             throw e;
108         } catch (final Exception e) {
109             LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
110                     this.getName(), e);
111             throw new AppenderLoggingException("Unable to write to database in appender: " + e.getMessage(), e);
112         } finally {
113             this.readLock.unlock();
114         }
115     }
116 
117     /**
118      * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log
119      * events are written to the database without losing buffered or in-progress events. The existing manager is
120      * released only after the new manager has been installed. This method is thread-safe.
121      *
122      * @param manager The new manager to install.
123      */
124     protected final void replaceManager(final T manager) {
125         this.writeLock.lock();
126         try {
127             final T old = this.getManager();
128             if (!manager.isRunning()) {
129                 manager.startup();
130             }
131             this.manager = manager;
132             old.release();
133         } finally {
134             this.writeLock.unlock();
135         }
136     }
137 }