Allow logger to add transports (#2363)

- Move all of the winston logic from FileLoggerAdapter to WinstonLoggerAdapter
- Export WinstonLoggerAdapter so it can be sublcassed
- Expost the ability to add adittional transports to logger
- Import FirehoseLoggerAdapter alongside other adapters so it can be configured.
This commit is contained in:
Arthur Cinader
2016-08-06 08:29:19 -04:00
committed by Florent Vilmart
parent 36891f4ed7
commit fa736f1df7
10 changed files with 79 additions and 58 deletions

View File

@@ -1,10 +1,10 @@
'use strict'; 'use strict';
var LoggerController = require('../src/Controllers/LoggerController').LoggerController; var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter; var WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
describe("Cloud Code Logger", () => { describe("Cloud Code Logger", () => {
it("should expose log to functions", (done) => { it("should expose log to functions", (done) => {
var logController = new LoggerController(new FileLoggerAdapter()); var logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.define("loggerTest", (req, res) => { Parse.Cloud.define("loggerTest", (req, res) => {
req.log.info('logTest', 'info log', {info: 'some log' }); req.log.info('logTest', 'info log', {info: 'some log' });
@@ -35,7 +35,7 @@ describe("Cloud Code Logger", () => {
}); });
it("should expose log to trigger", (done) => { it("should expose log to trigger", (done) => {
var logController = new LoggerController(new FileLoggerAdapter()); var logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.beforeSave("MyObject", (req, res) => { Parse.Cloud.beforeSave("MyObject", (req, res) => {
req.log.info('beforeSave MyObject', 'info log', {info: 'some log' }); req.log.info('beforeSave MyObject', 'info log', {info: 'some log' });

18
spec/Logger.spec.js Normal file
View File

@@ -0,0 +1,18 @@
var logger = require('../src/logger');
var winston = require('winston');
class TestTransport extends winston.Transport {
log(level, msg, meta, callback) {
callback(null, true);
}
}
describe('Logger', () => {
it('should add transport', () => {
const testTransport = new (TestTransport)({});
spyOn(testTransport, 'log');
logger.addTransport(testTransport);
logger.logger.info('hi');
expect(testTransport.log).toHaveBeenCalled();
});
});

View File

@@ -1,12 +1,12 @@
var LoggerController = require('../src/Controllers/LoggerController').LoggerController; var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter; var WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
describe('LoggerController', () => { describe('LoggerController', () => {
it('can check process a query without throwing', (done) => { it('can check process a query without throwing', (done) => {
// Make mock request // Make mock request
var query = {}; var query = {};
var loggerController = new LoggerController(new FileLoggerAdapter()); var loggerController = new LoggerController(new WinstonLoggerAdapter());
expect(() => { expect(() => {
loggerController.getLogs(query).then(function(res) { loggerController.getLogs(query).then(function(res) {
@@ -69,7 +69,7 @@ describe('LoggerController', () => {
level: 'error' level: 'error'
}; };
var loggerController = new LoggerController(new FileLoggerAdapter()); var loggerController = new LoggerController(new WinstonLoggerAdapter());
expect(() => { expect(() => {
loggerController.getLogs(query).then(function(res) { loggerController.getLogs(query).then(function(res) {

View File

@@ -3,9 +3,9 @@
const request = require('request'); const request = require('request');
var LogsRouter = require('../src/Routers/LogsRouter').LogsRouter; var LogsRouter = require('../src/Routers/LogsRouter').LogsRouter;
var LoggerController = require('../src/Controllers/LoggerController').LoggerController; var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter; var WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
const loggerController = new LoggerController(new FileLoggerAdapter()); const loggerController = new LoggerController(new WinstonLoggerAdapter());
describe('LogsRouter', () => { describe('LogsRouter', () => {
it('can check valid master key of request', (done) => { it('can check valid master key of request', (done) => {

View File

@@ -1,35 +1,36 @@
'use strict'; 'use strict';
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter; var WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
var Parse = require('parse/node').Parse; var Parse = require('parse/node').Parse;
var request = require('request'); var request = require('request');
describe('info logs', () => { describe('info logs', () => {
it("Verify INFO logs", (done) => { it("Verify INFO logs", (done) => {
var fileLoggerAdapter = new FileLoggerAdapter(); var winstonLoggerAdapter = new WinstonLoggerAdapter();
fileLoggerAdapter.info('testing info logs', () => { winstonLoggerAdapter.info('testing info logs', () => {
fileLoggerAdapter.query({ winstonLoggerAdapter.query({
from: new Date(Date.now() - 500), from: new Date(Date.now() - 500),
size: 100, size: 100,
level: 'info' level: 'info'
}, (results) => { }, (results) => {
if(results.length == 0) { if (results.length == 0) {
fail('The adapter should return non-empty results'); fail('The adapter should return non-empty results');
done(); done();
} else { } else {
expect(results[0].message).toEqual('testing info logs'); expect(results[0].message).toEqual('testing info logs');
done(); done();
} }
});
}); });
}); });
});
}); });
describe('error logs', () => { describe('error logs', () => {
it("Verify ERROR logs", (done) => { it("Verify ERROR logs", (done) => {
var fileLoggerAdapter = new FileLoggerAdapter(); var winstonLoggerAdapter = new WinstonLoggerAdapter();
fileLoggerAdapter.error('testing error logs', () => { winstonLoggerAdapter.error('testing error logs', () => {
fileLoggerAdapter.query({ winstonLoggerAdapter.query({
from: new Date(Date.now() - 500), from: new Date(Date.now() - 500),
size: 100, size: 100,
level: 'error' level: 'error'
@@ -52,8 +53,8 @@ describe('verbose logs', () => {
reconfigureServer({ verbose: true }) reconfigureServer({ verbose: true })
.then(() => createTestUser()) .then(() => createTestUser())
.then(() => { .then(() => {
let fileLoggerAdapter = new FileLoggerAdapter(); let winstonLoggerAdapter = new WinstonLoggerAdapter();
return fileLoggerAdapter.query({ return winstonLoggerAdapter.query({
from: new Date(Date.now() - 500), from: new Date(Date.now() - 500),
size: 100, size: 100,
level: 'verbose' level: 'verbose'
@@ -71,8 +72,8 @@ describe('verbose logs', () => {
headers: headers, headers: headers,
url: 'http://localhost:8378/1/login?username=test&password=moon-y' url: 'http://localhost:8378/1/login?username=test&password=moon-y'
}, (error, response, body) => { }, (error, response, body) => {
let fileLoggerAdapter = new FileLoggerAdapter(); let winstonLoggerAdapter = new WinstonLoggerAdapter();
return fileLoggerAdapter.query({ return winstonLoggerAdapter.query({
from: new Date(Date.now() - 500), from: new Date(Date.now() - 500),
size: 100, size: 100,
level: 'verbose' level: 'verbose'
@@ -93,8 +94,8 @@ describe('verbose logs', () => {
let obj = new Parse.Object('users'); let obj = new Parse.Object('users');
obj.set('password', 'pw'); obj.set('password', 'pw');
obj.save().then(() => { obj.save().then(() => {
let fileLoggerAdapter = new FileLoggerAdapter(); let winstonLoggerAdapter = new WinstonLoggerAdapter();
return fileLoggerAdapter.query({ return winstonLoggerAdapter.query({
from: new Date(Date.now() - 500), from: new Date(Date.now() - 500),
size: 100, size: 100,
level: 'verbose' level: 'verbose'

View File

@@ -6,7 +6,7 @@
// * info(obj1 [, obj2, .., objN]) // * info(obj1 [, obj2, .., objN])
// * error(obj1 [, obj2, .., objN]) // * error(obj1 [, obj2, .., objN])
// * query(options, callback) // * query(options, callback)
// Default is FileLoggerAdapter.js // Default is WinstonLoggerAdapter.js
export class LoggerAdapter { export class LoggerAdapter {
info() {} info() {}

View File

@@ -1,23 +1,9 @@
// Logger
//
// Wrapper around Winston logging library with custom query
//
// expected log entry to be in the shape of:
// {"level":"info","message":"Your Message","timestamp":"2016-02-04T05:59:27.412Z"}
//
import { LoggerAdapter } from './LoggerAdapter'; import { LoggerAdapter } from './LoggerAdapter';
import { Parse } from 'parse/node'; import { logger, addTransport } from '../../logger';
import { logger, configure } from '../../logger';
const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000; const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000;
const CACHE_TIME = 1000 * 60; const CACHE_TIME = 1000 * 60;
let LOGS_FOLDER = './logs/';
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
LOGS_FOLDER = './test_logs/'
}
let currentDate = new Date(); let currentDate = new Date();
let simpleCache = { let simpleCache = {
@@ -65,8 +51,7 @@ let _isValidLogEntry = (from, until, entry) => {
: false : false
}; };
export class FileLoggerAdapter extends LoggerAdapter { export class WinstonLoggerAdapter extends LoggerAdapter {
info() { info() {
return logger.info.apply(undefined, arguments); return logger.info.apply(undefined, arguments);
} }
@@ -75,6 +60,13 @@ export class FileLoggerAdapter extends LoggerAdapter {
return logger.error.apply(undefined, arguments); return logger.error.apply(undefined, arguments);
} }
addTransport(transport) {
// Note that this is calling addTransport
// from logger. See import - confusing.
// but this is not recursive.
addTransport(transport);
}
// custom query as winston is currently limited // custom query as winston is currently limited
query(options, callback = () => {}) { query(options, callback = () => {}) {
if (!options) { if (!options) {
@@ -114,4 +106,4 @@ export class FileLoggerAdapter extends LoggerAdapter {
} }
} }
export default FileLoggerAdapter; export default WinstonLoggerAdapter;

View File

@@ -28,7 +28,7 @@ import { InMemoryCacheAdapter } from './Adapters/Cache/InMemoryCacheAdapter';
import { AnalyticsController } from './Controllers/AnalyticsController'; import { AnalyticsController } from './Controllers/AnalyticsController';
import { CacheController } from './Controllers/CacheController'; import { CacheController } from './Controllers/CacheController';
import { AnalyticsAdapter } from './Adapters/Analytics/AnalyticsAdapter'; import { AnalyticsAdapter } from './Adapters/Analytics/AnalyticsAdapter';
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter'; import { WinstonLoggerAdapter } from './Adapters/Logger/WinstonLoggerAdapter';
import { FilesController } from './Controllers/FilesController'; import { FilesController } from './Controllers/FilesController';
import { FilesRouter } from './Routers/FilesRouter'; import { FilesRouter } from './Routers/FilesRouter';
import { FunctionsRouter } from './Routers/FunctionsRouter'; import { FunctionsRouter } from './Routers/FunctionsRouter';
@@ -71,7 +71,7 @@ const requiredUserFields = { fields: { ...SchemaController.defaultColumns._Defau
// "analyticsAdapter": an adapter class for analytics // "analyticsAdapter": an adapter class for analytics
// "filesAdapter": a class like GridStoreAdapter providing create, get, // "filesAdapter": a class like GridStoreAdapter providing create, get,
// and delete // and delete
// "loggerAdapter": a class like FileLoggerAdapter providing info, error, // "loggerAdapter": a class like WinstonLoggerAdapter providing info, error,
// and query // and query
// "jsonLogs": log as structured JSON objects // "jsonLogs": log as structured JSON objects
// "databaseURI": a uri like mongodb://localhost:27017/dbname to tell us // "databaseURI": a uri like mongodb://localhost:27017/dbname to tell us
@@ -186,7 +186,7 @@ class ParseServer {
}); });
// Pass the push options too as it works with the default // Pass the push options too as it works with the default
const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push || {}); const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push || {});
const loggerControllerAdapter = loadAdapter(loggerAdapter, FileLoggerAdapter); const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter);
const emailControllerAdapter = loadAdapter(emailAdapter); const emailControllerAdapter = loadAdapter(emailAdapter);
const cacheControllerAdapter = loadAdapter(cacheAdapter, InMemoryCacheAdapter, {appId: appId}); const cacheControllerAdapter = loadAdapter(cacheAdapter, InMemoryCacheAdapter, {appId: appId});
const analyticsControllerAdapter = loadAdapter(analyticsAdapter, AnalyticsAdapter); const analyticsControllerAdapter = loadAdapter(analyticsAdapter, AnalyticsAdapter);

View File

@@ -172,7 +172,7 @@ export default {
}, },
"customPages": { "customPages": {
env: "PARSE_SERVER_CUSTOM_PAGES", env: "PARSE_SERVER_CUSTOM_PAGES",
help: "custom pages for pasword validation and reset", help: "custom pages for password validation and reset",
action: objectParser action: objectParser
}, },
"maxUploadSize": { "maxUploadSize": {

View File

@@ -13,6 +13,7 @@ LOGS_FOLDER = process.env.PARSE_SERVER_LOGS_FOLDER || LOGS_FOLDER;
const JSON_LOGS = process.env.JSON_LOGS || false; const JSON_LOGS = process.env.JSON_LOGS || false;
let currentLogsFolder = LOGS_FOLDER; let currentLogsFolder = LOGS_FOLDER;
const additionalTransports = [];
function generateTransports(level, options = {}) { function generateTransports(level, options = {}) {
let transports = [ let transports = [
@@ -32,7 +33,7 @@ function generateTransports(level, options = {}) {
level: 'error' level: 'error'
} }
), options) ), options)
]; ].concat(additionalTransports);
if (!process.env.TESTING || process.env.VERBOSE) { if (!process.env.TESTING || process.env.VERBOSE) {
transports = [ transports = [
new (winston.transports.Console)( new (winston.transports.Console)(
@@ -90,5 +91,14 @@ export function addGroup(groupName) {
return winston.loggers.get(groupName); return winston.loggers.get(groupName);
} }
export { logger }; export function addTransport(transport) {
const level = winston.level;
additionalTransports.push(transport);
const transports = generateTransports(level);
logger.configure({
transports: transports
});
}
export { logger, addTransport };
export default logger; export default logger;