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 }