001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.appender.db.nosql.mongo; 018 019 import java.lang.reflect.Field; 020 import java.lang.reflect.Method; 021 import java.util.List; 022 023 import org.apache.logging.log4j.Logger; 024 import org.apache.logging.log4j.core.appender.db.nosql.NoSQLProvider; 025 import org.apache.logging.log4j.core.config.plugins.Plugin; 026 import org.apache.logging.log4j.core.config.plugins.PluginAttr; 027 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 028 import org.apache.logging.log4j.core.helpers.NameUtil; 029 import org.apache.logging.log4j.status.StatusLogger; 030 031 import com.mongodb.DB; 032 import com.mongodb.MongoClient; 033 import com.mongodb.ServerAddress; 034 import com.mongodb.WriteConcern; 035 036 /** 037 * The MongoDB implementation of {@link NoSQLProvider}. 038 */ 039 @Plugin(name = "MongoDb", category = "Core", printObject = true) 040 public final class MongoDBProvider implements NoSQLProvider<MongoDBConnection> { 041 private static final Logger LOGGER = StatusLogger.getLogger(); 042 043 private final String collectionName; 044 private final DB database; 045 private final String description; 046 047 private final WriteConcern writeConcern; 048 049 private MongoDBProvider(final DB database, final WriteConcern writeConcern, final String collectionName, 050 final String description) { 051 this.database = database; 052 this.writeConcern = writeConcern; 053 this.collectionName = collectionName; 054 this.description = "mongoDb{ " + description + " }"; 055 } 056 057 @Override 058 public MongoDBConnection getConnection() { 059 return new MongoDBConnection(this.database, this.writeConcern, this.collectionName); 060 } 061 062 @Override 063 public String toString() { 064 return this.description; 065 } 066 067 /** 068 * Factory method for creating a MongoDB provider within the plugin manager. 069 * 070 * @param collectionName The name of the MongoDB collection to which log events should be written. 071 * @param writeConcernConstant The {@link WriteConcern} constant to control writing details, defaults to 072 * {@link WriteConcern#ACKNOWLEDGED}. 073 * @param writeConcernConstantClassName The name of a class containing the aforementioned static WriteConcern 074 * constant. Defaults to {@link WriteConcern}. 075 * @param databaseName The name of the MongoDB database containing the collection to which log events should be 076 * written. Mutually exclusive with {@code factoryClassName&factoryMethodName!=null}. 077 * @param server The host name of the MongoDB server, defaults to localhost and mutually exclusive with 078 * {@code factoryClassName&factoryMethodName!=null}. 079 * @param port The port the MongoDB server is listening on, defaults to the default MongoDB port and mutually 080 * exclusive with {@code factoryClassName&factoryMethodName!=null}. 081 * @param username The username to authenticate against the MongoDB server with. 082 * @param password The password to authenticate against the MongoDB server with. 083 * @param factoryClassName A fully qualified class name containing a static factory method capable of returning a 084 * {@link DB} or a {@link MongoClient}. 085 * @param factoryMethodName The name of the public static factory method belonging to the aforementioned factory 086 * class. 087 * @return a new MongoDB provider. 088 */ 089 @PluginFactory 090 public static MongoDBProvider createNoSQLProvider(@PluginAttr("collectionName") final String collectionName, 091 @PluginAttr("writeConcernConstant") final String 092 writeConcernConstant, 093 @PluginAttr("writeConcernConstantClass") final String 094 writeConcernConstantClassName, 095 @PluginAttr("databaseName") final String databaseName, 096 @PluginAttr("server") final String server, 097 @PluginAttr("port") final String port, 098 @PluginAttr("username") final String username, 099 @PluginAttr("password") final String password, 100 @PluginAttr("factoryClassName") final String factoryClassName, 101 @PluginAttr("factoryMethodName") final String factoryMethodName) { 102 DB database; 103 String description; 104 if (factoryClassName != null && factoryClassName.length() > 0 && 105 factoryMethodName != null && factoryMethodName.length() > 0) { 106 try { 107 final Class<?> factoryClass = Class.forName(factoryClassName); 108 final Method method = factoryClass.getMethod(factoryMethodName); 109 final Object object = method.invoke(null); 110 111 if (object instanceof DB) { 112 database = (DB) object; 113 } else if (object instanceof MongoClient) { 114 if (databaseName != null && databaseName.length() > 0) { 115 database = ((MongoClient) object).getDB(databaseName); 116 } else { 117 LOGGER.error("The factory method [{}.{}()] returned a MongoClient so the database name is " 118 + "required.", factoryClassName, factoryMethodName); 119 return null; 120 } 121 } else if (object == null) { 122 LOGGER.error("The factory method [{}.{}()] returned null.", factoryClassName, factoryMethodName); 123 return null; 124 } else { 125 LOGGER.error("The factory method [{}.{}()] returned an unsupported type [{}].", factoryClassName, 126 factoryMethodName, object.getClass().getName()); 127 return null; 128 } 129 130 description = "database=" + database.getName(); 131 final List<ServerAddress> addresses = database.getMongo().getAllAddress(); 132 if (addresses.size() == 1) { 133 description += ", server=" + addresses.get(0).getHost() + ", port=" + addresses.get(0).getPort(); 134 } else { 135 description += ", servers=["; 136 for (final ServerAddress address : addresses) { 137 description += " { " + address.getHost() + ", " + address.getPort() + " } "; 138 } 139 description += "]"; 140 } 141 } catch (final ClassNotFoundException e) { 142 LOGGER.error("The factory class [{}] could not be loaded.", factoryClassName, e); 143 return null; 144 } catch (final NoSuchMethodException e) { 145 LOGGER.error("The factory class [{}] does not have a no-arg method named [{}].", factoryClassName, 146 factoryMethodName, e); 147 return null; 148 } catch (final Exception e) { 149 LOGGER.error("The factory method [{}.{}()] could not be invoked.", factoryClassName, factoryMethodName, 150 e); 151 return null; 152 } 153 } else if (databaseName != null && databaseName.length() > 0) { 154 description = "database=" + databaseName; 155 try { 156 if (server != null && server.length() > 0) { 157 int portInt = 0; 158 if (port != null && port.length() > 0) { 159 try { 160 portInt = Integer.parseInt(port); 161 } catch (final NumberFormatException ignore) { 162 // we don't care 163 } 164 } 165 166 description += ", server=" + server; 167 if (portInt > 0) { 168 description += ", port=" + portInt; 169 database = new MongoClient(server, portInt).getDB(databaseName); 170 } else { 171 database = new MongoClient(server).getDB(databaseName); 172 } 173 } else { 174 database = new MongoClient().getDB(databaseName); 175 } 176 } catch (final Exception e) { 177 LOGGER.error("Failed to obtain a database instance from the MongoClient at server [{}] and " 178 + "port [{}].", server, port); 179 return null; 180 } 181 } else { 182 LOGGER.error("No factory method was provided so the database name is required."); 183 return null; 184 } 185 186 if (!database.isAuthenticated()) { 187 if (username != null && username.length() > 0 && password != null && password.length() > 0) { 188 description += ", username=" + username + ", passwordHash=" 189 + NameUtil.md5(password + MongoDBProvider.class.getName()); 190 } else { 191 LOGGER.error("The database is not already authenticated so you must supply a username and password " 192 + "for the MongoDB provider."); 193 return null; 194 } 195 } 196 197 WriteConcern writeConcern; 198 if (writeConcernConstant != null && writeConcernConstant.length() > 0) { 199 if (writeConcernConstantClassName != null && writeConcernConstantClassName.length() > 0) { 200 try { 201 final Class<?> writeConcernConstantClass = Class.forName(writeConcernConstantClassName); 202 final Field field = writeConcernConstantClass.getField(writeConcernConstant); 203 writeConcern = (WriteConcern) field.get(null); 204 } catch (final Exception e) { 205 LOGGER.error("Write concern constant [{}.{}] not found, using default.", 206 writeConcernConstantClassName, writeConcernConstant); 207 writeConcern = WriteConcern.ACKNOWLEDGED; 208 } 209 } else { 210 writeConcern = WriteConcern.valueOf(writeConcernConstant); 211 if (writeConcern == null) { 212 LOGGER.warn("Write concern constant [{}] not found, using default.", writeConcernConstant); 213 writeConcern = WriteConcern.ACKNOWLEDGED; 214 } 215 } 216 } else { 217 writeConcern = WriteConcern.ACKNOWLEDGED; 218 } 219 220 return new MongoDBProvider(database, writeConcern, collectionName, description); 221 } 222 }