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.util.Map; 20 21 import javax.persistence.Inheritance; 22 import javax.persistence.InheritanceType; 23 import javax.persistence.MappedSuperclass; 24 import javax.persistence.Transient; 25 26 import org.apache.logging.log4j.Level; 27 import org.apache.logging.log4j.Marker; 28 import org.apache.logging.log4j.ThreadContext; 29 import org.apache.logging.log4j.core.AbstractLogEvent; 30 import org.apache.logging.log4j.core.LogEvent; 31 import org.apache.logging.log4j.message.Message; 32 33 /** 34 * <p> 35 * Users of the JPA appender MUST extend this class, using JPA annotations on the concrete class and all of its 36 * accessor methods (as needed) to map them to the proper table and columns. Accessors you do not want persisted should 37 * be annotated with {@link Transient @Transient}. All accessors should call {@link #getWrappedEvent()} and delegate the 38 * call to the underlying event. Users may want to instead extend {@link BasicLogEventEntity}, which takes care of all 39 * of this for you. 40 * </p> 41 * <p> 42 * The concrete class must have two constructors: a public no-arg constructor to convince the JPA provider that it's a 43 * valid entity, and a public constructor that takes a single {@link LogEvent event} and passes it to the parent class 44 * with {@link #AbstractLogEventWrapperEntity(LogEvent) super(event)}. Furthermore, the concrete class must be annotated 45 * {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table} and must implement a fully 46 * mutable ID property annotated with {@link javax.persistence.Id @Id} and 47 * {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new 48 * events. 49 * </p> 50 * <p> 51 * Many of the return types of {@link LogEvent} methods (e.g., {@link StackTraceElement}, {@link Message}, 52 * {@link Marker}, {@link Throwable}, 53 * {@link org.apache.logging.log4j.ThreadContext.ContextStack ThreadContext.ContextStack}, and 54 * {@link Map Map<String, String>}) will not be recognized by the JPA provider. In conjunction with 55 * {@link javax.persistence.Convert @Convert}, you can use the converters in the 56 * {@link org.apache.logging.log4j.core.appender.db.jpa.converter} package to convert these types to database columns. 57 * If you want to retrieve log events from the database, you can create a true POJO entity and also use these 58 * converters for extracting persisted values.<br> 59 * </p> 60 * <p> 61 * The mutator methods in this class not specified in {@link LogEvent} are no-op methods, implemented to satisfy the JPA 62 * requirement that accessor methods have matching mutator methods. If you create additional accessor methods, you must 63 * likewise create matching no-op mutator methods. 64 * </p> 65 * 66 * @see BasicLogEventEntity 67 */ 68 @MappedSuperclass 69 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 70 public abstract class AbstractLogEventWrapperEntity implements LogEvent { 71 private static final long serialVersionUID = 1L; 72 73 private final LogEvent wrappedEvent; 74 75 /** 76 * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's 77 * signature. The no-argument constructor is required for a standards-compliant JPA provider to accept this as an 78 * entity. 79 */ 80 @SuppressWarnings("unused") 81 protected AbstractLogEventWrapperEntity() { 82 this(new NullLogEvent()); 83 } 84 85 /** 86 * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's 87 * signature. This constructor is used for wrapping this entity around a logged event. 88 * 89 * @param wrappedEvent The underlying event from which information is obtained. 90 */ 91 protected AbstractLogEventWrapperEntity(final LogEvent wrappedEvent) { 92 if (wrappedEvent == null) { 93 throw new IllegalArgumentException("The wrapped event cannot be null."); 94 } 95 this.wrappedEvent = wrappedEvent; 96 } 97 98 /** 99 * All eventual accessor methods must call this method and delegate the method call to the underlying wrapped event. 100 * Annotated {@link Transient} so as not to be included in the persisted entity. 101 * 102 * @return The underlying event from which information is obtained. 103 */ 104 @Transient 105 protected final LogEvent getWrappedEvent() { 106 return this.wrappedEvent; 107 } 108 109 /** 110 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 111 * 112 * @param level Ignored. 113 */ 114 @SuppressWarnings("unused") 115 public void setLevel(final Level level) { 116 // this entity is write-only 117 } 118 119 /** 120 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 121 * 122 * @param loggerName Ignored. 123 */ 124 @SuppressWarnings("unused") 125 public void setLoggerName(final String loggerName) { 126 // this entity is write-only 127 } 128 129 /** 130 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 131 * 132 * @param source Ignored. 133 */ 134 @SuppressWarnings("unused") 135 public void setSource(final StackTraceElement source) { 136 // this entity is write-only 137 } 138 139 /** 140 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 141 * 142 * @param message Ignored. 143 */ 144 @SuppressWarnings("unused") 145 public void setMessage(final Message message) { 146 // this entity is write-only 147 } 148 149 /** 150 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 151 * 152 * @param marker Ignored. 153 */ 154 @SuppressWarnings("unused") 155 public void setMarker(final Marker marker) { 156 // this entity is write-only 157 } 158 159 /** 160 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 161 * 162 * @param threadName Ignored. 163 */ 164 @SuppressWarnings("unused") 165 public void setThreadName(final String threadName) { 166 // this entity is write-only 167 } 168 169 /** 170 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 171 * 172 * @param millis Ignored. 173 */ 174 @SuppressWarnings("unused") 175 public void setTimeMillis(final long millis) { 176 // this entity is write-only 177 } 178 179 /** 180 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 181 * 182 * @param throwable Ignored. 183 */ 184 @SuppressWarnings("unused") 185 public void setThrown(final Throwable throwable) { 186 // this entity is write-only 187 } 188 189 /** 190 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 191 * 192 * @param contextMap Ignored. 193 */ 194 @SuppressWarnings("unused") 195 public void setContextMap(final Map<String, String> contextMap) { 196 // this entity is write-only 197 } 198 199 /** 200 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 201 * 202 * @param contextStack Ignored. 203 */ 204 @SuppressWarnings("unused") 205 public void setContextStack(final ThreadContext.ContextStack contextStack) { 206 // this entity is write-only 207 } 208 209 /** 210 * A no-op mutator to satisfy JPA requirements, as this entity is write-only. 211 * 212 * @param fqcn Ignored. 213 */ 214 @SuppressWarnings("unused") 215 public void setLoggerFqcn(final String fqcn) { 216 // this entity is write-only 217 } 218 219 /** 220 * Indicates whether the source of the logging request is required downstream. Annotated 221 * {@link Transient @Transient} so as to not be included in the persisted entity. 222 * 223 * @return whether the source of the logging request is required downstream. 224 */ 225 @Override 226 @Transient 227 public final boolean isIncludeLocation() { 228 return this.getWrappedEvent().isIncludeLocation(); 229 } 230 231 @Override 232 public final void setIncludeLocation(final boolean locationRequired) { 233 this.getWrappedEvent().setIncludeLocation(locationRequired); 234 } 235 236 /** 237 * Indicates whether this event is the last one in a batch. Annotated {@link Transient @Transient} so as to not be 238 * included in the persisted entity. 239 * 240 * @return whether this event is the last one in a batch. 241 */ 242 @Override 243 @Transient 244 public final boolean isEndOfBatch() { 245 return this.getWrappedEvent().isEndOfBatch(); 246 } 247 248 @Override 249 public final void setEndOfBatch(final boolean endOfBatch) { 250 this.getWrappedEvent().setEndOfBatch(endOfBatch); 251 } 252 253 /** 254 * A no-op log event class to prevent {@code NullPointerException}s. O/RMs tend to create instances of entities in 255 * order to "play around" with them. 256 */ 257 private static class NullLogEvent extends AbstractLogEvent { 258 259 private static final long serialVersionUID = 1L; 260 // Inherits everything 261 } 262 }