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