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