Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bunyan and Loggly updates #2425

Merged
merged 7 commits into from
Jun 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@
"bcrypt": "^1.0.2",
"bootstrap": "^3.3.7",
"braintree": "^2.0.2",
"bunyan": "^1.8.9",
"bunyan": "^2.0.0",
"bunyan-format": "^0.2.1",
"bunyan-loggly": "^1.2.0",
"classnames": "^2.2.5",
"copy-to-clipboard": "^3.0.6",
"country-data": "^0.0.31",
Expand Down Expand Up @@ -59,6 +58,7 @@
"moment-timezone": "^0.5.11",
"nexmo": "^1.2.0",
"node-geocoder": "^3.16.0",
"node-loggly-bulk": "^2.0.0",
"nodemailer-wellknown": "^0.2.1",
"nouislider-algolia-fork": "^10.0.0",
"npm-shrinkwrap": "^6.0.2",
Expand Down
2 changes: 1 addition & 1 deletion server/api/core/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Mongo } from "meteor/mongo";
import { EJSON } from "meteor/ejson";
import * as Collections from "/lib/collections";
import Hooks from "../hooks";
import Logger from "../logger";
import { Logger } from "../logger";
import doRightJoinNoIntersection from "./rightJoin";

/**
Expand Down
2 changes: 1 addition & 1 deletion server/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as Accounts from "./core/accounts";
import Router from "./router";
import GeoCoder from "./geocoder";
import Hooks from "./hooks";
import Logger from "./logger";
import { Logger } from "./logger";
import { MethodHooks } from "./method-hooks";

export {
Expand Down
1 change: 1 addition & 0 deletions server/api/logger/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Logger } from "./main";
67 changes: 67 additions & 0 deletions server/api/logger/loggly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import loggly from "node-loggly-bulk";

class Bunyan2Loggly {
constructor(logglyConfig, bufferLength = 1, bufferTimeout, callback) {
if (!logglyConfig || !logglyConfig.token || !logglyConfig.subdomain) {
throw new Error("bunyan-loggly requires a config object with token and subdomain");
}

logglyConfig.json = true;

this.logglyClient = loggly.createClient(logglyConfig);

this._buffer = [];
this.bufferLength = bufferLength;
this.bufferTimeout = bufferTimeout;
this.callback = callback || function () {};
}

write(data) {
if (typeof data !== "object") {
throw new Error("bunyan-loggly requires a raw stream. Please define the type as raw when setting up the bunyan stream.");
}

// loggly prefers timestamp over time
if (data.time) {
data.timestamp = data.time;
delete data.time;
}

this._buffer.push(data);

this._checkBuffer();
}

_processBuffer() {
clearTimeout(this._timeoutId);

let content = this._buffer.slice();

this._buffer = [];

if (content.length === 1) {
content = content[0];
}

this.logglyClient.log(content, (error, result) => {
this.callback(error, result, content);
});
}

_checkBuffer() {
if (!this._buffer.length) {
return;
}

if (this._buffer.length >= this.bufferLength) {
return this._processBuffer();
}

if (this.bufferTimeout) {
clearTimeout(this._timeoutId);
this._timeoutId = setTimeout(() => { this._processBuffer(); }, this.bufferTimeout);
}
}
}

export default Bunyan2Loggly;
19 changes: 10 additions & 9 deletions server/api/logger.js → server/api/logger/main.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import _ from "lodash";
import bunyan from "bunyan";
import bunyanFormat from "bunyan-format";
import Bunyan2Loggly from "bunyan-loggly";
import { includes } from "lodash";
import { Meteor } from "meteor/meteor";
import Bunyan2Loggly from "./loggly";

// configure bunyan logging module for reaction server
// See: https://github.com/trentm/node-bunyan#levels
const levels = ["FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];

// set stdout log level
let level = process.env.REACTION_LOG_LEVEL || Meteor.settings.REACTION_LOG_LEVEL || "INFO";
let outputMode = "short";

// allow overriding the stdout log formatting
// available options: short|long|simple|json|bunyan
// https://www.npmjs.com/package/bunyan-format
const outputMode = process.env.REACTION_LOG_FORMAT || "short";

level = level.toUpperCase();

if (!includes(levels, level)) {
if (!_.includes(levels, level)) {
level = "INFO";
}

if (level === "TRACE") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we not going to have a "trace" dump then, or was this just an unlikely use case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, my thinking here was that the default stdout output stream (the console) is intended entirely for human consumption, so readability should be the main focus of the stdout configuration. Also, there isn't really anything that an unformatted output gives you that's unique or useful. The only things the formatter strips out is the hostname and pid (process ID). Any other objects (errors, data, etc.) passed to the logger calls for debugging are still included in the formatted log output. The main advantage being that they're much easier to read when formatted like that. So in short, all of the same data gets shown, it's just easier to parse visually when it's run through the formatter.

However, in case someone really wants to have that raw JSON output, I added a new env var (REACTION_LOG_FORMAT) which you can use to set the formatter. So...

REACTION_LOG_FORMAT=json reaction

outputs this...

1____code_meteor_reaction_reaction__node__and_main_js_ ___code_meteor_reaction_reaction

And technically you could set that to anything bunyan-format supports. Which is...

short|long|simple|json|bunyan (default: short)

Sort of relevant... there's not a single instance of Logger.trace() in the whole app, so this shouldn't have any effect on anyone's current experience.

outputMode = "json";
}

// default console config (stdout)
const streams = [{
level,
Expand All @@ -38,7 +39,7 @@ if (logglyToken && logglySubdomain) {
stream: new Bunyan2Loggly({
token: logglyToken,
subdomain: logglySubdomain
})
}, process.env.LOGGLY_BUFFER_LENGTH || 1)
};
streams.push(logglyStream);
}
Expand Down