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.nosql;
18  
19  import java.util.Map;
20  
21  import org.apache.logging.log4j.Marker;
22  import org.apache.logging.log4j.ThreadContext;
23  import org.apache.logging.log4j.core.LogEvent;
24  import org.apache.logging.log4j.core.appender.AppenderLoggingException;
25  import org.apache.logging.log4j.core.appender.ManagerFactory;
26  import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
27  
28  /**
29   * An {@link AbstractDatabaseManager} implementation for all NoSQL databases.
30   *
31   * @param <W> A type parameter for reassuring the compiler that all operations are using the same {@link NoSQLObject}.
32   */
33  public final class NoSQLDatabaseManager<W> extends AbstractDatabaseManager {
34      private static final NoSQLDatabaseManagerFactory FACTORY = new NoSQLDatabaseManagerFactory();
35  
36      private final NoSQLProvider<NoSQLConnection<W, ? extends NoSQLObject<W>>> provider;
37  
38      private NoSQLConnection<W, ? extends NoSQLObject<W>> connection;
39  
40      private NoSQLDatabaseManager(final String name, final int bufferSize,
41              final NoSQLProvider<NoSQLConnection<W, ? extends NoSQLObject<W>>> provider) {
42          super(name, bufferSize);
43          this.provider = provider;
44      }
45  
46      @Override
47      protected void startupInternal() {
48          // nothing to see here
49      }
50  
51      @Override
52      protected void shutdownInternal() {
53          if (this.connection != null) {
54              this.commitAndClose();
55          }
56      }
57  
58      @Override
59      protected void connectAndStart() {
60          try {
61              this.connection = this.provider.getConnection();
62          } catch (Exception e) {
63              throw new AppenderLoggingException("Failed to get connection from NoSQL connection provider.", e);
64          }
65      }
66  
67      @Override
68      protected void writeInternal(final LogEvent event) {
69          if (!this.isRunning() || this.connection == null || this.connection.isClosed()) {
70              throw new AppenderLoggingException(
71                      "Cannot write logging event; NoSQL manager not connected to the database.");
72          }
73  
74          final NoSQLObject<W> entity = this.connection.createObject();
75          entity.set("level", event.getLevel());
76          entity.set("loggerName", event.getLoggerName());
77          entity.set("message", event.getMessage() == null ? null : event.getMessage().getFormattedMessage());
78  
79          final StackTraceElement source = event.getSource();
80          if (source == null) {
81              entity.set("source", (Object) null);
82          } else {
83              entity.set("source", this.convertStackTraceElement(source));
84          }
85  
86          Marker marker = event.getMarker();
87          if (marker == null) {
88              entity.set("marker", (Object) null);
89          } else {
90              final NoSQLObject<W> originalMarkerEntity = this.connection.createObject();
91              NoSQLObject<W> markerEntity = originalMarkerEntity;
92              markerEntity.set("name", marker.getName());
93              while (marker.getParent() != null) {
94                  marker = marker.getParent();
95                  final NoSQLObject<W> parentMarkerEntity = this.connection.createObject();
96                  parentMarkerEntity.set("name", marker.getName());
97                  markerEntity.set("parent", parentMarkerEntity);
98                  markerEntity = parentMarkerEntity;
99              }
100             entity.set("marker", originalMarkerEntity);
101         }
102 
103         entity.set("threadName", event.getThreadName());
104         entity.set("millis", event.getMillis());
105         entity.set("date", new java.util.Date(event.getMillis()));
106 
107         @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
108         Throwable thrown = event.getThrown();
109         if (thrown == null) {
110             entity.set("thrown", (Object) null);
111         } else {
112             final NoSQLObject<W> originalExceptionEntity = this.connection.createObject();
113             NoSQLObject<W> exceptionEntity = originalExceptionEntity;
114             exceptionEntity.set("type", thrown.getClass().getName());
115             exceptionEntity.set("message", thrown.getMessage());
116             exceptionEntity.set("stackTrace", this.convertStackTrace(thrown.getStackTrace()));
117             while (thrown.getCause() != null) {
118                 thrown = thrown.getCause();
119                 final NoSQLObject<W> causingExceptionEntity = this.connection.createObject();
120                 causingExceptionEntity.set("type", thrown.getClass().getName());
121                 causingExceptionEntity.set("message", thrown.getMessage());
122                 causingExceptionEntity.set("stackTrace", this.convertStackTrace(thrown.getStackTrace()));
123                 exceptionEntity.set("cause", causingExceptionEntity);
124                 exceptionEntity = causingExceptionEntity;
125             }
126 
127             entity.set("thrown", originalExceptionEntity);
128         }
129 
130         final Map<String, String> contextMap = event.getContextMap();
131         if (contextMap == null) {
132             entity.set("contextMap", (Object) null);
133         } else {
134             final NoSQLObject<W> contextMapEntity = this.connection.createObject();
135             for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
136                 contextMapEntity.set(entry.getKey(), entry.getValue());
137             }
138             entity.set("contextMap", contextMapEntity);
139         }
140 
141         final ThreadContext.ContextStack contextStack = event.getContextStack();
142         if (contextStack == null) {
143             entity.set("contextStack", (Object) null);
144         } else {
145             entity.set("contextStack", contextStack.asList().toArray());
146         }
147 
148         this.connection.insertObject(entity);
149     }
150 
151     @Override
152     protected void commitAndClose() {
153         try {
154             if (this.connection != null && !this.connection.isClosed()) {
155                 this.connection.close();
156             }
157         } catch (Exception e) {
158             throw new AppenderLoggingException("Failed to commit and close NoSQL connection in manager.", e);
159         }
160     }
161 
162     private NoSQLObject<W>[] convertStackTrace(final StackTraceElement[] stackTrace) {
163         final NoSQLObject<W>[] stackTraceEntities = this.connection.createList(stackTrace.length);
164         for (int i = 0; i < stackTrace.length; i++) {
165             stackTraceEntities[i] = this.convertStackTraceElement(stackTrace[i]);
166         }
167         return stackTraceEntities;
168     }
169 
170     private NoSQLObject<W> convertStackTraceElement(final StackTraceElement element) {
171         final NoSQLObject<W> elementEntity = this.connection.createObject();
172         elementEntity.set("className", element.getClassName());
173         elementEntity.set("methodName", element.getMethodName());
174         elementEntity.set("fileName", element.getFileName());
175         elementEntity.set("lineNumber", element.getLineNumber());
176         return elementEntity;
177     }
178 
179     /**
180      * Creates a NoSQL manager for use within the {@link NoSQLAppender}, or returns a suitable one if it already exists.
181      *
182      * @param name The name of the manager, which should include connection details and hashed passwords where possible.
183      * @param bufferSize The size of the log event buffer.
184      * @param provider A provider instance which will be used to obtain connections to the chosen NoSQL database.
185      * @return a new or existing NoSQL manager as applicable.
186      */
187     public static NoSQLDatabaseManager<?> getNoSQLDatabaseManager(final String name, final int bufferSize,
188                                                                   final NoSQLProvider<?> provider) {
189         return AbstractDatabaseManager.getManager(name, new FactoryData(bufferSize, provider), FACTORY);
190     }
191 
192     /**
193      * Encapsulates data that {@link NoSQLDatabaseManagerFactory} uses to create managers.
194      */
195     private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
196         private final NoSQLProvider<?> provider;
197 
198         protected FactoryData(final int bufferSize, final NoSQLProvider<?> provider) {
199             super(bufferSize);
200             this.provider = provider;
201         }
202     }
203 
204     /**
205      * Creates managers.
206      */
207     private static final class NoSQLDatabaseManagerFactory implements
208             ManagerFactory<NoSQLDatabaseManager<?>, FactoryData> {
209         @Override
210         @SuppressWarnings("unchecked")
211         public NoSQLDatabaseManager<?> createManager(final String name, final FactoryData data) {
212             return new NoSQLDatabaseManager(name, data.getBufferSize(), data.provider);
213         }
214     }
215 }