Exposes the loggerAdapter as log to Cloud Functions and Triggers (#1565)

This allows access to logging inside cloud code and triggers via
    request.log.info
    request.log.error
This commit is contained in:
Blayne Chard
2016-04-22 08:20:14 +12:00
committed by Florent Vilmart
parent 2d94a885d2
commit 6e9529f81c
6 changed files with 87 additions and 21 deletions

View File

@@ -0,0 +1,61 @@
'use strict';
var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
describe("Cloud Code Logger", () => {
it("should expose log to functions", (done) => {
var logController = new LoggerController(new FileLoggerAdapter());
Parse.Cloud.define("loggerTest", (req, res) => {
req.log.info('logTest', 'info log', {info: 'some log' });
req.log.error('logTest','error log', {error: 'there was an error'});
res.success({});
});
Parse.Cloud.run('loggerTest').then(() => {
Parse.Cloud._removeHook('Functions', 'logTest');
return logController.getLogs({from: Date.now() - 500, size: 1000});
}).then((res) => {
expect(res.length).not.toBe(0);
let lastLogs = res.slice(0, 2);
let errorMessage = lastLogs[0];
let infoMessage = lastLogs[1];
expect(errorMessage.level).toBe('error');
expect(errorMessage.error).toBe('there was an error');
expect(errorMessage.message).toBe('logTest error log');
expect(infoMessage.level).toBe('info');
expect(infoMessage.info).toBe('some log');
expect(infoMessage.message).toBe('logTest info log');
done();
});
});
it("should expose log to trigger", (done) => {
var logController = new LoggerController(new FileLoggerAdapter());
Parse.Cloud.beforeSave("MyObject", (req, res) => {
req.log.info('beforeSave MyObject', 'info log', {info: 'some log' });
req.log.error('beforeSave MyObject','error log', {error: 'there was an error'});
res.success({});
});
let obj = new Parse.Object('MyObject');
obj.save().then(() => {
Parse.Cloud._removeHook('Triggers', 'beforeSave', 'MyObject');
return logController.getLogs({from: Date.now() - 500, size: 1000})
}).then((res) => {
expect(res.length).not.toBe(0);
let lastLogs = res.slice(0, 2);
let errorMessage = lastLogs[0];
let infoMessage = lastLogs[1];
expect(errorMessage.level).toBe('error');
expect(errorMessage.error).toBe('there was an error');
expect(errorMessage.message).toBe('beforeSave MyObject error log');
expect(infoMessage.level).toBe('info');
expect(infoMessage.info).toBe('some log');
expect(infoMessage.message).toBe('beforeSave MyObject info log');
done();
});
});
});

View File

@@ -7,7 +7,8 @@ describe('info logs', () => {
var fileLoggerAdapter = new FileLoggerAdapter(); var fileLoggerAdapter = new FileLoggerAdapter();
fileLoggerAdapter.info('testing info logs', () => { fileLoggerAdapter.info('testing info logs', () => {
fileLoggerAdapter.query({ fileLoggerAdapter.query({
size: 1, from: new Date(Date.now() - 500),
size: 100,
level: 'info' level: 'info'
}, (results) => { }, (results) => {
if(results.length == 0) { if(results.length == 0) {
@@ -28,7 +29,8 @@ describe('error logs', () => {
var fileLoggerAdapter = new FileLoggerAdapter(); var fileLoggerAdapter = new FileLoggerAdapter();
fileLoggerAdapter.error('testing error logs', () => { fileLoggerAdapter.error('testing error logs', () => {
fileLoggerAdapter.query({ fileLoggerAdapter.query({
size: 1, from: new Date(Date.now() - 500),
size: 100,
level: 'error' level: 'error'
}, (results) => { }, (results) => {
if(results.length == 0) { if(results.length == 0) {

View File

@@ -162,7 +162,7 @@ RestWrite.prototype.runBeforeTrigger = function() {
updatedObject.set(this.sanitizedData()); updatedObject.set(this.sanitizedData());
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config.applicationId); return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config);
}).then((response) => { }).then((response) => {
if (response && response.object) { if (response && response.object) {
this.data = response.object; this.data = response.object;
@@ -824,7 +824,7 @@ RestWrite.prototype.runAfterTrigger = function() {
this.config.liveQueryController.onAfterSave(updatedObject.className, updatedObject, originalObject); this.config.liveQueryController.onAfterSave(updatedObject.className, updatedObject, originalObject);
// Run afterSave trigger // Run afterSave trigger
triggers.maybeRunTrigger(triggers.Types.afterSave, this.auth, updatedObject, originalObject, this.config.applicationId); triggers.maybeRunTrigger(triggers.Types.afterSave, this.auth, updatedObject, originalObject, this.config);
}; };
// A helper to figure out what location this operation happens at. // A helper to figure out what location this operation happens at.

View File

@@ -32,13 +32,13 @@ export class FunctionsRouter extends PromiseRouter {
var theFunction = triggers.getFunction(req.params.functionName, applicationId); var theFunction = triggers.getFunction(req.params.functionName, applicationId);
var theValidator = triggers.getValidator(req.params.functionName, applicationId); var theValidator = triggers.getValidator(req.params.functionName, applicationId);
if (theFunction) { if (theFunction) {
const params = Object.assign({}, req.body, req.query); const params = Object.assign({}, req.body, req.query);
var request = { var request = {
params: params, params: params,
master: req.auth && req.auth.isMaster, master: req.auth && req.auth.isMaster,
user: req.auth && req.auth.user, user: req.auth && req.auth.user,
installationId: req.info.installationId installationId: req.info.installationId,
log: req.config.loggerController && req.config.loggerController.adapter
}; };
if (theValidator && typeof theValidator === "function") { if (theValidator && typeof theValidator === "function") {

View File

@@ -52,7 +52,7 @@ function del(config, auth, className, objectId) {
inflatedObject = Parse.Object.fromJSON(response.results[0]); inflatedObject = Parse.Object.fromJSON(response.results[0]);
// Notify LiveQuery server if possible // Notify LiveQuery server if possible
config.liveQueryController.onAfterDelete(inflatedObject.className, inflatedObject); config.liveQueryController.onAfterDelete(inflatedObject.className, inflatedObject);
return triggers.maybeRunTrigger(triggers.Types.beforeDelete, auth, inflatedObject, null, config.applicationId); return triggers.maybeRunTrigger(triggers.Types.beforeDelete, auth, inflatedObject, null, config);
} }
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found for delete.'); 'Object not found for delete.');
@@ -79,7 +79,7 @@ function del(config, auth, className, objectId) {
objectId: objectId objectId: objectId
}, options); }, options);
}).then(() => { }).then(() => {
triggers.maybeRunTrigger(triggers.Types.afterDelete, auth, inflatedObject, null, config.applicationId); triggers.maybeRunTrigger(triggers.Types.afterDelete, auth, inflatedObject, null, config);
return Promise.resolve(); return Promise.resolve();
}); });
} }

View File

@@ -92,15 +92,18 @@ export function getValidator(functionName, applicationId) {
return undefined; return undefined;
} }
export function getRequestObject(triggerType, auth, parseObject, originalParseObject) { export function getRequestObject(triggerType, auth, parseObject, originalParseObject, config) {
var request = { var request = {
triggerName: triggerType, triggerName: triggerType,
object: parseObject, object: parseObject,
master: false master: false,
log: config.loggerController && config.loggerController.adapter
}; };
if (originalParseObject) { if (originalParseObject) {
request.original = originalParseObject; request.original = originalParseObject;
} }
if (!auth) { if (!auth) {
return request; return request;
} }
@@ -145,19 +148,19 @@ export function getResponseObject(request, resolve, reject) {
// Resolves to an object, empty or containing an object key. A beforeSave // Resolves to an object, empty or containing an object key. A beforeSave
// trigger will set the object key to the rest format object to save. // trigger will set the object key to the rest format object to save.
// originalParseObject is optional, we only need that for befote/afterSave functions // originalParseObject is optional, we only need that for befote/afterSave functions
export function maybeRunTrigger(triggerType, auth, parseObject, originalParseObject, applicationId) { export function maybeRunTrigger(triggerType, auth, parseObject, originalParseObject, config) {
if (!parseObject) { if (!parseObject) {
return Promise.resolve({}); return Promise.resolve({});
} }
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var trigger = getTrigger(parseObject.className, triggerType, applicationId); var trigger = getTrigger(parseObject.className, triggerType, config.applicationId);
if (!trigger) return resolve(); if (!trigger) return resolve();
var request = getRequestObject(triggerType, auth, parseObject, originalParseObject); var request = getRequestObject(triggerType, auth, parseObject, originalParseObject, config);
var response = getResponseObject(request, resolve, reject); var response = getResponseObject(request, resolve, reject);
// Force the current Parse app before the trigger // Force the current Parse app before the trigger
Parse.applicationId = applicationId; Parse.applicationId = config.applicationId;
Parse.javascriptKey = cache.apps.get(applicationId).javascriptKey || ''; Parse.javascriptKey = config.javascriptKey || '';
Parse.masterKey = cache.apps.get(applicationId).masterKey; Parse.masterKey = config.masterKey;
trigger(request, response); trigger(request, response);
}); });
}; };