Skip to content

Commit

Permalink
WIP #23: new API. Not crashing, but incorrect serialization choice.
Browse files Browse the repository at this point in the history
  • Loading branch information
fgm committed Mar 23, 2017
1 parent 1e0057b commit 520f6c3
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 55 deletions.
29 changes: 4 additions & 25 deletions src/Logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,13 @@ const Logger = class {
*
* @param {StrategyBase} strategy
* The sender selection strategy to apply.
* @param {Processor[]} processors
* @param {ProcessorBase[]} processors
* An array of processor instances.
*/
constructor(strategy, 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);
}

Expand Down Expand Up @@ -137,24 +133,6 @@ 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.
*
Expand Down Expand Up @@ -186,13 +164,14 @@ const Logger = class {
}

const context = cooked
? this.processors.reduce(this.processorReducer, { ...rawContext })
? this.processors.reduce(this.processorReducer, rawContext)
: rawContext;

// A timestamp is required, so insert it forcefully.
context.timestamp = { log: Date.now() };

const senders = this.strategy.selectSenders(level, message, context);
console.log("senders", senders);
senders.forEach(sender => {
sender.send(level, message, context, this.formatOptions);
});
Expand Down
1 change: 0 additions & 1 deletion src/Processors/BrowserProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const BrowserProcessor = class extends ProcessorBase {
return ['browser'];
}


/**
* The only required method for processor implementations.
*
Expand Down
14 changes: 11 additions & 3 deletions src/Senders/ConsoleSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ import SenderBase from "./SenderBase";
* @extends SenderBase
*/
const ConsoleSender = class extends SenderBase {
constructor() {
super();
/**
* @constructor
*
* @param {ProcessorBase[]} processors
* Optional. Processors to be applied by this sender instead of globally.
*/
constructor(processors = []) {
super(processors);
if (!console) {
throw new Error("Console sender needs a console object.");
}
Expand All @@ -30,8 +36,10 @@ const ConsoleSender = class extends SenderBase {
console.log
];

const processedContext = super.send(level, message, context);
const method = methods[level].bind(console);
method(LogLevel.Names[level], message, context);
method(LogLevel.Names[level], message, processedContext);
return processedContext;
}
};

Expand Down
14 changes: 9 additions & 5 deletions src/Senders/MeteorClientHttpSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ import NullFn from "../NullFn";
* @extends SenderBase
*/
const MeteorClientHttpSender = class extends SenderBase {
// noinspection JSClassNamingConvention
/**
* @constructor
*
* @param {ProcessorBase[]} processors
* Processors to be applied by this sender instead of globally.
* @param {String} loggerUrl
* The absolute URL of the logger server. Usually /logger on the Meteor app.
*/
constructor(loggerUrl) {
super();
constructor(processors = [], loggerUrl) {
super(processors);

if (!Meteor || !Meteor.isClient) {
throw new Error("MeteorClientHttpSender is only meant for Meteor client side.");
}
Expand All @@ -35,16 +37,18 @@ const MeteorClientHttpSender = class extends SenderBase {
}

send(level, message, context) {
const processedContext = super.send(level, message, context);
let data = { level, message };
if (typeof context !== "undefined") {
data.context = context;
if (typeof processedContext !== "undefined") {
data.context = processedContext;
}

let options = {
data,
headers: this.requestHeaders
};
this.http.post(this.loggerUrl, options, NullFn);
return processedContext;
}
};

Expand Down
44 changes: 37 additions & 7 deletions src/Senders/MongodbSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ const MongodbSender = class extends SenderBase {
/**
* @constructor
*
* @param {ProcessorBase[]} processors
* Processors to be applied by this sender instead of globally.
* @param {Mongo} mongo
* 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", formatOptions = null) {
super();
constructor(processors = [], mongo, collection = "logger", formatOptions = null) {
console.log("Monfo sender", processors);
super(processors);
if (collection instanceof mongo.Collection) {
this.store = collection;
}
Expand All @@ -42,14 +45,17 @@ const MongodbSender = class extends SenderBase {
}

send(level, message, context) {
let defaultedContext = context || {};
let doc = {level, message};
const defaultedContext = context || {};
const processedContext = super.send(level, message, defaultedContext);
let doc = { level, message };

const serializedContext = this.serializeContext(processedContext);

// It should contain a timestamp object if it comes from ClientLogger.
if (typeof defaultedContext.timestamp === "undefined") {
defaultedContext.timestamp = {};
if (typeof serializedContext.timestamp === "undefined") {
serializedContext.timestamp = {};
}
doc.context = defaultedContext;
doc.context = serializedContext;
doc.context.timestamp.store = Date.now();
try {
this.store.insert(doc);
Expand All @@ -58,6 +64,30 @@ const MongodbSender = class extends SenderBase {
doc.context = util.inspect(doc, this.formatOptions);
this.store.insert(doc);
}
return serializedContext;
}

/**
* Serialize the untrusted parts of a context, keeping the trusted ones.
*
* @param {object} context
* The raw context to semi-serialize.
*
* @returns {object}
* The context, with the non-trusted keys serialized together under the
* "serialized" key.
*/
serializeContext(context) {
let cloned = Object.assign({}, context);
let trusted = {};
this.processorKeys.forEach((value, index) => {
trusted[index] = cloned[index];
delete cloned[index];
});
const result = Object.assign(trusted, {
serialized: JSON.stringify(cloned)
});
return result;
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/Senders/NullSender.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @fileOverview NulllSender class.
* @fileOverview NullSender class.
*/


Expand Down
70 changes: 68 additions & 2 deletions src/Senders/SenderBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,86 @@

/**
* SenderBase is an "abstract" class defining the sender interface.
*
* @property {Array} processorKeys
*/
const SenderBase = class {

/**
* {@constructor}
*
* @param {ProcessorBase[]} processors
* Optional. Processors to be applied by this sender instead of globally.
*/
constructor(processors = []) {
this.processors = processors;
this.processorKeys = processors.reduce((accu, processor) => {
return [...accu, ...processor.getTrustedKeys()];
}, []);
console.log("SBC", processors, this.processorKeys);
}

/**
* Getter.
*
* @returns {ProcessorBase[]}
* The array of processors for this sender.
*/
getProcessors() {
return this.processors;
}

/**
* Reduce callback for processors.
*
* @see Logger.log()
*
* @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.
*/
processorReducer(accu, current) {
const result = Object.assign(accu, current.process(accu));
return result;
}

/**
* The single method for a sender: send data somewhere.
*
* @param {int} level
* One of the 8 RFC5424 levels: 0 to 7.
* @param {string} message
* Unlike LoggerBase::log(), it is not expected to handler non-string data.
* @param {object} context
* @param {object} rawContext
* A log event context object.
*
* @returns {object}
* The processed context, as sent.
*/
send(level, message, rawContext) {
const context = this.processors.reduce(this.processorReducer, rawContext);
return context;
}

/**
* Record the context keys added by processors.
*
* These keys are considered reliable, so do not needs serializing in
* long-term storage, to ease searching on them: it is up to the sender to
* decide how to handle them.
*
* @param {Array} keys
* An array of strings.
*
* @returns {void}
*/
send() {}
setProcessorKeys(keys) {
this.processorKeys = keys;
}
};

export default SenderBase;
13 changes: 9 additions & 4 deletions src/Senders/SyslogSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const SyslogSender = class extends SenderBase {
/**
* @constructor
*
* @param {ProcessorBase[]} processors
* Processors to be applied by this sender instead of globally.
* @param {String} ident
* Optional: the syslog identifier. Used as a prefix on messages.
* @param {Object} syslogOptions
Expand All @@ -35,8 +37,9 @@ const SyslogSender = class extends SenderBase {
* @param {Object} formatOptions
* Optional : The options used to format the message (default to { depth: 5 }).
*/
constructor(ident = null, syslogOptions = null, syslogFacility = null, syslog = null, formatOptions = null) {
super();
constructor(processors = [], ident = null, syslogOptions = null, syslogFacility = null, syslog = null, formatOptions = null) {
console.log("syslog sender", processors);
super(processors);
const programName = path.basename(process.argv[1]);
const actualIdent = ident || programName;

Expand All @@ -60,11 +63,13 @@ const SyslogSender = class extends SenderBase {
facility: this.syslog.facility[this.facility]
};

const processedContext = super.send(level, message, context);
// It should already contain a timestamp object anyway.
if (typeof context !== "undefined") {
doc.context = context;
if (typeof processedContext !== "undefined") {
doc.context = processedContext;
}
this.syslog.log(level, util.inspect(doc, this.formatOptions));
return processedContext;
}
};

Expand Down
12 changes: 8 additions & 4 deletions src/Senders/TeeSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,22 @@ const TeeSender = class extends SenderBase {
/**
* Constructor.
*
* @param {ProcessorBase[]} processors
* Processors to be applied by this sender instead of globally.
* @param {Array} senders
* An array of senders to which to send the input.
*/
constructor(senders) {
super();
constructor(processors = [], senders) {
console.log("Tee sender", processors);
super(processors);
this.senders = senders;
}

/** @inheritdoc */
send(level, message, context) {
const result = this.senders.map(sender => sender.send(level, message, context));
return result;
const processedContext = super.send(level, message, context);
this.senders.map(sender => sender.send(level, message, processedContext));
return processedContext;
}
};

Expand Down
6 changes: 3 additions & 3 deletions src/ServerLogger.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ 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 {Processor[]} processors
* An array of processor instances.
* @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, processors, webapp = null, parameters = {}) {
constructor(strategy, webapp = null, processors = [], parameters = {}) {
super(strategy, processors);
const defaultParameters = {
logRequestHeaders: true,
Expand Down
Loading

0 comments on commit 520f6c3

Please sign in to comment.