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.io.Serializable;
20  import java.util.concurrent.TimeUnit;
21  import java.util.concurrent.locks.Lock;
22  import java.util.concurrent.locks.ReadWriteLock;
23  import java.util.concurrent.locks.ReentrantReadWriteLock;
24  
25  import org.apache.logging.log4j.LoggingException;
26  import org.apache.logging.log4j.core.Filter;
27  import org.apache.logging.log4j.core.Layout;
28  import org.apache.logging.log4j.core.LogEvent;
29  import org.apache.logging.log4j.core.appender.AbstractAppender;
30  import org.apache.logging.log4j.core.appender.AppenderLoggingException;
31  
32  /**
33   * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders
34   * should inherit from this base appender. Three implementations are currently provided:
35   * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa
36   * JPA}, and {@link org.apache.logging.log4j.core.appender.nosql NoSQL}.
37   *
38   * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires.
39   */
40  public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender {
41  
42      private final ReadWriteLock lock = new ReentrantReadWriteLock();
43      private final Lock readLock = lock.readLock();
44      private final Lock writeLock = lock.writeLock();
45  
46      private T manager;
47  
48      /**
49       * Instantiates the base appender.
50       *
51       * @param name The appender name.
52       * @param filter The filter, if any, to use.
53       * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
54       *                         they are propagated to the caller.
55       * @param manager The matching {@link AbstractDatabaseManager} implementation.
56       */
57      protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean ignoreExceptions,
58                                         final T manager) {
59          super(name, filter, null, ignoreExceptions);
60          this.manager = manager;
61      }
62  
63      /**
64       * Instantiates the base appender.
65       *
66       * @param name The appender name.
67       * @param filter The filter, if any, to use.
68       * @param layout The layout to use to format the event.
69       * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
70       *                         they are propagated to the caller.
71       * @param manager The matching {@link AbstractDatabaseManager} implementation.
72       */
73      protected AbstractDatabaseAppender(final String name, final Filter filter,
74              final Layout<? extends Serializable> layout, final boolean ignoreExceptions, final T manager) {
75          super(name, filter, layout, ignoreExceptions);
76          this.manager = manager;
77      }
78  
79      /**
80       * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders
81       * do not use a layout at all. The JDBC appender has a layout-per-column pattern.
82       *
83       * @return {@code null}.
84       */
85      @Override
86      public final Layout<LogEvent> getLayout() {
87          return null;
88      }
89  
90      /**
91       * Returns the underlying manager in use within this appender.
92       *
93       * @return the manager.
94       */
95      public final T getManager() {
96          return this.manager;
97      }
98  
99      @Override
100     public final void start() {
101         if (this.getManager() == null) {
102             LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName());
103         }
104         super.start();
105         if (this.getManager() != null) {
106             this.getManager().startup();
107         }
108     }
109 
110     @Override
111     public boolean stop(final long timeout, final TimeUnit timeUnit) {
112         setStopping();
113         boolean stopped = super.stop(timeout, timeUnit, false);
114         if (this.getManager() != null) {
115             stopped &= this.getManager().stop(timeout, timeUnit);
116         }
117         setStopped();
118         return stopped;
119     }
120 
121     @Override
122     public final void append(final LogEvent event) {
123         this.readLock.lock();
124         try {
125             this.getManager().write(event, toSerializable(event));
126         } catch (final LoggingException e) {
127             LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
128                     this.getName(), e);
129             throw e;
130         } catch (final Exception e) {
131             LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
132                     this.getName(), e);
133             throw new AppenderLoggingException("Unable to write to database in appender: " + e.getMessage(), e);
134         } finally {
135             this.readLock.unlock();
136         }
137     }
138 
139     /**
140      * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log
141      * events are written to the database without losing buffered or in-progress events. The existing manager is
142      * released only after the new manager has been installed. This method is thread-safe.
143      *
144      * @param manager The new manager to install.
145      */
146     protected final void replaceManager(final T manager) {
147         this.writeLock.lock();
148         try {
149             final T old = this.getManager();
150             if (!manager.isRunning()) {
151                 manager.startup();
152             }
153             this.manager = manager;
154             old.close();
155         } finally {
156             this.writeLock.unlock();
157         }
158     }
159 }