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 }