From 8e1a53414fd1da5c0d0c03173807cb071f2582f7 Mon Sep 17 00:00:00 2001 From: "Frederic G. MARAND" Date: Mon, 12 Dec 2016 18:08:36 +0100 Subject: [PATCH] Issue #23: WIP serialization - currently broken - processors report trusted keys on logger construct - but senders, which use them, are created before processors --- CHANGELOG.md | 3 ++- package.json | 2 +- src/Logger.js | 33 +++++++++++++++++++++++---- src/Processors/BrowserProcessor.js | 6 +++++ src/Processors/MeteorUserProcessor.js | 5 ++++ src/Processors/ProcessorBase.js | 12 ++++++++++ src/Processors/RoutingProcessor.js | 5 ++++ src/Senders/MongodbSender.js | 21 ++++++++++++++--- src/ServerLogger.js | 8 +++++-- 9 files changed, 84 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6613af..fa9a225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ### 0.1.12 -- LeveledStrategy.constructor() no longer accepts non-Senders as sender arguments. +* Messages are now stored serialized by default in MongoDB too, not just Syslog (#23). +* LeveledStrategy.constructor() no longer accepts non-Senders as sender arguments. * Logger.log() no longer accepts invalid log levels (#26), to match PSR-3. * Loggers no longer modify the message context (#25). * Removed incorrect `readme` clause in `package.json`. diff --git a/package.json b/package.json index 4d9d208..d66f729 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "scripts": { "compile": "rm -r lib/* ; npm i && babel --presets es2015,stage-0 -d lib/ src/", "doc": "rm -fr out ; jsdoc -c jsdoc.json", - "test": "npm run test-compile && time -p mocha --compilers js:babel-core/register test/*", + "test": "npm run test-compile && time -p mocha --compilers js:babel-core/register test/unit/* -w", "test-compile": "time -p babel --presets es2015,stage-0 -d lib/ src/", "test-integration": "npm run test-compile && time -p mocha --compilers js:babel-core/register test/integration", "test-unit": "npm run test-compile && time -p mocha --compilers js:babel-core/register test/unit" diff --git a/src/Logger.js b/src/Logger.js index c9b4193..3fc3fab 100644 --- a/src/Logger.js +++ b/src/Logger.js @@ -17,12 +17,17 @@ const Logger = class { * * @param {StrategyBase} strategy * The sender selection strategy to apply. + * @param {Processor[]} processors + * An array of processor instances. */ - constructor(strategy) { - this.processors = []; + constructor(strategy, processors) { + this.processors = processors; this.strategy = strategy; this.tk = TraceKit; + this.formatOptions = { + trustedKeys: this.processors.reduce(this.processorTrustReducer, []) + }; this.strategy.customizeLogger(this); } @@ -132,6 +137,24 @@ const Logger = class { return result; } + /** + * Reduce callback for processor trust. + * + * @see Logger.constructor() + * + * @param {Object} accu + * The reduction accumulator. + * @param {ProcessorBase} current + * The current process to apply in the reduction. + * + * @returns {Object} + * The result of the current reduction step. + */ + processorTrustReducer(accu, current) { + const result = [...accu, ...current.getTrustedKeys()]; + return result; + } + /** * Log an event. This is the *MAIN* method in the whole package. * @@ -145,7 +168,9 @@ const Logger = class { * @param {Object} rawContext * (Optional). An object complementing the message. * @param {Boolean} cooked - * (Optional). Is the context already reduced ? + * (Optional). Is the context already reduced ? This is typically the case + * on the server logger when it comes from the client logger: it should not + * usually be set by the user. * * @returns {void} * @@ -169,7 +194,7 @@ const Logger = class { const senders = this.strategy.selectSenders(level, message, context); senders.forEach(sender => { - sender.send(level, message, context); + sender.send(level, message, context, this.formatOptions); }); } diff --git a/src/Processors/BrowserProcessor.js b/src/Processors/BrowserProcessor.js index dfb27c5..51a5c36 100644 --- a/src/Processors/BrowserProcessor.js +++ b/src/Processors/BrowserProcessor.js @@ -22,6 +22,12 @@ const BrowserProcessor = class extends ProcessorBase { } } + /** @inheritdoc */ + getTrustedKeys() { + return ['browser']; + } + + /** * The only required method for processor implementations. * diff --git a/src/Processors/MeteorUserProcessor.js b/src/Processors/MeteorUserProcessor.js index e0a66ab..1944bf5 100644 --- a/src/Processors/MeteorUserProcessor.js +++ b/src/Processors/MeteorUserProcessor.js @@ -105,6 +105,11 @@ const MeteorUserProcessor = class extends ProcessorBase { return result; } + /** @inheritdoc */ + getTrustedKeys() { + return ['meteor']; + } + /** * Return the current user information, as far as possible. * diff --git a/src/Processors/ProcessorBase.js b/src/Processors/ProcessorBase.js index 8dc489f..b8e6165 100644 --- a/src/Processors/ProcessorBase.js +++ b/src/Processors/ProcessorBase.js @@ -8,6 +8,7 @@ * It exists only to document the processor interface. */ const ProcessorBase = class { + /** * The only required method for processor implementations. * @@ -23,6 +24,17 @@ const ProcessorBase = class { process(context) { return context; } + + /** + * Provide the list of context keys this processor recommends not serializing. + * + * @returns {Array} + * An array of context keys which will not be serialized by default if this + * processor is involved. + */ + getTrustedKeys() { + return []; + } }; export default ProcessorBase; diff --git a/src/Processors/RoutingProcessor.js b/src/Processors/RoutingProcessor.js index f1c0928..79470c6 100644 --- a/src/Processors/RoutingProcessor.js +++ b/src/Processors/RoutingProcessor.js @@ -19,6 +19,11 @@ const RoutingProcessor = class extends ProcessorBase { } } + /** @inheritdoc */ + getTrustedKeys() { + return ['routing']; + } + /** * Overwrite any previous routing information in context. * diff --git a/src/Senders/MongodbSender.js b/src/Senders/MongodbSender.js index 34b8221..cdf2108 100644 --- a/src/Senders/MongodbSender.js +++ b/src/Senders/MongodbSender.js @@ -1,6 +1,8 @@ /** * @fileOverview MongoDB Sender class. */ + +import * as util from "util"; import SenderBase from "./SenderBase"; /** @@ -17,8 +19,10 @@ const MongodbSender = class extends SenderBase { * The Meteor Mongo service. * @param {(String|Collection)} collection * The collection or the name of the collection in which to log. + * @param {Object} formatOptions + * Optional : The options used to format the message (default to { depth: 5 }). */ - constructor(mongo, collection = "logger") { + constructor(mongo, collection = "logger", formatOptions = null) { super(); if (collection instanceof mongo.Collection) { this.store = collection; @@ -30,11 +34,16 @@ const MongodbSender = class extends SenderBase { else { throw new Error("MongodbSender requires a Collection or a collection name"); } + + this.formatOptions = formatOptions || { + depth: 5, + rawKeys: [] + }; } send(level, message, context) { let defaultedContext = context || {}; - let doc = { level, message }; + let doc = {level, message}; // It should contain a timestamp object if it comes from ClientLogger. if (typeof defaultedContext.timestamp === "undefined") { @@ -42,7 +51,13 @@ const MongodbSender = class extends SenderBase { } doc.context = defaultedContext; doc.context.timestamp.store = Date.now(); - this.store.insert(doc); + try { + this.store.insert(doc); + } + catch (e) { + doc.context = util.inspect(doc, this.formatOptions); + this.store.insert(doc); + } } }; diff --git a/src/ServerLogger.js b/src/ServerLogger.js index 3c9d610..511b0d8 100644 --- a/src/ServerLogger.js +++ b/src/ServerLogger.js @@ -9,6 +9,8 @@ import * as util from "util"; * * Its main method is log(level, message, context). * + * @extends Logger + * * @see ServerLogger.log */ class ServerLogger extends Logger { @@ -18,14 +20,16 @@ class ServerLogger extends Logger { * * @param {StrategyBase} strategy * A logging strategy instance. + * @param {Processor[]} processors + * An array of processor instances. * @param {Webapp} webapp * The Meteor WebApp service. * @param {Object} parameters * - logRequestHeaders: add request headers to the log context. Defaults to true. * - servePath: the path on which to expose the logger endpoint. Defaults to "/logger". */ - constructor(strategy, webapp = null, parameters = {}) { - super(strategy); + constructor(strategy, processors, webapp = null, parameters = {}) { + super(strategy, processors); const defaultParameters = { logRequestHeaders: true, servePath: "/logger"