From 06c0a379b3a009ba4d3507b2f8dd9ac7ca91d4b7 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 5 Dec 2023 16:52:16 -0300 Subject: [PATCH] Strip less-important logging from modular variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to reduce bundle size further, we’ve decided to strip all logging from the modular variant of the SDK, except for errors and certain network events. (We also considered providing a separate tree-shakable module with all of this logging code so that a user of the modular variant of the library can opt in to it, but decided against it for now; we might add it in later. This does mean that there is currently no version of the SDK that allows you to use both deltas and verbose logging on web.) I couldn’t find any out-of-the-box esbuild functionality that let us do this. The only stuff I could find related to stripping code was: - the `pure` option, but that code only gets stripped if you minify the code (and even in that case I couldn’t actually get it to be stripped, perhaps would have been able to with further trying though), but minifying our generated modules bundle causes the bundle size of those who use it (as tested by our modulereport script) to increase considerably (for reasons I’m not sure of) - the `drop` option, but that only lets you remove calls to `console` or `debugger` So instead I’ve implemented it as an esbuild plugin. Resolves #1526 --- Gruntfile.js | 92 +++++++++++++- README.md | 9 +- modules.d.ts | 18 +++ package-lock.json | 115 +++++++++--------- package.json | 3 + scripts/moduleReport.ts | 2 +- src/common/lib/client/realtimechannel.ts | 13 +- src/common/lib/client/resource.ts | 8 +- src/common/lib/transport/connectionmanager.ts | 13 +- src/common/lib/transport/protocol.ts | 2 +- src/common/lib/transport/transport.ts | 4 +- src/common/lib/util/logger.ts | 14 ++- 12 files changed, 214 insertions(+), 79 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 8b65e4a43c..17a3325d79 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,6 +7,12 @@ var esbuild = require('esbuild'); var umdWrapper = require('esbuild-plugin-umd-wrapper'); var banner = require('./src/fragments/license'); var process = require('process'); +var babel = { + types: require('@babel/types'), + parser: require('@babel/parser'), + traverse: require('@babel/traverse'), + generator: require('@babel/generator'), +}; module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-concat'); @@ -123,6 +129,90 @@ module.exports = function (grunt) { }; } + // This esbuild plugin strips all log messages from the modular variant of + // the library, except for error-level logs and other logging statements + // explicitly marked as not to be stripped. + var stripLogsPlugin = { + name: 'stripLogs', + setup(build) { + let foundErrorLog = false; + let foundNoStripLog = false; + + const filter = new RegExp(`^${__dirname}/src/.*\.[tj]s$`); + build.onLoad({ filter }, async (args) => { + const contents = (await fs.promises.readFile(args.path)).toString(); + const lines = contents.split('\n'); + const ast = babel.parser.parse(contents, { sourceType: 'module', plugins: ['typescript'] }); + const errors = []; + + babel.traverse.default(ast, { + enter(path) { + if ( + path.isCallExpression() && + babel.types.isMemberExpression(path.node.callee) && + babel.types.isIdentifier(path.node.callee.object, { name: 'Logger' }) + ) { + if (babel.types.isIdentifier(path.node.callee.property, { name: 'logAction' })) { + const firstArgument = path.node.arguments[0]; + + if ( + babel.types.isMemberExpression(firstArgument) && + babel.types.isIdentifier(firstArgument.object, { name: 'Logger' }) && + firstArgument.property.name.startsWith('LOG_') + ) { + if (firstArgument.property.name === 'LOG_ERROR') { + // `path` is a call to `Logger.logAction(Logger.LOG_ERROR, ...)`; preserve it. + foundErrorLog = true; + } else { + // `path` is a call to `Logger.logAction(Logger.LOG_*, ...) for some other log level; strip it. + path.remove(); + } + } else { + // `path` is a call to `Logger.logAction(...)` with some argument other than a `Logger.LOG_*` expression; raise an error because we can’t determine whether to strip it. + errors.push({ + location: { + file: args.path, + column: firstArgument.loc.start.column, + line: firstArgument.loc.start.line, + lineText: lines[firstArgument.loc.start.line - 1], + }, + text: `First argument passed to Logger.logAction() must be Logger.LOG_*, got \`${ + babel.generator.default(firstArgument).code + }\``, + }); + } + } else if (babel.types.isIdentifier(path.node.callee.property, { name: 'logActionNoStrip' })) { + // `path` is a call to `Logger.logActionNoStrip()`; preserve it. + foundNoStripLog = true; + } + } + }, + }); + + return { contents: babel.generator.default(ast).code, loader: 'ts', errors }; + }); + + build.onEnd(() => { + const errorMessages = []; + + // Perform a sense check to make sure that we found some logging + // calls to preserve (to protect us against accidentally changing the + // internal logging API in such a way that would cause us to + // accidentally strip all logging calls). + + if (!foundErrorLog) { + errorMessages.push('Did not find any Logger.logAction(Logger.LOG_ERROR, ...) calls to preserve'); + } + + if (!foundNoStripLog) { + errorMessages.push('Did not find any Logger.logActionNoStrip(...) calls to preserve'); + } + + return { errors: errorMessages.map((text) => ({ text })) }; + }); + }, + }; + function createModulesConfig() { return { // We need to create a new copy of the base config, because calling @@ -133,7 +223,7 @@ module.exports = function (grunt) { entryPoints: ['src/platform/web/modules.ts'], outfile: 'build/modules/index.js', format: 'esm', - plugins: [], + plugins: [stripLogsPlugin], }; } diff --git a/README.md b/README.md index 309bae8705..76bc21f677 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,14 @@ You must provide: `BaseRealtime` offers the same API as the `Realtime` class described in the rest of this `README`. This means that you can develop an application using the default variant of the SDK and switch to the modular version when you wish to optimize your bundle size. -For more information, see the [generated documentation](https://sdk.ably.com/builds/ably/ably-js/main/typedoc/modules/index.html) (this link points to the documentation for the `main` branch). +In order to further reduce bundle size, the modular variant of the SDK performs less logging than the default variant. It only logs: + +- messages that have a `logLevel` of 1 (that is, errors) +- a small number of other network events + +If you need more verbose logging, use the default variant of the SDK. + +For more information about the modular variant of the SDK, see the [generated documentation](https://sdk.ably.com/builds/ably/ably-js/main/typedoc/modules/index.html) (this link points to the documentation for the `main` branch). ### TypeScript diff --git a/modules.d.ts b/modules.d.ts index 76f54a5aee..0bbfd4eda1 100644 --- a/modules.d.ts +++ b/modules.d.ts @@ -241,6 +241,15 @@ export interface ModulesMap { * A client that offers a simple stateless API to interact directly with Ably's REST API. * * `BaseRest` is the equivalent, in the modular variant of the Ably Client Library SDK, of the [`Rest`](../../default/classes/Rest.html) class in the default variant of the SDK. The difference is that its constructor allows you to decide exactly which functionality the client should include. This allows unused functionality to be tree-shaken, reducing bundle size. + * + * > **Note** + * > + * > In order to further reduce bundle size, `BaseRest` performs less logging than the `Rest` class exported by the default variant of the SDK. It only logs: + * > + * > - messages that have a {@link Types.ClientOptions.logLevel | `logLevel`} of 1 (that is, errors) + * > - a small number of other network events + * > + * > If you need more verbose logging, use the default variant of the SDK. */ export declare class BaseRest extends Types.Rest { /** @@ -260,6 +269,15 @@ export declare class BaseRest extends Types.Rest { * A client that extends the functionality of {@link BaseRest} and provides additional realtime-specific features. * * `BaseRealtime` is the equivalent, in the modular variant of the Ably Client Library SDK, of the [`Realtime`](../../default/classes/Realtime.html) class in the default variant of the SDK. The difference is that its constructor allows you to decide exactly which functionality the client should include. This allows unused functionality to be tree-shaken, reducing bundle size. + * + * > **Note** + * > + * > In order to further reduce bundle size, `BaseRealtime` performs less logging than the `Realtime` class exported by the default variant of the SDK. It only logs: + * > + * > - messages that have a {@link Types.ClientOptions.logLevel | `logLevel`} of 1 (that is, errors) + * > - a small number of other network events + * > + * > If you need more verbose logging, use the default variant of the SDK. */ export declare class BaseRealtime extends Types.Realtime { /** diff --git a/package-lock.json b/package-lock.json index 58689871d8..ba0b2c4bf0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,9 @@ }, "devDependencies": { "@ably/vcdiff-decoder": "1.0.6", + "@babel/generator": "^7.23.5", + "@babel/parser": "^7.23.5", + "@babel/traverse": "^7.23.5", "@testing-library/react": "^13.3.0", "@types/node": "^18.0.0", "@types/request": "^2.48.7", @@ -119,12 +122,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -251,12 +254,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", + "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -401,9 +404,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -442,9 +445,9 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -527,9 +530,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", + "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -644,19 +647,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", + "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.23.5", + "@babel/types": "^7.23.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -665,12 +668,12 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", + "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -10699,12 +10702,12 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "requires": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "dependencies": { @@ -10804,12 +10807,12 @@ } }, "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", + "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", "dev": true, "requires": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -10917,9 +10920,9 @@ } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true }, "@babel/helper-validator-identifier": { @@ -10946,9 +10949,9 @@ } }, "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.22.20", @@ -11015,9 +11018,9 @@ } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", + "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", "dev": true }, "@babel/plugin-syntax-jsx": { @@ -11090,30 +11093,30 @@ } }, "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", + "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.23.5", + "@babel/types": "^7.23.5", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", + "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } diff --git a/package.json b/package.json index 3446ecb1f8..4378f444d5 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,9 @@ }, "devDependencies": { "@ably/vcdiff-decoder": "1.0.6", + "@babel/generator": "^7.23.5", + "@babel/parser": "^7.23.5", + "@babel/traverse": "^7.23.5", "@testing-library/react": "^13.3.0", "@types/node": "^18.0.0", "@types/request": "^2.48.7", diff --git a/scripts/moduleReport.ts b/scripts/moduleReport.ts index 56ebce35c2..0ef1cb5647 100644 --- a/scripts/moduleReport.ts +++ b/scripts/moduleReport.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { explore } from 'source-map-explorer'; // The maximum size we allow for a minimal useful Realtime bundle (i.e. one that can subscribe to a channel) -const minimalUsefulRealtimeBundleSizeThresholdKiB = 108; +const minimalUsefulRealtimeBundleSizeThresholdKiB = 93; // List of all modules accepted in ModulesMap const moduleNames = [ diff --git a/src/common/lib/client/realtimechannel.ts b/src/common/lib/client/realtimechannel.ts index 27d409ea38..ab56e31746 100644 --- a/src/common/lib/client/realtimechannel.ts +++ b/src/common/lib/client/realtimechannel.ts @@ -753,12 +753,13 @@ class RealtimeChannel extends EventEmitter { this.errorReason = reason; } const change = new ChannelStateChange(this.state, state, resumed, hasBacklog, reason); - const logLevel = state === 'failed' ? Logger.LOG_ERROR : Logger.LOG_MAJOR; - Logger.logAction( - logLevel, - 'Channel state for channel "' + this.name + '"', - state + (reason ? '; reason: ' + reason : '') - ); + const action = 'Channel state for channel "' + this.name + '"'; + const message = state + (reason ? '; reason: ' + reason : ''); + if (state === 'failed') { + Logger.logAction(Logger.LOG_ERROR, action, message); + } else { + Logger.logAction(Logger.LOG_MAJOR, action, message); + } if (state !== 'attaching' && state !== 'suspended') { this.retryCount = 0; diff --git a/src/common/lib/client/resource.ts b/src/common/lib/client/resource.ts index 3f0a8d68da..b3bf45cce8 100644 --- a/src/common/lib/client/resource.ts +++ b/src/common/lib/client/resource.ts @@ -102,13 +102,13 @@ function logResponseHandler( ): ResourceCallback { return (err, body, headers, unpacked, statusCode) => { if (err) { - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Resource.' + method + '()', 'Received Error; ' + urlFromPathAndParams(path, params) + '; Error: ' + Utils.inspectError(err) ); } else { - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Resource.' + method + '()', 'Received; ' + @@ -215,7 +215,7 @@ class Resource { function doRequest(this: any, headers: Record, params: Record) { if (Logger.shouldLog(Logger.LOG_MICRO)) { - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Resource.' + method + '()', 'Sending; ' + urlFromPathAndParams(path, params) @@ -238,7 +238,7 @@ class Resource { ); } } - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Resource.' + method + '()', 'Sending; ' + urlFromPathAndParams(path, params) + '; Body: ' + decodedBody diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 3b4484fde4..e7c66d8a97 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -1187,12 +1187,13 @@ class ConnectionManager extends EventEmitter { } enactStateChange(stateChange: ConnectionStateChange): void { - const logLevel = stateChange.current === 'failed' ? Logger.LOG_ERROR : Logger.LOG_MAJOR; - Logger.logAction( - logLevel, - 'Connection state', - stateChange.current + (stateChange.reason ? '; reason: ' + stateChange.reason : '') - ); + const action = 'Connection state'; + const message = stateChange.current + (stateChange.reason ? '; reason: ' + stateChange.reason : ''); + if (stateChange.current === 'failed') { + Logger.logAction(Logger.LOG_ERROR, action, message); + } else { + Logger.logAction(Logger.LOG_MAJOR, action, message); + } Logger.logAction( Logger.LOG_MINOR, 'ConnectionManager.enactStateChange', diff --git a/src/common/lib/transport/protocol.ts b/src/common/lib/transport/protocol.ts index 436fb7d202..683b2c36ec 100644 --- a/src/common/lib/transport/protocol.ts +++ b/src/common/lib/transport/protocol.ts @@ -71,7 +71,7 @@ class Protocol extends EventEmitter { this.messageQueue.push(pendingMessage); } if (Logger.shouldLog(Logger.LOG_MICRO)) { - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Protocol.send()', 'sending msg; ' + diff --git a/src/common/lib/transport/transport.ts b/src/common/lib/transport/transport.ts index 76e9b49f94..d35c90fae2 100644 --- a/src/common/lib/transport/transport.ts +++ b/src/common/lib/transport/transport.ts @@ -117,7 +117,7 @@ abstract class Transport extends EventEmitter { onProtocolMessage(message: ProtocolMessage): void { if (Logger.shouldLog(Logger.LOG_MICRO)) { - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Transport.onProtocolMessage()', 'received on ' + @@ -132,7 +132,7 @@ abstract class Transport extends EventEmitter { switch (message.action) { case actions.HEARTBEAT: - Logger.logAction( + Logger.logActionNoStrip( Logger.LOG_MICRO, 'Transport.onProtocolMessage()', this.shortName + ' heartbeat; connectionId = ' + this.connectionManager.connectionId diff --git a/src/common/lib/util/logger.ts b/src/common/lib/util/logger.ts index bfafe2c500..da4c9b2b64 100644 --- a/src/common/lib/util/logger.ts +++ b/src/common/lib/util/logger.ts @@ -98,11 +98,23 @@ class Logger { } /* public static functions */ + /** + * In the modular variant of the SDK, the `stripLogs` esbuild plugin strips out all calls to this method (when invoked as `Logger.logAction(...)`) except when called with level `Logger.LOG_ERROR`. + * + * The aforementioned plugin expects `level` to be an expression of the form `Logger.LOG_*`; that is, you can’t dynamically specify the log level. + */ static logAction = (level: LogLevels, action: string, message?: string) => { + this.logActionNoStrip(level, action, message); + }; + + /** + * Calls to this method are never stripped by the `stripLogs` esbuild plugin. Use it for log statements that you wish to always be included in the modular variant of the SDK. + */ + static logActionNoStrip(level: LogLevels, action: string, message?: string) { if (Logger.shouldLog(level)) { (level === LogLevels.Error ? Logger.logErrorHandler : Logger.logHandler)('Ably: ' + action + ': ' + message); } - }; + } /* Where a logging operation is expensive, such as serialisation of data, use shouldLog will prevent the object being serialised if the log level will not output the message */