View Javadoc
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&lt;String, String&gt;}) 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 }