1 : <?php
2 : /**
3 : * Licensed to the Apache Software Foundation (ASF) under one or more
4 : * contributor license agreements. See the NOTICE file distributed with
5 : * this work for additional information regarding copyright ownership.
6 : * The ASF licenses this file to You under the Apache License, Version 2.0
7 : * (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : *
18 : * @package log4php
19 : */
20 :
21 : /**
22 : * Appender for writing to MongoDB.
23 : *
24 : * This class was originally contributed by Vladimir Gorej.
25 : *
26 : * @link http://github.com/log4mongo/log4mongo-php Vladimir Gorej's original submission.
27 : * @link http://www.mongodb.org/ MongoDB website.
28 : *
29 : * @version $Revision: 806678 $
30 : * @package log4php
31 : * @subpackage appenders
32 : * @since 2.1
33 : */
34 : class LoggerAppenderMongoDB extends LoggerAppender {
35 :
36 : // ******************************************
37 : // ** Constants **
38 : // ******************************************
39 :
40 : /** Default prefix for the {@link $host}. */
41 : const DEFAULT_MONGO_URL_PREFIX = 'mongodb://';
42 :
43 : /** Default value for {@link $host}, without a prefix. */
44 : const DEFAULT_MONGO_HOST = 'localhost';
45 :
46 : /** Default value for {@link $port} */
47 : const DEFAULT_MONGO_PORT = 27017;
48 :
49 : /** Default value for {@link $databaseName} */
50 : const DEFAULT_DB_NAME = 'log4php_mongodb';
51 :
52 : /** Default value for {@link $collectionName} */
53 : const DEFAULT_COLLECTION_NAME = 'logs';
54 :
55 : /** Default value for {@link $timeout} */
56 : const DEFAULT_TIMEOUT_VALUE = 3000;
57 :
58 : // ******************************************
59 : // ** Configurable parameters **
60 : // ******************************************
61 :
62 : /** Server on which mongodb instance is located. */
63 : protected $host;
64 :
65 : /** Port on which the instance is bound. */
66 : protected $port;
67 :
68 : /** Name of the database to which to log. */
69 : protected $databaseName;
70 :
71 : /** Name of the collection within the given database. */
72 : protected $collectionName;
73 :
74 : /** Username used to connect to the database. */
75 : protected $userName;
76 :
77 : /** Password used to connect to the database. */
78 : protected $password;
79 :
80 : /** Timeout value used when connecting to the database (in milliseconds). */
81 : protected $timeout;
82 :
83 : // ******************************************
84 : // ** Member variables **
85 : // ******************************************
86 :
87 : /**
88 : * Connection to the MongoDB instance.
89 : * @var Mongo
90 : */
91 : protected $connection;
92 :
93 : /**
94 : * The collection to which log is written.
95 : * @var MongoCollection
96 : */
97 : protected $collection;
98 :
99 : /**
100 : * Set to true if the appender can append.
101 : * @todo Maybe we should use $closed here instead?
102 : */
103 : protected $canAppend = false;
104 :
105 : /** Appender does not require a layout. */
106 : protected $requiresLayout = false;
107 :
108 : public function __construct($name = '') {
109 3 : parent::__construct($name);
110 3 : $this->host = self::DEFAULT_MONGO_URL_PREFIX . self::DEFAULT_MONGO_HOST;
111 3 : $this->port = self::DEFAULT_MONGO_PORT;
112 3 : $this->databaseName = self::DEFAULT_DB_NAME;
113 3 : $this->collectionName = self::DEFAULT_COLLECTION_NAME;
114 3 : $this->timeout = self::DEFAULT_TIMEOUT_VALUE;
115 3 : }
116 :
117 : /**
118 : * Setup db connection.
119 : * Based on defined options, this method connects to the database and
120 : * creates a {@link $collection}.
121 : *
122 : * @throws Exception if the attempt to connect to the requested database fails.
123 : */
124 : public function activateOptions() {
125 : try {
126 4 : $this->connection = new Mongo(sprintf('%s:%d', $this->host, $this->port), array("timeout" => $this->timeout));
127 4 : $db = $this->connection->selectDB($this->databaseName);
128 4 : if ($this->userName !== null && $this->password !== null) {
129 0 : $authResult = $db->authenticate($this->userName, $this->password);
130 0 : if ($authResult['ok'] == floatval(0)) {
131 0 : throw new Exception($authResult['errmsg'], $authResult['ok']);
132 : }
133 0 : }
134 :
135 4 : $this->collection = $db->selectCollection($this->collectionName);
136 4 : } catch (Exception $ex) {
137 0 : $this->canAppend = false;
138 0 : throw new LoggerException($ex);
139 : }
140 :
141 4 : $this->canAppend = true;
142 4 : }
143 :
144 : /**
145 : * Appends a new event to the mongo database.
146 : *
147 : * @throws LoggerException If the pattern conversion or the INSERT statement fails.
148 : */
149 : public function append(LoggerLoggingEvent $event) {
150 3 : if ($this->canAppend == true && $this->collection != null) {
151 3 : $document = $this->format($event);
152 3 : $this->collection->insert($document);
153 3 : }
154 3 : }
155 :
156 : /**
157 : * Converts the logging event into an array which can be logged to mongodb.
158 : *
159 : * @param LoggerLoggingEvent $event
160 : * @return array The array representation of the logging event.
161 : */
162 : protected function format(LoggerLoggingEvent $event) {
163 3 : $timestampSec = (int) $event->getTimestamp();
164 3 : $timestampUsec = (int) (($event->getTimestamp() - $timestampSec) * 1000000);
165 :
166 : $document = array(
167 3 : 'timestamp' => new MongoDate($timestampSec, $timestampUsec),
168 3 : 'level' => $event->getLevel()->toString(),
169 3 : 'thread' => (int) $event->getThreadName(),
170 3 : 'message' => $event->getMessage(),
171 3 : 'loggerName' => $event->getLoggerName()
172 3 : );
173 :
174 3 : $locationInfo = $event->getLocationInformation();
175 3 : if ($locationInfo != null) {
176 3 : $document['fileName'] = $locationInfo->getFileName();
177 3 : $document['method'] = $locationInfo->getMethodName();
178 3 : $document['lineNumber'] = ($locationInfo->getLineNumber() == 'NA') ? 'NA' : (int) $locationInfo->getLineNumber();
179 3 : $document['className'] = $locationInfo->getClassName();
180 3 : }
181 :
182 3 : $throwableInfo = $event->getThrowableInformation();
183 3 : if ($throwableInfo != null) {
184 2 : $document['exception'] = $this->formatThrowable($throwableInfo->getThrowable());
185 2 : }
186 :
187 3 : return $document;
188 : }
189 :
190 : /**
191 : * Converts an Exception into an array which can be logged to mongodb.
192 : *
193 : * Supports innner exceptions (PHP >= 5.3)
194 : *
195 : * @param Exception $ex
196 : * @return array
197 : */
198 : protected function formatThrowable(Exception $ex) {
199 : $array = array(
200 2 : 'message' => $ex->getMessage(),
201 2 : 'code' => $ex->getCode(),
202 2 : 'stackTrace' => $ex->getTraceAsString(),
203 2 : );
204 :
205 2 : if (method_exists($ex, 'getPrevious') && $ex->getPrevious() !== null) {
206 1 : $array['innerException'] = $this->formatThrowable($ex->getPrevious());
207 1 : }
208 :
209 2 : return $array;
210 : }
211 :
212 : /**
213 : * Closes the connection to the logging database
214 : */
215 : public function close() {
216 4 : if($this->closed != true) {
217 4 : $this->collection = null;
218 4 : if ($this->connection !== null) {
219 4 : $this->connection->close();
220 4 : $this->connection = null;
221 4 : }
222 4 : $this->closed = true;
223 4 : }
224 4 : }
225 :
226 : /** Sets the value of {@link $host} parameter. */
227 : public function setHost($host) {
228 4 : if (!preg_match('/^mongodb\:\/\//', $host)) {
229 3 : $host = self::DEFAULT_MONGO_URL_PREFIX . $host;
230 3 : }
231 4 : $this->host = $host;
232 4 : }
233 :
234 : /** Returns the value of {@link $host} parameter. */
235 : public function getHost() {
236 1 : return $this->host;
237 : }
238 :
239 : /** Sets the value of {@link $port} parameter. */
240 : public function setPort($port) {
241 1 : $this->setPositiveInteger('port', $port);
242 1 : }
243 :
244 : /** Returns the value of {@link $port} parameter. */
245 : public function getPort() {
246 1 : return $this->port;
247 : }
248 :
249 : /** Sets the value of {@link $databaseName} parameter. */
250 : public function setDatabaseName($databaseName) {
251 1 : $this->setString('databaseName', $databaseName);
252 1 : }
253 :
254 : /** Returns the value of {@link $databaseName} parameter. */
255 : public function getDatabaseName() {
256 1 : return $this->databaseName;
257 : }
258 :
259 : /** Sets the value of {@link $collectionName} parameter. */
260 : public function setCollectionName($collectionName) {
261 1 : $this->setString('collectionName', $collectionName);
262 1 : }
263 :
264 : /** Returns the value of {@link $collectionName} parameter. */
265 : public function getCollectionName() {
266 1 : return $this->collectionName;
267 : }
268 :
269 : /** Sets the value of {@link $userName} parameter. */
270 : public function setUserName($userName) {
271 2 : $this->setString('userName', $userName, true);
272 2 : }
273 :
274 : /** Returns the value of {@link $userName} parameter. */
275 : public function getUserName() {
276 1 : return $this->userName;
277 : }
278 :
279 : /** Sets the value of {@link $password} parameter. */
280 : public function setPassword($password) {
281 2 : $this->setString('password', $password, true);
282 2 : }
283 :
284 : /** Returns the value of {@link $password} parameter. */
285 : public function getPassword() {
286 1 : return $this->password;
287 : }
288 :
289 : /** Sets the value of {@link $timeout} parameter. */
290 : public function setTimeout($timeout) {
291 0 : $this->setPositiveInteger('timeout', $timeout);
292 0 : }
293 :
294 : /** Returns the value of {@link $timeout} parameter. */
295 : public function getTimeout() {
296 0 : return $this->timeout;
297 : }
298 : /**
299 : * Returns the mongodb connection.
300 : * @return Mongo
301 : */
302 : public function getConnection() {
303 3 : return $this->connection;
304 : }
305 :
306 : /**
307 : * Returns the active mongodb collection.
308 : * @return MongoCollection
309 : */
310 : public function getCollection() {
311 0 : return $this->collection;
312 : }
313 : }
|