From efd3e0fd2bafd7e77ca192b5a9f54c460cbd8f79 Mon Sep 17 00:00:00 2001 From: Sean Perkins <13732623+sean-perkins@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:15:02 -0600 Subject: [PATCH] feat(config): add logLevel option to suppress ionic warnings and errors (#30015) resolves #29814 --------- - Developers can assign a value to `logLevel` in the Ionic config to control the log level that Ionic Framework will produce logs for. - `OFF` will completely disable all warnings and errors from Ionic - `WARN` will log warnings and errors - `ERROR` will log only errors - Default behavior is that developers receive both Ionic warnings and errors - Configuration only applies to usages of `printIonWarning` and `printIonError` --------- Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> --- core/src/utils/config.ts | 10 ++ core/src/utils/logging/index.ts | 22 +++- core/src/utils/logging/test/logging.spec.ts | 114 ++++++++++++++++++++ 3 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 core/src/utils/logging/test/logging.spec.ts diff --git a/core/src/utils/config.ts b/core/src/utils/config.ts index e38d43beb4e..6b66417f7fc 100644 --- a/core/src/utils/config.ts +++ b/core/src/utils/config.ts @@ -2,6 +2,7 @@ import type { SpinnerTypes } from '../components/spinner/spinner-configs'; import type { TabButtonLayout } from '../components/tab-bar/tab-bar-interface'; import type { AnimationBuilder, Mode } from '../interface'; +import type { LogLevel } from './logging'; import type { PlatformConfig } from './platform'; export interface IonicConfig { @@ -220,6 +221,15 @@ export interface IonicConfig { */ experimentalCloseWatcher?: boolean; + /** + * Configures the logging level for Ionic Framework: + * + * - `'OFF'`: No errors or warnings are logged. + * - `'ERROR'`: Logs only errors. + * - `'WARN'`: Logs errors and warnings. + */ + logLevel?: LogLevel; + // PRIVATE configs keyboardHeight?: number; inputShims?: boolean; diff --git a/core/src/utils/logging/index.ts b/core/src/utils/logging/index.ts index ee4234cb6a5..4bf58ce6297 100644 --- a/core/src/utils/logging/index.ts +++ b/core/src/utils/logging/index.ts @@ -1,3 +1,11 @@ +import { config } from '@global/config'; + +export const enum LogLevel { + OFF = 'OFF', + ERROR = 'ERROR', + WARN = 'WARN', +} + /** * Logs a warning to the console with an Ionic prefix * to indicate the library that is warning the developer. @@ -5,18 +13,24 @@ * @param message - The string message to be logged to the console. */ export const printIonWarning = (message: string, ...params: any[]) => { - return console.warn(`[Ionic Warning]: ${message}`, ...params); + const logLevel = config.get('logLevel', LogLevel.WARN); + if ([LogLevel.WARN].includes(logLevel)) { + return console.warn(`[Ionic Warning]: ${message}`, ...params); + } }; -/* +/** * Logs an error to the console with an Ionic prefix * to indicate the library that is warning the developer. * * @param message - The string message to be logged to the console. * @param params - Additional arguments to supply to the console.error. */ -export const printIonError = (message: string, ...params: any) => { - return console.error(`[Ionic Error]: ${message}`, ...params); +export const printIonError = (message: string, ...params: any[]) => { + const logLevel = config.get('logLevel', LogLevel.ERROR); + if ([LogLevel.ERROR, LogLevel.WARN].includes(logLevel)) { + return console.error(`[Ionic Error]: ${message}`, ...params); + } }; /** diff --git a/core/src/utils/logging/test/logging.spec.ts b/core/src/utils/logging/test/logging.spec.ts new file mode 100644 index 00000000000..e0f1bdcb923 --- /dev/null +++ b/core/src/utils/logging/test/logging.spec.ts @@ -0,0 +1,114 @@ +import { config } from '@global/config'; +import { LogLevel } from '../index'; + +import { printIonError, printIonWarning } from '../index'; + +describe('Logging', () => { + describe('#printIonWarning', () => { + let consoleWarnSpy: jest.SpyInstance; + + beforeEach(() => { + consoleWarnSpy = jest.spyOn(console, 'warn'); + // Suppress console.warn output from polluting the test output + consoleWarnSpy.mockImplementation(() => {}); + }); + + afterEach(() => { + consoleWarnSpy.mockRestore(); + }); + + describe('when the logLevel configuration is not set', () => { + it('logs a warning to the console', () => { + config.set('logLevel', undefined); + + printIonWarning('This is a warning message'); + + expect(consoleWarnSpy).toHaveBeenCalledWith('[Ionic Warning]: This is a warning message'); + }); + }); + + describe("when the logLevel configuration is set to 'WARN'", () => { + it('logs a warning to the console', () => { + config.set('logLevel', LogLevel.WARN); + + printIonWarning('This is a warning message'); + + expect(consoleWarnSpy).toHaveBeenCalledWith('[Ionic Warning]: This is a warning message'); + }); + }); + + describe("when the logLevel configuration is set to 'ERROR'", () => { + it('does not log a warning to the console', () => { + config.set('logLevel', LogLevel.ERROR); + + printIonWarning('This is a warning message'); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + }); + + describe("when the logLevel configuration is set to 'OFF'", () => { + it('does not log a warning to the console', () => { + config.set('logLevel', LogLevel.OFF); + + printIonWarning('This is a warning message'); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + }); + }); + + describe('#printIonError', () => { + let consoleErrorSpy: jest.SpyInstance; + + beforeEach(() => { + consoleErrorSpy = jest.spyOn(console, 'error'); + // Suppress console.error output from polluting the test output + consoleErrorSpy.mockImplementation(() => {}); + }); + + afterEach(() => { + consoleErrorSpy.mockRestore(); + }); + + describe('when the logLevel configuration is not set', () => { + it('logs an error to the console', () => { + config.set('logLevel', undefined); + + printIonError('This is an error message'); + + expect(consoleErrorSpy).toHaveBeenCalledWith('[Ionic Error]: This is an error message'); + }); + }); + + describe("when the logLevel configuration is set to 'ERROR'", () => { + it('logs an error to the console', () => { + config.set('logLevel', LogLevel.ERROR); + + printIonError('This is an error message'); + + expect(consoleErrorSpy).toHaveBeenCalledWith('[Ionic Error]: This is an error message'); + }); + }); + + describe("when the logLevel configuration is set to 'WARN'", () => { + it('logs an error to the console', () => { + config.set('logLevel', LogLevel.WARN); + + printIonError('This is an error message'); + + expect(consoleErrorSpy).toHaveBeenCalledWith('[Ionic Error]: This is an error message'); + }); + }); + + describe("when the logLevel configuration is set to 'OFF'", () => { + it('does not log an error to the console', () => { + config.set('logLevel', LogLevel.OFF); + + printIonError('This is an error message'); + + expect(consoleErrorSpy).not.toHaveBeenCalled(); + }); + }); + }); +});