From ea509702b6ed88951a1b7b109b5dc597ee81112d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Sat, 2 Oct 2021 16:53:46 +0200 Subject: [PATCH] feat(web): add client-side logging support Also modified langauge-web/src/main/js/xtext/ServiceBuilder.js to make sure the new logger is used as soon as xtext is initialized. --- language-web/package.json | 5 +- .../src/main/js/editor/EditorArea.tsx | 3 +- .../src/main/js/editor/EditorStore.ts | 13 ++++- language-web/src/main/js/global.d.ts | 6 +++ language-web/src/main/js/logging.tsx | 49 +++++++++++++++++++ .../src/main/js/xtext/ServiceBuilder.js | 27 +++++++--- language-web/webpack.config.js | 8 +++ language-web/yarn.lock | 15 ++++++ 8 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 language-web/src/main/js/logging.tsx diff --git a/language-web/package.json b/language-web/package.json index ddb00f579..79e633425 100644 --- a/language-web/package.json +++ b/language-web/package.json @@ -1,5 +1,5 @@ { - "name": "language-web", + "name": "refinery", "version": "0.0.0", "description": "Web frontend for VIATRA-Generator", "main": "index.js", @@ -58,6 +58,7 @@ "webpack-subresource-integrity": "^5.0.0-rc.1" }, "dependencies": { + "ansi-styles": "^6.1.0", "@babel/runtime": "^7.15.0", "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", @@ -67,6 +68,8 @@ "@material-ui/icons": "5.0.0-beta.4", "codemirror": "^5.62.3", "jquery": "^3.6.0", + "loglevel": "^1.7.1", + "loglevel-plugin-prefix": "^0.8.4", "mobx": "^6.3.2", "mobx-react-lite": "^3.2.0", "react": "^17.0.2", diff --git a/language-web/src/main/js/editor/EditorArea.tsx b/language-web/src/main/js/editor/EditorArea.tsx index f07a0ad86..531a57c9b 100644 --- a/language-web/src/main/js/editor/EditorArea.tsx +++ b/language-web/src/main/js/editor/EditorArea.tsx @@ -15,8 +15,7 @@ export const EditorArea = observer(() => { onChange={(e) => editorStore.updateValue(e.target.value)} ref={fallbackTextarea} className={`problem-fallback-editor cm-s-${editorStore.codeMirrorTheme}`} - > - + /> ); } diff --git a/language-web/src/main/js/editor/EditorStore.ts b/language-web/src/main/js/editor/EditorStore.ts index 1ac2e79fe..8b9432dd5 100644 --- a/language-web/src/main/js/editor/EditorStore.ts +++ b/language-web/src/main/js/editor/EditorStore.ts @@ -8,8 +8,11 @@ import { import type { IXtextOptions, IXtextServices } from 'xtext/xtext-codemirror'; import type { IEditorChunk } from './editor'; +import { getLogger } from '../logging'; import type { ThemeStore } from '../theme/ThemeStore'; +const log = getLogger('EditorStore'); + const xtextLang = 'problem'; const xtextOptions: IXtextOptions = { @@ -52,12 +55,20 @@ export class EditorStore { xtextServices: observable.ref, initialSelection: false, }); + this.loadChunk(); + } + + private loadChunk(): void { + const loadingStartMillis = Date.now(); + log.info('Requesting editor chunk'); import('./editor').then(({ editorChunk }) => { runInAction(() => { this.chunk = editorChunk; }); + const loadingDurationMillis = Date.now() - loadingStartMillis; + log.info('Loaded editor chunk in', loadingDurationMillis, 'ms'); }).catch((error) => { - console.warn('Error while loading editor', error); + log.error('Error while loading editor', error); }); } diff --git a/language-web/src/main/js/global.d.ts b/language-web/src/main/js/global.d.ts index 39bda7f39..0533a46e3 100644 --- a/language-web/src/main/js/global.d.ts +++ b/language-web/src/main/js/global.d.ts @@ -1,3 +1,9 @@ +declare const DEBUG: boolean; + +declare const PACKAGE_NAME: string; + +declare const PACKAGE_VERSION: string; + declare module '*.module.scss' { const cssVariables: { [key in string]?: string }; // eslint-disable-next-line import/no-default-export diff --git a/language-web/src/main/js/logging.tsx b/language-web/src/main/js/logging.tsx new file mode 100644 index 000000000..25f50f19c --- /dev/null +++ b/language-web/src/main/js/logging.tsx @@ -0,0 +1,49 @@ +import styles, { CSPair } from 'ansi-styles'; +import log from 'loglevel'; +import * as prefix from 'loglevel-plugin-prefix'; + +const colors: Record = { + TRACE: styles.magenta, + DEBUG: styles.cyan, + INFO: styles.blue, + WARN: styles.yellow, + ERROR: styles.red, +}; + +prefix.reg(log); + +if (DEBUG) { + log.setLevel(log.levels.DEBUG); +} else { + log.setLevel(log.levels.WARN); +} + +if ('chrome' in window) { + // Only Chromium supports console ANSI escape sequences. + prefix.apply(log, { + format(level, name, timestamp) { + const formattedTimestamp = `${styles.gray.open}[${timestamp.toString()}]${styles.gray.close}`; + const levelColor = colors[level.toUpperCase()] || styles.red; + const formattedLevel = `${levelColor.open}${level}${levelColor.close}`; + const formattedName = `${styles.green.open}(${name || 'root'})${styles.green.close}`; + return `${formattedTimestamp} ${formattedLevel} ${formattedName}`; + }, + }); +} else { + prefix.apply(log, { + template: '[%t] %l (%n)', + }); +} + +const appLogger = log.getLogger(PACKAGE_NAME); + +appLogger.info('Version:', PACKAGE_NAME, PACKAGE_VERSION); +appLogger.info('Debug mode:', DEBUG); + +export function getLoggerFromRoot(name: string | symbol): log.Logger { + return log.getLogger(name); +} + +export function getLogger(name: string | symbol): log.Logger { + return getLoggerFromRoot(`${PACKAGE_NAME}.${name.toString()}`); +} diff --git a/language-web/src/main/js/xtext/ServiceBuilder.js b/language-web/src/main/js/xtext/ServiceBuilder.js index 38b08ecc3..57fcb3102 100644 --- a/language-web/src/main/js/xtext/ServiceBuilder.js +++ b/language-web/src/main/js/xtext/ServiceBuilder.js @@ -18,10 +18,11 @@ define([ 'xtext/services/ContentAssistService', 'xtext/services/HoverService', 'xtext/services/OccurrencesService', - 'xtext/services/FormattingService' + 'xtext/services/FormattingService', + '../logging', ], function(jQuery, XtextService, LoadResourceService, SaveResourceService, HighlightingService, ValidationService, UpdateService, ContentAssistService, HoverService, OccurrencesService, - FormattingService) { + FormattingService, logging) { /** * Builder class for the Xtext services. @@ -179,12 +180,22 @@ define([ }); } - services.successListeners = []; + const log = logging.getLoggerFromRoot('xtext.XtextService'); + services.successListeners = [function(serviceType, result) { + if (log.getLevel() <= log.levels.TRACE) { + log.trace('service', serviceType, 'request success', JSON.parse(JSON.stringify(result))); + } + }]; services.errorListeners = [function(serviceType, severity, message, requestData) { - if (options.showErrorDialogs) - window.alert('Xtext service \'' + serviceType + '\' failed: ' + message); - else - console.log('Xtext service \'' + serviceType + '\' failed: ' + message); + const messageParts = ['service', serviceType, 'failed:', message || '(no message)']; + if (requestData) { + messageParts.push(JSON.parse(JSON.stringify(requestData))); + } + if (severity === 'warning') { + log.warn(...messageParts); + } else { + log.error(...messageParts); + } }]; } @@ -271,4 +282,4 @@ define([ } return ServiceBuilder; -}); \ No newline at end of file +}); diff --git a/language-web/webpack.config.js b/language-web/webpack.config.js index 1bd0edb2c..c51d55d66 100644 --- a/language-web/webpack.config.js +++ b/language-web/webpack.config.js @@ -1,11 +1,14 @@ const fs = require('fs'); const path = require('path'); +const { DefinePlugin } = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackInjectPreload = require('@principalstudio/html-webpack-inject-preload'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity'); +const packageInfo = require('./package.json'); + const currentNodeEnv = process.env.NODE_ENV || 'development'; const devMode = currentNodeEnv !== 'production'; const outputPath = path.resolve(__dirname, 'build/webpack', currentNodeEnv); @@ -194,6 +197,11 @@ module.exports = { }, }, plugins: [ + new DefinePlugin({ + 'DEBUG': JSON.stringify(devMode), + 'PACKAGE_NAME': JSON.stringify(packageInfo.name), + 'PACKAGE_VERSION': JSON.stringify(packageInfo.version), + }), new MiniCssExtractPlugin({ filename: '[name].[contenthash].css', chunkFilename: '[name].[contenthash].css', diff --git a/language-web/yarn.lock b/language-web/yarn.lock index 3aac4633d..a307229f6 100644 --- a/language-web/yarn.lock +++ b/language-web/yarn.lock @@ -1739,6 +1739,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" + integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== + anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -4967,6 +4972,16 @@ logalot@^2.0.0, logalot@^2.1.0: figures "^1.3.5" squeak "^1.0.0" +loglevel-plugin-prefix@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz#2fe0e05f1a820317d98d8c123e634c1bd84ff644" + integrity sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g== + +loglevel@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" + integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== + longest-streak@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4"