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.nosql.appender.mongodb;
018
019 import org.apache.logging.log4j.Level;
020 import org.apache.logging.log4j.Logger;
021 import org.apache.logging.log4j.core.appender.AppenderLoggingException;
022 import org.apache.logging.log4j.nosql.appender.NoSqlConnection;
023 import org.apache.logging.log4j.nosql.appender.NoSqlObject;
024 import org.apache.logging.log4j.status.StatusLogger;
025 import org.apache.logging.log4j.util.Strings;
026 import org.bson.BSON;
027 import org.bson.Transformer;
028
029 import com.mongodb.BasicDBObject;
030 import com.mongodb.DB;
031 import com.mongodb.DBCollection;
032 import com.mongodb.Mongo;
033 import com.mongodb.MongoException;
034 import com.mongodb.WriteConcern;
035 import com.mongodb.WriteResult;
036
037 /**
038 * The MongoDB implementation of {@link NoSqlConnection}.
039 */
040 public final class MongoDbConnection implements NoSqlConnection<BasicDBObject, MongoDbObject> {
041
042 private static final Logger LOGGER = StatusLogger.getLogger();
043
044 static {
045 BSON.addEncodingHook(Level.class, new Transformer() {
046 @Override
047 public Object transform(final Object o) {
048 if (o instanceof Level) {
049 return ((Level) o).name();
050 }
051 return o;
052 }
053 });
054 }
055
056 private final DBCollection collection;
057 private final Mongo mongo;
058 private final WriteConcern writeConcern;
059
060 public MongoDbConnection(final DB database, final WriteConcern writeConcern, final String collectionName) {
061 this.mongo = database.getMongo();
062 this.collection = database.getCollection(collectionName);
063 this.writeConcern = writeConcern;
064 }
065
066 @Override
067 public MongoDbObject createObject() {
068 return new MongoDbObject();
069 }
070
071 @Override
072 public MongoDbObject[] createList(final int length) {
073 return new MongoDbObject[length];
074 }
075
076 @Override
077 public void insertObject(final NoSqlObject<BasicDBObject> object) {
078 try {
079 final WriteResult result = this.collection.insert(object.unwrap(), this.writeConcern);
080 if (Strings.isNotEmpty(result.getError())) {
081 throw new AppenderLoggingException("Failed to write log event to MongoDB due to error: " +
082 result.getError() + '.');
083 }
084 } catch (final MongoException e) {
085 throw new AppenderLoggingException("Failed to write log event to MongoDB due to error: " + e.getMessage(),
086 e);
087 }
088 }
089
090 @Override
091 public void close() {
092 // there's no need to call this.mongo.close() since that literally closes the connection
093 // MongoDBClient uses internal connection pooling
094 // for more details, see LOG4J2-591
095 }
096
097 @Override
098 public boolean isClosed() {
099 return !this.mongo.getConnector().isOpen();
100 }
101
102 /**
103 * To prevent class loading issues during plugin discovery, this code cannot live within MongoDbProvider. This
104 * is because of how Java treats references to Exception classes different from references to other classes. When
105 * Java loads a class, it normally won't load that class's dependent classes until and unless A) they are used, B)
106 * the class being loaded extends or implements those classes, or C) those classes are the types of static members
107 * in the class. However, exceptions that a class uses are always loaded when the class is loaded, even before
108 * they are actually used.
109 *
110 * @param database The database to authenticate
111 * @param username The username to authenticate with
112 * @param password The password to authenticate with
113 */
114 static void authenticate(final DB database, final String username, final String password) {
115 try {
116 if (!database.authenticate(username, password.toCharArray())) {
117 LOGGER.error("Failed to authenticate against MongoDB server. Unknown error.");
118 }
119 } catch (final MongoException e) {
120 LOGGER.error("Failed to authenticate against MongoDB: " + e.getMessage(), e);
121 } catch (final IllegalStateException e) {
122 LOGGER.error("Factory-supplied MongoDB database connection already authenticated with different" +
123 "credentials but lost connection.", e);
124 }
125 }
126 }