diff --git a/cmp/grid/impl/Utils.ts b/cmp/grid/impl/Utils.ts index a5aceeee91..f8396a3d75 100644 --- a/cmp/grid/impl/Utils.ts +++ b/cmp/grid/impl/Utils.ts @@ -19,7 +19,7 @@ export function managedRenderer( try { return fn.apply(null, arguments); } catch (e) { - console.warn(`Renderer for '${identifier}' has thrown an error.`, e); + console.warn(`Renderer for '${identifier}' has thrown an error`, e); return '#ERROR'; } } as unknown as T; diff --git a/cmp/relativetimestamp/RelativeTimestamp.ts b/cmp/relativetimestamp/RelativeTimestamp.ts index 816fd414e1..41007f4488 100644 --- a/cmp/relativetimestamp/RelativeTimestamp.ts +++ b/cmp/relativetimestamp/RelativeTimestamp.ts @@ -20,7 +20,7 @@ import {fmtCompactDate, fmtDateTime} from '@xh/hoist/format'; import {action, computed, makeObservable, observable} from '@xh/hoist/mobx'; import {Timer} from '@xh/hoist/utils/async'; import {DAYS, HOURS, LocalDate, SECONDS} from '@xh/hoist/utils/datetime'; -import {withDefault} from '@xh/hoist/utils/js'; +import {logWarn, withDefault} from '@xh/hoist/utils/js'; interface RelativeTimestampProps extends HoistProps, BoxProps { /** @@ -231,7 +231,10 @@ function doFormat(timestamp: Date | number, opts: RelativeTimestampOptions): str // 1) Degenerate cases if (isFuture && !allowFuture) { - console.warn(`Unexpected future date provided for timestamp: ${elapsed}ms in the future.`); + logWarn( + `Unexpected future date provided for timestamp: ${elapsed}ms in the future.`, + RelativeTimestamp + ); return '[????]'; } diff --git a/cmp/zoneGrid/ZoneGridModel.ts b/cmp/zoneGrid/ZoneGridModel.ts index 3ea65aec61..f3e31d5557 100644 --- a/cmp/zoneGrid/ZoneGridModel.ts +++ b/cmp/zoneGrid/ZoneGridModel.ts @@ -620,7 +620,7 @@ export class ZoneGridModel extends HoistModel { ret[zone] = this.parseZoneMapping(zone, rawMapping); } catch (e) { if (strict) throw e; - console.warn(e.message); + this.logWarn(e.message); ret[zone] = this._defaultState.mappings[zone]; } }); diff --git a/core/HoistBase.ts b/core/HoistBase.ts index 3fa6352906..b826b1ad20 100644 --- a/core/HoistBase.ts +++ b/core/HoistBase.ts @@ -267,9 +267,10 @@ export abstract class HoistBase { run: data => provider.write(data) }); } catch (e) { - console.error( + this.logError( `Failed to configure Persistence for '${property}'. Be sure to fully specify ` + - `'persistWith' on this object or in the method call.` + `'persistWith' on this object or in the method call`, + e ); } } diff --git a/core/HoistBaseDecorators.ts b/core/HoistBaseDecorators.ts index d55f48b064..c93f03ffad 100644 --- a/core/HoistBaseDecorators.ts +++ b/core/HoistBaseDecorators.ts @@ -6,7 +6,7 @@ */ import {cloneDeep, isUndefined} from 'lodash'; import {wait} from '../promise'; -import {throwIf} from '../utils/js'; +import {logError, throwIf} from '../utils/js'; import {HoistBaseClass, PersistenceProvider, PersistOptions} from './'; /** @@ -66,9 +66,10 @@ function createPersistDescriptor( '@persist decorator should be applied to an instance of HoistBase' ); if (descriptor.get || descriptor.set) { - console.error( + logError( `Error defining ${property} : @persist or @persistWith should be defined closest ` + - `to property, and after mobx annotation e.g. '@bindable @persist ${property}'` + `to property, and after mobx annotation e.g. '@bindable @persist ${property}'`, + target ); return descriptor; } @@ -89,9 +90,13 @@ function createPersistDescriptor( }); }); } catch (e) { - console.error( - `Failed to configure Persistence for '${property}'. Be sure to fully specify ` + - `'persistWith' on this object or annotation.` + logError( + [ + `Failed to configure Persistence for '${property}'. Be sure to fully specify ` + + `'persistWith' on this object or annotation`, + e + ], + target ); } diff --git a/core/HoistComponent.ts b/core/HoistComponent.ts index 8a3899527e..2795de8dd2 100644 --- a/core/HoistComponent.ts +++ b/core/HoistComponent.ts @@ -282,9 +282,7 @@ function wrapWithModel(render: RenderFn, cfg: Config): RenderFn { if (!model && !spec.optional && spec instanceof UsesSpec) { console.error(` Failed to find model with selector '${formatSelector(spec.selector)}' for - component '${ - cfg.displayName - }'. Ensure the proper model is available via context, or + component '${cfg.displayName}'. Ensure the proper model is available via context, or specify explicitly using the 'model' prop. `); return cmpErrDisplay({...getLayoutProps(props), item: 'No model found'}); diff --git a/core/exception/ExceptionHandler.ts b/core/exception/ExceptionHandler.ts index abfe2707ee..bd9285a43b 100644 --- a/core/exception/ExceptionHandler.ts +++ b/core/exception/ExceptionHandler.ts @@ -6,7 +6,7 @@ */ import {Exception} from './Exception'; import {fragment, span} from '@xh/hoist/cmp/layout'; -import {stripTags} from '@xh/hoist/utils/js'; +import {logError, logWarn, stripTags} from '@xh/hoist/utils/js'; import {Icon} from '@xh/hoist/icon'; import {forOwn, has, isArray, isNil, isObject, omitBy, pick, set} from 'lodash'; import {HoistException, PlainObject, XH} from '../'; @@ -189,7 +189,7 @@ export class ExceptionHandler { username = XH.getUsername(); if (!username) { - console.warn('Error report cannot be submitted to UI server - user unknown'); + logWarn('Error report cannot be submitted to UI server - user unknown', this); return false; } @@ -206,7 +206,7 @@ export class ExceptionHandler { }); return true; } catch (e) { - console.error('Exception while submitting error report to UI server', e); + logError(['Exception while submitting error report to UI server', e], this); return false; } } @@ -261,7 +261,7 @@ export class ExceptionHandler { return stripTags(JSON.stringify(ret, null, 4)); } catch (e) { const message = 'Failed to serialize error'; - console.error(message, exception, e); + logError([message, exception, e], this); return JSON.stringify({message}, null, 4); } } diff --git a/core/load/LoadSupport.ts b/core/load/LoadSupport.ts index 4fcc6df0a2..20f02b4cdf 100644 --- a/core/load/LoadSupport.ts +++ b/core/load/LoadSupport.ts @@ -7,8 +7,8 @@ import {HoistBase, managed, PlainObject, RefreshContextModel, TaskObserver} from '../'; import {LoadSpec, Loadable} from './'; import {makeObservable, observable, runInAction} from '@xh/hoist/mobx'; -import {throwIf} from '@xh/hoist/utils/js'; -import {isPlainObject} from 'lodash'; +import {logDebug, logError, throwIf} from '@xh/hoist/utils/js'; +import {isPlainObject, pull} from 'lodash'; /** * Provides support for objects that participate in Hoist's loading/refresh lifecycle. @@ -98,18 +98,17 @@ export class LoadSupport extends HoistBase implements Loadable { if (target instanceof RefreshContextModel) return; const elapsed = this.lastLoadCompleted.getTime() - this.lastLoadRequested.getTime(), - msg = `[${target.constructor.name}] | ${loadSpec.typeDisplay} | ${ - exception ? 'failed | ' : '' - }${elapsed}ms`; + status = exception ? 'failed' : null, + msg = pull([loadSpec.typeDisplay, status, `${elapsed}ms`, exception], null); if (exception) { if (exception.isRoutine) { - console.debug(msg, exception); + logDebug(msg, target); } else { - console.error(msg, exception); + logError(msg, target); } } else { - console.debug(msg); + logDebug(msg, target); } }); } diff --git a/desktop/cmp/button/ColChooserButton.ts b/desktop/cmp/button/ColChooserButton.ts index f5a10da885..132756031a 100644 --- a/desktop/cmp/button/ColChooserButton.ts +++ b/desktop/cmp/button/ColChooserButton.ts @@ -12,7 +12,7 @@ import {ColChooserModel} from '@xh/hoist/desktop/cmp/grid/impl/colchooser/ColCho import '@xh/hoist/desktop/register'; import {Icon} from '@xh/hoist/icon'; import {popover, Position} from '@xh/hoist/kit/blueprint'; -import {stopPropagation, withDefault} from '@xh/hoist/utils/js'; +import {logError, stopPropagation, withDefault} from '@xh/hoist/utils/js'; import {button, ButtonProps} from './Button'; export interface ColChooserButtonProps extends ButtonProps { @@ -40,15 +40,17 @@ export const [ColChooserButton, colChooserButton] = hoistCmp.withFactory({ model = model ?? (formModel && field ? formModel.fields[field] : null); if (!model) { - console.warn(`Unable to bind FormField to field "${field}" on backing FormModel`); + logWarn(`Unable to bind FormField to field "${field}" on backing FormModel`, FormField); } // Model related props diff --git a/desktop/cmp/grid/editors/BooleanEditor.ts b/desktop/cmp/grid/editors/BooleanEditor.ts index 20bb6a5ace..de29c8ee6c 100644 --- a/desktop/cmp/grid/editors/BooleanEditor.ts +++ b/desktop/cmp/grid/editors/BooleanEditor.ts @@ -12,6 +12,7 @@ import {useImperativeHandle} from 'react'; import {EditorProps} from './EditorProps'; import './Editors.scss'; import {useInlineEditorModel} from './impl/InlineEditorModel'; +import {logWarn} from '@xh/hoist/utils/js'; export interface BooleanEditorProps extends EditorProps { /** @@ -35,7 +36,10 @@ export const [BooleanEditor, booleanEditor] = hoistCmp.withFactory({ render({model, className, testId, ...props}, ref) { if (!Highcharts) { - console.error( + logError( 'Highcharts has not been imported in to this application. Please import and ' + - 'register in Bootstrap.js. See Toolbox for an example.' + 'register in Bootstrap.js. See Toolbox for an example.', + TreeMap ); return 'Highcharts not available'; } diff --git a/inspector/instances/InstancesModel.ts b/inspector/instances/InstancesModel.ts index db06674e58..c7ac5f9fd5 100644 --- a/inspector/instances/InstancesModel.ts +++ b/inspector/instances/InstancesModel.ts @@ -369,8 +369,8 @@ export class InstancesModel extends HoistModel { const displayGroup = inst.isHoistService ? 'Services' : inst.isStore - ? 'Stores' - : 'Models'; + ? 'Stores' + : 'Models'; data.push({...inst, displayGroup}); }); @@ -464,8 +464,8 @@ export class InstancesModel extends HoistModel { isGetter && !isLoadedGetter ? 'get(?)' : isProxy - ? 'Proxy' - : v?.constructor?.name ?? typeof v; + ? 'Proxy' + : v?.constructor?.name ?? typeof v; return { id: `${xhId}-${property}${fromWatchlistItem ? '-wl' : ''}`, @@ -479,8 +479,8 @@ export class InstancesModel extends HoistModel { isHoistModel || isHoistService || isStore ? v.xhId : isProxy - ? '[cannot render]' - : v, + ? '[cannot render]' + : v, valueType, isOwnProperty, isObservable, diff --git a/mobile/cmp/button/ColAutosizeButton.ts b/mobile/cmp/button/ColAutosizeButton.ts index 97e13949b2..444aea0cfd 100644 --- a/mobile/cmp/button/ColAutosizeButton.ts +++ b/mobile/cmp/button/ColAutosizeButton.ts @@ -10,7 +10,7 @@ import {hoistCmp, useContextModel} from '@xh/hoist/core'; import {Icon} from '@xh/hoist/icon'; import {button, ButtonProps} from '@xh/hoist/mobile/cmp/button'; import '@xh/hoist/mobile/register'; -import {withDefault} from '@xh/hoist/utils/js'; +import {logError, withDefault} from '@xh/hoist/utils/js'; export interface ColAutosizeButtonProps extends ButtonProps { /** GridModel of the grid for which this button should autosize columns. */ @@ -31,8 +31,9 @@ export const [ColAutosizeButton, colAutosizeButton] = hoistCmp.withFactory { - console.error(e); + this.logError(e); throw e; }); }; diff --git a/mobile/cmp/panel/Panel.ts b/mobile/cmp/panel/Panel.ts index 6d70b8985c..b869bb3f4b 100644 --- a/mobile/cmp/panel/Panel.ts +++ b/mobile/cmp/panel/Panel.ts @@ -24,6 +24,7 @@ import {omitBy} from 'lodash'; import {isValidElement, ReactNode, ReactElement} from 'react'; import {panelHeader} from './impl/PanelHeader'; import './Panel.scss'; +import {logWarn} from '@xh/hoist/utils/js'; export interface PanelProps extends HoistProps, Omit { /** A toolbar to be docked at the bottom of the panel. */ @@ -141,8 +142,9 @@ function parseLoadDecorator(prop, name, contextModel) { if (prop === 'onLoad') { const loadModel = contextModel?.loadModel; if (!loadModel) { - console.warn( - `Cannot use 'onLoad' for '${name}'. Context model does not implement loading.` + logWarn( + `Cannot use 'onLoad' for '${name}'. Context model does not implement loading.`, + Panel ); return null; } diff --git a/svc/ChangelogService.ts b/svc/ChangelogService.ts index 13701699d9..6d464d6d69 100644 --- a/svc/ChangelogService.ts +++ b/svc/ChangelogService.ts @@ -96,12 +96,12 @@ export class ChangelogService extends HoistService { const {latestAvailableVersion, LAST_READ_PREF_KEY} = this; if (includes(latestAvailableVersion, 'SNAPSHOT')) { - console.warn('Unable to mark changelog as read when latest version is SNAPSHOT.'); + this.logWarn('Unable to mark changelog as read when latest version is SNAPSHOT.'); return; } if (!latestAvailableVersion || !XH.prefService.hasKey(LAST_READ_PREF_KEY)) { - console.warn( + this.logWarn( 'Unable to mark changelog as read - latest version or required pref not found.' ); return; @@ -147,7 +147,7 @@ export class ChangelogService extends HoistService { return versions; } catch (e) { - console.error( + this.logError( 'Error parsing changelog JSON into versions - changelog will not be available', e ); diff --git a/svc/EnvironmentService.ts b/svc/EnvironmentService.ts index 4cfdf9d46a..85ebeaf80b 100644 --- a/svc/EnvironmentService.ts +++ b/svc/EnvironmentService.ts @@ -136,7 +136,7 @@ export class EnvironmentService extends HoistService { // prompted to refresh. const clientVersion = this.get('clientVersion'); if (appVersion !== clientVersion) { - console.warn( + this.logWarn( `Version mismatch detected between client and server - ${clientVersion} vs ${appVersion}` ); } diff --git a/svc/GridExportService.ts b/svc/GridExportService.ts index cc7ec47546..8355f6301a 100644 --- a/svc/GridExportService.ts +++ b/svc/GridExportService.ts @@ -327,13 +327,8 @@ export class GridExportService extends HoistService { : it.exportName; if (!isString(ret)) { - console.warn( - 'Tried to export column ' + - it.colId + - ' with an invalid "exportName", ' + - 'probably caused by setting "headerName" to a React element. Please specify an ' + - 'appropriate "exportName". Defaulting to ' + - it.colId + this.logWarn( + `Tried to export column '${it.colId}' with an invalid "exportName", probably caused by setting "headerName" to a React element. Please specify an appropriate "exportName". Defaulting to '${it.colId}'` ); ret = it.colId; } @@ -345,7 +340,7 @@ export class GridExportService extends HoistService { const headerCounts = countBy(headers.map(it => it.toLowerCase())), dupeHeaders = keys(pickBy(headerCounts, it => it > 1)); if (type === 'excelTable' && !isEmpty(dupeHeaders)) { - console.warn( + this.logWarn( 'Excel tables require unique headers on each column. Consider using the "exportName" property to ensure unique headers. Duplicate headers: ', dupeHeaders ); diff --git a/svc/IdentityService.ts b/svc/IdentityService.ts index 70ce07ca84..752c76757e 100644 --- a/svc/IdentityService.ts +++ b/svc/IdentityService.ts @@ -62,7 +62,7 @@ export class IdentityService extends HoistService { try { await XH.appModel?.logoutAsync(); } catch (e) { - console.error('Error calling XH.appModel.logoutAsync()', e); + this.logError('Error calling XH.appModel.logoutAsync()', e); } return XH.fetchJson({url: 'xh/logout'}) .then(() => XH.reloadApp()) diff --git a/svc/TrackService.ts b/svc/TrackService.ts index 3034f40fb1..73c59d3564 100644 --- a/svc/TrackService.ts +++ b/svc/TrackService.ts @@ -45,19 +45,13 @@ export class TrackService extends HoistService { // Short-circuit if disabled... if (!this.enabled) { - console.debug( - '[TrackService] | Activity tracking disabled - activity will not be tracked.', - options - ); + this.logDebug('Activity tracking disabled - activity will not be tracked.', options); return; } // ...or invalid request (with warning for developer)... if (!options.message) { - console.warn( - '[TrackService] | Required message not provided - activity will not be tracked.', - options - ); + this.logWarn('Required message not provided - activity will not be tracked.', options); return; } @@ -113,7 +107,7 @@ export class TrackService extends HoistService { await XH.fetchJson({url: 'xh/track', params}); } catch (e) { - console.error(`[TrackService] | Failed to persist track log`, options, e); + this.logError('Failed to persist track log', options, e); } } } diff --git a/utils/async/Timer.ts b/utils/async/Timer.ts index 3a1f90b6c4..b593be22ed 100644 --- a/utils/async/Timer.ts +++ b/utils/async/Timer.ts @@ -7,7 +7,7 @@ import {XH} from '@xh/hoist/core'; import {wait} from '@xh/hoist/promise'; import {MILLISECONDS, MINUTES, olderThan} from '@xh/hoist/utils/datetime'; -import {logWarn, throwIf} from '@xh/hoist/utils/js'; +import {logError, logWarn, throwIf} from '@xh/hoist/utils/js'; import {isBoolean, isFinite, isFunction, isNil, isString, pull} from 'lodash'; /** @@ -122,7 +122,7 @@ export class Timer { try { await (this.internalRunFn() as any).timeout(this.timeoutMs); } catch (e) { - console.error('Error executing timer:', e); + logError(['Error executing timer', e], this); } this.isRunning = false; this.lastRun = new Date();