├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── jsconfig.json ├── lib └── index.js ├── package.json └── test ├── demo.js └── lib └── index.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules 3 | .DS_Store 4 | .idea/ 5 | atlassian-ide-plugin.xml -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "forin": true, 6 | "immed": true, 7 | "latedef": true, 8 | "newcap": true, 9 | "noarg": true, 10 | "noempty": true, 11 | "nonew": true, 12 | "regexp": true, 13 | "undef": true, 14 | "unused": true, 15 | "indent": 4, 16 | "quotmark": "single", 17 | "node": true 18 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | atlassian-ide-plugin.xml 3 | Gruntfile.js 4 | build/ 5 | test/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | services: 3 | - mongodb 4 | language: node_js 5 | cache: 6 | directories: 7 | - node_modules 8 | node_js: 9 | - '6' 10 | - '7' 11 | - '8' 12 | - 'stable' 13 | before_script: 14 | - npm install -g grunt-cli -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (grunt) { 4 | // load grunt tasks 5 | require('load-grunt-tasks')(grunt); 6 | 7 | // Project configuration. 8 | grunt.initConfig({ 9 | jshintFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js'], 10 | clean: { 11 | jasmine: ['build/jasmine'], 12 | reports: ['build/reports'], 13 | coverage: ['build/coverage'] 14 | }, 15 | jshint: { 16 | options: { 17 | jshintrc: true 18 | }, 19 | test: '<%= jshintFiles %>', 20 | jslint: { 21 | options: { 22 | reporter: 'jslint', 23 | reporterOutput: 'build/reports/jshint.xml' 24 | }, 25 | files: { 26 | src: '<%= jshintFiles %>' 27 | } 28 | }, 29 | checkstyle: { 30 | options: { 31 | reporter: 'checkstyle', 32 | reporterOutput: 'build/reports/jshint_checkstyle.xml' 33 | }, 34 | files: { 35 | src: '<%= jshintFiles %>' 36 | } 37 | } 38 | }, 39 | bgShell: { 40 | coverage: { 41 | cmd: 'node node_modules/istanbul/lib/cli.js cover --dir build/coverage node_modules/jasmine-node/bin/jasmine-node -- test --forceexit' 42 | }, 43 | cobertura: { 44 | cmd: 'node node_modules/istanbul/lib/cli.js report --root build/coverage --dir build/coverage/cobertura cobertura' 45 | } 46 | }, 47 | open: { 48 | file: { 49 | path: 'build/coverage/lcov-report/index.html' 50 | } 51 | }, 52 | jasmine_node: { 53 | options: { 54 | forceExit: true, 55 | match: '.', 56 | matchall: false, 57 | extensions: 'js', 58 | specNameMatcher: 'spec', 59 | requirejs: false 60 | }, 61 | unit: ['test/'], 62 | ci: { 63 | options: { 64 | jUnit: { 65 | report: true, 66 | savePath: 'build/jasmine/', 67 | useDotNotation: true, 68 | consolidate: true 69 | } 70 | }, 71 | src: ['test/'] 72 | } 73 | 74 | //specNameMatcher: './*.spec', // load only specs containing specNameMatcher 75 | //projectRoot: 'test', 76 | //requirejs: false, 77 | //forceExit: true, 78 | //jUnit: { 79 | // report: true, 80 | // savePath: 'build/jasmine/', 81 | // useDotNotation: true, 82 | // consolidate: true 83 | //} 84 | } 85 | }); 86 | 87 | // Register tasks. 88 | grunt.registerTask('test', ['clean:jasmine', 'jshint:test', 'jasmine_node:unit']); 89 | grunt.registerTask('cover', ['clean:coverage', 'jshint:test', 'bgShell:coverage', 'open']); 90 | grunt.registerTask('ci', ['clean', 'jshint:jslint', 'jshint:checkstyle', 'bgShell:coverage', 'bgShell:cobertura', 'jasmine_node:unit', 'jasmine_node:ci']); 91 | 92 | // Default task. 93 | grunt.registerTask('default', ['test']); 94 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2016 Litixsoft GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # log4js-node-mongodb 2 | 3 | > A log4js-node log appender to write logs into MongoDB. 4 | 5 | > [![Build Status](https://travis-ci.org/litixsoft/log4js-node-mongodb.svg?branch=master)](https://travis-ci.org/litixsoft/log4js-node-mongodb) [![david-dm](https://david-dm.org/litixsoft/log4js-node-mongodb.svg)](https://david-dm.org/litixsoft/log4js-node-mongodb/) [![david-dm](https://david-dm.org/litixsoft/log4js-node-mongodb/dev-status.svg)](https://david-dm.org/litixsoft/log4js-node-mongodb#info=devDependencies&view=table) 6 | 7 | ## Install 8 | 9 | $ npm install log4js-node-mongodb 10 | 11 | ## Documentation 12 | 13 | You can use this appender like all other log4js-node appenders. It just needs the connection-string to the mongo db. ([mongodb connection-string doku](http://docs.mongodb.org/manual/reference/connection-string/)) 14 | The default collection used is log. You can log a `string` or any kind of `object`. The objects are stored as they are and not converted to strings. 15 | 16 | ```js 17 | var log4js = require('log4js'); 18 | var mongoAppender = require('log4js-node-mongodb'); 19 | 20 | log4js.addAppender( 21 | mongoAppender.appender({connectionString: 'localhost:27017/logs'}), 22 | 'cheese' 23 | ); 24 | 25 | var logger = log4js.getLogger('cheese'); 26 | logger.trace('Entering cheese testing'); 27 | logger.debug('Got cheese.'); 28 | logger.info('Cheese is Gouda.'); 29 | logger.warn('Cheese is quite smelly.'); 30 | logger.error('Cheese is too ripe!'); 31 | logger.fatal('Cheese was breeding ground for listeria.'); 32 | 33 | // log objects 34 | logger.info({id: 1, name: 'wayne'}); 35 | logger.info([1, 2, 3]); 36 | ``` 37 | 38 | Or you can use the configure method. 39 | 40 | ```js 41 | var log4js = require('log4js'); 42 | 43 | log4js.configure({ 44 | appenders: [ 45 | { 46 | type: 'console' 47 | }, 48 | { 49 | type: 'log4js-node-mongodb', 50 | connectionString: 'localhost:27017/logs', 51 | category: 'cheese' 52 | } 53 | ] 54 | }); 55 | ``` 56 | 57 | The log data is stored in the following format. 58 | 59 | ```js 60 | { 61 | _id: ObjectID, 62 | timestamp: loggingEvent.startTime, 63 | data: loggingEvent.data, 64 | level: loggingEvent.level, 65 | category: loggingEvent.categoryName 66 | } 67 | ``` 68 | 69 | Here some examples. 70 | 71 | ```js 72 | var log4js = require('log4js'), 73 | mongoAppender = require('log4js-node-mongodb'); 74 | 75 | log4js.addAppender( 76 | mongoAppender.appender( 77 | {connectionString: 'localhost:27017/logs'}), 78 | 'audit' 79 | ); 80 | 81 | var logger = log4js.getLogger('audit'); 82 | logger.debug('Hello %s, your are the %d user logged in!', 'wayne', 10); 83 | 84 | // saved as 85 | { 86 | _id: new ObjectID(), 87 | timestamp: new Date(), 88 | data: 'Hello wayne, your are the 10 user logged in!', 89 | level: { 90 | level: 10000, 91 | levelStr: 'DEBUG' 92 | }, 93 | category: 'audit' 94 | } 95 | 96 | logger.info({id: 1, name: 'wayne'}); 97 | 98 | // saved as 99 | { 100 | _id: new ObjectID(), 101 | timestamp: new Date(), 102 | data: { 103 | id: 1, 104 | name: 'wayne' 105 | }, 106 | level: { 107 | level: 20000, 108 | levelStr: 'INFO' 109 | }, 110 | category: 'audit' 111 | } 112 | ``` 113 | 114 | ### Configuration 115 | There are some options which can by set through the config object. 116 | 117 | #### connectionOptions 118 | The connectionOptions object to pass to the mongo db client. 119 | 120 | .|. 121 | ---|--- 122 | Type|`object` 123 | Required|`false` 124 | Default value|`{}` 125 | 126 | ```js 127 | var log4js = require('log4js'), 128 | mongoAppender = require('log4js-node-mongodb'); 129 | 130 | log4js.addAppender( 131 | mongoAppender.appender({connectionString: 'localhost:27017/logs', 132 | connectionOptions : {server: {ssl: false, sslValidate: false}}}), 133 | 'cheese' 134 | ); 135 | ``` 136 | 137 | #### connectionString 138 | The connection-string to the mongo db. 139 | 140 | .|. 141 | ---|--- 142 | Type|`string` 143 | Required|`true` 144 | Default value| 145 | 146 | ```js 147 | var log4js = require('log4js'), 148 | mongoAppender = require('log4js-node-mongodb'); 149 | 150 | log4js.addAppender( 151 | mongoAppender.appender({connectionString: 'localhost:27017/logs'}), 152 | 'cheese' 153 | ); 154 | ``` 155 | 156 | #### collectionName 157 | The name of the mongo db collection where the logs are stored. 158 | 159 | .|. 160 | ---|--- 161 | Type|`string` 162 | Required|`false` 163 | Default value|`'log'` 164 | 165 | ```js 166 | var log4js = require('log4js'), 167 | mongoAppender = require('log4js-node-mongodb'); 168 | 169 | log4js.addAppender( 170 | mongoAppender.appender({ 171 | connectionString: 'localhost:27017/logs', 172 | collectionName: 'audit' 173 | }), 174 | 'cheese' 175 | ); 176 | ``` 177 | 178 | #### write 179 | The write mode of the mongo db insert operation. With this option you have control over the [write concern](http://docs.mongodb.org/manual/core/write-concern/) of mongo db. 180 | 181 | .|. 182 | ---|--- 183 | Type|`string` 184 | Required|`false` 185 | Default value|`'fast'` 186 | 187 | There are 3 options available. The default value is 'fast'. 188 | 189 | .|mongo options object|error logging 190 | ---|---|--- 191 | fast|`{w: 0}`|`no` 192 | normal|`{w: 1}`|`yes` 193 | safe|`{w: 1, journal: true}`|`yes` 194 | 195 | ```js 196 | var log4js = require('log4js'), 197 | mongoAppender = require('log4js-node-mongodb'); 198 | 199 | // fast write mode 200 | log4js.addAppender( 201 | mongoAppender.appender({connectionString: 'localhost:27017/logs'}), 202 | 'cheese' 203 | ); 204 | 205 | // normal write mode 206 | log4js.addAppender( 207 | mongoAppender.appender({ 208 | connectionString: 'localhost:27017/logs', 209 | write: 'normal' 210 | }), 211 | 'cheese' 212 | ); 213 | 214 | // safe write mode 215 | log4js.addAppender( 216 | mongoAppender.appender({ 217 | connectionString: 'localhost:27017/logs', 218 | write: 'safe' 219 | }), 220 | 'cheese' 221 | ); 222 | ``` 223 | 224 | #### layout 225 | The log4js-node layout which is used when logging a string. ([log4js-node layouts](https://github.com/nomiddlename/log4js-node/wiki/Layouts)) 226 | 227 | .|. 228 | ---|--- 229 | Type|`string` 230 | Required|`false` 231 | Default value|`'messagePassThroughLayout'` 232 | 233 | ```js 234 | var log4js = require('log4js'), 235 | mongoAppender = require('log4js-node-mongodb'); 236 | 237 | log4js.addAppender( 238 | mongoAppender.appender({ 239 | connectionString: 'localhost:27017/logs', 240 | layout: 'colored' 241 | }), 242 | 'cheese' 243 | ); 244 | ``` 245 | 246 | ## Contributing 247 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [grunt](http://gruntjs.com/). 248 | 249 | ## Release History 250 | ### v2.2.1 251 | * Update mongodb driver 252 | 253 | ### v2.2.0 254 | * Fix problem when saving data which includes mongodb ObjectID's 255 | 256 | ### v2.1.0 257 | * Add property `connectionOptions` to configuration options 258 | 259 | ### v2.0.0 260 | * Use native mongodb node.js driver instead of mongojs 261 | 262 | ### v1.0.0 263 | * add support for MongoDB 3.x 264 | 265 | ### v0.1.5 266 | * wrap Errors in a new object because otherwhise they are saved as an empty object {} 267 | 268 | ### v0.1.4 269 | * Errors are now saved as string (previously saved as empty object) 270 | 271 | ### v0.1.3 272 | * Date and Regexp objects are now saved correctly (previously saved as {}) 273 | 274 | ### v0.1.2 275 | * replace keys when logging an object to prevent mongo exception 276 | * $ is converted to "_dollar_" (only replaced at start of key) 277 | * . is converted to "_dot_" (all occurrences are replaced) 278 | 279 | ### v0.1.1 280 | * Safely check if the log message is a string 281 | 282 | ### v0.1.0 project initial 283 | 284 | ## Author 285 | [Litixsoft GmbH](http://www.litixsoft.de) 286 | 287 | ## License 288 | Copyright (C) 2013-2017 Litixsoft GmbH 289 | Licensed under the MIT license. 290 | 291 | Permission is hereby granted, free of charge, to any person obtaining a copy 292 | of this software and associated documentation files (the "Software"), to deal 293 | in the Software without restriction, including without limitation the rights 294 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 295 | copies of the Software, and to permit persons to whom the Software is 296 | furnished to do so, subject to the following conditions: 297 | 298 | The above copyright notice and this permission notice shall be included in 299 | all copies or substantial portions of the Software. 300 | 301 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 302 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 303 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 304 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 305 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 306 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 307 | THE SOFTWARE. 308 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=759670 3 | // for the documentation about the jsconfig.json format 4 | "compilerOptions": { 5 | "target": "es5", 6 | "module": "commonjs", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "exclude": [ 10 | "node_modules", 11 | "build" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var log4js = require('log4js'); 2 | var lxHelpers = require('lx-helpers'); 3 | var mongodb = require('mongodb'); 4 | 5 | /** 6 | * Returns a function to log data in mongodb. 7 | * 8 | * @param {Object} config The configuration object. 9 | * @param {string} config.connectionString The connection string to the mongo db. 10 | * @param {string=} config.layout The log4js layout. 11 | * @param {string=} config.write The write mode. 12 | * @returns {Function} 13 | */ 14 | function mongodbAppender(config) { 15 | if (!config || !config.connectionString) { 16 | throw new Error('connectionString is missing. Cannot connect to mongdb.'); 17 | } 18 | 19 | var collection; 20 | var cache = []; 21 | var layout = config.layout || log4js.layouts.messagePassThroughLayout; 22 | var collectionName = config.collectionName || 'log'; 23 | var connectionOptions = config.connectionOptions || {}; 24 | 25 | function ERROR(err) { 26 | Error.call(this); 27 | Error.captureStackTrace(this, this.constructor); 28 | 29 | this.name = err.toString(); 30 | this.message = err.message || 'error'; 31 | } 32 | 33 | function replaceKeys(src) { 34 | var result = {}; 35 | 36 | function mixin(dest, source, cloneFunc) { 37 | if (lxHelpers.isObject(source)) { 38 | lxHelpers.forEach(source, function (value, key) { 39 | // replace $ at start 40 | if (key[0] === '$') { 41 | key = key.replace('$', '_dollar_'); 42 | } 43 | 44 | // replace all dots 45 | key = key.replace(/\./g, '_dot_'); 46 | 47 | dest[key] = cloneFunc ? cloneFunc(value) : value; 48 | }); 49 | } 50 | 51 | return dest; 52 | } 53 | 54 | if (!src || typeof src !== 'object' || typeof src === 'function' || src instanceof Date || src instanceof RegExp ||  src instanceof mongodb.ObjectID) { 55 | return src; 56 | } 57 | 58 | // wrap Errors in a new object because otherwise they are saved as an empty object {} 59 | if (lxHelpers.getType(src) === 'error') { 60 | return new ERROR(src); 61 | } 62 | 63 | // Array 64 | if (lxHelpers.isArray(src)) { 65 | result = []; 66 | 67 | lxHelpers.arrayForEach(src, function (item) { 68 | result.push(replaceKeys(item)); 69 | }); 70 | } 71 | 72 | return mixin(result, src, replaceKeys); 73 | } 74 | 75 | function getOptions() { 76 | var options = { w: 0 }; 77 | 78 | if (config.write === 'normal') { 79 | options.w = 1; 80 | } 81 | 82 | if (config.write === 'safe') { 83 | options.w = 1; 84 | options.journal = true; 85 | } 86 | 87 | return options; 88 | } 89 | 90 | function insert(loggingEvent) { 91 | var options = getOptions(); 92 | 93 | if (collection) { 94 | if (options.w === 0) { 95 | // fast write 96 | collection.insert({ 97 | timestamp: loggingEvent.startTime, 98 | data: loggingEvent.data, 99 | level: loggingEvent.level, 100 | category: loggingEvent.categoryName 101 | }, options); 102 | } else { 103 | // save write 104 | collection.insert({ 105 | timestamp: loggingEvent.startTime, 106 | data: loggingEvent.data, 107 | level: loggingEvent.level, 108 | category: loggingEvent.categoryName 109 | }, options, function (error) { 110 | if (error) { 111 | console.error('log: Error writing data to log!'); 112 | console.error(error); 113 | console.log('log: Connection: %s, collection: %, data: %j', config.connectionString, collectionName, loggingEvent); 114 | } 115 | }); 116 | } 117 | } else { 118 | cache.push(loggingEvent); 119 | } 120 | } 121 | 122 | // check connection string 123 | if (config.connectionString.indexOf('mongodb://') !== 0) { 124 | config.connectionString = 'mongodb://' + config.connectionString; 125 | } 126 | 127 | // connect to mongodb 128 | mongodb.MongoClient.connect(config.connectionString, connectionOptions, function (err, db) { 129 | if (err) { 130 | console.error('Error connecting to mongodb! URL: %s', config.connectionString); 131 | console.error(err); 132 | } 133 | 134 | collection = db.collection(config.collectionName || 'log'); 135 | 136 | // process cache 137 | cache.forEach(function (loggingEvent) { 138 | setImmediate(function () { 139 | insert(loggingEvent); 140 | }); 141 | }); 142 | }); 143 | 144 | return function (loggingEvent) { 145 | // get the information to log 146 | if (Object.prototype.toString.call(loggingEvent.data[0]) === '[object String]') { 147 | // format string with layout 148 | loggingEvent.data = layout(loggingEvent); 149 | } else if (loggingEvent.data.length === 1) { 150 | loggingEvent.data = loggingEvent.data[0]; 151 | } 152 | 153 | loggingEvent.data = replaceKeys(loggingEvent.data); 154 | 155 | // save in db 156 | insert(loggingEvent); 157 | }; 158 | } 159 | 160 | function configure(config) { 161 | if (config.layout) { 162 | config.layout = log4js.layouts.layout(config.layout.type, config.layout); 163 | } 164 | 165 | return mongodbAppender(config); 166 | } 167 | 168 | exports.appender = mongodbAppender; 169 | exports.configure = configure; 170 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "log4js-node-mongodb", 3 | "description": "A log4js-node log appender to write logs into MongoDB.", 4 | "version": "2.2.1", 5 | "homepage": "https://github.com/litixsoft/log4js-node-mongodb", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/litixsoft/log4js-node-mongodb.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/litixsoft/log4js-node-mongodb/issues" 12 | }, 13 | "author": "Litixsoft GmbH (http://www.litixsoft.de)", 14 | "maintainers": [ 15 | "Andreas Krummsdorf ", 16 | "Thomas Scheibe " 17 | ], 18 | "contributors": [ 19 | "Max Nachlinger", 20 | "Ilya Starostin", 21 | "Adam Ball" 22 | ], 23 | "keywords": [ 24 | "log4js", 25 | "mongo", 26 | "mongodb", 27 | "logging", 28 | "log" 29 | ], 30 | "license": "MIT", 31 | "licenses": [ 32 | { 33 | "type": "MIT", 34 | "url": "https://github.com/litixsoft/log4js-node-mongodb/blob/master/LICENSE" 35 | } 36 | ], 37 | "main": "lib/index.js", 38 | "scripts": { 39 | "test": "grunt test", 40 | "demo": "node ./test/demo.js" 41 | }, 42 | "directories": { 43 | "lib": "./lib" 44 | }, 45 | "devDependencies": { 46 | "grunt": "1.0.1", 47 | "grunt-bg-shell": "^2.3.3", 48 | "grunt-contrib-clean": "^1.0.0", 49 | "grunt-contrib-jshint": "^1.0.0", 50 | "grunt-jasmine-node": "^0.3.1", 51 | "grunt-open": "^0.2.3", 52 | "istanbul": "^0.4.4", 53 | "jasmine-node": "1.14.5", 54 | "load-grunt-tasks": "^3.5.0" 55 | }, 56 | "dependencies": { 57 | "log4js": "^2", 58 | "lx-helpers": "^0.5.1", 59 | "mongodb": "^2.2.5" 60 | } 61 | } -------------------------------------------------------------------------------- /test/demo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var lib = require('../lib'); 4 | var log4js = require('log4js'); 5 | var mongodb = require('mongodb'); 6 | var url = 'localhost:27017/test_log4js_mongo'; 7 | 8 | // logger 9 | var appenders = [ 10 | { 11 | type: 'console' 12 | }, 13 | { 14 | type: 'log4js-node-mongodb', 15 | category: 'demo', 16 | level: 'DEBUG', 17 | connectionString: url 18 | } 19 | ]; 20 | 21 | mongodb.MongoClient.connect('mongodb://' + url, function (err, db) { 22 | if (err || !db) { 23 | return console.log(err || new Error('Unknown error, no database returned.')); 24 | } 25 | 26 | console.log('Successfully connected to MongoDb: %s', url); 27 | 28 | // clear 29 | var collection = db.collection('log'); 30 | 31 | collection.removeMany({}, function () { 32 | log4js.addAppender(lib.configure(appenders[1])); 33 | var logger = log4js.getLogger('demo'); 34 | var i = 500; 35 | 36 | for (var u = 0; u < i; u++) { 37 | logger.info(u); 38 | } 39 | 40 | var interval = setInterval(function () { 41 | logger.info(i); 42 | i++; 43 | }, 1); 44 | 45 | setTimeout(function () { 46 | clearInterval(interval); 47 | 48 | setTimeout(function () { 49 | db.close(function () { 50 | process.exit(0); 51 | }); 52 | }, 2000); 53 | }, 5000); 54 | }); 55 | }); -------------------------------------------------------------------------------- /test/lib/index.spec.js: -------------------------------------------------------------------------------- 1 | /*global describe, it, expect, beforeEach */ 2 | 'use strict'; 3 | 4 | var log4js = require('log4js'); 5 | var mongodb = require('mongodb'); 6 | var sut = require('../../lib/index'); 7 | var connectionString = 'mongodb://localhost:27017/test_log4js_mongo?w=0'; 8 | var db; 9 | 10 | describe('log4js-node-mongoappender', function () { 11 | beforeEach(function (done) { 12 | log4js.clearAppenders(); 13 | 14 | if (db) { 15 | db.collection('log').drop(function () { 16 | db.collection('audit').drop(function () { 17 | done(); 18 | }); 19 | }); 20 | } else { 21 | mongodb.MongoClient.connect(connectionString, function (err, database) { 22 | //var db = mongojs(connectionString, ['log', 'audit']); 23 | db = database; 24 | 25 | db.collection('log').drop(function () { 26 | db.collection('audit').drop(function () { 27 | done(); 28 | }); 29 | }); 30 | }); 31 | } 32 | }); 33 | 34 | it('should be initialized correctly', function () { 35 | expect(typeof sut.configure).toBe('function'); 36 | expect(typeof sut.appender).toBe('function'); 37 | }); 38 | 39 | it('should throw an Error when the connectionString is not set', function () { 40 | expect(function () { return log4js.addAppender(sut.appender()); }).toThrow(); 41 | }); 42 | 43 | it('should log to the mongo database when initialized through the configure function', function (done) { 44 | log4js.addAppender(sut.configure({ connectionString: 'localhost:27017/test_log4js_mongo' })); 45 | log4js.getLogger().info('Ready to log!'); 46 | log4js.getLogger().debug({ a: 1 }); 47 | 48 | var id = new mongodb.ObjectID(); 49 | log4js.getLogger().error({ _id: id }); 50 | 51 | setTimeout(function () { 52 | db.collection('log').find({}).toArray(function (err, res) { 53 | expect(err).toBeNull(); 54 | expect(res.length).toBe(3); 55 | expect(res[0].category).toBe('[default]'); 56 | expect(res[0].data).toBe('Ready to log!'); 57 | expect(res[0].level).toEqual({ level: 20000, levelStr: 'INFO' }); 58 | expect(res[1].data).toEqual({ a: 1 }); 59 | expect(res[1].level).toEqual({ level: 10000, levelStr: 'DEBUG' }); 60 | expect(res[2].data._id.toString()).toEqual(id.toString()); 61 | expect(res[2].data._id instanceof mongodb.ObjectID).toBeTruthy(); 62 | expect(res[2].level).toEqual({ level: 40000, levelStr: 'ERROR' }); 63 | 64 | done(); 65 | }); 66 | }, 1500); 67 | }); 68 | 69 | it('should log an object to the mongo database when initialized through the configure function', function (done) { 70 | log4js.addAppender(sut.configure({ connectionString: 'localhost:27017/test_log4js_mongo' })); 71 | log4js.getLogger().info({ ok: 1, date: new Date(), regex: new RegExp('aaa', 'i') }); 72 | 73 | setTimeout(function () { 74 | db.collection('log').find({}).toArray(function (err, res) { 75 | expect(err).toBeNull(); 76 | expect(res.length).toBe(1); 77 | expect(res[0].category).toBe('[default]'); 78 | expect(res[0].data.ok).toBe(1); 79 | expect(res[0].data.date instanceof Date).toBeTruthy(); 80 | expect(res[0].data.regex instanceof RegExp).toBeTruthy(); 81 | expect(res[0].level).toEqual({ level: 20000, levelStr: 'INFO' }); 82 | 83 | done(); 84 | }); 85 | }, 500); 86 | }); 87 | 88 | it('should log an error object to the mongo database when initialized through the configure function', function (done) { 89 | var error = new Error('wayne'); 90 | log4js.addAppender(sut.configure({ connectionString: 'localhost:27017/test_log4js_mongo' })); 91 | log4js.getLogger().warn(error); 92 | 93 | setTimeout(function () { 94 | db.collection('log').find({}).toArray(function (err, res) { 95 | expect(err).toBeNull(); 96 | expect(res.length).toBe(1); 97 | expect(res[0].category).toBe('[default]'); 98 | expect(res[0].data).toEqual({ name: 'Error: wayne', message: 'wayne' }); 99 | expect(res[0].level).toEqual({ level: 30000, levelStr: 'WARN' }); 100 | expect(error instanceof Error).toBeTruthy(); 101 | 102 | done(); 103 | }); 104 | }, 500); 105 | }); 106 | 107 | it('should log an object to the mongo database and replace keys that contains $ or .', function (done) { 108 | log4js.addAppender(sut.configure({ connectionString: 'localhost:27017/test_log4js_mongo' })); 109 | log4js.getLogger().info({ $and: [{ 'a.d': 3 }, { $or: { a: 1 } }], 'test.1.2': 5 }); 110 | 111 | setTimeout(function () { 112 | db.collection('log').find({}).toArray(function (err, res) { 113 | expect(err).toBeNull(); 114 | expect(res.length).toBe(1); 115 | expect(res[0].category).toBe('[default]'); 116 | expect(res[0].data).toEqual({ _dollar_and: [{ a_dot_d: 3 }, { _dollar_or: { a: 1 } }], test_dot_1_dot_2: 5 }); 117 | expect(res[0].level).toEqual({ level: 20000, levelStr: 'INFO' }); 118 | 119 | done(); 120 | }); 121 | }, 500); 122 | }); 123 | 124 | it('should log to the mongo database with a given layout', function (done) { 125 | log4js.addAppender(sut.configure({ connectionString: 'localhost:27017/test_log4js_mongo', layout: 'colored' })); 126 | log4js.getLogger().info('Ready to log!'); 127 | 128 | setTimeout(function () { 129 | db.collection('log').find({}).toArray(function (err, res) { 130 | expect(err).toBeNull(); 131 | expect(res.length).toBe(1); 132 | expect(res[0].category).toBe('[default]'); 133 | expect(res[0].data).toBe('Ready to log!'); 134 | expect(res[0].level).toEqual({ level: 20000, levelStr: 'INFO' }); 135 | 136 | done(); 137 | }); 138 | }, 100); 139 | }); 140 | 141 | it('should log to the mongo database given connection options', function (done) { 142 | log4js.addAppender(sut.configure({ connectionString: 'localhost:27017/test_log4js_mongo', connectionOptions: {server: {ssl: false, sslValidate: false}} })); 143 | log4js.getLogger().info('Ready to log!'); 144 | 145 | setTimeout(function () { 146 | db.collection('log').find({}).toArray(function (err, res) { 147 | expect(err).toBeNull(); 148 | expect(res.length).toBe(1); 149 | expect(res[0].category).toBe('[default]'); 150 | expect(res[0].data).toBe('Ready to log!'); 151 | expect(res[0].level).toEqual({ level: 20000, levelStr: 'INFO' }); 152 | 153 | done(); 154 | }); 155 | }, 100); 156 | }); 157 | 158 | it('should log to the mongo database with category [default]', function (done) { 159 | log4js.addAppender(sut.appender({ connectionString: 'localhost:27017/test_log4js_mongo' })); 160 | log4js.getLogger().info('Ready to log!'); 161 | 162 | setTimeout(function () { 163 | db.collection('log').find({}).toArray(function (err, res) { 164 | expect(err).toBeNull(); 165 | expect(res.length).toBe(1); 166 | expect(res[0].category).toBe('[default]'); 167 | expect(res[0].data).toBe('Ready to log!'); 168 | expect(res[0].level).toEqual({ level: 20000, levelStr: 'INFO' }); 169 | 170 | done(); 171 | }); 172 | }, 100); 173 | }); 174 | 175 | it('should log to the mongo database with a category', function (done) { 176 | log4js.addAppender(sut.appender({ connectionString: 'localhost:27017/test_log4js_mongo' }), 'demo'); 177 | log4js.getLogger('demo').warn({ id: 1, name: 'test' }); 178 | 179 | setTimeout(function () { 180 | db.collection('log').find({}).toArray(function (err, res) { 181 | expect(err).toBeNull(); 182 | expect(res.length).toBe(1); 183 | expect(res[0].category).toBe('demo'); 184 | expect(res[0].data).toEqual({ id: 1, name: 'test' }); 185 | expect(res[0].level).toEqual({ level: 30000, levelStr: 'WARN' }); 186 | 187 | done(); 188 | }); 189 | }, 100); 190 | }); 191 | 192 | //it('should log to the mongo database with a given collection', function (done) { 193 | // var db = mongojs(connectionString, ['audit']); 194 | // log4js.addAppender(sut.appender({connectionString: 'localhost:27017/test_log4js_mongo', collectionName: 'audit'}), 'demo'); 195 | // log4js.getLogger('demo').error({id: 1, name: 'test'}); 196 | // 197 | // setTimeout(function () { 198 | // db.collection('audit').find({}, function (err, res) { 199 | // expect(err).toBeNull(); 200 | // expect(res.length).toBe(1); 201 | // expect(res[0].category).toBe('demo'); 202 | // expect(res[0].data).toEqual({id: 1, name: 'test'}); 203 | // expect(res[0].level).toEqual({level: 40000, levelStr: 'ERROR'}); 204 | // 205 | // done(); 206 | // }); 207 | // }, 1000); 208 | //}); 209 | 210 | it('should log to the mongo database with write mode "normal"', function (done) { 211 | log4js.addAppender(sut.appender({ connectionString: 'localhost:27017/test_log4js_mongo', write: 'normal' })); 212 | log4js.getLogger().fatal('Ready to log!'); 213 | 214 | setTimeout(function () { 215 | db.collection('log').find({}).toArray(function (err, res) { 216 | expect(err).toBeNull(); 217 | expect(res.length).toBe(1); 218 | expect(res[0].category).toBe('[default]'); 219 | expect(res[0].data).toBe('Ready to log!'); 220 | expect(res[0].level).toEqual({ level: 50000, levelStr: 'FATAL' }); 221 | 222 | done(); 223 | }); 224 | }, 1000); 225 | }); 226 | 227 | it('should log to the mongo database with write mode "safe"', function (done) { 228 | log4js.addAppender(sut.appender({ connectionString: 'localhost:27017/test_log4js_mongo', write: 'safe' })); 229 | log4js.getLogger().debug('Ready to log!'); 230 | 231 | setTimeout(function () { 232 | db.collection('log').find({}).toArray(function (err, res) { 233 | expect(err).toBeNull(); 234 | expect(res.length).toBe(1); 235 | expect(res[0].category).toBe('[default]'); 236 | expect(res[0].data).toBe('Ready to log!'); 237 | expect(res[0].level).toEqual({ level: 10000, levelStr: 'DEBUG' }); 238 | 239 | done(); 240 | }); 241 | }, 1000); 242 | }); 243 | }); --------------------------------------------------------------------------------