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