From 14d6454eafaa2ccb50e133c2945c9558052ea27e Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Thu, 25 Feb 2021 18:29:28 -0300 Subject: [PATCH] feat(log): add new log service --- extensions/debugging/src/DebugReportModal.css | 12 +++ extensions/debugging/src/DebugReportModal.js | 14 +++ extensions/debugging/src/commandsModule.js | 4 + .../src/DicomMicroscopyViewport.js | 12 ++- extensions/dicom-segmentation/src/index.js | 3 +- extensions/vtk/src/OHIFVTKViewport.js | 13 ++- extensions/vtk/src/commandsModule.js | 12 ++- extensions/vtk/src/index.js | 3 +- platform/core/src/classes/HotkeysManager.js | 9 +- platform/core/src/index.js | 3 + .../core/src/services/LoggerService/index.js | 67 +++++++++++++ platform/core/src/services/index.js | 2 + .../ui/src/contextProviders/LoggerProvider.js | 94 +++++++++++++++++++ platform/ui/src/contextProviders/index.js | 5 + platform/ui/src/index.js | 10 +- platform/viewer/src/App.js | 49 ++++++---- 16 files changed, 276 insertions(+), 36 deletions(-) create mode 100644 platform/core/src/services/LoggerService/index.js create mode 100644 platform/ui/src/contextProviders/LoggerProvider.js diff --git a/extensions/debugging/src/DebugReportModal.css b/extensions/debugging/src/DebugReportModal.css index ea83312f7..f61e83327 100644 --- a/extensions/debugging/src/DebugReportModal.css +++ b/extensions/debugging/src/DebugReportModal.css @@ -12,3 +12,15 @@ display: flex; flex-direction: row; } + +.debug-report-modal-container .errors { + margin-top: 10px; +} + +.debug-report-modal-container .errors-container { + border: 1px solid var(--active-color); + border-radius: 5px; + padding: 10px; + overflow: scroll; + max-height: 300px; +} diff --git a/extensions/debugging/src/DebugReportModal.js b/extensions/debugging/src/DebugReportModal.js index edc722335..5926bb291 100644 --- a/extensions/debugging/src/DebugReportModal.js +++ b/extensions/debugging/src/DebugReportModal.js @@ -10,6 +10,7 @@ const DubugReportModal = ({ extensionManager, mailTo, debugModalMessage, + errors = [], }) => { const copyDebugDataToClipboard = () => { const body = getEmailBody(); @@ -131,6 +132,19 @@ const DubugReportModal = ({ {getLayout(viewports)} +
+

Errors ({errors.length})

+
+ {errors.map(error => { + return ( +
+
Message: {error.message}
+ {error.error &&
Stack: {error.error.stack}
} +
+ ); + })} +
+
); }; diff --git a/extensions/debugging/src/commandsModule.js b/extensions/debugging/src/commandsModule.js index 4969a07d0..a6ad0498f 100644 --- a/extensions/debugging/src/commandsModule.js +++ b/extensions/debugging/src/commandsModule.js @@ -1,4 +1,6 @@ import OHIF from '@ohif/core'; +import { ToolbarButton, useLogger } from '@ohif/ui'; + import { save, upload, @@ -95,6 +97,7 @@ export function getCommands(context, servicesManager, extensionManager) { const { UIModalService } = servicesManager.services; const WrappedDebugReportModal = function() { + const { state } = useLogger(); return ( ); }; diff --git a/extensions/dicom-microscopy/src/DicomMicroscopyViewport.js b/extensions/dicom-microscopy/src/DicomMicroscopyViewport.js index 37b539ea5..8e047007f 100644 --- a/extensions/dicom-microscopy/src/DicomMicroscopyViewport.js +++ b/extensions/dicom-microscopy/src/DicomMicroscopyViewport.js @@ -70,12 +70,18 @@ class DicomMicroscopyViewport extends Component { }); } catch (error) { console.error('[Microscopy Viewer] Failed to load:', error); - const { UINotificationService } = this.props.servicesManager.services; + const { + UINotificationService, + LoggerService, + } = this.props.servicesManager.services; if (UINotificationService) { + const message = + 'Failed to load viewport. Please check that you have hardware acceleration enabled.'; + LoggerService.error({ error, message }); UINotificationService.show({ + autoClose: false, title: 'Microscopy Viewport', - message: - 'Failed to load viewport. Please check that you have hardware acceleration enabled.', + message, type: 'error', }); } diff --git a/extensions/dicom-segmentation/src/index.js b/extensions/dicom-segmentation/src/index.js index d6c171490..33123b23e 100644 --- a/extensions/dicom-segmentation/src/index.js +++ b/extensions/dicom-segmentation/src/index.js @@ -25,12 +25,13 @@ export default { return toolbarModule; }, getPanelModule({ commandsManager, api, servicesManager }) { - const { UINotificationService } = servicesManager.services; + const { UINotificationService, LoggerService } = servicesManager.services; const ExtendedSegmentationPanel = props => { const { activeContexts } = api.hooks.useAppContext(); const onDisplaySetLoadFailureHandler = error => { + LoggerService.error({ error, message: error.message }); UINotificationService.show({ title: 'DICOM Segmentation Loader', message: error.message, diff --git a/extensions/vtk/src/OHIFVTKViewport.js b/extensions/vtk/src/OHIFVTKViewport.js index bac284a4e..aa11b5c32 100644 --- a/extensions/vtk/src/OHIFVTKViewport.js +++ b/extensions/vtk/src/OHIFVTKViewport.js @@ -360,12 +360,15 @@ class OHIFVTKViewport extends Component { } catch (error) { const errorTitle = 'Failed to load 2D MPR'; console.error(errorTitle, error); - const { UINotificationService } = this.props.servicesManager.services; + const { + UINotificationService, + LoggerService, + } = this.props.servicesManager.services; if (this.props.viewportIndex === 0) { const message = error.message.includes('buffer') ? 'Dataset is too big to display in MPR' : error.message; - console.error(errorTitle, error); + LoggerService.error({ error, message }); UINotificationService.show({ title: errorTitle, message, @@ -428,11 +431,15 @@ class OHIFVTKViewport extends Component { }; const onPixelDataInsertedErrorCallback = error => { - const { UINotificationService } = this.props.servicesManager.services; + const { + UINotificationService, + LoggerService, + } = this.props.servicesManager.services; if (!this.hasError) { if (this.props.viewportIndex === 0) { // Only show the notification from one viewport 1 in MPR2D. + LoggerService.error({ error, message: error.message }); UINotificationService.show({ title: 'MPR Load Error', message: error.message, diff --git a/extensions/vtk/src/commandsModule.js b/extensions/vtk/src/commandsModule.js index dcd1e2d7e..287f92f9d 100644 --- a/extensions/vtk/src/commandsModule.js +++ b/extensions/vtk/src/commandsModule.js @@ -14,7 +14,9 @@ import OHIFVTKViewport from './OHIFVTKViewport'; const { BlendMode } = Constants; -const commandsModule = ({ commandsManager, UINotificationService }) => { +const commandsModule = ({ commandsManager, servicesManager }) => { + const { UINotificationService, LoggerService } = servicesManager.services; + // TODO: Put this somewhere else let apis = {}; let defaultVOI; @@ -175,7 +177,7 @@ const commandsModule = ({ commandsManager, UINotificationService }) => { segmentNumber, frameIndex, frame, - done = () => { }, + done = () => {}, }) => { let api = apis[viewports.activeViewportIndex]; @@ -473,10 +475,12 @@ const commandsModule = ({ commandsManager, UINotificationService }) => { const volumeLength = dimensions[0] * dimensions[1] * dimensions[2]; if (volumeLength > maxBufferLengthFloat32) { + const message = + 'This volume is too large to fit in WebGL 1 textures and will display incorrectly. Please use a different browser to view this data'; + LoggerService.error({ message }); UINotificationService.show({ title: 'Browser does not support WebGL 2', - message: - 'This volume is too large to fit in WebGL 1 textures and will display incorrectly. Please use a different browser to view this data', + message, type: 'error', autoClose: false, }); diff --git a/extensions/vtk/src/index.js b/extensions/vtk/src/index.js index 18e69c22c..9fb5475bc 100644 --- a/extensions/vtk/src/index.js +++ b/extensions/vtk/src/index.js @@ -35,8 +35,7 @@ const vtkExtension = { return toolbarModule; }, getCommandsModule({ commandsManager, servicesManager }) { - const { UINotificationService } = servicesManager.services; - return commandsModule({ commandsManager, UINotificationService }); + return commandsModule({ commandsManager, servicesManager }); }, }; diff --git a/platform/core/src/classes/HotkeysManager.js b/platform/core/src/classes/HotkeysManager.js index ee65f08a3..f4369b2d7 100644 --- a/platform/core/src/classes/HotkeysManager.js +++ b/platform/core/src/classes/HotkeysManager.js @@ -63,10 +63,15 @@ export class HotkeysManager { definitions.forEach(definition => this.registerHotkeys(definition)); } catch (error) { - const { UINotificationService } = this._servicesManager.services; + const { + UINotificationService, + LoggerService, + } = this._servicesManager.services; + const message = 'Erro while setting hotkeys'; + LoggerService.error({ error, message }); UINotificationService.show({ title: 'Hotkeys Manager', - message: 'Erro while setting hotkeys', + message, type: 'error', }); } diff --git a/platform/core/src/index.js b/platform/core/src/index.js index 3f7727d2d..8aa24ea05 100644 --- a/platform/core/src/index.js +++ b/platform/core/src/index.js @@ -26,6 +26,7 @@ import { UIModalService, UIDialogService, MeasurementService, + LoggerService, } from './services'; const OHIF = { @@ -60,6 +61,7 @@ const OHIF = { UIModalService, UIDialogService, MeasurementService, + LoggerService, }; export { @@ -93,6 +95,7 @@ export { UIModalService, UIDialogService, MeasurementService, + LoggerService, }; export { OHIF }; diff --git a/platform/core/src/services/LoggerService/index.js b/platform/core/src/services/LoggerService/index.js new file mode 100644 index 000000000..3a42ac118 --- /dev/null +++ b/platform/core/src/services/LoggerService/index.js @@ -0,0 +1,67 @@ +const name = 'LoggerService'; + +const publicAPI = { + name, + info: _info, + error: _error, + setServiceImplementation, +}; + +const serviceImplementation = { + _info: () => console.warn('info() NOT IMPLEMENTED'), + _error: () => console.warn('error() NOT IMPLEMENTED'), +}; + +/** + * Logs an info + * + * @param {object} props { message, displayOnConsole } + */ +function _info({ message, displayOnConsole }) { + return serviceImplementation._info({ + message, + displayOnConsole, + }); +} + +/** + * Logs an error + * + * @param {object} props { error, stack, message, displayOnConsole } + * @returns void + */ +function _error({ error, stack, message, displayOnConsole }) { + return serviceImplementation._error({ + error, + stack, + message, + displayOnConsole, + }); +} + +/** + * + * + * @param {*} { + * info: infoImplementation, + * error: errorImplementation, + * } + */ +function setServiceImplementation({ + info: infoImplementation, + error: errorImplementation, +}) { + if (infoImplementation) { + serviceImplementation._info = infoImplementation; + } + if (errorImplementation) { + serviceImplementation._error = errorImplementation; + } +} + +export default { + name, + create: ({ configuration = {} }) => { + return publicAPI; + }, +}; diff --git a/platform/core/src/services/index.js b/platform/core/src/services/index.js index a7fa17072..b4eb4cbfc 100644 --- a/platform/core/src/services/index.js +++ b/platform/core/src/services/index.js @@ -3,6 +3,7 @@ import UINotificationService from './UINotificationService'; import UIModalService from './UIModalService'; import UIDialogService from './UIDialogService'; import MeasurementService from './MeasurementService'; +import LoggerService from './LoggerService'; export { UINotificationService, @@ -10,4 +11,5 @@ export { UIDialogService, ServicesManager, MeasurementService, + LoggerService, }; diff --git a/platform/ui/src/contextProviders/LoggerProvider.js b/platform/ui/src/contextProviders/LoggerProvider.js new file mode 100644 index 000000000..457afe262 --- /dev/null +++ b/platform/ui/src/contextProviders/LoggerProvider.js @@ -0,0 +1,94 @@ +import React, { useState, createContext, useContext, useEffect } from 'react'; +import PropTypes from 'prop-types'; + +const LoggerContext = createContext(null); +const { Provider } = LoggerContext; + +export const useLogger = () => useContext(LoggerContext); + +const LoggerProvider = ({ children, service }) => { + const [state, setState] = useState({ + errors: [], + infos: [], + }); + + /** + * Logs an error + * + * @param {object} props { error, stack, message, displayOnConsole } + * @returns void + */ + const error = ({ + error = {}, + stack = '', + message = '', + displayOnConsole = true, + }) => { + const errorObject = { error, stack, message, displayOnConsole }; + setState(state => ({ ...state, errors: [...state.errors, errorObject] })); + + if (displayOnConsole) { + console.error(error); + } + }; + + /** + * Logs an info + * + * @param {object} props { message, displayOnConsole } + * @returns void + */ + const info = ({ message = '', displayOnConsole = true }) => { + setState(state => ({ + ...state, + infos: state.infos.push({ message, displayOnConsole }), + })); + + if (displayOnConsole) { + console.info(message); + } + }; + + /** + * Sets the implementation of a log service that can be used by extensions. + * + * @returns void + */ + useEffect(() => { + if (service) { + service.setServiceImplementation({ error, info }); + } + }, [error, service, info]); + + return {children}; +}; + +/** + * Higher Order Component to use the log methods through a Class Component. + * + * @returns + */ +export const withLogger = Component => { + return function WrappedComponent(props) { + const { error, info, state } = useLogger(); + return ; + }; +}; + +LoggerProvider.defaultProps = { + service: null, +}; + +LoggerProvider.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]).isRequired, + service: PropTypes.shape({ + setServiceImplementation: PropTypes.func, + }), +}; + +export default LoggerProvider; + +export const LogConsumer = LoggerContext.Consumer; diff --git a/platform/ui/src/contextProviders/index.js b/platform/ui/src/contextProviders/index.js index e4ca06143..725f48109 100644 --- a/platform/ui/src/contextProviders/index.js +++ b/platform/ui/src/contextProviders/index.js @@ -18,3 +18,8 @@ export { withDialog, useDialog, } from './DialogProvider.js'; +export { + default as LoggerProvider, + withLogger, + useLogger, +} from './LoggerProvider.js'; diff --git a/platform/ui/src/index.js b/platform/ui/src/index.js index e95547357..fa79a831f 100644 --- a/platform/ui/src/index.js +++ b/platform/ui/src/index.js @@ -30,7 +30,7 @@ import { AboutContent, OHIFModal, ErrorBoundary, - ErrorPage + ErrorPage, } from './components'; import { useDebounce, useMedia } from './hooks'; @@ -66,6 +66,9 @@ import { ModalConsumer, useModal, withModal, + LoggerProvider, + withLogger, + useLogger, } from './contextProviders'; export { @@ -127,11 +130,14 @@ export { useDialog, ErrorBoundary, ErrorPage, + LoggerProvider, + withLogger, + useLogger, // Hooks useDebounce, useMedia, // Utils ViewerbaseDragDropContext, asyncComponent, - retryImport + retryImport, }; diff --git a/platform/viewer/src/App.js b/platform/viewer/src/App.js index 2bf95a8bf..603ee4e36 100644 --- a/platform/viewer/src/App.js +++ b/platform/viewer/src/App.js @@ -13,6 +13,7 @@ import { ModalProvider, DialogProvider, OHIFModal, + LoggerProvider, ErrorBoundary, } from '@ohif/ui'; @@ -24,6 +25,7 @@ import { UINotificationService, UIModalService, UIDialogService, + LoggerService, MeasurementService, utils, redux as reduxOHIF, @@ -141,6 +143,7 @@ class App extends Component { UIModalService, UIDialogService, MeasurementService, + LoggerService, ]); _initExtensions( [...defaultExtensions, ...extensions], @@ -164,6 +167,7 @@ class App extends Component { UIDialogService, UIModalService, MeasurementService, + LoggerService, } = servicesManager.services; if (this._userManager) { @@ -176,18 +180,20 @@ class App extends Component { - - - - - - - + + + + + + + + + @@ -206,13 +212,18 @@ class App extends Component { - - - - - - - + + + + + + + + +