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