Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Add silent log level for Cloud Code #8803

Merged
merged 4 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions spec/CloudCodeLogger.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ describe('Cloud Code Logger', () => {
// useful to flip to false for fine tuning :).
silent: true,
logLevel: undefined,
logLevels: {
cloudFunctionError: 'error',
cloudFunctionSuccess: 'info',
triggerAfter: 'info',
triggerBeforeError: 'error',
triggerBeforeSuccess: 'info',
},
})
.then(() => {
return Parse.User.signUp('tester', 'abc')
Expand Down Expand Up @@ -334,4 +341,53 @@ describe('Cloud Code Logger', () => {
expect(args[0]).toBe('Parse error: ');
expect(args[1].message).toBe('Object not found.');
});

it('should log cloud function execution using the silent log level', async () => {
await reconfigureServer({
logLevels: {
cloudFunctionSuccess: 'silent',
cloudFunctionError: 'silent',
},
});
Parse.Cloud.define('aFunction', () => {
return 'it worked!';
});
Parse.Cloud.define('bFunction', () => {
throw new Error('Failed');
});
spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();

await Parse.Cloud.run('aFunction', { foo: 'bar' });
expect(spy).toHaveBeenCalledTimes(0);

await expectAsync(Parse.Cloud.run('bFunction', { foo: 'bar' })).toBeRejected();
// Not "Failed running cloud function message..."
expect(spy).toHaveBeenCalledTimes(1);
});

it('should log cloud function triggers using the silent log level', async () => {
await reconfigureServer({
logLevels: {
triggerAfter: 'silent',
triggerBeforeSuccess: 'silent',
triggerBeforeError: 'silent',
},
});
Parse.Cloud.beforeSave('TestClassError', () => {
throw new Error('Failed');
});
Parse.Cloud.beforeSave('TestClass', () => {});
Parse.Cloud.afterSave('TestClass', () => {});

spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();

const obj = new Parse.Object('TestClass');
await obj.save();
expect(spy).toHaveBeenCalledTimes(0);

const objError = new Parse.Object('TestClassError');
await expectAsync(objError.save()).toBeRejected();
// Not "beforeSave failed for TestClassError for user ..."
expect(spy).toHaveBeenCalledTimes(1);
});
});
19 changes: 13 additions & 6 deletions spec/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ const defaultConfiguration = {
allowClientClassCreation: true,
};

if (silent) {
defaultConfiguration.logLevels = {
cloudFunctionSuccess: 'silent',
cloudFunctionError: 'silent',
triggerAfter: 'silent',
triggerBeforeError: 'silent',
triggerBeforeSuccess: 'silent',
};
}

if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
defaultConfiguration.cacheAdapter = new RedisCacheAdapter();
}
Expand Down Expand Up @@ -434,8 +444,8 @@ try {
// Fetch test exclusion list
testExclusionList = require('./testExclusionList.json');
console.log(`Using test exclusion list with ${testExclusionList.length} entries`);
} catch(error) {
if(error.code !== 'MODULE_NOT_FOUND') {
} catch (error) {
if (error.code !== 'MODULE_NOT_FOUND') {
throw error;
}
}
Expand All @@ -445,10 +455,7 @@ global.it_id = (id, func) => {
if (testExclusionList.includes(id)) {
return xit;
} else {
if(func === undefined)
return it;
else
return func;
return func || it;
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/Controllers/LoggerController.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const LogOrder = {
ASCENDING: 'asc',
};

export const logLevels = ['error', 'warn', 'info', 'debug', 'verbose', 'silly'];
export const logLevels = ['error', 'warn', 'info', 'debug', 'verbose', 'silly', 'silent'];

export class LoggerController extends AdaptableController {
constructor(adapter, appId, options = { logLevel: 'info' }) {
Expand Down
45 changes: 25 additions & 20 deletions src/Routers/FunctionsRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,36 +141,41 @@ export class FunctionsRouter extends PromiseRouter {

return new Promise(function (resolve, reject) {
const userString = req.auth && req.auth.user ? req.auth.user.id : undefined;
const cleanInput = logger.truncateLogMessage(JSON.stringify(params));
const { success, error } = FunctionsRouter.createResponseObject(
result => {
try {
const cleanResult = logger.truncateLogMessage(JSON.stringify(result.response.result));
logger[req.config.logLevels.cloudFunctionSuccess](
`Ran cloud function ${functionName} for user ${userString} with:\n Input: ${cleanInput}\n Result: ${cleanResult}`,
{
functionName,
params,
user: userString,
}
);
if (req.config.logLevels.cloudFunctionSuccess !== 'silent') {
const cleanInput = logger.truncateLogMessage(JSON.stringify(params));
const cleanResult = logger.truncateLogMessage(JSON.stringify(result.response.result));
logger[req.config.logLevels.cloudFunctionSuccess](
`Ran cloud function ${functionName} for user ${userString} with:\n Input: ${cleanInput}\n Result: ${cleanResult}`,
{
functionName,
params,
user: userString,
}
);
}
resolve(result);
} catch (e) {
reject(e);
}
},
error => {
try {
logger[req.config.logLevels.cloudFunctionError](
`Failed running cloud function ${functionName} for user ${userString} with:\n Input: ${cleanInput}\n Error: ` +
JSON.stringify(error),
{
functionName,
error,
params,
user: userString,
}
);
if (req.config.logLevels.cloudFunctionError !== 'silent') {
const cleanInput = logger.truncateLogMessage(JSON.stringify(params));
logger[req.config.logLevels.cloudFunctionError](
`Failed running cloud function ${functionName} for user ${userString} with:\n Input: ${cleanInput}\n Error: ` +
JSON.stringify(error),
{
functionName,
error,
params,
user: userString,
}
);
}
reject(error);
} catch (e) {
reject(e);
Expand Down
9 changes: 9 additions & 0 deletions src/triggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ function userIdForLog(auth) {
}

function logTriggerAfterHook(triggerType, className, input, auth, logLevel) {
if (logLevel === 'silent') {
return;
}
const cleanInput = logger.truncateLogMessage(JSON.stringify(input));
logger[logLevel](
`${triggerType} triggered for ${className} for user ${userIdForLog(
Expand All @@ -396,6 +399,9 @@ function logTriggerAfterHook(triggerType, className, input, auth, logLevel) {
}

function logTriggerSuccessBeforeHook(triggerType, className, input, result, auth, logLevel) {
if (logLevel === 'silent') {
return;
}
const cleanInput = logger.truncateLogMessage(JSON.stringify(input));
const cleanResult = logger.truncateLogMessage(JSON.stringify(result));
logger[logLevel](
Expand All @@ -411,6 +417,9 @@ function logTriggerSuccessBeforeHook(triggerType, className, input, result, auth
}

function logTriggerErrorBeforeHook(triggerType, className, input, auth, error, logLevel) {
if (logLevel === 'silent') {
return;
}
const cleanInput = logger.truncateLogMessage(JSON.stringify(input));
logger[logLevel](
`${triggerType} failed for ${className} for user ${userIdForLog(
Expand Down
Loading