client.db
獲取 database 事例;const util = require('util'); const log4js = require('log4js'); const lxHelpers = require('lx-helpers'); const mongodb = require('mongodb'); function messagePassThroughLayout(loggingEvent) { return util.format(...loggingEvent.data); } /** * Returns a function to log data in mongodb. * * @param {Object} config The configuration object. * @param {string} config.connectionString The connection string to the mongo db. * @param {string=} config.layout The log4js layout. * @param {string=} config.write The write mode. * @returns {Function} */ function appender( config ){ if( !config || !config.connectionString ){ throw new Error('connectionString is missing. Cannot connect to mongdb.'); } var collection; var cache = []; var layout = config.layout || messagePassThroughLayout ; var collectionName = config.collectionName || 'log'; var connectionOptions = config.connectionOptions || {}; function ERROR(err) { Error.call(this); Error.captureStackTrace(this, this.constructor); this.name = err.toString(); this.message = err.message || 'error'; } function replaceKeys(src) { var result = {}; function mixin(dest, source, cloneFunc) { if (lxHelpers.isObject(source)) { lxHelpers.forEach(source, function (value, key) { // replace $ at start if (key[0] === '$') { key = key.replace('$', '_dollar_'); } // replace all dots key = key.replace(/\./g, '_dot_'); dest[key] = cloneFunc ? cloneFunc(value) : value; }); } return dest; } if( ( !src ) || ( typeof src !== 'object' ) || ( typeof src === 'function' ) || ( src instanceof Date ) || ( src instanceof RegExp ) || ( src instanceof mongodb.ObjectID ) ){ return src; } // wrap Errors in a new object because otherwise they are saved as an empty object {} if( lxHelpers.getType(src) === 'error' ){ return new ERROR(src); } // Array if( lxHelpers.isArray( src ) ){ result = []; lxHelpers.arrayForEach(src, function (item) { result.push( replaceKeys( item ) ); }); } return mixin( result, src, replaceKeys ); } function getOptions() { var options = { w: 0 }; if (config.write === 'normal') { options.w = 1; } if (config.write === 'safe') { options.w = 1; options.journal = true; } return options; } function insert( loggingEvent ){ // if( loggingEvent.data == null ) return; var options = getOptions(); if (collection) { if (options.w === 0) { // fast write collection.insertOne( { timestamp: loggingEvent.startTime, data: loggingEvent.data, level: loggingEvent.level, category: loggingEvent.categoryName, }, options ); } else { // save write collection.insert( { timestamp: loggingEvent.startTime, data: loggingEvent.data, level: loggingEvent.level, category: loggingEvent.categoryName, }, options, function (error) { if (error) { console.error('log: Error writing data to log!'); console.error(error); console.log('log: Connection: %s, collection: %, data: %j', config.connectionString, collectionName, loggingEvent); } } ); } } else { cache.push(loggingEvent); } } // check connection string if (config.connectionString.indexOf('mongodb://') !== 0) { config.connectionString = 'mongodb://' + config.connectionString; } // connect to mongodb mongodb.MongoClient.connect( config.connectionString, connectionOptions, ( err, cli ) => { if( err ){ console.error( err ); throw new Error( 'This code not compatible latest mongodb'); } if( cli.s.options.dbName == null ) { throw new Error( 'This code not compatible latest mongodb'); } let db = cli.db( cli.s.options.dbName ); collection = db.collection( config.collectionName || 'log' ); // process cache cache.forEach( ( loggingEvent ) => { setImmediate( () => { insert(loggingEvent); } ); } ); } ); return function (loggingEvent) { // get the information to log if( Object.prototype.toString.call(loggingEvent.data[0]) === '[object String]') { // format string with layout loggingEvent.data = layout( loggingEvent ); }else if( loggingEvent.data.length === 1 ){ loggingEvent.data = loggingEvent.data[0]; }else{ console.log( 'unknow type' ); } loggingEvent.data = replaceKeys( loggingEvent.data ); // save in db insert(loggingEvent); }; } function configure(config) { if( config.layout ){ config.layout = log4js.layouts.layout( config.layout.type, config.layout ); } return appender(config); } module.exports.appender = appender; module.exports.configure = configure;
const fs = require( 'fs' ); const log4js = require('log4js'); const lvCA = fs.readFileSync( './ssl/CA.crt', 'utf8' ); const lvCert = fs.readFileSync( './ssl/cli.crt', 'utf8' ); const lvKey = fs.readFileSync( './ssl/cli.key', 'utf8' ); log4js.configure( { appenders: { mongodb: { type: 'log/mongodbAppender', connectionString: '192.168.1.200:9002/log?ssl=true', collectionName: 'log', connectionOptions: { // useNewUrlParser: true, ssl: true, sslValidate: true, sslCA: lvCA, sslCert: lvCert, sslKey: lvKey, checkServerIdentity: false, } } }, categories: { default: { appenders: [ 'mongodb' ], level: 'debug' } } } ) const logger = log4js.getLogger(); logger.level = 'debug'; logger.debug("Some debug messages", 'aaa'); logger.fatal({ whatever: 'foo' })
不須要用到 lxHelper 這個庫了,ES6+ 判斷是否爲 Array 能夠用 Array.isArray
, 由於擔憂 mongodb 存儲會影響磁盤 iops 所以應該用 redis 會好一點。html
const util = require( 'util' ); const redis = require("redis"); // function messagePassThroughLayout(loggingEvent) { // return util.format(...loggingEvent.data); // } let lvOutsideClient = null; let lvInsideClient = null; let lvLayout = null; let lvCache = []; /** * Returns a function to log data in mongodb. * * @param {string} config.host The redis host * @param {string} config.port The redis port * @param {Object} config.redisOption Option, If this parameters is exist, it * will ignore host and port, and you can set all redis params in this object * @returns {Function} */ function appender( config ){ if( ( config == null ) || ( ( config.redisOption == null ) && ( ( config.host == null ) || ( config.port == null ) ) ) || ( config.dbIndex == null ) ){ throw new Error( 'Please provide full params' ); } let tvReconnLoop = null; let tvReconnectTimes = 0; tfPersistenceCache = ( ) => { if( ( lvCache.length != 0 ) && ( lvOutsideClient ) ){ let tvLen = lvCache.length; for( let i = 0; i < tvLen; i++ ){ let tvDocument = lvCache.shift(); lvOutsideClient.lpush( tvDocument.keys, tvDocument.document, ( err ) => { if( err ){ lvCache.splice(0, 0, tvDocument); } } ); } } } tfConnectToRedis = () => { if( lvOutsideClient ) return; if( config.redisOption ) lvInsideClient = redis.createClient( config.redisOption ); else lvInsideClient = redis.createClient( config.port, config.host ); console.log( '-> connect' ) lvInsideClient.on('connect', (err) => { console.log( '-> connected' ) if( tvReconnLoop ) clearInterval( tvReconnLoop ); tvReconnLoop = null; lvInsideClient.select( config.dbIndex, ( err, res ) => { if( err ){ tfConnectServerPerSeconds(); return; } lvOutsideClient = lvInsideClient; tfPersistenceCache(); } ); }); lvInsideClient.on('reconnecting', (err) => { console.log( '-> reconnecting' ) } ) lvInsideClient.on('error', (err) => { console.log( '-> error' ) lvOutsideClient = null; tfConnectServerPerSeconds(); }); lvInsideClient.on('end', (err) => { console.log( '-> end' ) lvOutsideClient = null; tfConnectServerPerSeconds(); }); } tfConnectServerPerSeconds = () => { if( tvReconnectTimes == 0 ){ tfConnectToRedis(); tvReconnectTimes++; }else{ if( tvReconnLoop ) return; tvReconnectTimes++; tvReconnLoop = setInterval( () => { tfConnectToRedis(); }, 20000 ) } } tfConnectServerPerSeconds(); // lvLayout = config.layout || messagePassThroughLayout ; return ( loggingEvent ) => { let tvDocument = JSON.stringify( loggingEvent ); console.log( `Document: ${tvDocument}` ); if( lvOutsideClient ) lvOutsideClient.lpush( loggingEvent.level.levelStr, tvDocument, () => {} ) else lvCache.push( { keys: loggingEvent.level.levelStr, document: tvDocument } ) }; } function configure( config, layouts ){ let layout = layouts.basicLayout; if (config.layout) { layout = layouts.layout(config.layout.type, config.layout); } return appender(config); } module.exports.configure = configure;