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