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