Winston 3 upgrade (#5496)

*  Release 3.1.3 (#5267)

*  Release 3.1.3

* Update CHANGELOG.md

* ⬆️ Bump winston and winston-daily-rotate-file

Bumps [winston](https://github.com/winstonjs/winston) and [winston-daily-rotate-file](https://github.com/winstonjs/winston-daily-rotate-file). These dependencies needed to be updated together.

Updates `winston` from 2.4.4 to 3.1.0
- [Release notes](https://github.com/winstonjs/winston/releases)
- [Changelog](https://github.com/winstonjs/winston/blob/master/CHANGELOG.md)
- [Commits](https://github.com/winstonjs/winston/compare/2.4.4...3.1.0)

Updates `winston-daily-rotate-file` from 1.7.2 to 3.5.1
- [Release notes](https://github.com/winstonjs/winston-daily-rotate-file/releases)
- [Commits](https://github.com/winstonjs/winston-daily-rotate-file/compare/v1.7.2...v3.5.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Rewrote WinstonLogger to work with winston 3.x api

* Changed winston logger test to use winston-transport

* Added winston-transport dependency

* Close and remove transports before adding them again

* Changed to strict equal

* Override adapter name

* Updated and added getLogs tests

* Bump winston and winston-daily-rotate-file

Bumps [winston](https://github.com/winstonjs/winston) and [winston-daily-rotate-file](https://github.com/winstonjs/winston-daily-rotate-file). These dependencies needed to be updated together.

Updates `winston` from 2.4.4 to 3.2.0
- [Release notes](https://github.com/winstonjs/winston/releases)
- [Changelog](https://github.com/winstonjs/winston/blob/master/CHANGELOG.md)
- [Commits](https://github.com/winstonjs/winston/compare/2.4.4...3.2.0)

Updates `winston-daily-rotate-file` from 1.7.2 to 3.6.0
- [Release notes](https://github.com/winstonjs/winston-daily-rotate-file/releases)
- [Commits](https://github.com/winstonjs/winston-daily-rotate-file/compare/v1.7.2...v3.6.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Fixed tests, updated parse logging

* Fixed tests, better error logging

* Fix failing tests

* Updates as per review
This commit is contained in:
Sam Ilic
2019-04-15 09:03:33 +10:00
committed by Arthur Cinader
parent 943134812e
commit 6ffc41345f
14 changed files with 1018 additions and 1010 deletions

1628
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -40,8 +40,8 @@
"semver": "6.0.0", "semver": "6.0.0",
"tv4": "1.3.0", "tv4": "1.3.0",
"uuid": "3.3.2", "uuid": "3.3.2",
"winston": "2.4.4", "winston": "3.2.1",
"winston-daily-rotate-file": "1.7.2", "winston-daily-rotate-file": "3.8.0",
"ws": "6.2.1" "ws": "6.2.1"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -189,52 +189,60 @@ describe('AudiencesRouter', () => {
}); });
}); });
it_exclude_dbs(['postgres'])('query installations with limit = 0 and count = 1', done => { it_exclude_dbs(['postgres'])(
const config = Config.get('test'); 'query installations with limit = 0 and count = 1',
const androidAudienceRequest = { done => {
name: 'Android Users', const config = Config.get('test');
query: '{ "test": "android" }', const androidAudienceRequest = {
}; name: 'Android Users',
const iosAudienceRequest = { query: '{ "test": "android" }',
name: 'Iphone Users', };
query: '{ "test": "ios" }', const iosAudienceRequest = {
}; name: 'Iphone Users',
const request = { query: '{ "test": "ios" }',
config: config, };
auth: auth.master(config), const request = {
body: {}, config: config,
query: { auth: auth.master(config),
limit: 0, body: {},
count: 1, query: {
}, limit: 0,
info: {}, count: 1,
}; },
info: {},
};
const router = new AudiencesRouter(); const router = new AudiencesRouter();
rest rest
.create(config, auth.nobody(config), '_Audience', androidAudienceRequest) .create(
.then(() => {
return rest.create(
config, config,
auth.nobody(config), auth.nobody(config),
'_Audience', '_Audience',
iosAudienceRequest androidAudienceRequest
); )
}) .then(() => {
.then(() => { return rest.create(
return router.handleFind(request); config,
}) auth.nobody(config),
.then(res => { '_Audience',
const response = res.response; iosAudienceRequest
expect(response.results.length).toEqual(0); );
expect(response.count).toEqual(2); })
done(); .then(() => {
}) return router.handleFind(request);
.catch(err => { })
fail(JSON.stringify(err)); .then(res => {
done(); const response = res.response;
}); expect(response.results.length).toEqual(0);
}); expect(response.count).toEqual(2);
done();
})
.catch(err => {
fail(JSON.stringify(err));
done();
});
}
);
it('should create, read, update and delete audiences throw api', done => { it('should create, read, update and delete audiences throw api', done => {
Parse._request( Parse._request(

View File

@@ -203,7 +203,9 @@ describe('Cloud Code Logger', () => {
.catch(() => {}) .catch(() => {})
.then(() => { .then(() => {
const logs = spy.calls.all().reverse(); const logs = spy.calls.all().reverse();
expect(logs[0].args[1]).toBe('it failed!'); expect(logs[0].args[1]).toBe('Parse error: ');
expect(logs[0].args[2].message).toBe('it failed!');
const log = logs[1].args; const log = logs[1].args;
expect(log[0]).toEqual('error'); expect(log[0]).toEqual('error');
expect(log[1]).toMatch( expect(log[1]).toMatch(
@@ -268,6 +270,7 @@ describe('Cloud Code Logger', () => {
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled();
expect(spy.calls.count()).toBe(1); expect(spy.calls.count()).toBe(1);
const { args } = spy.calls.mostRecent(); const { args } = spy.calls.mostRecent();
expect(args[0]).toBe('Object not found.'); expect(args[0]).toBe('Parse error: ');
expect(args[1].message).toBe('Object not found.');
}); });
}); });

View File

@@ -9,7 +9,7 @@ const FilesController = require('../lib/Controllers/FilesController').default;
const mockAdapter = { const mockAdapter = {
createFile: () => { createFile: () => {
return Promise.reject(new Error('it failed')); return Promise.reject(new Error('it failed with xyz'));
}, },
deleteFile: () => {}, deleteFile: () => {},
getFileData: () => {}, getFileData: () => {},
@@ -59,13 +59,18 @@ describe('FilesController', () => {
.then(logs => { .then(logs => {
// we get two logs here: 1. the source of the failure to save the file // we get two logs here: 1. the source of the failure to save the file
// and 2 the message that will be sent back to the client. // and 2 the message that will be sent back to the client.
const log1 = logs.pop();
const log1 = logs.find(
x => x.message === 'Error creating a file: it failed with xyz'
);
expect(log1.level).toBe('error'); expect(log1.level).toBe('error');
expect(log1.message).toBe('it failed');
const log2 = logs.pop(); const log2 = logs.find(
x => x.message === 'Parse error: Could not store file: yolo.txt.'
);
expect(log2.level).toBe('error'); expect(log2.level).toBe('error');
expect(log2.code).toBe(130); expect(log2.code).toBe(130);
expect(log2.message).toBe('Could not store file.');
done(); done();
}); });
}); });

View File

@@ -1,31 +1,30 @@
const logging = require('../lib/Adapters/Logger/WinstonLogger'); const logging = require('../lib/Adapters/Logger/WinstonLogger');
const winston = require('winston'); const Transport = require('winston-transport');
class TestTransport extends winston.Transport { class TestTransport extends Transport {
log(level, msg, meta, callback) { log(info, callback) {
callback(null, true); callback(null, true);
} }
} }
describe('Logger', () => { describe('WinstonLogger', () => {
it('should add transport', () => { it('should add transport', () => {
const testTransport = new TestTransport({ const testTransport = new TestTransport();
name: 'test',
});
spyOn(testTransport, 'log'); spyOn(testTransport, 'log');
logging.addTransport(testTransport); logging.addTransport(testTransport);
expect(Object.keys(logging.logger.transports).length).toBe(4); expect(logging.logger.transports.length).toBe(4);
logging.logger.info('hi'); logging.logger.info('hi');
expect(testTransport.log).toHaveBeenCalled(); expect(testTransport.log).toHaveBeenCalled();
logging.logger.error('error');
expect(testTransport.log).toHaveBeenCalled();
logging.removeTransport(testTransport); logging.removeTransport(testTransport);
expect(Object.keys(logging.logger.transports).length).toBe(3); expect(logging.logger.transports.length).toBe(3);
}); });
it('should have files transports', done => { it('should have files transports', done => {
reconfigureServer().then(() => { reconfigureServer().then(() => {
const transports = logging.logger.transports; const transports = logging.logger.transports;
const transportKeys = Object.keys(transports); expect(transports.length).toBe(3);
expect(transportKeys.length).toBe(3);
done(); done();
}); });
}); });
@@ -35,8 +34,7 @@ describe('Logger', () => {
logsFolder: null, logsFolder: null,
}).then(() => { }).then(() => {
const transports = logging.logger.transports; const transports = logging.logger.transports;
const transportKeys = Object.keys(transports); expect(transports.length).toBe(1);
expect(transportKeys.length).toBe(1);
done(); done();
}); });
}); });

View File

@@ -44,7 +44,7 @@ describe('LoggerController', () => {
done(); done();
}); });
it('can process an ascending query without throwing', done => { it('can parse an ascending query without throwing', done => {
// Make mock request // Make mock request
const query = { const query = {
from: '2016-01-01Z00:00:00', from: '2016-01-01Z00:00:00',
@@ -65,11 +65,59 @@ describe('LoggerController', () => {
done(); done();
}); });
it('can process an ascending query without throwing', done => {
// Make mock request
const query = {
from: '2016-01-01Z00:00:00',
until: '2016-01-01Z00:00:00',
size: 5,
order: 'asc',
level: 'error',
};
const loggerController = new LoggerController(new WinstonLoggerAdapter());
expect(() => {
loggerController
.getLogs(query)
.then(function(res) {
expect(res.length).not.toBe(0);
done();
})
.catch(err => {
jfail(err);
fail('should not fail');
done();
});
}).not.toThrow();
});
it('can parse a descending query without throwing', done => {
// Make mock request
const query = {
from: '2016-01-01Z00:00:00',
until: '2016-01-01Z00:00:00',
size: 5,
order: 'desc',
level: 'error',
};
const result = LoggerController.parseOptions(query);
expect(result.from.getTime()).toEqual(1451606400000);
expect(result.until.getTime()).toEqual(1451606400000);
expect(result.size).toEqual(5);
expect(result.order).toEqual('desc');
expect(result.level).toEqual('error');
done();
});
it('can process a descending query without throwing', done => { it('can process a descending query without throwing', done => {
// Make mock request // Make mock request
const query = { const query = {
from: '2016-01-01', from: '2016-01-01Z00:00:00',
until: '2016-01-30', until: '2016-01-01Z00:00:00',
size: 5, size: 5,
order: 'desc', order: 'desc',
level: 'error', level: 'error',
@@ -81,7 +129,7 @@ describe('LoggerController', () => {
loggerController loggerController
.getLogs(query) .getLogs(query)
.then(function(res) { .then(function(res) {
expect(res.length).toBe(0); expect(res.length).not.toBe(0);
done(); done();
}) })
.catch(err => { .catch(err => {

View File

@@ -6,6 +6,11 @@ const { MongoClient } = require('mongodb');
const databaseURI = const databaseURI =
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
const fakeClient = {
s: { options: { dbName: null } },
db: () => null,
};
// These tests are specific to the mongo storage adapter + mongo storage format // These tests are specific to the mongo storage adapter + mongo storage format
// and will eventually be moved into their own repo // and will eventually be moved into their own repo
describe_only_db('mongo')('MongoStorageAdapter', () => { describe_only_db('mongo')('MongoStorageAdapter', () => {
@@ -16,7 +21,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
}); });
it('auto-escapes symbols in auth information', () => { it('auto-escapes symbols in auth information', () => {
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null)); spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({ new MongoStorageAdapter({
uri: uri:
'mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse', 'mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse',
@@ -28,7 +33,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
}); });
it("doesn't double escape already URI-encoded information", () => { it("doesn't double escape already URI-encoded information", () => {
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null)); spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({ new MongoStorageAdapter({
uri: uri:
'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse', 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse',
@@ -41,7 +46,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
// https://github.com/parse-community/parse-server/pull/148#issuecomment-180407057 // https://github.com/parse-community/parse-server/pull/148#issuecomment-180407057
it('preserves replica sets', () => { it('preserves replica sets', () => {
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null)); spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({ new MongoStorageAdapter({
uri: uri:
'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415', 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415',

View File

@@ -1512,7 +1512,10 @@ describe('PushController', () => {
date: noTimezone, date: noTimezone,
isLocalTime: true, isLocalTime: true,
}) })
).toBe(`2017-09-06T${expectedHour}:14:01.048`, 'No timezone'); ).toBe(
`2017-09-06T${expectedHour.toString().padStart(2, '0')}:14:01.048`,
'No timezone'
);
expect( expect(
PushController.formatPushTime({ PushController.formatPushTime({
date: new Date('2017-09-06'), date: new Date('2017-09-06'),
@@ -1569,7 +1572,7 @@ describe('PushController', () => {
.then(pushStatus => { .then(pushStatus => {
expect(pushStatus.get('status')).toBe('scheduled'); expect(pushStatus.get('status')).toBe('scheduled');
expect(pushStatus.get('pushTime')).toBe( expect(pushStatus.get('pushTime')).toBe(
`2017-09-06T${expectedHour}:14:01.048` `2017-09-06T${expectedHour.toString().padStart(2, '0')}:14:01.048`
); );
}) })
.then(done, done.fail); .then(done, done.fail);

View File

@@ -7,59 +7,64 @@ const request = require('../lib/request');
describe('info logs', () => { describe('info logs', () => {
it('Verify INFO logs', done => { it('Verify INFO logs', done => {
const winstonLoggerAdapter = new WinstonLoggerAdapter(); const winstonLoggerAdapter = new WinstonLoggerAdapter();
winstonLoggerAdapter.log('info', 'testing info logs', () => { winstonLoggerAdapter.log('info', 'testing info logs with 1234');
winstonLoggerAdapter.query( winstonLoggerAdapter.query(
{ {
from: new Date(Date.now() - 500), from: new Date(Date.now() - 500),
size: 100, size: 100,
level: 'info', level: 'info',
}, order: 'desc',
results => { },
if (results.length == 0) { results => {
fail('The adapter should return non-empty results'); if (results.length == 0) {
} else { fail('The adapter should return non-empty results');
expect(results[0].message).toEqual('testing info logs'); } else {
} const log = results.find(
// Check the error log x => x.message === 'testing info logs with 1234'
// Regression #2639
winstonLoggerAdapter.query(
{
from: new Date(Date.now() - 200),
size: 100,
level: 'error',
},
results => {
expect(results.length).toEqual(0);
done();
}
); );
expect(log.level).toEqual('info');
} }
); // Check the error log
}); // Regression #2639
winstonLoggerAdapter.query(
{
from: new Date(Date.now() - 200),
size: 100,
level: 'error',
},
errors => {
const log = errors.find(
x => x.message === 'testing info logs with 1234'
);
expect(log).toBeUndefined();
done();
}
);
}
);
}); });
}); });
describe('error logs', () => { describe('error logs', () => {
it('Verify ERROR logs', done => { it('Verify ERROR logs', done => {
const winstonLoggerAdapter = new WinstonLoggerAdapter(); const winstonLoggerAdapter = new WinstonLoggerAdapter();
winstonLoggerAdapter.log('error', 'testing error logs', () => { winstonLoggerAdapter.log('error', 'testing error logs');
winstonLoggerAdapter.query( winstonLoggerAdapter.query(
{ {
from: new Date(Date.now() - 500), from: new Date(Date.now() - 500),
size: 100, size: 100,
level: 'error', level: 'error',
}, },
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 error logs'); expect(results[0].message).toEqual('testing error logs');
done(); done();
}
} }
); }
}); );
}); });
}); });

View File

@@ -5,59 +5,58 @@ import DailyRotateFile from 'winston-daily-rotate-file';
import _ from 'lodash'; import _ from 'lodash';
import defaults from '../../defaults'; import defaults from '../../defaults';
const logger = new winston.Logger(); const logger = winston.createLogger();
const additionalTransports = [];
function updateTransports(options) { function configureTransports(options) {
const transports = Object.assign({}, logger.transports); const transports = [];
if (options) { if (options) {
const silent = options.silent; const silent = options.silent;
delete options.silent; delete options.silent;
if (_.isNull(options.dirname)) {
delete transports['parse-server']; if (!_.isNil(options.dirname)) {
delete transports['parse-server-error']; const parseServer = new DailyRotateFile(
} else if (!_.isUndefined(options.dirname)) {
transports['parse-server'] = new DailyRotateFile(
Object.assign( Object.assign(
{},
{ {
filename: 'parse-server.info', filename: 'parse-server.info',
name: 'parse-server', json: true,
}, },
options, options,
{ timestamp: true } { timestamp: true }
) )
); );
transports['parse-server-error'] = new DailyRotateFile( parseServer.name = 'parse-server';
transports.push(parseServer);
const parseServerError = new DailyRotateFile(
Object.assign( Object.assign(
{},
{ {
filename: 'parse-server.err', filename: 'parse-server.err',
name: 'parse-server-error', json: true,
}, },
options, options,
{ level: 'error', timestamp: true } { level: 'error', timestamp: true }
) )
); );
parseServerError.name = 'parse-server-error';
transports.push(parseServerError);
} }
transports.console = new winston.transports.Console( transports.push(
Object.assign( new winston.transports.Console(
{ Object.assign(
colorize: true, {
name: 'console', colorize: true,
silent, name: 'console',
}, silent,
options },
options
)
) )
); );
} }
// Mount the additional transports
additionalTransports.forEach(transport => {
transports[transport.name] = transport;
});
logger.configure({ logger.configure({
transports: _.values(transports), transports,
}); });
} }
@@ -93,25 +92,27 @@ export function configureLogger({
options.json = true; options.json = true;
options.stringify = true; options.stringify = true;
} }
updateTransports(options); configureTransports(options);
} }
export function addTransport(transport) { export function addTransport(transport) {
additionalTransports.push(transport); // we will remove the existing transport
updateTransports(); // before replacing it with a new one
removeTransport(transport.name);
logger.add(transport);
} }
export function removeTransport(transport) { export function removeTransport(transport) {
const transportName = const matchingTransport = logger.transports.find(t1 => {
typeof transport == 'string' ? transport : transport.name; return typeof transport === 'string'
const transports = Object.assign({}, logger.transports); ? t1.name === transport
delete transports[transportName]; : t1 === transport;
logger.configure({
transports: _.values(transports),
});
_.remove(additionalTransports, transport => {
return transport.name === transportName;
}); });
if (matchingTransport) {
logger.remove(matchingTransport);
}
} }
export { logger }; export { logger };

View File

@@ -48,7 +48,8 @@ export class WinstonLoggerAdapter extends LoggerAdapter {
callback(err); callback(err);
return reject(err); return reject(err);
} }
if (level == 'error') {
if (level === 'error') {
callback(res['parse-server-error']); callback(res['parse-server-error']);
resolve(res['parse-server-error']); resolve(res['parse-server-error']);
} else { } else {

View File

@@ -111,9 +111,12 @@ export class FilesRouter {
res.json(result); res.json(result);
}) })
.catch(e => { .catch(e => {
logger.error(e.message, e); logger.error('Error creating a file: ', e);
next( next(
new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Could not store file.') new Parse.Error(
Parse.Error.FILE_SAVE_ERROR,
`Could not store file: ${filename}.`
)
); );
}); });
} }

View File

@@ -325,7 +325,7 @@ export function handleParseErrors(err, req, res, next) {
res.status(httpStatus); res.status(httpStatus);
res.json({ code: err.code, error: err.message }); res.json({ code: err.code, error: err.message });
log.error(err.message, err); log.error('Parse error: ', err);
if (req.config && req.config.enableExpressErrorHandler) { if (req.config && req.config.enableExpressErrorHandler) {
next(err); next(err);
} }