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.core.Filter; 024 import org.apache.logging.log4j.core.Layout; 025 import org.apache.logging.log4j.core.LogEvent; 026 import org.apache.logging.log4j.core.appender.AbstractAppender; 027 import org.apache.logging.log4j.core.appender.AppenderRuntimeException; 028 029 /** 030 * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders 031 * should inherit from this base appender. Three implementations are currently provided: 032 * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa 033 * JPA}, and {@link org.apache.logging.log4j.core.appender.db.nosql NoSQL}. 034 * 035 * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires. 036 */ 037 public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender<LogEvent> { 038 private final ReadWriteLock lock = new ReentrantReadWriteLock(); 039 private final Lock readLock = lock.readLock(); 040 private final Lock writeLock = lock.writeLock(); 041 042 private T manager; 043 044 /** 045 * Instantiates the base appender. 046 * 047 * @param name The appender name. 048 * @param filter The filter, if any, to use. 049 * @param handleException Whether logging exceptions should be reported to the application. 050 * @param manager The matching {@link AbstractDatabaseManager} implementation. 051 */ 052 protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean handleException, 053 final T manager) { 054 super(name, filter, null, handleException); 055 this.manager = manager; 056 } 057 058 /** 059 * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders 060 * do not use a layout at all. The JDBC appender has a layout-per-column pattern. 061 * 062 * @return {@code null}. 063 */ 064 @Override 065 public final Layout<LogEvent> getLayout() { 066 return null; 067 } 068 069 /** 070 * Returns the underlying manager in use within this appender. 071 * 072 * @return the manager. 073 */ 074 public final T getManager() { 075 return this.manager; 076 } 077 078 @Override 079 public final void start() { 080 if (this.getManager() == null) { 081 LOGGER.error("No AbstractDatabaseManager set for the appender named [" + this.getName() + "]."); 082 } 083 super.start(); 084 if (this.getManager() != null) { 085 this.getManager().connect(); 086 } 087 } 088 089 @Override 090 public final void stop() { 091 super.stop(); 092 if (this.getManager() != null) { 093 this.getManager().release(); 094 } 095 } 096 097 @Override 098 public final void append(final LogEvent event) { 099 this.readLock.lock(); 100 try { 101 this.getManager().write(event); 102 } catch (final AppenderRuntimeException e) { 103 this.error("Unable to write to database [" + this.getManager().getName() + "] for appender [" + 104 this.getName() + "].", e); 105 throw e; 106 } finally { 107 this.readLock.unlock(); 108 } 109 } 110 111 /** 112 * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log 113 * events are written to the database without losing buffered or in-progress events. The existing manager is 114 * released only after the new manager has been installed. This method is thread-safe. 115 * 116 * @param manager The new manager to install. 117 */ 118 protected final void replaceManager(final T manager) { 119 this.writeLock.lock(); 120 try { 121 final T old = this.getManager(); 122 if (!manager.isConnected()) { 123 manager.connect(); 124 } 125 this.manager = manager; 126 old.release(); 127 } finally { 128 this.writeLock.unlock(); 129 } 130 } 131 }