001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.appender.db; 018 019 import java.util.concurrent.locks.Lock; 020 import java.util.concurrent.locks.ReadWriteLock; 021 import java.util.concurrent.locks.ReentrantReadWriteLock; 022 023 import org.apache.logging.log4j.LoggingException; 024 import org.apache.logging.log4j.core.Filter; 025 import org.apache.logging.log4j.core.Layout; 026 import org.apache.logging.log4j.core.LogEvent; 027 import org.apache.logging.log4j.core.appender.AbstractAppender; 028 import org.apache.logging.log4j.core.appender.AppenderLoggingException; 029 030 /** 031 * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders 032 * should inherit from this base appender. Three implementations are currently provided: 033 * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa 034 * JPA}, and {@link org.apache.logging.log4j.core.appender.db.nosql NoSQL}. 035 * 036 * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires. 037 */ 038 public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender { 039 private final ReadWriteLock lock = new ReentrantReadWriteLock(); 040 private final Lock readLock = lock.readLock(); 041 private final Lock writeLock = lock.writeLock(); 042 043 private T manager; 044 045 /** 046 * Instantiates the base appender. 047 * 048 * @param name The appender name. 049 * @param filter The filter, if any, to use. 050 * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise 051 * they are propagated to the caller. 052 * @param manager The matching {@link AbstractDatabaseManager} implementation. 053 */ 054 protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean ignoreExceptions, 055 final T manager) { 056 super(name, filter, null, ignoreExceptions); 057 this.manager = manager; 058 } 059 060 /** 061 * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders 062 * do not use a layout at all. The JDBC appender has a layout-per-column pattern. 063 * 064 * @return {@code null}. 065 */ 066 @Override 067 public final Layout<LogEvent> getLayout() { 068 return null; 069 } 070 071 /** 072 * Returns the underlying manager in use within this appender. 073 * 074 * @return the manager. 075 */ 076 public final T getManager() { 077 return this.manager; 078 } 079 080 @Override 081 public final void start() { 082 if (this.getManager() == null) { 083 LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName()); 084 } 085 super.start(); 086 if (this.getManager() != null) { 087 this.getManager().startup(); 088 } 089 } 090 091 @Override 092 public final void stop() { 093 super.stop(); 094 if (this.getManager() != null) { 095 this.getManager().release(); 096 } 097 } 098 099 @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 }