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.jpa;
    18
    19  import java.lang.reflect.Constructor;
    20
    21  import org.apache.logging.log4j.core.Filter;
    22  import org.apache.logging.log4j.core.LogEvent;
    23  import org.apache.logging.log4j.core.appender.AbstractAppender;
    24  import org.apache.logging.log4j.core.appender.db.AbstractDatabaseAppender;
    25  import org.apache.logging.log4j.core.config.plugins.Plugin;
    26  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
    27  import org.apache.logging.log4j.core.config.plugins.PluginElement;
    28  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
    29  import org.apache.logging.log4j.core.util.Booleans;
    30  import org.apache.logging.log4j.core.util.Loader;
    31  import org.apache.logging.log4j.util.Strings;
    32
    33  /**
    34   * This Appender writes logging events to a relational database using the Java Persistence API. It requires a
    35   * pre-configured JPA persistence unit and a concrete implementation of the abstract
    36   * {@link AbstractLogEventWrapperEntity} class decorated with JPA annotations.
    37   *
    38   * @see AbstractLogEventWrapperEntity
    39   */
    40  @Plugin(name = "JPA", category = "Core", elementType = "appender", printObject = true)
    41  public final class JpaAppender extends AbstractDatabaseAppender<JpaDatabaseManager> {
    42      private static final long serialVersionUID = 1L;
    43
    44      private final String description;
    45
    46      private JpaAppender(final String name, final Filter filter, final boolean ignoreExceptions,
    47              final JpaDatabaseManager manager) {
    48          super(name, filter, ignoreExceptions, manager);
    49          this.description = this.getName() + "{ manager=" + this.getManager() + " }";
    50      }
    51
    52      @Override
    53      public String toString() {
    54          return this.description;
    55      }
    56
    57      /**
    58       * Factory method for creating a JPA appender within the plugin manager.
    59       *
    60       * @param name The name of the appender.
    61       * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
    62       *               they are propagated to the caller.
    63       * @param filter The filter, if any, to use.
    64       * @param bufferSize If an integer greater than 0, this causes the appender to buffer log events and flush whenever
    65       *                   the buffer reaches this size.
    66       * @param entityClassName The fully qualified name of the concrete {@link AbstractLogEventWrapperEntity}
    67       *                        implementation that has JPA annotations mapping it to a database table.
    68       * @param persistenceUnitName The name of the JPA persistence unit that should be used for persisting log events.
    69       * @return a new JPA appender.
    70       */
    71      @PluginFactory
    72      public static JpaAppender createAppender(
    73              @PluginAttribute("name") final String name,
    74              @PluginAttribute("ignoreExceptions") final String ignore,
    75              @PluginElement("Filter") final Filter filter,
    76              @PluginAttribute("bufferSize") final String bufferSize,
    77              @PluginAttribute("entityClassName") final String entityClassName,
    78              @PluginAttribute("persistenceUnitName") final String persistenceUnitName) {
    79          if (Strings.isEmpty(entityClassName) || Strings.isEmpty(persistenceUnitName)) {
    80              LOGGER.error("Attributes entityClassName and persistenceUnitName are required for JPA Appender.");
    81              return null;
    82          }
    83
    84          final int bufferSizeInt = AbstractAppender.parseInt(bufferSize, 0);
    85          final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
    86
    87          try {
    88              @SuppressWarnings("unchecked")
    89              final Class<? extends AbstractLogEventWrapperEntity> entityClass =
    90                      (Class<? extends AbstractLogEventWrapperEntity>) Loader.loadClass(entityClassName);
    91
    92              if (!AbstractLogEventWrapperEntity.class.isAssignableFrom(entityClass)) {
    93                  LOGGER.error("Entity class [{}] does not extend AbstractLogEventWrapperEntity.", entityClassName);
    94                  return null;
    95              }
    96
    97              try {
    98                  entityClass.getConstructor();
    99              } catch (final NoSuchMethodException e) {
    100                 LOGGER.error("Entity class [{}] does not have a no-arg constructor. The JPA provider will reject it.",
    101                         entityClassName);
    102                 return null;
    103             }
    104
    105             final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor =
    106                     entityClass.getConstructor(LogEvent.class);
    107
    108             final String managerName = "jpaManager{ description=" + name + ", bufferSize=" + bufferSizeInt
    109                     + ", persistenceUnitName=" + persistenceUnitName + ", entityClass=" + entityClass.getName() + '}';
    110
    111             final JpaDatabaseManager manager = JpaDatabaseManager.getJPADatabaseManager(
    112                     managerName, bufferSizeInt, entityClass, entityConstructor, persistenceUnitName
    113             );
    114             if (manager == null) {
    115                 return null;
    116             }
    117
    118             return new JpaAppender(name, filter, ignoreExceptions, manager);
    119         } catch (final ClassNotFoundException e) {
    120             LOGGER.error("Could not load entity class [{}].", entityClassName, e);
    121             return null;
    122         } catch (final NoSuchMethodException e) {
    123             LOGGER.error("Entity class [{}] does not have a constructor with a single argument of type LogEvent.",
    124                     entityClassName);
    125             return null;
    126         }
    127     }
    128 }