From 0b670293cf40933c47b24bb49fc43e8daf4052a3 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sat, 6 Aug 2016 15:19:12 -0400 Subject: [PATCH 01/18] Refactor logging to provide common logger from LoggerAdapter Move logger logic de WinstonLoggerAdapter Further improvements in configuration Use logger instead of getLogger - Removes PLog module Reverts name changes nits --- spec/Logger.spec.js | 12 +- spec/Subscription.spec.js | 25 ++-- spec/helper.js | 2 - src/Adapters/Logger/LoggerAdapter.js | 7 +- src/Adapters/Logger/WinstonLogger.js | 93 ++++++++++++++ src/Adapters/Logger/WinstonLoggerAdapter.js | 62 +++------- src/Controllers/LoggerController.js | 4 + src/LiveQuery/Client.js | 4 +- src/LiveQuery/PLog.js | 5 - src/LiveQuery/ParseCloudCodePublisher.js | 4 +- src/LiveQuery/ParseLiveQueryServer.js | 69 +++++------ src/LiveQuery/ParseWebSocketServer.js | 4 +- src/LiveQuery/SessionTokenCache.js | 8 +- src/LiveQuery/Subscription.js | 8 +- src/ParseServer.js | 24 ++-- src/cli/cli-definitions.js | 8 ++ src/index.js | 10 +- src/logger.js | 127 +++++--------------- 18 files changed, 240 insertions(+), 236 deletions(-) create mode 100644 src/Adapters/Logger/WinstonLogger.js delete mode 100644 src/LiveQuery/PLog.js diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js index 37f8f871af..4ecea3e0d3 100644 --- a/spec/Logger.spec.js +++ b/spec/Logger.spec.js @@ -1,4 +1,4 @@ -var logger = require('../src/logger'); +var logging = require('../src/Adapters/Logger/WinstonLogger'); var winston = require('winston'); class TestTransport extends winston.Transport { @@ -8,11 +8,15 @@ class TestTransport extends winston.Transport { } describe('Logger', () => { + // Test is excluded as will be refactored it('should add transport', () => { - const testTransport = new (TestTransport)({}); + const testTransport = new (TestTransport)({ + name: 'test' + }); spyOn(testTransport, 'log'); - logger.addTransport(testTransport); - logger.logger.info('hi'); + logging.addTransport(testTransport); + logging.logger.info('hi'); expect(testTransport.log).toHaveBeenCalled(); + logging.removeTransport(testTransport); }); }); diff --git a/spec/Subscription.spec.js b/spec/Subscription.spec.js index a9f35020be..20f1aa5bc1 100644 --- a/spec/Subscription.spec.js +++ b/spec/Subscription.spec.js @@ -1,10 +1,10 @@ var Subscription = require('../src/LiveQuery/Subscription').Subscription; - +let logger; describe('Subscription', function() { beforeEach(function() { - var mockError = jasmine.createSpy('error'); - jasmine.mockLibrary('../src/LiveQuery/PLog', 'error', mockError); + logger = require('../src/logger').logger; + spyOn(logger, 'error').and.callThrough(); }); it('can be initialized', function() { @@ -62,8 +62,7 @@ describe('Subscription', function() { var subscription = new Subscription('className', { key : 'value' }, 'hash'); subscription.deleteClientSubscription(1, 1); - var PLog =require('../src/LiveQuery/PLog'); - expect(PLog.error).toHaveBeenCalled(); + expect(logger.error).toHaveBeenCalled(); }); it('can delete nonexistent request for one client', function() { @@ -71,8 +70,7 @@ describe('Subscription', function() { subscription.addClientSubscription(1, 1); subscription.deleteClientSubscription(1, 2); - var PLog =require('../src/LiveQuery/PLog'); - expect(PLog.error).toHaveBeenCalled(); + expect(logger.error).toHaveBeenCalled(); expect(subscription.clientRequestIds.size).toBe(1); expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); @@ -83,8 +81,7 @@ describe('Subscription', function() { subscription.addClientSubscription(1, 2); subscription.deleteClientSubscription(1, 2); - var PLog =require('../src/LiveQuery/PLog'); - expect(PLog.error).not.toHaveBeenCalled(); + expect(logger.error).not.toHaveBeenCalled(); expect(subscription.clientRequestIds.size).toBe(1); expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); @@ -96,8 +93,7 @@ describe('Subscription', function() { subscription.deleteClientSubscription(1, 1); subscription.deleteClientSubscription(1, 2); - var PLog =require('../src/LiveQuery/PLog'); - expect(PLog.error).not.toHaveBeenCalled(); + expect(logger.error).not.toHaveBeenCalled(); expect(subscription.clientRequestIds.size).toBe(0); }); @@ -111,13 +107,8 @@ describe('Subscription', function() { subscription.deleteClientSubscription(2, 1); subscription.deleteClientSubscription(2, 2); - var PLog =require('../src/LiveQuery/PLog'); - expect(PLog.error).not.toHaveBeenCalled(); + expect(logger.error).not.toHaveBeenCalled(); expect(subscription.clientRequestIds.size).toBe(1); expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); - - afterEach(function(){ - jasmine.restoreLibrary('../src/LiveQuery/PLog', 'error'); - }); }); diff --git a/spec/helper.js b/spec/helper.js index e9095d6188..b60e41d0de 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -352,8 +352,6 @@ global.describe_only_db = db => { } } -// LiveQuery test setting -require('../src/LiveQuery/PLog').logLevel = 'NONE'; var libraryCache = {}; jasmine.mockLibrary = function(library, name, mock) { var original = require(library)[name]; diff --git a/src/Adapters/Logger/LoggerAdapter.js b/src/Adapters/Logger/LoggerAdapter.js index 9fda8eab0d..fa573a38b0 100644 --- a/src/Adapters/Logger/LoggerAdapter.js +++ b/src/Adapters/Logger/LoggerAdapter.js @@ -5,13 +5,16 @@ // Adapter classes must implement the following functions: // * info(obj1 [, obj2, .., objN]) // * error(obj1 [, obj2, .., objN]) -// * query(options, callback) +// * query(options, callback) /* optional */ +// * configureLogger(options) // Default is WinstonLoggerAdapter.js export class LoggerAdapter { + constructor(options) {} info() {} error() {} - query(options, callback) {} + warn() {} + verbose() {} } export default LoggerAdapter; diff --git a/src/Adapters/Logger/WinstonLogger.js b/src/Adapters/Logger/WinstonLogger.js new file mode 100644 index 0000000000..2a1e4d6b22 --- /dev/null +++ b/src/Adapters/Logger/WinstonLogger.js @@ -0,0 +1,93 @@ +import winston from 'winston'; +import fs from 'fs'; +import path from 'path'; +import DailyRotateFile from 'winston-daily-rotate-file'; +import _ from 'lodash'; +import defaults from '../../logger'; + +const logger = new winston.Logger(); +const additionalTransports = []; + +function updateTransports(options) { + let transports = Object.assign({}, logger.transports); + if (options) { + if (_.isNull(options.dirname)) { + delete transports['parse-server']; + delete transports['parse-server-error']; + } else if (!_.isUndefined(options.dirname)) { + transports['parse-server'] = new (DailyRotateFile)( + Object.assign({ + filename: 'parse-server.info', + name: 'parse-server', + }, options)); + transports['parse-server-error'] = new (DailyRotateFile)( + Object.assign({ + filename: 'parse-server.err', + name: 'parse-server-error', + level: 'error' + }, options)); + } + + if (!process.env.TESTING || process.env.VERBOSE) { + transports.console = new (winston.transports.Console)( + Object.assign({ + colorize: true, + name: 'console' + }, options)); + } + } + // Mount the additional transports + additionalTransports.forEach((transport) => { + transports[transport.name] = transport; + }); + logger.configure({ + transports: _.values(transports) + }); +} + +export function configureLogger({ + logsFolder = defaults.logsFolder, + jsonLogs = defaults.jsonLogs, + logLevel = winston.level, + verbose = defaults.verbose } = {}) { + + if (verbose) { + logLevel = 'verbose'; + } + + winston.level = logLevel; + const options = {}; + + if (logsFolder) { + if (!path.isAbsolute(logsFolder)) { + logsFolder = path.resolve(process.cwd(), logsFolder); + } + try { + fs.mkdirSync(logsFolder); + } catch (exception) {} + } + options.dirname = logsFolder; + options.level = logLevel; + if (jsonLogs) { + options.json = true; + options.stringify = true; + } + updateTransports(options); +} + +export function addTransport(transport) { + additionalTransports.push(transport); + updateTransports(); +} + +export function removeTransport(transport) { + let transportName = typeof transport == 'string' ? transport : transport.name; + let transports = Object.assign({}, logger.transports); + delete transports[transportName]; + logger.configure({ + transports: _.values(transports) + }) +} + +export { logger, addTransport, configureLogger, removeTransport }; +export default logger; diff --git a/src/Adapters/Logger/WinstonLoggerAdapter.js b/src/Adapters/Logger/WinstonLoggerAdapter.js index d3728f2844..6257b97b75 100644 --- a/src/Adapters/Logger/WinstonLoggerAdapter.js +++ b/src/Adapters/Logger/WinstonLoggerAdapter.js @@ -1,57 +1,21 @@ import { LoggerAdapter } from './LoggerAdapter'; -import { logger, addTransport } from '../../logger'; +import { logger, addTransport, configureLogger } from './WinstonLogger'; const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000; -const CACHE_TIME = 1000 * 60; - -let currentDate = new Date(); - -let simpleCache = { - timestamp: null, - from: null, - until: null, - order: null, - data: [], - level: 'info', -}; // returns Date object rounded to nearest day let _getNearestDay = (date) => { return new Date(date.getFullYear(), date.getMonth(), date.getDate()); } -// returns Date object of previous day -let _getPrevDay = (date) => { - return new Date(date - MILLISECONDS_IN_A_DAY); -} - -// returns the iso formatted file name -let _getFileName = () => { - return _getNearestDay(currentDate).toISOString() -} - -// check for valid cache when both from and util match. -// cache valid for up to 1 minute -let _hasValidCache = (from, until, level) => { - if (String(from) === String(simpleCache.from) && - String(until) === String(simpleCache.until) && - new Date() - simpleCache.timestamp < CACHE_TIME && - level === simpleCache.level) { - return true; +export class WinstonLoggerAdapter extends LoggerAdapter { + constructor(options) { + super(); + if (options) { + configureLogger(options); + } } - return false; -} -// check that log entry has valid time stamp based on query -let _isValidLogEntry = (from, until, entry) => { - var _entry = JSON.parse(entry), - timestamp = new Date(_entry.timestamp); - return timestamp >= from && timestamp <= until - ? true - : false -}; - -export class WinstonLoggerAdapter extends LoggerAdapter { info() { return logger.info.apply(undefined, arguments); } @@ -60,6 +24,18 @@ export class WinstonLoggerAdapter extends LoggerAdapter { return logger.error.apply(undefined, arguments); } + warn() { + return logger.warn.apply(undefined, arguments); + } + + verbose() { + return logger.verbose.apply(undefined, arguments); + } + + log() { + return logger.log.apply(undefined, arguments); + } + addTransport(transport) { // Note that this is calling addTransport // from logger. See import - confusing. diff --git a/src/Controllers/LoggerController.js b/src/Controllers/LoggerController.js index 7cfbe41ec0..9d7a3d2e70 100644 --- a/src/Controllers/LoggerController.js +++ b/src/Controllers/LoggerController.js @@ -60,6 +60,10 @@ export class LoggerController extends AdaptableController { throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Logger adapter is not availabe'); } + if (typeof this.adapter.query !== 'function') { + throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, + 'Querying logs is not supported with this adapter'); + } options = LoggerController.parseOptions(options); return this.adapter.query(options); } diff --git a/src/LiveQuery/Client.js b/src/LiveQuery/Client.js index 72e4a9d393..8d84429010 100644 --- a/src/LiveQuery/Client.js +++ b/src/LiveQuery/Client.js @@ -1,5 +1,5 @@ -import PLog from './PLog'; import Parse from 'parse/node'; +import logger from '../logger'; import type { FlattenedObjectData } from './Subscription'; export type Message = { [attr: string]: any }; @@ -37,7 +37,7 @@ class Client { } static pushResponse(parseWebSocket: any, message: Message): void { - PLog.verbose('Push Response : %j', message); + logger.verbose('Push Response : %j', message); parseWebSocket.send(message); } diff --git a/src/LiveQuery/PLog.js b/src/LiveQuery/PLog.js deleted file mode 100644 index 8ae8f69145..0000000000 --- a/src/LiveQuery/PLog.js +++ /dev/null @@ -1,5 +0,0 @@ -import { addGroup } from '../logger'; - -let PLog = addGroup('parse-live-query-server'); - -module.exports = PLog; diff --git a/src/LiveQuery/ParseCloudCodePublisher.js b/src/LiveQuery/ParseCloudCodePublisher.js index ac5e9d3483..b50a50820d 100644 --- a/src/LiveQuery/ParseCloudCodePublisher.js +++ b/src/LiveQuery/ParseCloudCodePublisher.js @@ -1,5 +1,5 @@ import { ParsePubSub } from './ParsePubSub'; -import PLog from './PLog'; +import logger from '../logger'; class ParseCloudCodePublisher { parsePublisher: Object; @@ -20,7 +20,7 @@ class ParseCloudCodePublisher { // Request is the request object from cloud code functions. request.object is a ParseObject. _onCloudCodeMessage(type: string, request: any): void { - PLog.verbose('Raw request from cloud code current : %j | original : %j', request.object, request.original); + logger.verbose('Raw request from cloud code current : %j | original : %j', request.object, request.original); // We need the full JSON which includes className let message = { currentParseObject: request.object._toFullJSON() diff --git a/src/LiveQuery/ParseLiveQueryServer.js b/src/LiveQuery/ParseLiveQueryServer.js index 0d59211f4b..044c653660 100644 --- a/src/LiveQuery/ParseLiveQueryServer.js +++ b/src/LiveQuery/ParseLiveQueryServer.js @@ -3,7 +3,7 @@ import Parse from 'parse/node'; import { Subscription } from './Subscription'; import { Client } from './Client'; import { ParseWebSocketServer } from './ParseWebSocketServer'; -import PLog from './PLog'; +import logger from '../logger'; import RequestSchema from './RequestSchema'; import { matchesQuery, queryHash } from './QueryTools'; import { ParsePubSub } from './ParsePubSub'; @@ -25,15 +25,14 @@ class ParseLiveQueryServer { this.subscriptions = new Map(); config = config || {}; - // Set LogLevel - PLog.level = config.logLevel || 'INFO'; + // Store keys, convert obj to map let keyPairs = config.keyPairs || {}; this.keyPairs = new Map(); for (let key of Object.keys(keyPairs)) { this.keyPairs.set(key, keyPairs[key]); } - PLog.verbose('Support key pairs', this.keyPairs); + logger.verbose('Support key pairs', this.keyPairs); // Initialize Parse Parse.Object.disableSingleInstance(); @@ -62,7 +61,7 @@ class ParseLiveQueryServer { // Register message handler for subscriber. When publisher get messages, it will publish message // to the subscribers and the handler will be called. this.subscriber.on('message', (channel, messageStr) => { - PLog.verbose('Subscribe messsage %j', messageStr); + logger.verbose('Subscribe messsage %j', messageStr); let message = JSON.parse(messageStr); this._inflateParseObject(message); if (channel === 'afterSave') { @@ -70,7 +69,7 @@ class ParseLiveQueryServer { } else if (channel === 'afterDelete') { this._onAfterDelete(message); } else { - PLog.error('Get message %s from unknown channel %j', message, channel); + logger.error('Get message %s from unknown channel %j', message, channel); } }); @@ -100,16 +99,16 @@ class ParseLiveQueryServer { // Message is the JSON object from publisher after inflated. Message.currentParseObject is the ParseObject after changes. // Message.originalParseObject is the original ParseObject. _onAfterDelete(message: any): void { - PLog.verbose('afterDelete is triggered'); + logger.verbose('afterDelete is triggered'); let deletedParseObject = message.currentParseObject.toJSON(); let className = deletedParseObject.className; - PLog.verbose('ClassName: %j | ObjectId: %s', className, deletedParseObject.id); - PLog.verbose('Current client number : %d', this.clients.size); + logger.verbose('ClassName: %j | ObjectId: %s', className, deletedParseObject.id); + logger.verbose('Current client number : %d', this.clients.size); let classSubscriptions = this.subscriptions.get(className); if (typeof classSubscriptions === 'undefined') { - PLog.error('Can not find subscriptions under this class ' + className); + logger.error('Can not find subscriptions under this class ' + className); return; } for (let subscription of classSubscriptions.values()) { @@ -131,7 +130,7 @@ class ParseLiveQueryServer { } client.pushDelete(requestId, deletedParseObject); }, (error) => { - PLog.error('Matching ACL error : ', error); + logger.error('Matching ACL error : ', error); }); } } @@ -141,7 +140,7 @@ class ParseLiveQueryServer { // Message is the JSON object from publisher after inflated. Message.currentParseObject is the ParseObject after changes. // Message.originalParseObject is the original ParseObject. _onAfterSave(message: any): void { - PLog.verbose('afterSave is triggered'); + logger.verbose('afterSave is triggered'); let originalParseObject = null; if (message.originalParseObject) { @@ -149,12 +148,12 @@ class ParseLiveQueryServer { } let currentParseObject = message.currentParseObject.toJSON(); let className = currentParseObject.className; - PLog.verbose('ClassName: %s | ObjectId: %s', className, currentParseObject.id); - PLog.verbose('Current client number : %d', this.clients.size); + logger.verbose('ClassName: %s | ObjectId: %s', className, currentParseObject.id); + logger.verbose('Current client number : %d', this.clients.size); let classSubscriptions = this.subscriptions.get(className); if (typeof classSubscriptions === 'undefined') { - PLog.error('Can not find subscriptions under this class ' + className); + logger.error('Can not find subscriptions under this class ' + className); return; } for (let subscription of classSubscriptions.values()) { @@ -192,7 +191,7 @@ class ParseLiveQueryServer { originalACLCheckingPromise, currentACLCheckingPromise ).then((isOriginalMatched, isCurrentMatched) => { - PLog.verbose('Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s', + logger.verbose('Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s', originalParseObject, currentParseObject, isOriginalSubscriptionMatched, @@ -220,7 +219,7 @@ class ParseLiveQueryServer { let functionName = 'push' + type; client[functionName](requestId, currentParseObject); }, (error) => { - PLog.error('Matching ACL error : ', error); + logger.error('Matching ACL error : ', error); }); } } @@ -232,12 +231,12 @@ class ParseLiveQueryServer { if (typeof request === 'string') { request = JSON.parse(request); } - PLog.verbose('Request: %j', request); + logger.verbose('Request: %j', request); // Check whether this request is a valid request, return error directly if not if (!tv4.validate(request, RequestSchema['general']) || !tv4.validate(request, RequestSchema[request.op])) { Client.pushError(parseWebsocket, 1, tv4.error.message); - PLog.error('Connect message error %s', tv4.error.message); + logger.error('Connect message error %s', tv4.error.message); return; } @@ -253,15 +252,15 @@ class ParseLiveQueryServer { break; default: Client.pushError(parseWebsocket, 3, 'Get unknown operation'); - PLog.error('Get unknown operation', request.op); + logger.error('Get unknown operation', request.op); } }); parseWebsocket.on('disconnect', () => { - PLog.log('Client disconnect: %d', parseWebsocket.clientId); + logger.info('Client disconnect: %d', parseWebsocket.clientId); let clientId = parseWebsocket.clientId; if (!this.clients.has(clientId)) { - PLog.error('Can not find client %d on disconnect', clientId); + logger.error('Can not find client %d on disconnect', clientId); return; } @@ -285,8 +284,8 @@ class ParseLiveQueryServer { } } - PLog.verbose('Current clients %d', this.clients.size); - PLog.verbose('Current subscriptions %d', this.subscriptions.size); + logger.verbose('Current clients %d', this.clients.size); + logger.verbose('Current subscriptions %d', this.subscriptions.size); }); } @@ -331,14 +330,14 @@ class ParseLiveQueryServer { _handleConnect(parseWebsocket: any, request: any): any { if (!this._validateKeys(request, this.keyPairs)) { Client.pushError(parseWebsocket, 4, 'Key in request is not valid'); - PLog.error('Key in request is not valid'); + logger.error('Key in request is not valid'); return; } let client = new Client(this.clientId, parseWebsocket); parseWebsocket.clientId = this.clientId; this.clientId += 1; this.clients.set(parseWebsocket.clientId, client); - PLog.log('Create new client: %d', parseWebsocket.clientId); + logger.info('Create new client: %d', parseWebsocket.clientId); client.pushConnect(); } @@ -361,7 +360,7 @@ class ParseLiveQueryServer { // If we can not find this client, return error to client if (!parseWebsocket.hasOwnProperty('clientId')) { Client.pushError(parseWebsocket, 2, 'Can not find this client, make sure you connect to server before subscribing'); - PLog.error('Can not find this client, make sure you connect to server before subscribing'); + logger.error('Can not find this client, make sure you connect to server before subscribing'); return; } let client = this.clients.get(parseWebsocket.clientId); @@ -400,15 +399,15 @@ class ParseLiveQueryServer { client.pushSubscribe(request.requestId); - PLog.verbose('Create client %d new subscription: %d', parseWebsocket.clientId, request.requestId); - PLog.verbose('Current client number: %d', this.clients.size); + logger.verbose('Create client %d new subscription: %d', parseWebsocket.clientId, request.requestId); + logger.verbose('Current client number: %d', this.clients.size); } _handleUnsubscribe(parseWebsocket: any, request: any): any { // If we can not find this client, return error to client if (!parseWebsocket.hasOwnProperty('clientId')) { Client.pushError(parseWebsocket, 2, 'Can not find this client, make sure you connect to server before unsubscribing'); - PLog.error('Can not find this client, make sure you connect to server before unsubscribing'); + logger.error('Can not find this client, make sure you connect to server before unsubscribing'); return; } let requestId = request.requestId; @@ -416,7 +415,7 @@ class ParseLiveQueryServer { if (typeof client === 'undefined') { Client.pushError(parseWebsocket, 2, 'Cannot find client with clientId ' + parseWebsocket.clientId + '. Make sure you connect to live query server before unsubscribing.'); - PLog.error('Can not find this client ' + parseWebsocket.clientId); + logger.error('Can not find this client ' + parseWebsocket.clientId); return; } @@ -424,7 +423,7 @@ class ParseLiveQueryServer { if (typeof subscriptionInfo === 'undefined') { Client.pushError(parseWebsocket, 2, 'Cannot find subscription with clientId ' + parseWebsocket.clientId + ' subscriptionId ' + requestId + '. Make sure you subscribe to live query server before unsubscribing.'); - PLog.error('Can not find subscription with clientId ' + parseWebsocket.clientId + ' subscriptionId ' + requestId); + logger.error('Can not find subscription with clientId ' + parseWebsocket.clientId + ' subscriptionId ' + requestId); return; } @@ -446,14 +445,10 @@ class ParseLiveQueryServer { client.pushUnsubscribe(request.requestId); - PLog.verbose('Delete client: %d | subscription: %d', parseWebsocket.clientId, request.requestId); + logger.verbose('Delete client: %d | subscription: %d', parseWebsocket.clientId, request.requestId); } } -ParseLiveQueryServer.setLogLevel = function(logLevel) { - PLog.logLevel = logLevel; -} - export { ParseLiveQueryServer } diff --git a/src/LiveQuery/ParseWebSocketServer.js b/src/LiveQuery/ParseWebSocketServer.js index e97223063b..b7c331c493 100644 --- a/src/LiveQuery/ParseWebSocketServer.js +++ b/src/LiveQuery/ParseWebSocketServer.js @@ -1,4 +1,4 @@ -import PLog from './PLog'; +import logger from '../logger'; let typeMap = new Map([['disconnect', 'close']]); @@ -9,7 +9,7 @@ export class ParseWebSocketServer { let WebSocketServer = require('ws').Server; let wss = new WebSocketServer({ server: server }); wss.on('listening', () => { - PLog.log('Parse LiveQuery Server starts running'); + logger.info('Parse LiveQuery Server starts running'); }); wss.on('connection', (ws) => { onConnect(new ParseWebSocket(ws)); diff --git a/src/LiveQuery/SessionTokenCache.js b/src/LiveQuery/SessionTokenCache.js index 07d9d62744..57f84720d0 100644 --- a/src/LiveQuery/SessionTokenCache.js +++ b/src/LiveQuery/SessionTokenCache.js @@ -1,6 +1,6 @@ import Parse from 'parse/node'; import LRU from 'lru-cache'; -import PLog from './PLog'; +import logger from '../logger'; class SessionTokenCache { cache: Object; @@ -18,16 +18,16 @@ class SessionTokenCache { } let userId = this.cache.get(sessionToken); if (userId) { - PLog.verbose('Fetch userId %s of sessionToken %s from Cache', userId, sessionToken); + logger.verbose('Fetch userId %s of sessionToken %s from Cache', userId, sessionToken); return Parse.Promise.as(userId); } return Parse.User.become(sessionToken).then((user) => { - PLog.verbose('Fetch userId %s of sessionToken %s from Parse', user.id, sessionToken); + logger.verbose('Fetch userId %s of sessionToken %s from Parse', user.id, sessionToken); let userId = user.id; this.cache.set(sessionToken, userId); return Parse.Promise.as(userId); }, (error) => { - PLog.error('Can not fetch userId for sessionToken %j, error %j', sessionToken, error); + logger.error('Can not fetch userId for sessionToken %j, error %j', sessionToken, error); return Parse.Promise.error(error); }); } diff --git a/src/LiveQuery/Subscription.js b/src/LiveQuery/Subscription.js index e3b63dafd3..56cf27b243 100644 --- a/src/LiveQuery/Subscription.js +++ b/src/LiveQuery/Subscription.js @@ -1,5 +1,5 @@ -import {matchesQuery, queryHash} from './QueryTools'; -import PLog from './PLog'; +import {matchesQuery, queryHash} from './QueryTools'; +import logger from '../logger'; export type FlattenedObjectData = { [attr: string]: any }; export type QueryData = { [attr: string]: any }; @@ -29,13 +29,13 @@ class Subscription { deleteClientSubscription(clientId: number, requestId: number): void { let requestIds = this.clientRequestIds.get(clientId); if (typeof requestIds === 'undefined') { - PLog.error('Can not find client %d to delete', clientId); + logger.error('Can not find client %d to delete', clientId); return; } let index = requestIds.indexOf(requestId); if (index < 0) { - PLog.error('Can not find client %d subscription %d to delete', clientId, requestId); + logger.error('Can not find client %d subscription %d to delete', clientId, requestId); return; } requestIds.splice(index, 1); diff --git a/src/ParseServer.js b/src/ParseServer.js index 243908db48..db23344b32 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -13,8 +13,7 @@ if (!global._babelPolyfill) { require('babel-polyfill'); } -import { logger, - configureLogger } from './logger'; +import * as logging from './logger'; import AppCache from './cache'; import Config from './Config'; import parseServerPackage from '../package.json'; @@ -98,8 +97,10 @@ class ParseServer { filesAdapter, push, loggerAdapter, - jsonLogs, - logsFolder, + jsonLogs = logging.defaults.jsonLogs, + logsFolder = logging.defaults.logsFolder, + verbose = logging.defaults.verbose, + logLevel = logging.defaults.level, databaseURI, databaseOptions, databaseAdapter, @@ -132,7 +133,6 @@ class ParseServer { liveQuery = {}, sessionLength = 31536000, // 1 Year in seconds expireInactiveSessions = true, - verbose = false, revokeSessionOnPasswordReset = true, schemaCacheTTL = 5, // cache for 5s __indexBuildCompletionCallbackForTests = () => {}, @@ -156,10 +156,6 @@ class ParseServer { throw 'When using an explicit database adapter, you must also use and explicit filesAdapter.'; } - if (logsFolder) { - configureLogger({logsFolder, jsonLogs}); - } - if (cloud) { addParseCloud(); if (typeof cloud === 'function') { @@ -171,16 +167,16 @@ class ParseServer { } } - if (verbose || process.env.VERBOSE || process.env.VERBOSE_PARSE_SERVER) { - configureLogger({level: 'silly', jsonLogs}); - } - const filesControllerAdapter = loadAdapter(filesAdapter, () => { return new GridStoreAdapter(databaseURI); }); // Pass the push options too as it works with the default const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push || {}); - const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter); + + const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, { jsonLogs, logsFolder, verbose, logLevel }); + + logging.setLogger(loggerControllerAdapter); + const emailControllerAdapter = loadAdapter(emailAdapter); const cacheControllerAdapter = loadAdapter(cacheAdapter, InMemoryCacheAdapter, {appId: appId}); const analyticsControllerAdapter = loadAdapter(analyticsAdapter, AnalyticsAdapter); diff --git a/src/cli/cli-definitions.js b/src/cli/cli-definitions.js index 9f1d7565bc..b7296db9f5 100644 --- a/src/cli/cli-definitions.js +++ b/src/cli/cli-definitions.js @@ -193,6 +193,14 @@ export default { env: "JSON_LOGS", help: "Log as structured JSON objects" }, + "logLevel": { + env: "PARSE_SERVER_LOG_LEVEL", + help: "Sets the level for logs" + }, + "logsFolder": { + env: "PARSE_SERVER_LOGS_FOLDER", + help: "Folder for the logs (defaults to './logs')", + }, "revokeSessionOnPasswordReset": { env: "PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET", help: "When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.", diff --git a/src/index.js b/src/index.js index 82d3c35a19..8f3e0e2d04 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,10 @@ import ParseServer from './ParseServer'; -import logger from './logger'; import S3Adapter from 'parse-server-s3-adapter' import FileSystemAdapter from 'parse-server-fs-adapter' import InMemoryCacheAdapter from './Adapters/Cache/InMemoryCacheAdapter' import TestUtils from './TestUtils'; -import { useExternal } from './deprecated' +import { useExternal } from './deprecated'; +import { getLogger } from './logger'; // Factory function let _ParseServer = function(options) { @@ -16,5 +16,9 @@ _ParseServer.createLiveQueryServer = ParseServer.createLiveQueryServer; let GCSAdapter = useExternal('GCSAdapter', 'parse-server-gcs-adapter'); +Object.defineProperty(module.exports, 'logger', { + get: getLogger +}); + export default ParseServer; -export { S3Adapter, GCSAdapter, FileSystemAdapter, InMemoryCacheAdapter, TestUtils, logger, _ParseServer as ParseServer }; +export { S3Adapter, GCSAdapter, FileSystemAdapter, InMemoryCacheAdapter, TestUtils, _ParseServer as ParseServer }; diff --git a/src/logger.js b/src/logger.js index 15ae1f6b74..9f82a5189f 100644 --- a/src/logger.js +++ b/src/logger.js @@ -1,104 +1,41 @@ -import winston from 'winston'; -import fs from 'fs'; -import path from 'path'; -import DailyRotateFile from 'winston-daily-rotate-file'; +'use strict'; +let logger; -let LOGS_FOLDER = './logs/'; - -if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') { - LOGS_FOLDER = './test_logs/' -} - -LOGS_FOLDER = process.env.PARSE_SERVER_LOGS_FOLDER || LOGS_FOLDER; -const JSON_LOGS = process.env.JSON_LOGS || false; - -let currentLogsFolder = LOGS_FOLDER; -const additionalTransports = []; - -function generateTransports(level, options = {}) { - let transports = [ - new (DailyRotateFile)( - Object.assign({ - filename: 'parse-server.info', - dirname: currentLogsFolder, - name: 'parse-server', - level: level - }, options) - ), - new (DailyRotateFile)( - Object.assign({ - filename: 'parse-server.err', - dirname: currentLogsFolder, - name: 'parse-server-error', - level: 'error' - } - ), options) - ].concat(additionalTransports); - if (!process.env.TESTING || process.env.VERBOSE) { - transports = [ - new (winston.transports.Console)( - Object.assign({ - colorize: true, - level: level - }, options) - ) - ].concat(transports); +let logsFolder = (() => { + let folder = './logs/'; + if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') { + folder = './test_logs/' } - return transports; + folder = process.env.PARSE_SERVER_LOGS_FOLDER || folder; + return folder; +})(); + +let { verbose, level } = (() => { + let verbose = process.env.VERBOSE ? true : false; + return { verbose, level: verbose ? 'verbose' : undefined } +})(); + +export const defaults = { + jsonLogs: process.env.JSON_LOGS || false, + logsFolder, + verbose, + level } -const logger = new winston.Logger(); - -export function configureLogger({ logsFolder, jsonLogs, level = winston.level }) { - winston.level = level; - logsFolder = logsFolder || currentLogsFolder; - - if (!path.isAbsolute(logsFolder)) { - logsFolder = path.resolve(process.cwd(), logsFolder); - } - try { - fs.mkdirSync(logsFolder); - } catch (exception) { - // Ignore, assume the folder already exists - } - currentLogsFolder = logsFolder; - - const options = {}; - if (jsonLogs) { - options.json = true; - options.stringify = true; - } - const transports = generateTransports(level, options); - logger.configure({ - transports: transports - }) +export function setLogger(aLogger) { + logger = aLogger; } -configureLogger({ logsFolder: LOGS_FOLDER, jsonLogs: JSON_LOGS }); - -export function addGroup(groupName) { - let level = winston.level; - let transports = generateTransports().concat(new (DailyRotateFile)({ - filename: groupName, - dirname: currentLogsFolder, - name: groupName, - level: level - })); - - winston.loggers.add(groupName, { - transports: transports - }); - return winston.loggers.get(groupName); +export function getLogger() { + return logger; } -export function addTransport(transport) { - const level = winston.level; - additionalTransports.push(transport); - const transports = generateTransports(level); - logger.configure({ - transports: transports - }); -} +// for: `import logger from './logger'` +Object.defineProperty(module.exports, 'default', { + get: getLogger +}); -export { logger, addTransport }; -export default logger; +// for: `import { logger } from './logger'` +Object.defineProperty(module.exports, 'logger', { + get: getLogger +}); From a75778a4e9ed90607b5d08237c5634cdfdb94430 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Aug 2016 14:33:58 -0400 Subject: [PATCH 02/18] Adds additional logging levels as requirements --- src/Adapters/Logger/LoggerAdapter.js | 14 +++++++++++--- src/Adapters/Logger/WinstonLoggerAdapter.js | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Adapters/Logger/LoggerAdapter.js b/src/Adapters/Logger/LoggerAdapter.js index fa573a38b0..96a33c89c3 100644 --- a/src/Adapters/Logger/LoggerAdapter.js +++ b/src/Adapters/Logger/LoggerAdapter.js @@ -3,18 +3,26 @@ // Allows you to change the logger mechanism // // Adapter classes must implement the following functions: -// * info(obj1 [, obj2, .., objN]) -// * error(obj1 [, obj2, .., objN]) +// * log() {} +// * error() {} +// * warn() {} +// * info() {} +// * verbose() {} +// * debug() {} +// * silly() {} // * query(options, callback) /* optional */ // * configureLogger(options) // Default is WinstonLoggerAdapter.js export class LoggerAdapter { constructor(options) {} - info() {} + log() {} error() {} warn() {} + info() {} verbose() {} + debug() {} + silly() {} } export default LoggerAdapter; diff --git a/src/Adapters/Logger/WinstonLoggerAdapter.js b/src/Adapters/Logger/WinstonLoggerAdapter.js index 6257b97b75..862477f5a8 100644 --- a/src/Adapters/Logger/WinstonLoggerAdapter.js +++ b/src/Adapters/Logger/WinstonLoggerAdapter.js @@ -15,11 +15,7 @@ export class WinstonLoggerAdapter extends LoggerAdapter { configureLogger(options); } } - - info() { - return logger.info.apply(undefined, arguments); - } - + error() { return logger.error.apply(undefined, arguments); } @@ -28,10 +24,22 @@ export class WinstonLoggerAdapter extends LoggerAdapter { return logger.warn.apply(undefined, arguments); } + info() { + return logger.info.apply(undefined, arguments); + } + verbose() { return logger.verbose.apply(undefined, arguments); } + debug() { + return logger.debug.apply(undefined, arguments); + } + + silly() { + return logger.silly.apply(undefined, arguments); + } + log() { return logger.log.apply(undefined, arguments); } From 52e40bf0968c490a8fac921b2186476860f4de1c Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Aug 2016 16:11:47 -0400 Subject: [PATCH 03/18] Adds tests for logging configuration --- spec/Logger.spec.js | 42 +++++++++++++++++++++++++++- src/Adapters/Logger/WinstonLogger.js | 9 ++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js index 4ecea3e0d3..792935d4ce 100644 --- a/spec/Logger.spec.js +++ b/spec/Logger.spec.js @@ -8,7 +8,6 @@ class TestTransport extends winston.Transport { } describe('Logger', () => { - // Test is excluded as will be refactored it('should add transport', () => { const testTransport = new (TestTransport)({ name: 'test' @@ -19,4 +18,45 @@ describe('Logger', () => { expect(testTransport.log).toHaveBeenCalled(); logging.removeTransport(testTransport); }); + + it('should have files transports', (done) => { + reconfigureServer().then(() => { + let transports = logging.logger.transports; + let transportKeys = Object.keys(transports); + expect(transportKeys.length).toBe(2); + done(); + }); + }); + + it('should disable files logs', (done) => { + reconfigureServer({ + logsFolder: null + }).then(() => { + let transports = logging.logger.transports; + let transportKeys = Object.keys(transports); + expect(transportKeys.length).toBe(0); + done(); + }); + }); + + it('should enable JSON logs', (done) => { + // Force console transport + process.env.VERBOSE=1; + reconfigureServer({ + logsFolder: null, + jsonLogs: true + }).then(() => { + let spy = spyOn(process.stdout, 'write'); + logging.logger.info('hi', {key: 'value'}); + expect(process.stdout.write).toHaveBeenCalled(); + var firstLog = process.stdout.write.calls.first().args[0]; + expect(firstLog).toEqual(JSON.stringify({key: 'value', level: 'info', message: 'hi' })+'\n'); + delete process.env.VERBOSE; + return reconfigureServer({ + jsonLogs: false + }); + }).then(() => { + done(); + }); + }); }); diff --git a/src/Adapters/Logger/WinstonLogger.js b/src/Adapters/Logger/WinstonLogger.js index 2a1e4d6b22..d36a8a1ba8 100644 --- a/src/Adapters/Logger/WinstonLogger.js +++ b/src/Adapters/Logger/WinstonLogger.js @@ -14,7 +14,7 @@ function updateTransports(options) { if (_.isNull(options.dirname)) { delete transports['parse-server']; delete transports['parse-server-error']; - } else if (!_.isUndefined(options.dirname)) { + } else { transports['parse-server'] = new (DailyRotateFile)( Object.assign({ filename: 'parse-server.info', @@ -34,6 +34,8 @@ function updateTransports(options) { colorize: true, name: 'console' }, options)); + } else { + delete transports['console']; } } // Mount the additional transports @@ -86,7 +88,10 @@ export function removeTransport(transport) { delete transports[transportName]; logger.configure({ transports: _.values(transports) - }) + }); + _.remove(additionalTransports, (transport) => { + return transport.name === transportName; + }); } export { logger, addTransport, configureLogger, removeTransport }; From 54fcbe45434c79e3d987d38d55452e33f558e3d7 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Aug 2016 16:49:00 -0400 Subject: [PATCH 04/18] removes flaky test --- spec/Logger.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js index 792935d4ce..51509d40e9 100644 --- a/spec/Logger.spec.js +++ b/spec/Logger.spec.js @@ -39,7 +39,7 @@ describe('Logger', () => { }); }); - it('should enable JSON logs', (done) => { + xit('should enable JSON logs', (done) => { // Force console transport process.env.VERBOSE=1; reconfigureServer({ From 4f1f346d583ff1947a789d7a874c9fbb95870f18 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Aug 2016 17:09:17 -0400 Subject: [PATCH 05/18] investigate... --- spec/Logger.spec.js | 77 +++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js index 51509d40e9..5896c01c04 100644 --- a/spec/Logger.spec.js +++ b/spec/Logger.spec.js @@ -17,46 +17,47 @@ describe('Logger', () => { logging.logger.info('hi'); expect(testTransport.log).toHaveBeenCalled(); logging.removeTransport(testTransport); + expect(Object.keys(logging.logger.transports).length).toBe(2); }); - it('should have files transports', (done) => { - reconfigureServer().then(() => { - let transports = logging.logger.transports; - let transportKeys = Object.keys(transports); - expect(transportKeys.length).toBe(2); - done(); - }); - }); + // xit('should have files transports', (done) => { + // reconfigureServer().then(() => { + // let transports = logging.logger.transports; + // let transportKeys = Object.keys(transports); + // expect(transportKeys.length).toBe(2); + // done(); + // }); + // }); - it('should disable files logs', (done) => { - reconfigureServer({ - logsFolder: null - }).then(() => { - let transports = logging.logger.transports; - let transportKeys = Object.keys(transports); - expect(transportKeys.length).toBe(0); - done(); - }); - }); + // xit('should disable files logs', (done) => { + // reconfigureServer({ + // logsFolder: null + // }).then(() => { + // let transports = logging.logger.transports; + // let transportKeys = Object.keys(transports); + // expect(transportKeys.length).toBe(0); + // done(); + // }); + // }); - xit('should enable JSON logs', (done) => { - // Force console transport - process.env.VERBOSE=1; - reconfigureServer({ - logsFolder: null, - jsonLogs: true - }).then(() => { - let spy = spyOn(process.stdout, 'write'); - logging.logger.info('hi', {key: 'value'}); - expect(process.stdout.write).toHaveBeenCalled(); - var firstLog = process.stdout.write.calls.first().args[0]; - expect(firstLog).toEqual(JSON.stringify({key: 'value', level: 'info', message: 'hi' })+'\n'); - delete process.env.VERBOSE; - return reconfigureServer({ - jsonLogs: false - }); - }).then(() => { - done(); - }); - }); + // xit('should enable JSON logs', (done) => { + // // Force console transport + // process.env.VERBOSE=1; + // reconfigureServer({ + // logsFolder: null, + // jsonLogs: true + // }).then(() => { + // let spy = spyOn(process.stdout, 'write'); + // logging.logger.info('hi', {key: 'value'}); + // expect(process.stdout.write).toHaveBeenCalled(); + // var firstLog = process.stdout.write.calls.first().args[0]; + // expect(firstLog).toEqual(JSON.stringify({key: 'value', level: 'info', message: 'hi' })+'\n'); + // delete process.env.VERBOSE; + // return reconfigureServer({ + // jsonLogs: false + // }); + // }).then(() => { + // done(); + // }); + // }); }); From ce11e74dabbf3eb0ed2e3c7c9b4a4ccc84af0cea Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Aug 2016 17:19:46 -0400 Subject: [PATCH 06/18] further investigation --- spec/Logger.spec.js | 76 ++++++++++++++-------------- src/Adapters/Logger/WinstonLogger.js | 2 +- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js index 5896c01c04..2eb9ddbafc 100644 --- a/spec/Logger.spec.js +++ b/spec/Logger.spec.js @@ -20,44 +20,44 @@ describe('Logger', () => { expect(Object.keys(logging.logger.transports).length).toBe(2); }); - // xit('should have files transports', (done) => { - // reconfigureServer().then(() => { - // let transports = logging.logger.transports; - // let transportKeys = Object.keys(transports); - // expect(transportKeys.length).toBe(2); - // done(); - // }); - // }); + it('should have files transports', (done) => { + reconfigureServer().then(() => { + let transports = logging.logger.transports; + let transportKeys = Object.keys(transports); + expect(transportKeys.length).toBe(2); + done(); + }); + }); - // xit('should disable files logs', (done) => { - // reconfigureServer({ - // logsFolder: null - // }).then(() => { - // let transports = logging.logger.transports; - // let transportKeys = Object.keys(transports); - // expect(transportKeys.length).toBe(0); - // done(); - // }); - // }); + it('should disable files logs', (done) => { + reconfigureServer({ + logsFolder: null + }).then(() => { + let transports = logging.logger.transports; + let transportKeys = Object.keys(transports); + expect(transportKeys.length).toBe(0); + done(); + }); + }); - // xit('should enable JSON logs', (done) => { - // // Force console transport - // process.env.VERBOSE=1; - // reconfigureServer({ - // logsFolder: null, - // jsonLogs: true - // }).then(() => { - // let spy = spyOn(process.stdout, 'write'); - // logging.logger.info('hi', {key: 'value'}); - // expect(process.stdout.write).toHaveBeenCalled(); - // var firstLog = process.stdout.write.calls.first().args[0]; - // expect(firstLog).toEqual(JSON.stringify({key: 'value', level: 'info', message: 'hi' })+'\n'); - // delete process.env.VERBOSE; - // return reconfigureServer({ - // jsonLogs: false - // }); - // }).then(() => { - // done(); - // }); - // }); + it('should enable JSON logs', (done) => { + // Force console transport + process.env.VERBOSE=1; + reconfigureServer({ + logsFolder: null, + jsonLogs: true + }).then(() => { + let spy = spyOn(process.stdout, 'write'); + logging.logger.info('hi', {key: 'value'}); + expect(process.stdout.write).toHaveBeenCalled(); + var firstLog = process.stdout.write.calls.first().args[0]; + expect(firstLog).toEqual(JSON.stringify({key: 'value', level: 'info', message: 'hi' })+'\n'); + delete process.env.VERBOSE; + return reconfigureServer({ + jsonLogs: false + }); + }).then(() => { + done(); + }); + }); }); diff --git a/src/Adapters/Logger/WinstonLogger.js b/src/Adapters/Logger/WinstonLogger.js index d36a8a1ba8..754b04c51f 100644 --- a/src/Adapters/Logger/WinstonLogger.js +++ b/src/Adapters/Logger/WinstonLogger.js @@ -14,7 +14,7 @@ function updateTransports(options) { if (_.isNull(options.dirname)) { delete transports['parse-server']; delete transports['parse-server-error']; - } else { + } else if (!_.isUndefined(options.dirname)) { transports['parse-server'] = new (DailyRotateFile)( Object.assign({ filename: 'parse-server.info', From 141c2a6cc9e7c39f3f4c2a4a9c973074bd5337be Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Aug 2016 17:58:47 -0400 Subject: [PATCH 07/18] Adds silent option to disable console output --- spec/Logger.spec.js | 12 ++++++------ spec/helper.js | 1 + src/Adapters/Logger/WinstonLogger.js | 22 ++++++++++++---------- src/ParseServer.js | 3 ++- src/cli/cli-definitions.js | 3 +++ src/logger.js | 3 ++- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js index 2eb9ddbafc..a131e0d53c 100644 --- a/spec/Logger.spec.js +++ b/spec/Logger.spec.js @@ -14,17 +14,18 @@ describe('Logger', () => { }); spyOn(testTransport, 'log'); logging.addTransport(testTransport); + expect(Object.keys(logging.logger.transports).length).toBe(4); logging.logger.info('hi'); expect(testTransport.log).toHaveBeenCalled(); logging.removeTransport(testTransport); - expect(Object.keys(logging.logger.transports).length).toBe(2); + expect(Object.keys(logging.logger.transports).length).toBe(3); }); it('should have files transports', (done) => { reconfigureServer().then(() => { let transports = logging.logger.transports; let transportKeys = Object.keys(transports); - expect(transportKeys.length).toBe(2); + expect(transportKeys.length).toBe(3); done(); }); }); @@ -35,24 +36,23 @@ describe('Logger', () => { }).then(() => { let transports = logging.logger.transports; let transportKeys = Object.keys(transports); - expect(transportKeys.length).toBe(0); + expect(transportKeys.length).toBe(1); done(); }); }); it('should enable JSON logs', (done) => { // Force console transport - process.env.VERBOSE=1; reconfigureServer({ logsFolder: null, - jsonLogs: true + jsonLogs: true, + silent: false }).then(() => { let spy = spyOn(process.stdout, 'write'); logging.logger.info('hi', {key: 'value'}); expect(process.stdout.write).toHaveBeenCalled(); var firstLog = process.stdout.write.calls.first().args[0]; expect(firstLog).toEqual(JSON.stringify({key: 'value', level: 'info', message: 'hi' })+'\n'); - delete process.env.VERBOSE; return reconfigureServer({ jsonLogs: false }); diff --git a/spec/helper.js b/spec/helper.js index b60e41d0de..edc4a39dd7 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -45,6 +45,7 @@ var defaultConfiguration = { webhookKey: 'hook', masterKey: 'test', fileKey: 'test', + silent: true, push: { 'ios': { cert: 'prodCert.pem', diff --git a/src/Adapters/Logger/WinstonLogger.js b/src/Adapters/Logger/WinstonLogger.js index 754b04c51f..f5dec5f877 100644 --- a/src/Adapters/Logger/WinstonLogger.js +++ b/src/Adapters/Logger/WinstonLogger.js @@ -11,6 +11,8 @@ const additionalTransports = []; function updateTransports(options) { let transports = Object.assign({}, logger.transports); if (options) { + let silent = options.silent; + delete options.silent; if (_.isNull(options.dirname)) { delete transports['parse-server']; delete transports['parse-server-error']; @@ -28,15 +30,12 @@ function updateTransports(options) { }, options)); } - if (!process.env.TESTING || process.env.VERBOSE) { - transports.console = new (winston.transports.Console)( - Object.assign({ - colorize: true, - name: 'console' - }, options)); - } else { - delete transports['console']; - } + transports.console = new (winston.transports.Console)( + Object.assign({ + colorize: true, + name: 'console', + silent + }, options)); } // Mount the additional transports additionalTransports.forEach((transport) => { @@ -51,7 +50,8 @@ export function configureLogger({ logsFolder = defaults.logsFolder, jsonLogs = defaults.jsonLogs, logLevel = winston.level, - verbose = defaults.verbose } = {}) { + verbose = defaults.verbose, + silent = defaults.silent } = {}) { if (verbose) { logLevel = 'verbose'; @@ -70,6 +70,8 @@ export function configureLogger({ } options.dirname = logsFolder; options.level = logLevel; + options.silent = silent; + if (jsonLogs) { options.json = true; options.stringify = true; diff --git a/src/ParseServer.js b/src/ParseServer.js index db23344b32..85b6cff5cb 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -101,6 +101,7 @@ class ParseServer { logsFolder = logging.defaults.logsFolder, verbose = logging.defaults.verbose, logLevel = logging.defaults.level, + silent = logging.defaults.silent, databaseURI, databaseOptions, databaseAdapter, @@ -173,7 +174,7 @@ class ParseServer { // Pass the push options too as it works with the default const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push || {}); - const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, { jsonLogs, logsFolder, verbose, logLevel }); + const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, { jsonLogs, logsFolder, verbose, logLevel, silent }); logging.setLogger(loggerControllerAdapter); diff --git a/src/cli/cli-definitions.js b/src/cli/cli-definitions.js index b7296db9f5..1b75c98c41 100644 --- a/src/cli/cli-definitions.js +++ b/src/cli/cli-definitions.js @@ -201,6 +201,9 @@ export default { env: "PARSE_SERVER_LOGS_FOLDER", help: "Folder for the logs (defaults to './logs')", }, + "silent": { + help: "Disables console output", + }, "revokeSessionOnPasswordReset": { env: "PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET", help: "When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.", diff --git a/src/logger.js b/src/logger.js index 9f82a5189f..0f992e3917 100644 --- a/src/logger.js +++ b/src/logger.js @@ -19,7 +19,8 @@ export const defaults = { jsonLogs: process.env.JSON_LOGS || false, logsFolder, verbose, - level + level, + silent: false } export function setLogger(aLogger) { From ecc9de94825b1d66a306bfdd3bc3161e07010ee3 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Aug 2016 18:36:22 -0400 Subject: [PATCH 08/18] Restores logs with VERBOSE in tests --- spec/helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helper.js b/spec/helper.js index edc4a39dd7..37d169f930 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -45,7 +45,7 @@ var defaultConfiguration = { webhookKey: 'hook', masterKey: 'test', fileKey: 'test', - silent: true, + silent: !process.env.VERBOSE, push: { 'ios': { cert: 'prodCert.pem', From 5276ab2de3c24455ac8479bbb6fadd9b3564a3e3 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Aug 2016 20:55:40 -0400 Subject: [PATCH 09/18] Expose controller instead of adapter, reduces method requirements for adapter --- spec/WinstonLoggerAdapter.spec.js | 4 +-- src/Adapters/Logger/LoggerAdapter.js | 15 +---------- src/Adapters/Logger/WinstonLoggerAdapter.js | 26 +------------------ src/Controllers/DatabaseController.js | 2 ++ src/Controllers/LoggerController.js | 28 +++++++++++++++++++++ src/ParseServer.js | 7 ++---- src/Routers/FunctionsRouter.js | 2 +- src/triggers.js | 2 +- 8 files changed, 38 insertions(+), 48 deletions(-) diff --git a/spec/WinstonLoggerAdapter.spec.js b/spec/WinstonLoggerAdapter.spec.js index ee2a72d40b..fa813756f1 100644 --- a/spec/WinstonLoggerAdapter.spec.js +++ b/spec/WinstonLoggerAdapter.spec.js @@ -8,7 +8,7 @@ describe('info logs', () => { it("Verify INFO logs", (done) => { var winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.info('testing info logs', () => { + winstonLoggerAdapter.log('info', 'testing info logs', () => { winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, @@ -29,7 +29,7 @@ describe('info logs', () => { describe('error logs', () => { it("Verify ERROR logs", (done) => { var winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.error('testing error logs', () => { + winstonLoggerAdapter.log('error', 'testing error logs', () => { winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, diff --git a/src/Adapters/Logger/LoggerAdapter.js b/src/Adapters/Logger/LoggerAdapter.js index 96a33c89c3..4a06558941 100644 --- a/src/Adapters/Logger/LoggerAdapter.js +++ b/src/Adapters/Logger/LoggerAdapter.js @@ -4,25 +4,12 @@ // // Adapter classes must implement the following functions: // * log() {} -// * error() {} -// * warn() {} -// * info() {} -// * verbose() {} -// * debug() {} -// * silly() {} // * query(options, callback) /* optional */ -// * configureLogger(options) // Default is WinstonLoggerAdapter.js export class LoggerAdapter { constructor(options) {} - log() {} - error() {} - warn() {} - info() {} - verbose() {} - debug() {} - silly() {} + log(level, message, /* meta */) {} } export default LoggerAdapter; diff --git a/src/Adapters/Logger/WinstonLoggerAdapter.js b/src/Adapters/Logger/WinstonLoggerAdapter.js index 862477f5a8..c13975944f 100644 --- a/src/Adapters/Logger/WinstonLoggerAdapter.js +++ b/src/Adapters/Logger/WinstonLoggerAdapter.js @@ -15,33 +15,9 @@ export class WinstonLoggerAdapter extends LoggerAdapter { configureLogger(options); } } - - error() { - return logger.error.apply(undefined, arguments); - } - - warn() { - return logger.warn.apply(undefined, arguments); - } - - info() { - return logger.info.apply(undefined, arguments); - } - - verbose() { - return logger.verbose.apply(undefined, arguments); - } - - debug() { - return logger.debug.apply(undefined, arguments); - } - - silly() { - return logger.silly.apply(undefined, arguments); - } log() { - return logger.log.apply(undefined, arguments); + return logger.log.apply(logger, arguments); } addTransport(transport) { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 9f43943325..f0dbb902cd 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -878,6 +878,8 @@ DatabaseController.prototype.addPointerPermissions = function(schema, className, } } +// TODO: create indexes on first creation of a _User object. Otherwise it's impossible to +// have a Parse app without it having a _User collection. DatabaseController.prototype.performInitizalization = function() { const requiredUserFields = { fields: { ...SchemaController.defaultColumns._Default, ...SchemaController.defaultColumns._User } }; diff --git a/src/Controllers/LoggerController.js b/src/Controllers/LoggerController.js index 9d7a3d2e70..5b1946e9e6 100644 --- a/src/Controllers/LoggerController.js +++ b/src/Controllers/LoggerController.js @@ -16,7 +16,35 @@ export const LogOrder = { } export class LoggerController extends AdaptableController { + + log(level, args) { + args = [].concat(level, [...args]); + this.adapter.log.apply(this.adapter, args); + } + + info() { + return this.log('info', arguments); + } + + error() { + return this.log('error', arguments); + } + + warn() { + return this.log('warn', arguments); + } + verbose() { + return this.log('verbose', arguments); + } + + debug() { + return this.log('debug', arguments); + } + + silly() { + return this.log('silly', arguments); + } // check that date input is valid static validDateTime(date) { if (!date) { diff --git a/src/ParseServer.js b/src/ParseServer.js index 85b6cff5cb..1f158908e4 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -175,9 +175,6 @@ class ParseServer { const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push || {}); const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, { jsonLogs, logsFolder, verbose, logLevel, silent }); - - logging.setLogger(loggerControllerAdapter); - const emailControllerAdapter = loadAdapter(emailAdapter); const cacheControllerAdapter = loadAdapter(cacheAdapter, InMemoryCacheAdapter, {appId: appId}); const analyticsControllerAdapter = loadAdapter(analyticsAdapter, AnalyticsAdapter); @@ -194,8 +191,8 @@ class ParseServer { const hooksController = new HooksController(appId, databaseController, webhookKey); const analyticsController = new AnalyticsController(analyticsControllerAdapter); - // TODO: create indexes on first creation of a _User object. Otherwise it's impossible to - // have a Parse app without it having a _User collection. + logging.setLogger(loggerController); + const dbInitPromise = databaseController.performInitizalization(); AppCache.put(appId, { diff --git a/src/Routers/FunctionsRouter.js b/src/Routers/FunctionsRouter.js index 78635c9326..c61c59fb7f 100644 --- a/src/Routers/FunctionsRouter.js +++ b/src/Routers/FunctionsRouter.js @@ -65,7 +65,7 @@ export class FunctionsRouter extends PromiseRouter { master: req.auth && req.auth.isMaster, user: req.auth && req.auth.user, installationId: req.info.installationId, - log: req.config.loggerController && req.config.loggerController.adapter, + log: req.config.loggerController, headers: req.headers, functionName: req.params.functionName }; diff --git a/src/triggers.js b/src/triggers.js index ea1853f99e..da74b4fc27 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -102,7 +102,7 @@ export function getRequestObject(triggerType, auth, parseObject, originalParseOb triggerName: triggerType, object: parseObject, master: false, - log: config.loggerController && config.loggerController.adapter + log: config.loggerController }; if (originalParseObject) { From 3d1539d710fbcfbff352a16aa0a69363229ec7d5 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Mon, 8 Aug 2016 09:03:37 -0400 Subject: [PATCH 10/18] Shuffles initializations around --- src/ParseServer.js | 25 ++++++++++++++----------- src/cli/cli-definitions.js | 8 ++++++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index 1f158908e4..3ed1bcf9a8 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -168,30 +168,33 @@ class ParseServer { } } + const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, { jsonLogs, logsFolder, verbose, logLevel, silent }); + const loggerController = new LoggerController(loggerControllerAdapter, appId); + logging.setLogger(loggerController); + const filesControllerAdapter = loadAdapter(filesAdapter, () => { return new GridStoreAdapter(databaseURI); }); + const filesController = new FilesController(filesControllerAdapter, appId); + // Pass the push options too as it works with the default const pushControllerAdapter = loadAdapter(push && push.adapter, ParsePushAdapter, push || {}); + // We pass the options and the base class for the adatper, + // Note that passing an instance would work too + const pushController = new PushController(pushControllerAdapter, appId, push); - const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, { jsonLogs, logsFolder, verbose, logLevel, silent }); const emailControllerAdapter = loadAdapter(emailAdapter); + const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails }); + const cacheControllerAdapter = loadAdapter(cacheAdapter, InMemoryCacheAdapter, {appId: appId}); + const cacheController = new CacheController(cacheControllerAdapter, appId); + const analyticsControllerAdapter = loadAdapter(analyticsAdapter, AnalyticsAdapter); + const analyticsController = new AnalyticsController(analyticsControllerAdapter); - // We pass the options and the base class for the adatper, - // Note that passing an instance would work too - const filesController = new FilesController(filesControllerAdapter, appId); - const pushController = new PushController(pushControllerAdapter, appId, push); - const loggerController = new LoggerController(loggerControllerAdapter, appId); - const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails }); const liveQueryController = new LiveQueryController(liveQuery); - const cacheController = new CacheController(cacheControllerAdapter, appId); const databaseController = new DatabaseController(databaseAdapter, new SchemaCache(cacheController, schemaCacheTTL)); const hooksController = new HooksController(appId, databaseController, webhookKey); - const analyticsController = new AnalyticsController(analyticsControllerAdapter); - - logging.setLogger(loggerController); const dbInitPromise = databaseController.performInitizalization(); diff --git a/src/cli/cli-definitions.js b/src/cli/cli-definitions.js index 1b75c98c41..02d643551a 100644 --- a/src/cli/cli-definitions.js +++ b/src/cli/cli-definitions.js @@ -32,6 +32,13 @@ function booleanParser(opt) { return false; } +function nullParser(opt) { + if (opt == 'null') { + return null; + } + return opt; +} + export default { "appId": { env: "PARSE_SERVER_APPLICATION_ID", @@ -200,6 +207,7 @@ export default { "logsFolder": { env: "PARSE_SERVER_LOGS_FOLDER", help: "Folder for the logs (defaults to './logs')", + action: nullParser }, "silent": { help: "Disables console output", From ff92deddb9cc3613ad9f95e44559e0a4995cf17c Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Mon, 8 Aug 2016 09:38:18 -0400 Subject: [PATCH 11/18] Fix doc --- src/cli/cli-definitions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/cli-definitions.js b/src/cli/cli-definitions.js index 02d643551a..b8acc3de26 100644 --- a/src/cli/cli-definitions.js +++ b/src/cli/cli-definitions.js @@ -206,7 +206,7 @@ export default { }, "logsFolder": { env: "PARSE_SERVER_LOGS_FOLDER", - help: "Folder for the logs (defaults to './logs')", + help: "Folder for the logs (defaults to './logs'); set to null to disable file based logging", action: nullParser }, "silent": { From 354316ee3ea445d2088213bb8396e8390207863b Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Mon, 8 Aug 2016 10:38:58 -0400 Subject: [PATCH 12/18] Load cloudCode last to make sure the logger is available --- src/ParseServer.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index 3ed1bcf9a8..f5834ea03e 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -157,17 +157,6 @@ class ParseServer { throw 'When using an explicit database adapter, you must also use and explicit filesAdapter.'; } - if (cloud) { - addParseCloud(); - if (typeof cloud === 'function') { - cloud(Parse) - } else if (typeof cloud === 'string') { - require(path.resolve(process.cwd(), cloud)); - } else { - throw "argument 'cloud' must either be a string or a function"; - } - } - const loggerControllerAdapter = loadAdapter(loggerAdapter, WinstonLoggerAdapter, { jsonLogs, logsFolder, verbose, logLevel, silent }); const loggerController = new LoggerController(loggerControllerAdapter, appId); logging.setLogger(loggerController); @@ -248,6 +237,17 @@ class ParseServer { if (process.env.TESTING) { __indexBuildCompletionCallbackForTests(dbInitPromise); } + + if (cloud) { + addParseCloud(); + if (typeof cloud === 'function') { + cloud(Parse) + } else if (typeof cloud === 'string') { + require(path.resolve(process.cwd(), cloud)); + } else { + throw "argument 'cloud' must either be a string or a function"; + } + } } get app() { From 10a5af1ac74966f41345e2483418ebe5596cb45e Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Mon, 8 Aug 2016 11:22:38 -0400 Subject: [PATCH 13/18] Adds test to make sure we can load an adapter from npm module --- spec/AdapterLoader.spec.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/AdapterLoader.spec.js b/spec/AdapterLoader.spec.js index 250a8a7c49..8c2e1aef96 100644 --- a/spec/AdapterLoader.spec.js +++ b/spec/AdapterLoader.spec.js @@ -45,6 +45,19 @@ describe("AdapterLoader", ()=>{ done(); }); + it("should instantiate an adapter from npm module", (done) => { + var adapter = loadAdapter({ + module: 'parse-server-fs-adapter' + }); + + expect(typeof adapter).toBe('object'); + expect(typeof adapter.createFile).toBe('function'); + expect(typeof adapter.deleteFile).toBe('function'); + expect(typeof adapter.getFileData).toBe('function'); + expect(typeof adapter.getFileLocation).toBe('function'); + done(); + }); + it("should instantiate an adapter from function/Class", (done) => { var adapter = loadAdapter({ adapter: FilesAdapter From 07615c7b730d4a2a6470980d493dcb3714a62d9e Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Mon, 8 Aug 2016 12:24:27 -0400 Subject: [PATCH 14/18] extract defaults --- src/Adapters/Logger/WinstonLogger.js | 2 +- src/ParseServer.js | 33 ++++++++++++++-------------- src/defaults.js | 30 +++++++++++++++++++++++++ src/logger.js | 22 ------------------- 4 files changed, 48 insertions(+), 39 deletions(-) create mode 100644 src/defaults.js diff --git a/src/Adapters/Logger/WinstonLogger.js b/src/Adapters/Logger/WinstonLogger.js index f5dec5f877..2015908b30 100644 --- a/src/Adapters/Logger/WinstonLogger.js +++ b/src/Adapters/Logger/WinstonLogger.js @@ -3,7 +3,7 @@ import fs from 'fs'; import path from 'path'; import DailyRotateFile from 'winston-daily-rotate-file'; import _ from 'lodash'; -import defaults from '../../logger'; +import defaults from '../../defaults'; const logger = new winston.Logger(); const additionalTransports = []; diff --git a/src/ParseServer.js b/src/ParseServer.js index f5834ea03e..60ac8e3791 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -13,6 +13,7 @@ if (!global._babelPolyfill) { require('babel-polyfill'); } +import defaults from './defaults'; import * as logging from './logger'; import AppCache from './cache'; import Config from './Config'; @@ -93,15 +94,15 @@ class ParseServer { appId = requiredParameter('You must provide an appId!'), masterKey = requiredParameter('You must provide a masterKey!'), appName, - analyticsAdapter = undefined, + analyticsAdapter, filesAdapter, push, loggerAdapter, - jsonLogs = logging.defaults.jsonLogs, - logsFolder = logging.defaults.logsFolder, - verbose = logging.defaults.verbose, - logLevel = logging.defaults.level, - silent = logging.defaults.silent, + jsonLogs = defaults.jsonLogs, + logsFolder = defaults.logsFolder, + verbose = defaults.verbose, + logLevel = defaults.level, + silent = defaults.silent, databaseURI, databaseOptions, databaseAdapter, @@ -112,15 +113,15 @@ class ParseServer { dotNetKey, restAPIKey, webhookKey, - fileKey = undefined, + fileKey, facebookAppIds = [], - enableAnonymousUsers = true, - allowClientClassCreation = true, + enableAnonymousUsers = defaults.enableAnonymousUsers, + allowClientClassCreation = defaults.allowClientClassCreation, oauth = {}, serverURL = requiredParameter('You must provide a serverURL!'), - maxUploadSize = '20mb', - verifyUserEmails = false, - preventLoginWithUnverifiedEmail = false, + maxUploadSize = defaults.maxUploadSize, + verifyUserEmails = defaults.verifyUserEmails, + preventLoginWithUnverifiedEmail = defaults.preventLoginWithUnverifiedEmail, emailVerifyTokenValidityDuration, cacheAdapter, emailAdapter, @@ -132,10 +133,10 @@ class ParseServer { passwordResetSuccess: undefined }, liveQuery = {}, - sessionLength = 31536000, // 1 Year in seconds - expireInactiveSessions = true, - revokeSessionOnPasswordReset = true, - schemaCacheTTL = 5, // cache for 5s + sessionLength = defaults.sessionLength, // 1 Year in seconds + expireInactiveSessions = defaults.expireInactiveSessions, + revokeSessionOnPasswordReset = defaults.revokeSessionOnPasswordReset, + schemaCacheTTL = defaults.schemaCacheTTL, // cache for 5s __indexBuildCompletionCallbackForTests = () => {}, }) { // Initialize the node client SDK automatically diff --git a/src/defaults.js b/src/defaults.js new file mode 100644 index 0000000000..95c2789244 --- /dev/null +++ b/src/defaults.js @@ -0,0 +1,30 @@ +let logsFolder = (() => { + let folder = './logs/'; + if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') { + folder = './test_logs/' + } + folder = process.env.PARSE_SERVER_LOGS_FOLDER || folder; + return folder; +})(); + +let { verbose, level } = (() => { + let verbose = process.env.VERBOSE ? true : false; + return { verbose, level: verbose ? 'verbose' : undefined } +})(); + +export default { + jsonLogs: process.env.JSON_LOGS || false, + logsFolder, + verbose, + level, + silent: false, + enableAnonymousUsers: true, + allowClientClassCreation: true, + maxUploadSize: '20mb', + verifyUserEmails: false, + preventLoginWithUnverifiedEmail: false, + sessionLength: 31536000, + expireInactiveSessions: true, + revokeSessionOnPasswordReset: true, + schemaCacheTTL: 5 +} \ No newline at end of file diff --git a/src/logger.js b/src/logger.js index 0f992e3917..7c82574333 100644 --- a/src/logger.js +++ b/src/logger.js @@ -1,28 +1,6 @@ 'use strict'; let logger; -let logsFolder = (() => { - let folder = './logs/'; - if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') { - folder = './test_logs/' - } - folder = process.env.PARSE_SERVER_LOGS_FOLDER || folder; - return folder; -})(); - -let { verbose, level } = (() => { - let verbose = process.env.VERBOSE ? true : false; - return { verbose, level: verbose ? 'verbose' : undefined } -})(); - -export const defaults = { - jsonLogs: process.env.JSON_LOGS || false, - logsFolder, - verbose, - level, - silent: false -} - export function setLogger(aLogger) { logger = aLogger; } From 91758cc241a60f03918346673984dd8016ccdf4a Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Mon, 8 Aug 2016 12:53:20 -0400 Subject: [PATCH 15/18] Adds defaultMongoURI to defaults --- src/Adapters/Files/GridStoreAdapter.js | 5 ++--- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 3 +-- src/ParseServer.js | 4 ++-- src/defaults.js | 3 ++- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Adapters/Files/GridStoreAdapter.js b/src/Adapters/Files/GridStoreAdapter.js index d7844a0b5f..a2584dc442 100644 --- a/src/Adapters/Files/GridStoreAdapter.js +++ b/src/Adapters/Files/GridStoreAdapter.js @@ -8,14 +8,13 @@ import { MongoClient, GridStore, Db} from 'mongodb'; import { FilesAdapter } from './FilesAdapter'; - -const DefaultMongoURI = 'mongodb://localhost:27017/parse'; +import defaults from '../../defaults'; export class GridStoreAdapter extends FilesAdapter { _databaseURI: string; _connectionPromise: Promise; - constructor(mongoDatabaseURI = DefaultMongoURI) { + constructor(mongoDatabaseURI = defaults.DefaultMongoURI) { super(); this._databaseURI = mongoDatabaseURI; this._connect(); diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 4bdb575b12..44c116ad7f 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -17,7 +17,6 @@ let mongodb = require('mongodb'); let MongoClient = mongodb.MongoClient; const MongoSchemaCollectionName = '_SCHEMA'; -const DefaultMongoURI = 'mongodb://localhost:27017/parse'; const storageAdapterAllCollections = mongoAdapter => { return mongoAdapter.connect() @@ -86,7 +85,7 @@ export class MongoStorageAdapter { database; constructor({ - uri = DefaultMongoURI, + uri = defaults.DefaultMongoURI, collectionPrefix = '', mongoOptions = {}, }) { diff --git a/src/ParseServer.js b/src/ParseServer.js index 60ac8e3791..c6290f690e 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -103,7 +103,7 @@ class ParseServer { verbose = defaults.verbose, logLevel = defaults.level, silent = defaults.silent, - databaseURI, + databaseURI = defaults.DefaultMongoURI, databaseOptions, databaseAdapter, cloud, @@ -142,7 +142,7 @@ class ParseServer { // Initialize the node client SDK automatically Parse.initialize(appId, javascriptKey || 'unused', masterKey); Parse.serverURL = serverURL; - if ((databaseOptions || databaseURI || collectionPrefix !== '') && databaseAdapter) { + if ((databaseOptions || (databaseURI && databaseURI != defaults.DefaultMongoURI) || collectionPrefix !== '') && databaseAdapter) { throw 'You cannot specify both a databaseAdapter and a databaseURI/databaseOptions/connectionPrefix.'; } else if (!databaseAdapter) { databaseAdapter = new MongoStorageAdapter({ diff --git a/src/defaults.js b/src/defaults.js index 95c2789244..8f9d196dc1 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -13,6 +13,7 @@ let { verbose, level } = (() => { })(); export default { + DefaultMongoURI: 'mongodb://localhost:27017/parse', jsonLogs: process.env.JSON_LOGS || false, logsFolder, verbose, @@ -27,4 +28,4 @@ export default { expireInactiveSessions: true, revokeSessionOnPasswordReset: true, schemaCacheTTL: 5 -} \ No newline at end of file +} From ca94f5161a5a8ee60dc463c488419a0cf2c79908 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Wed, 10 Aug 2016 09:06:09 -0400 Subject: [PATCH 16/18] fix defaults values --- src/Controllers/SchemaCache.js | 3 ++- src/defaults.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Controllers/SchemaCache.js b/src/Controllers/SchemaCache.js index 7a56f10710..a72fc0210d 100644 --- a/src/Controllers/SchemaCache.js +++ b/src/Controllers/SchemaCache.js @@ -3,11 +3,12 @@ const SCHEMA_CACHE_PREFIX = "__SCHEMA"; const ALL_KEYS = "__ALL_KEYS"; import { randomString } from '../cryptoUtils'; +import defaults from '../defaults'; export default class SchemaCache { cache: Object; - constructor(cacheController, ttl = 30) { + constructor(cacheController, ttl = defaults.schemaCacheTTL) { this.ttl = ttl; if (typeof ttl == 'string') { this.ttl = parseInt(ttl); diff --git a/src/defaults.js b/src/defaults.js index 8f9d196dc1..838346d623 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -27,5 +27,5 @@ export default { sessionLength: 31536000, expireInactiveSessions: true, revokeSessionOnPasswordReset: true, - schemaCacheTTL: 5 + schemaCacheTTL: 5000 // in ms } From ea160488a06907d581a3e18cdf30096d8cf22824 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Wed, 10 Aug 2016 15:30:14 -0400 Subject: [PATCH 17/18] Proper error for PG failures --- spec/InstallationsRouter.spec.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/InstallationsRouter.spec.js b/spec/InstallationsRouter.spec.js index 60965ff967..65c9a39e08 100644 --- a/spec/InstallationsRouter.spec.js +++ b/spec/InstallationsRouter.spec.js @@ -71,6 +71,9 @@ describe('InstallationsRouter', () => { var results = res.response.results; expect(results.length).toEqual(1); done(); + }).catch((err) => { + fail(JSON.stringify(err)); + done(); }); }); @@ -172,6 +175,9 @@ describe('InstallationsRouter', () => { expect(response.results.length).toEqual(0); expect(response.count).toEqual(2); done(); + }).catch((err) => { + fail(JSON.stringify(err)); + done(); }); }); }); From f2c47706eeec990bf99eea15a54fb4e51e5ec9ba Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Fri, 12 Aug 2016 12:44:03 -0400 Subject: [PATCH 18/18] Disable flaky test --- spec/InstallationsRouter.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/InstallationsRouter.spec.js b/spec/InstallationsRouter.spec.js index 65c9a39e08..b2725a09af 100644 --- a/spec/InstallationsRouter.spec.js +++ b/spec/InstallationsRouter.spec.js @@ -5,7 +5,7 @@ var InstallationsRouter = require('../src/Routers/InstallationsRouter').Installa var config = new Config('test'); -describe('InstallationsRouter', () => { +describe_only_db(['mongo'])('InstallationsRouter', () => { it('uses find condition from request.body', (done) => { var androidDeviceRequest = { 'installationId': '12345678-abcd-abcd-abcd-123456789abc',