blob: f327378eab38150f04e0b70bfa43911f6cdff860 [file] [log] [blame]
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Appender for writing to MongoDB.
*
* This class was originally contributed by Vladimir Gorej.
*
* ## Configurable parameters: ##
*
* - **connectionString ** - Connection string, if used host and port properties are ignored. Allows defining multiple hosts.
* - **host** - Server on which mongodb instance is located.
* - **port** - Port on which the instance is bound.
* - **databaseName** - Name of the database to which to log.
* - **collectionName** - Name of the target collection within the given database.
* - **username** - Username used to connect to the database.
* - **password** - Password used to connect to the database.
* - **timeout** - DEPRECATED; For how long the driver should try to connect to the database (in milliseconds).
* - **connectionTimeout** - How long a connection can take to be opened before timing out.
* - **socketTimeout** - How long a send or receive on a socket can take before timing out.
* - **capped** - Whether the collection should be a fixed size.
* - **cappedMax** - If the collection is fixed size, the maximum number of elements to store in the collection.
* - **cappedSize** - If the collection is fixed size, its size in bytes.
* - **writeConcern** - Controls how many nodes must acknowledge the write instruction before the driver continues.
* - **writeConcernJournaled** - The write will be acknowledged by primary and the journal flushed to disk.
* - **writeConcernTimeout** - Controls how many milliseconds the server waits for the write concern to be satisfied.
* - **replicaSet** - The name of the replica set to connect to. Primary will be automatically determined.
*
* @package log4php
* @subpackage appenders
* @since 2.1
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @link http://logging.apache.org/log4php/docs/appenders/mongodb.html Appender documentation
* @link http://github.com/log4mongo/log4mongo-php Vladimir Gorej's original submission.
* @link http://www.mongodb.org/ MongoDB website.
*/
class LoggerAppenderMongoDB extends LoggerAppender {
// ******************************************
// ** Constants **
// ******************************************
/** Default prefix for the {@link $host}. */
const DEFAULT_MONGO_URL_PREFIX = 'mongodb://';
/** Default value for {@link $host}, without a prefix. */
const DEFAULT_MONGO_HOST = 'localhost';
/** Default value for {@link $port} */
const DEFAULT_MONGO_PORT = 27017;
/** Default value for {@link $databaseName}. */
const DEFAULT_DB_NAME = 'log4php_mongodb';
/** Default value for {@link $collectionName}. */
const DEFAULT_COLLECTION_NAME = 'logs';
/** Default value for {@link $timeout}
* @deprecated
*/
const DEFAULT_TIMEOUT_VALUE = 3000;
/** Default value for {@link $capped}. */
const DEFAULT_CAPPED = false;
/** Default value for {@link $cappedMax}. */
const DEFAULT_CAPPED_MAX = 1000;
/** Default value for {@link $cappedSize}. */
const DEFAULT_CAPPED_SIZE = 1000000;
/** Single/Primary server acknowledgement of write operation. {@link $writeConcern}. */
const WC_ACKNOWLEDGED = 1;
/** Default value for {@link $writeConcernTimeout}. */
const WC_TIMEOUT = 3000;
/**
* Default value for {@link $writeConcernJournaled}.
*/
const WC_JOURNALED = false;
// ******************************************
// ** Configurable parameters **
// ******************************************
/**
* Support for multiple hosts and additional connection options.
* @var string
*/
protected $connectionString;
/** Server on which mongodb instance is located.
* @var string
*/
protected $host;
/** Port on which the instance is bound.
* @var int
*/
protected $port;
/** Name of the database to which to log.
* @var string
*/
protected $databaseName;
/** Name of the collection within the given database.
* @var string
*/
protected $collectionName;
/** Username used to connect to the database.
* @var string
*/
protected $userName;
/** Password used to connect to the database.
* @var string
*/
protected $password;
/** Timeout value used when connecting to the database (in milliseconds).
* @var int
*/
protected $timeout;
/** Whether the collection should be a fixed size.
* @var bool
*/
protected $capped;
/** If the collection is fixed size, the maximum number of elements to store in the collection.
* @var int
*/
protected $cappedMax;
/** If the collection is fixed size, its size in bytes.
* @var int
*/
protected $cappedSize;
/**
* How long the driver blocks when writing
* @var string
*/
protected $writeConcern;
/**
* The write will be acknowledged by primary and the journal flushed to disk.
* @var bool
*/
protected $writeConcernJournaled;
/**
* Write concern timeout in milliseconds.
* @var int
*/
protected $writeConcernTimeout;
/**
* Connection timeout in milliseconds.
* @var int
*/
protected $connectionTimeout;
/**
* Socket timeout in milliseconds.
* @var int
*/
protected $socketTimeout;
/**
* Name of the replicaSet.
* @var string
*/
protected $replicaSet;
// ******************************************
// ** Member variables **
// ******************************************
/**
* Connection to the MongoDB instance.
* @var Mongo
*/
protected $connection;
/**
* The collection to which log is written.
* @var MongoCollection
*/
protected $collection;
/**
* Write options.
* @var array
*/
protected $writeOptions;
public function __construct($name = '') {
parent::__construct($name);
$this->host = self::DEFAULT_MONGO_HOST;
$this->port = self::DEFAULT_MONGO_PORT;
$this->databaseName = self::DEFAULT_DB_NAME;
$this->collectionName = self::DEFAULT_COLLECTION_NAME;
$this->timeout = self::DEFAULT_TIMEOUT_VALUE;
$this->requiresLayout = false;
$this->capped = self::DEFAULT_CAPPED;
$this->cappedMax = self::DEFAULT_CAPPED_MAX;
$this->cappedSize = self::DEFAULT_CAPPED_SIZE;
$this->writeConcern = self::WC_ACKNOWLEDGED;
$this->writeConcernJournaled = self::WC_JOURNALED;
$this->writeConcernTimeout = self::WC_TIMEOUT;
}
/**
* Setup db connection.
* Based on defined options, this method connects to the database and
* creates a {@link $collection}.
*/
public function activateOptions() {
# Building connection options.
$options = array(
'w' => (is_numeric($this->writeConcern)) ? (int) $this->writeConcern : $this->writeConcern,
'timeout' => $this->timeout,
'wTimeout' => $this->writeConcernTimeout
);
if ($this->replicaSet !== null) {
$options['replicaSet'] = $this->replicaSet;
}
if ($this->socketTimeout !== null) {
$options['socketTimeoutMS'] = $this->socketTimeout;
}
# Backwards compatibility with timeout parameter.
if ($this->connectionTimeout !== null) {
$options['connectTimeoutMS'] = $options['timeout'] = $this->connectionTimeout;
}
# Building write options.
$this->writeOptions = array(
'w' => (is_numeric($this->writeConcern)) ? (int) $this->writeConcern : $this->writeConcern,
'j' => $this->writeConcernJournaled
);
try {
$clientClass = class_exists('MongoClient') ? 'MongoClient' : 'Mongo';
# Connection string generation.
if ($this->connectionString === null) {
$connectionString = sprintf('%s%s:%d', self::DEFAULT_MONGO_URL_PREFIX,
preg_replace('/^'.preg_quote(self::DEFAULT_MONGO_URL_PREFIX, '/').'/',
'', $this->host),
$this->port);
} else {
$connectionString = $this->connectionString;
}
$this->connection = new $clientClass($connectionString, $options);
$db = $this->connection->selectDB($this->databaseName);
if ($this->userName !== null && $this->password !== null) {
$authResult = $db->authenticate($this->userName, $this->password);
if ($authResult['ok'] == floatval(0)) {
throw new Exception($authResult['errmsg'], $authResult['ok']);
}
}
if ($this->capped === true) {
$this->collection = $db->createCollection($this->collectionName, $this->capped, $this->cappedSize,
$this->cappedMax);
} else {
$this->collection = $db->selectCollection($this->collectionName);
}
} catch (MongoConnectionException $ex) {
$this->closed = true;
$this->warn(sprintf('Failed to connect to mongo daemon: %s', $ex->getMessage()));
} catch (InvalidArgumentException $ex) {
$this->closed = true;
$this->warn(sprintf('Error while selecting mongo database: %s', $ex->getMessage()));
} catch (Exception $ex) {
$this->closed = true;
$this->warn('Invalid credentials for mongo database authentication');
}
}
/**
* Appends a new event to the mongo database.
*
* @param LoggerLoggingEvent $event
*/
public function append(LoggerLoggingEvent $event) {
try {
if ($this->collection != null) {
$this->collection->insert($this->format($event), $this->writeOptions);
}
} catch (MongoCursorException $ex) {
$this->warn(sprintf('Error while writing to mongo collection: %s', $ex->getMessage()));
}
}
/**
* Converts the logging event into an array which can be logged to mongodb.
* Note that MDC context keys that are the same as mongo appender default keys will be overridden.
*
* @param LoggerLoggingEvent $event
* @return array The array representation of the logging event.
*/
protected function format(LoggerLoggingEvent $event) {
$timestampSec = (int) $event->getTimestamp();
$timestampUsec = (int) (($event->getTimestamp() - $timestampSec) * 1000000);
$document = LoggerMDC::getMap();
$document['timestamp'] = new MongoDate($timestampSec, $timestampUsec);
$document['level'] = $event->getLevel()->toString();
$document['thread'] = (int) $event->getThreadName();
$document['message'] = $event->getMessage();
$document['loggerName'] = $event->getLoggerName();
$locationInfo = $event->getLocationInformation();
if ($locationInfo != null) {
$document['fileName'] = $locationInfo->getFileName();
$document['method'] = $locationInfo->getMethodName();
$document['lineNumber'] = ($locationInfo->getLineNumber() == 'NA') ? 'NA' : (int) $locationInfo->getLineNumber();
$document['className'] = $locationInfo->getClassName();
}
$throwableInfo = $event->getThrowableInformation();
if ($throwableInfo != null) {
$document['exception'] = $this->formatThrowable($throwableInfo->getThrowable());
}
return $document;
}
/**
* Converts an Exception into an array which can be logged to mongodb.
*
* Supports inner exceptions (PHP >= 5.3)
*
* @param Exception $ex
* @return array
*/
protected function formatThrowable(Exception $ex) {
$array = array(
'message' => $ex->getMessage(),
'code' => $ex->getCode(),
'stackTrace' => $ex->getTraceAsString(),
);
if (method_exists($ex, 'getPrevious') && $ex->getPrevious() !== null) {
$array['innerException'] = $this->formatThrowable($ex->getPrevious());
}
return $array;
}
/**
* Closes the connection to the logging database
*/
public function close() {
if($this->closed != true) {
$this->collection = null;
if ($this->connection !== null) {
$this->connection->close();
$this->connection = null;
}
$this->closed = true;
}
}
/**
* Sets the value of {@link $connectionString}.
* @param string $connectionString
*/
public function setConnectionString($connectionString) {
$this->setString('connectionString', $connectionString);
}
/**
* Returns the value of {@link $connectionString}.
* @return string
*/
public function getConnectionString() {
return $this->connectionString;
}
/**
* Sets the value of {@link $host} parameter.
* @param string $host
*/
public function setHost($host) {
$this->setString('host', $host);
}
/**
* Returns the value of {@link $host} parameter.
* @return string
*/
public function getHost() {
return $this->host;
}
/**
* Sets the value of {@link $port} parameter.
* @param int $port
*/
public function setPort($port) {
$this->setPositiveInteger('port', $port);
}
/**
* Returns the value of {@link $port} parameter.
* @return int
*/
public function getPort() {
return $this->port;
}
/**
* Sets the value of {@link $databaseName} parameter.
* @param string $databaseName
*/
public function setDatabaseName($databaseName) {
$this->setString('databaseName', $databaseName);
}
/**
* Returns the value of {@link $databaseName} parameter.
* @return string
*/
public function getDatabaseName() {
return $this->databaseName;
}
/**
* Sets the value of {@link $collectionName} parameter.
* @param string $collectionName
*/
public function setCollectionName($collectionName) {
$this->setString('collectionName', $collectionName);
}
/**
* Returns the value of {@link $collectionName} parameter.
* @return string
*/
public function getCollectionName() {
return $this->collectionName;
}
/**
* Sets the value of {@link $userName} parameter.
* @param string $userName
*/
public function setUserName($userName) {
$this->setString('userName', $userName, true);
}
/**
* Returns the value of {@link $userName} parameter.
* @return string
*/
public function getUserName() {
return $this->userName;
}
/**
* Sets the value of {@link $password} parameter.
* @param string $password
*/
public function setPassword($password) {
$this->setString('password', $password, true);
}
/**
* Returns the value of {@link $password} parameter.
* @return string
*/
public function getPassword() {
return $this->password;
}
/**
* Sets the value of {@link $timeout} parameter.
* @deprecated Use {@link $connectionTimeout} and {@link $socketTimeout} instead.
* @param int $timeout
*/
public function setTimeout($timeout) {
$this->setPositiveInteger('timeout', $timeout);
}
/**
* Returns the value of {@link $timeout} parameter.
* @deprecated Use {@link $connectionTimeout} and {@link $socketTimeout} instead.
* @return int
*/
public function getTimeout() {
return $this->timeout;
}
/**
* Sets the value of {@link $capped} parameter.
* @param bool $capped
*/
public function setCapped($capped) {
$this->setBoolean('capped', $capped);
}
/**
* Returns the value of {@link $capped} parameter.
* @return bool
*/
public function getCapped() {
return $this->capped;
}
/**
* Sets the value of {@link $cappedMax} parameter.
* @param int $cappedMax
*/
public function setCappedMax($cappedMax) {
$this->setPositiveInteger('cappedMax', $cappedMax);
}
/**
* Returns the value of {@link $cappedMax} parameter.
* @return int
*/
public function getCappedMax() {
return $this->cappedMax;
}
/**
* Sets the value of {@link $cappedSize} parameter.
* @param int $cappedSize
*/
public function setCappedSize($cappedSize) {
$this->setPositiveInteger('cappedSize', $cappedSize);
}
/**
* Returns the value of {@link $cappedSzie} parameter.
* @return int
*/
public function getCappedSize() {
return $this->cappedSize;
}
/**
* Sets the value of {@link $writeConcern} parameter.
* @param string $writeConcern
*/
public function setWriteConcern($writeConcern) {
$this->setString('writeConcern', $writeConcern);
}
/**
* Returns the value of {@link $writeConcern} parameter.
* @return string
*/
public function getWriteConcern() {
return $this->writeConcern;
}
/**
* Sets the value of {@link $writeConcernJournaled} parameter.
* @param bool $writeConcernJournaled
*/
public function setWriteConcernJournaled($writeConcernJournaled) {
$this->setBoolean('writeConcernJournaled', $writeConcernJournaled);
}
/**
* Returns the value of {@link $writeConcernJournaled} parameter.
* @return bool
*/
public function getWriteConcernJournaled() {
return $this->writeConcernJournaled;
}
/**
* Sets the value of {@link $writeConcernTimeout} parameter.
* @param int $writeConcernTimeout
*/
public function setWriteConcernTimeout($writeConcernTimeout) {
$this->setPositiveInteger('writeConcernTimeout', $writeConcernTimeout);
}
/**
* Returns the value of {@link $writeConcernTimeout} parameter.
* @return int
*/
public function getWriteConcernTimeout() {
return $this->writeConcernTimeout;
}
/**
* Sets the value of {@link $connectionTimeout} parameter.
* @param int $connectionTimeout
*/
public function setConnectionTimeout($connectionTimeout) {
$this->setPositiveInteger('connectionTimeout', $connectionTimeout);
}
/**
* Returns the value of {@link $connectionTimeout} parameter.
* @return int
*/
public function getConnectionTimeout() {
return $this->connectionTimeout;
}
/**
* Sets the value of {@link $socketTimeout} parameter.
* @param int $socketTimeout
*/
public function setSocketTimeout($socketTimeout) {
$this->setPositiveInteger('socketTimeout', $socketTimeout);
}
/**
* Returns the value of {@link $socketTimeout} parameter.
* @return int
*/
public function getSocketTimeout() {
return $this->socketTimeout;
}
/**
* Sets the value of {@link $replicaSet} parameter.
* @param string $replicaSet
*/
public function setReplicaSet($replicaSet) {
$this->setString('replicaSet', $replicaSet);
}
/**
* Returns the value of {@link $replicaSet} parameter.
* @return string
*/
public function getReplicaSet() {
return $this->replicaSet;
}
/**
* Returns the mongodb connection.
* @return Mongo
*/
public function getConnection() {
return $this->connection;
}
/**
* Returns the active mongodb collection.
* @return MongoCollection
*/
public function getCollection() {
return $this->collection;
}
}