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 }