diff --git a/spec/Deprecator.spec.js b/spec/Deprecator.spec.js index 993d18682f..16373385ff 100644 --- a/spec/Deprecator.spec.js +++ b/spec/Deprecator.spec.js @@ -35,6 +35,26 @@ describe('Deprecator', () => { expect(logSpy).not.toHaveBeenCalled(); }); + it('does not log deprecation for new default if logLevels.deprecation is silent', async () => { + deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; + + spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); + const logger = require('../lib/logger').logger; + const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); + await reconfigureServer({ logLevels: { deprecation: 'silent' } }); + expect(logSpy).not.toHaveBeenCalled(); + }); + + it('does not log deprecation for new default if logLevels.deprecation_exampleKey is silent', async () => { + deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; + + spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); + const logger = require('../lib/logger').logger; + const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); + await reconfigureServer({ logLevels: { deprecation_exampleKey: 'silent' } }); + expect(logSpy).not.toHaveBeenCalled(); + }); + it('logs runtime deprecation', async () => { const logger = require('../lib/logger').logger; const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); diff --git a/src/Deprecator/Deprecator.js b/src/Deprecator/Deprecator.js index 626f397a36..58c2e704b8 100644 --- a/src/Deprecator/Deprecator.js +++ b/src/Deprecator/Deprecator.js @@ -23,8 +23,11 @@ class Deprecator { const changeNewKey = deprecation.changeNewKey; // If default will change, only throw a warning if option is not set - if (changeNewDefault != null && Utils.getNestedProperty(options, optionKey) == null) { - Deprecator._logOption({ optionKey, changeNewDefault, solution }); + if ( + changeNewDefault != null && + Utils.getNestedProperty(options, optionKey) == null + ) { + Deprecator._logOption({ optionKey, changeNewDefault, solution, options }); } // If key will be removed or renamed, only throw a warning if option is set; @@ -32,7 +35,7 @@ class Deprecator { const resolvedValue = deprecation.resolvedValue; const optionValue = Utils.getNestedProperty(options, optionKey); if (changeNewKey != null && optionValue != null && optionValue !== resolvedValue) { - Deprecator._logOption({ optionKey, changeNewKey, solution }); + Deprecator._logOption({ optionKey, changeNewKey, solution, options }); } } } @@ -103,8 +106,9 @@ class Deprecator { * message must not include the warning that the parameter is deprecated, that is * automatically added to the message. It should only contain the instruction on how * to resolve this warning. + * @param {Object} [options] The Parse Server options, used to determine log levels. */ - static _logOption({ optionKey, envKey, changeNewKey, changeNewDefault, solution }) { + static _logOption({ optionKey, envKey, changeNewKey, changeNewDefault, solution, options }) { const type = optionKey ? 'option' : 'environment key'; const key = optionKey ? optionKey : envKey; const keyAction = @@ -114,6 +118,19 @@ class Deprecator { ? `renamed to '${changeNewKey}'` : `removed`; + // Determine the log level + const logLevels = (options && options.logLevels) || {}; + let level = 'warn'; + if (key && logLevels[`deprecation_${key}`]) { + level = logLevels[`deprecation_${key}`]; + } else if (logLevels['deprecation']) { + level = logLevels['deprecation']; + } + + if (level === 'silent') { + return; + } + // Compose message let output = `DeprecationWarning: The Parse Server ${type} '${key}' `; output += changeNewKey != null ? `is deprecated and will be ${keyAction} in a future version.` : ''; @@ -121,7 +138,12 @@ class Deprecator { ? `default will change to '${changeNewDefault}' in a future version.` : ''; output += solution ? ` ${solution}` : ''; - logger.warn(output); + + if (typeof logger[level] === 'function') { + logger[level](output); + } else { + logger.warn(output); + } } } diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 64c54561eb..986310e770 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -1599,4 +1599,9 @@ module.exports.LogLevels = { help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on success. Default is `info`. See [LogLevel](LogLevel.html) for available values.', default: 'info', }, + deprecation: { + env: 'PARSE_SERVER_LOG_LEVELS_DEPRECATION', + help: 'Log level used for deprecation warnings. Default is `warn`. Set to `silent` to suppress all deprecations. You can also specify per-deprecation log levels by appending the option key, e.g., `deprecation_fileUpload`. See [LogLevel](LogLevel.html) for available values.', + default: 'warn', + }, }; diff --git a/src/Options/docs.js b/src/Options/docs.js index 564a9b1718..bc6f9fc69e 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -13,6 +13,7 @@ /** * @interface ParseServerOptions * @property {AccountLockoutOptions} accountLockout The account lockout policy for failed login attempts.

Note: Setting a user's ACL to an empty object `{}` via master key is a separate mechanism that only prevents new logins; it does not invalidate existing session tokens. To immediately revoke a user's access, destroy their sessions via master key in addition to setting the ACL. + * @property {Boolean} acknowledgeFutureDefaults Set to `true` to acknowledge and suppress warnings about Parse Server options whose default values will change in a future version. * @property {Boolean} allowAggregationForReadOnlyMasterKey Whether the `readOnlyMasterKey` is allowed to run aggregation pipelines via the aggregate endpoint. An aggregation pipeline can contain write-capable stages (for example MongoDB `$out` and `$merge`), so allowing aggregation effectively gives the read-only master key a way to perform writes, contrary to its read-only intent. If `true` (default), the read-only master key can run aggregation pipelines. If `false`, the read-only master key cannot run aggregation pipelines at all. Note that the `readOnlyMasterKey` is a secret key for internal server-side use only and must never be distributed; this option is an additional safeguard, not a substitute for keeping the key confidential. Defaults to `true`. * @property {Boolean} allowClientClassCreation Enable (or disable) client class creation, defaults to false * @property {Boolean} allowCustomObjectId Enable (or disable) custom objectId diff --git a/src/Options/index.js b/src/Options/index.js index 42f0ffb3b8..baad39efc4 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -954,4 +954,8 @@ export interface LogLevels { :DEFAULT: info */ signupUsernameTaken: ?string; + /* Log level used for deprecation warnings. Default is `warn`. Set to `silent` to suppress all deprecations. You can also specify per-deprecation log levels by appending the option key, e.g., `deprecation_fileUpload`. See [LogLevel](LogLevel.html) for available values. + :DEFAULT: warn + */ + deprecation: ?string; }