From ed81aa8201a98fb67e598657d0573c02c15beb6b Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Fri, 2 Aug 2024 10:25:39 +0800 Subject: [PATCH] Integrate JS remote logging package in WooCommerce Admin (#50134) * Update remote logger to check dev env and whether logging is enabled * Add changelog * Integrate JS remote logging package in WooCommerce Admin * Add changelog * Update remote logger package * Update test * Log error in error boundary * Update remote logger * Fix webpack config * Update log stack format * Update handleError * Add init debug --- packages/js/remote-logging/README.md | 5 +- .../changelog/add-wcadmin-remote-logging | 4 + packages/js/remote-logging/src/index.ts | 1 + .../js/remote-logging/src/remote-logger.ts | 142 ++++++++++++++---- packages/js/remote-logging/src/test/index.ts | 122 +++++++++++++-- packages/js/remote-logging/src/utils.ts | 2 + .../client/core-profiler/index.tsx | 2 + .../client/error-boundary/index.tsx | 18 ++- plugins/woocommerce-admin/client/index.js | 6 + .../client/lib/init-remote-logging.ts | 10 ++ plugins/woocommerce-admin/package.json | 4 + plugins/woocommerce-admin/webpack.config.js | 1 + .../changelog/add-wcadmin-remote-logging | 4 + .../src/Blocks/Assets/AssetDataRegistry.php | 2 + .../src/Internal/Admin/WCAdminAssets.php | 1 + pnpm-lock.yaml | 3 + 16 files changed, 280 insertions(+), 47 deletions(-) create mode 100644 packages/js/remote-logging/changelog/add-wcadmin-remote-logging create mode 100644 plugins/woocommerce-admin/client/lib/init-remote-logging.ts create mode 100644 plugins/woocommerce/changelog/add-wcadmin-remote-logging diff --git a/packages/js/remote-logging/README.md b/packages/js/remote-logging/README.md index 9129a2dedec..8ae44259b4c 100644 --- a/packages/js/remote-logging/README.md +++ b/packages/js/remote-logging/README.md @@ -28,7 +28,7 @@ The WooCommerce Remote Logging package offers the following features: 2. Log messages or errors: ```js - import { log } from '@woocommerce/remote-logging'; + import { log, captureException } from '@woocommerce/remote-logging'; // Log an informational message log('info', 'User completed checkout', { extra: { orderId: '12345' } }); @@ -40,7 +40,7 @@ The WooCommerce Remote Logging package offers the following features: try { // Some operation that might throw } catch (error) { - log('error', 'Failed to process order', { extra: { error } }); + captureException(error, { extra: { orderId: '12345' } }); } ``` @@ -76,5 +76,6 @@ addFilter( - `init(config: RemoteLoggerConfig): void`: Initializes the remote logger with the given configuration. - `log(severity: LogSeverity, message: string, extraData?: object): Promise`: Logs a message with the specified severity and optional extra data. +- `captureException(error: Error, extraData?: object): void`: Captures an error and sends it to the remote API. For more detailed information about types and interfaces, refer to the source code and inline documentation. diff --git a/packages/js/remote-logging/changelog/add-wcadmin-remote-logging b/packages/js/remote-logging/changelog/add-wcadmin-remote-logging new file mode 100644 index 00000000000..230dd1e821f --- /dev/null +++ b/packages/js/remote-logging/changelog/add-wcadmin-remote-logging @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update remote logger to check dev env and whether logging is enabled diff --git a/packages/js/remote-logging/src/index.ts b/packages/js/remote-logging/src/index.ts index a90f0bbbbf2..dc8cd33b779 100644 --- a/packages/js/remote-logging/src/index.ts +++ b/packages/js/remote-logging/src/index.ts @@ -1,6 +1,7 @@ export { init, log, + captureException, REMOTE_LOGGING_SHOULD_SEND_ERROR_FILTER, REMOTE_LOGGING_ERROR_DATA_FILTER, REMOTE_LOGGING_LOG_ENDPOINT_FILTER, diff --git a/packages/js/remote-logging/src/remote-logger.ts b/packages/js/remote-logging/src/remote-logger.ts index e8fec915b0a..6508bb3ab42 100644 --- a/packages/js/remote-logging/src/remote-logger.ts +++ b/packages/js/remote-logging/src/remote-logger.ts @@ -9,7 +9,7 @@ import { applyFilters } from '@wordpress/hooks'; /** * Internal dependencies */ -import { mergeLogData } from './utils'; +import { mergeLogData, isDevelopmentEnvironment } from './utils'; import { LogData, ErrorData, RemoteLoggerConfig } from './types'; const debug = debugFactory( 'wc:remote-logging' ); @@ -80,9 +80,38 @@ export class RemoteLogger { ...extraData, } ); + debug( 'Logging:', logData ); await this.sendLog( logData ); } + /** + * Logs an error to Logstash. + * + * @param error - The error to log. + * @param extraData - Optional additional data to include in the log. + * + * @return {Promise} - A promise that resolves when the error is logged. + */ + public async error( error: Error, extraData?: Partial< LogData > ) { + if ( this.isRateLimited() ) { + return; + } + + const errorData: ErrorData = { + ...mergeLogData( DEFAULT_LOG_DATA, { + message: error.message, + severity: 'error', + ...extraData, + } ), + trace: this.getFormattedStackFrame( + TraceKit.computeStackTrace( error ) + ), + }; + + debug( 'Logging error:', errorData ); + await this.sendError( errorData ); + } + /** * Initializes error event listeners for catching unhandled errors and unhandled rejections. */ @@ -115,6 +144,11 @@ export class RemoteLogger { * @param logData - The log data to be sent. */ private async sendLog( logData: LogData ): Promise< void > { + if ( isDevelopmentEnvironment ) { + debug( 'Skipping send log in development environment' ); + return; + } + const body = new window.FormData(); body.append( 'params', JSON.stringify( logData ) ); @@ -142,24 +176,18 @@ export class RemoteLogger { } /** - * Handles an error and prepares it for sending to the remote API. + * Handles an uncaught error and sends it to the remote API. * * @param error - The error to handle. */ private async handleError( error: Error ) { - const currentTime = Date.now(); - - if ( - currentTime - this.lastErrorSentTime < - this.config.errorRateLimitMs - ) { - debug( 'Rate limit reached. Skipping send error', error ); + const trace = TraceKit.computeStackTrace( error ); + if ( ! this.shouldHandleError( error, trace.stack ) ) { + debug( 'Irrelevant error. Skipping handling.', error ); return; } - const trace = TraceKit.computeStackTrace( error ); - if ( ! this.shouldSendError( error, trace.stack ) ) { - debug( 'Skipping error:', error ); + if ( this.isRateLimited() ) { return; } @@ -196,12 +224,15 @@ export class RemoteLogger { * @param error - The error data to be sent. */ private async sendError( error: ErrorData ) { + if ( isDevelopmentEnvironment ) { + debug( 'Skipping send error in development environment' ); + return; + } + const body = new window.FormData(); body.append( 'error', JSON.stringify( error ) ); try { - debug( 'Sending error to API:', error ); - /** * Filters the JS error endpoint URL. * @@ -212,6 +243,8 @@ export class RemoteLogger { 'https://public-api.wordpress.com/rest/v1.1/js-error' ) as string; + debug( 'Sending error to API:', error ); + await window.fetch( endpoint, { method: 'POST', body, @@ -279,13 +312,13 @@ export class RemoteLogger { } /** - * Determines whether an error should be sent to the remote API. + * Determines whether an error should be handled. * * @param error - The error to check. * @param stackFrames - The stack frames of the error. - * @return Whether the error should be sent. + * @return Whether the error should be handled. */ - private shouldSendError( + private shouldHandleError( error: Error, stackFrames: TraceKit.StackFrame[] ) { @@ -310,10 +343,41 @@ export class RemoteLogger { stackFrames ) as boolean; } + + private isRateLimited(): boolean { + const currentTime = Date.now(); + if ( + currentTime - this.lastErrorSentTime < + this.config.errorRateLimitMs + ) { + debug( 'Rate limit reached. Skipping send error' ); + return true; + } + return false; + } } let logger: RemoteLogger | null = null; +/** + * Checks if remote logging is enabled and if the logger is initialized. + * + * @return {boolean} - Returns true if remote logging is enabled and the logger is initialized, otherwise false. + */ +function canLog(): boolean { + if ( ! getSetting( 'isRemoteLoggingEnabled', false ) ) { + debug( 'Remote logging is disabled.' ); + return false; + } + + if ( ! logger ) { + warnLog( 'RemoteLogger is not initialized. Call init() first.' ); + return false; + } + + return true; +} + /** * Initializes the remote logging and error handlers. * This function should be called once at the start of the application. @@ -321,9 +385,9 @@ let logger: RemoteLogger | null = null; * @param config - Configuration object for the RemoteLogger. * */ -export function init( config: RemoteLoggerConfig ): void { - if ( ! window.wcTracks || ! window.wcTracks.isEnabled ) { - debug( 'Tracks is not enabled.' ); +export function init( config: RemoteLoggerConfig ) { + if ( ! getSetting( 'isRemoteLoggingEnabled', false ) ) { + debug( 'Remote logging is disabled.' ); return; } @@ -335,38 +399,56 @@ export function init( config: RemoteLoggerConfig ): void { try { logger = new RemoteLogger( config ); logger.initializeErrorHandlers(); + + debug( 'RemoteLogger initialized.' ); } catch ( error ) { errorLog( 'Failed to initialize RemoteLogger:', error ); } } /** - * Logs a message or error, respecting rate limiting. + * Logs a message or error. * * This function is inefficient because the data goes over the REST API, so use sparingly. * * @param severity - The severity of the log. * @param message - The message to log. * @param extraData - Optional additional data to include in the log. + * */ export async function log( severity: Exclude< LogData[ 'severity' ], undefined >, message: string, extraData?: Partial< Exclude< LogData, 'message' | 'severity' > > -) { - if ( ! window.wcTracks || ! window.wcTracks.isEnabled ) { - debug( 'Tracks is not enabled.' ); - return; - } - - if ( ! logger ) { - warnLog( 'RemoteLogger is not initialized. Call init() first.' ); +): Promise< void > { + if ( ! canLog() ) { return; } try { - await logger.log( severity, message, extraData ); + await logger?.log( severity, message, extraData ); } catch ( error ) { errorLog( 'Failed to send log:', error ); } } + +/** + * Captures an error and sends it to the remote API. Respects the error rate limit. + * + * @param error - The error to capture. + * @param extraData - Optional additional data to include in the log. + */ +export async function captureException( + error: Error, + extraData?: Partial< LogData > +) { + if ( ! canLog() ) { + return; + } + + try { + await logger?.error( error, extraData ); + } catch ( _error ) { + errorLog( 'Failed to send log:', _error ); + } +} diff --git a/packages/js/remote-logging/src/test/index.ts b/packages/js/remote-logging/src/test/index.ts index 52537362f08..a2542330bb5 100644 --- a/packages/js/remote-logging/src/test/index.ts +++ b/packages/js/remote-logging/src/test/index.ts @@ -3,10 +3,12 @@ */ import '@wordpress/jest-console'; import { addFilter, removeFilter } from '@wordpress/hooks'; +import { getSetting } from '@woocommerce/settings'; + /** * Internal dependencies */ -import { init, log } from '../'; +import { init, log, captureException } from '../'; import { RemoteLogger, REMOTE_LOGGING_SHOULD_SEND_ERROR_FILTER, @@ -16,6 +18,12 @@ import { } from '../remote-logger'; import { fetchMock } from './__mocks__/fetch'; +jest.mock( '@woocommerce/settings', () => ( { + getSetting: jest.fn().mockReturnValue( { + isRemoteLoggingEnabled: true, + } ), +} ) ); + jest.mock( 'tracekit', () => ( { computeStackTrace: jest.fn().mockReturnValue( { name: 'Error', @@ -93,6 +101,51 @@ describe( 'RemoteLogger', () => { } ); } ); + describe( 'error', () => { + it( 'should send an error to the API with default data', async () => { + const error = new Error( 'Test error' ); + await logger.error( error ); + + expect( fetchMock ).toHaveBeenCalledWith( + 'https://public-api.wordpress.com/rest/v1.1/js-error', + expect.objectContaining( { + method: 'POST', + body: expect.any( FormData ), + } ) + ); + + const formData = fetchMock.mock.calls[0][1].body; + const payload = JSON.parse(formData.get('error')); + expect( payload['message'] ).toBe( 'Test error' ); + expect( payload['severity'] ).toBe( 'error' ); + expect( payload['trace'] ).toContain( '#1 at testFunction (http://example.com/woocommerce/assets/js/admin/app.min.js:1:1)' ); + } ); + + it( 'should send an error to the API with extra data', async () => { + const error = new Error( 'Test error' ); + const extraData = { + severity: 'warning' as const, + tags: ['custom-tag'], + }; + await logger.error( error, extraData ); + + expect( fetchMock ).toHaveBeenCalledWith( + 'https://public-api.wordpress.com/rest/v1.1/js-error', + expect.objectContaining( { + method: 'POST', + body: expect.any( FormData ), + } ) + ); + + const formData = fetchMock.mock.calls[0][1].body; + const payload = JSON.parse(formData.get('error')); + expect( payload['message'] ).toBe( 'Test error' ); + expect( payload['severity'] ).toBe( 'warning' ); + expect( payload['tags'] ).toEqual( ["woocommerce", "js", "custom-tag"]); + expect( payload['trace'] ).toContain( '#1 at testFunction (http://example.com/woocommerce/assets/js/admin/app.min.js:1:1)' ); + } ); + } ); + describe( 'handleError', () => { it( 'should send an error to the API', async () => { const error = new Error( 'Test error' ); @@ -164,7 +217,7 @@ describe( 'RemoteLogger', () => { } ); } ); - describe( 'shouldSendError', () => { + describe( 'shouldHandleError', () => { it( 'should return true for WooCommerce errors', () => { const error = new Error( 'Test error' ); const stackFrames = [ @@ -176,7 +229,7 @@ describe( 'RemoteLogger', () => { column: 1, }, ]; - const result = ( logger as any ).shouldSendError( + const result = ( logger as any ).shouldHandleError( error, stackFrames ); @@ -194,7 +247,7 @@ describe( 'RemoteLogger', () => { column: 1, }, ]; - const result = ( logger as any ).shouldSendError( + const result = ( logger as any ).shouldHandleError( error, stackFrames ); @@ -203,7 +256,7 @@ describe( 'RemoteLogger', () => { it( 'should return false for WooCommerce errors with no stack frames', () => { const error = new Error( 'Test error' ); - const result = ( logger as any ).shouldSendError( error, [] ); + const result = ( logger as any ).shouldHandleError( error, [] ); expect( result ).toBe( false ); } ); @@ -214,7 +267,7 @@ describe( 'RemoteLogger', () => { () => true ); const error = new Error( 'Test error' ); - const result = ( logger as any ).shouldSendError( error, [] ); + const result = ( logger as any ).shouldHandleError( error, [] ); expect( result ).toBe( true ); } ); } ); @@ -255,18 +308,37 @@ describe( 'RemoteLogger', () => { describe( 'init', () => { beforeEach( () => { jest.clearAllMocks(); - window.wcTracks = { isEnabled: true }; + + ( getSetting as jest.Mock ).mockImplementation( + ( key, defaultValue ) => { + if ( key === 'isRemoteLoggingEnabled' ) { + return true; + } + return defaultValue; + } + ); } ); - it( 'should not initialize the logger if Tracks is not enabled', () => { - window.wcTracks = { isEnabled: false }; + it( 'should not initialize or log when remote logging is disabled', () => { + // Mock the getSetting function to return false for isRemoteLoggingEnabled + ( getSetting as jest.Mock ).mockImplementation( + ( key, defaultValue ) => { + if ( key === 'isRemoteLoggingEnabled' ) { + return false; + } + return defaultValue; + } + ); + init( { errorRateLimitMs: 1000 } ); - expect( () => log( 'info', 'Test message' ) ).not.toThrow(); + log( 'info', 'Test message' ); + expect( fetchMock ).not.toHaveBeenCalled(); } ); - it( 'should initialize the logger if Tracks is enabled', () => { + it( 'should initialize and log without throwing when remote logging is enabled', () => { init( { errorRateLimitMs: 1000 } ); expect( () => log( 'info', 'Test message' ) ).not.toThrow(); + expect( fetchMock ).toHaveBeenCalled(); } ); it( 'should not initialize the logger twice', () => { @@ -280,9 +352,33 @@ describe( 'init', () => { } ); describe( 'log', () => { - it( 'should not log if Tracks is not enabled', () => { - window.wcTracks = { isEnabled: false }; + it( 'should not log if remote logging is disabled', () => { + ( getSetting as jest.Mock ).mockImplementation( + ( key, defaultValue ) => { + if ( key === 'isRemoteLoggingEnabled' ) { + return false; + } + return defaultValue; + } + ); + log( 'info', 'Test message' ); expect( fetchMock ).not.toHaveBeenCalled(); } ); } ); + +describe( 'captureException', () => { + it( 'should not log error if remote logging is disabled', () => { + ( getSetting as jest.Mock ).mockImplementation( + ( key, defaultValue ) => { + if ( key === 'isRemoteLoggingEnabled' ) { + return false; + } + return defaultValue; + } + ); + + captureException( new Error( 'Test error' ) ); + expect( fetchMock ).not.toHaveBeenCalled(); + } ); +} ); diff --git a/packages/js/remote-logging/src/utils.ts b/packages/js/remote-logging/src/utils.ts index a5c516fc605..b38c2151073 100644 --- a/packages/js/remote-logging/src/utils.ts +++ b/packages/js/remote-logging/src/utils.ts @@ -39,3 +39,5 @@ export function mergeLogData( target: LogData, source: Partial< LogData > ) { } return result; } + +export const isDevelopmentEnvironment = process.env.NODE_ENV === 'development'; diff --git a/plugins/woocommerce-admin/client/core-profiler/index.tsx b/plugins/woocommerce-admin/client/core-profiler/index.tsx index ede4bd2b55a..19f97c3684d 100644 --- a/plugins/woocommerce-admin/client/core-profiler/index.tsx +++ b/plugins/woocommerce-admin/client/core-profiler/index.tsx @@ -43,6 +43,7 @@ import CurrencyFactory from '@woocommerce/currency'; * Internal dependencies */ import { findComponentMeta } from '~/utils/xstate/find-component'; +import { initRemoteLogging } from '~/lib/init-remote-logging'; import { IntroOptIn } from './pages/IntroOptIn'; import { UserProfile, @@ -330,6 +331,7 @@ const updateTrackingOption = fromPromise( ) { window.wcTracks.enable( () => { initializeExPlat(); + initRemoteLogging(); resolve(); // resolve the promise only after explat is enabled by the callback } ); } else { diff --git a/plugins/woocommerce-admin/client/error-boundary/index.tsx b/plugins/woocommerce-admin/client/error-boundary/index.tsx index 8789990eabd..1a36519730c 100644 --- a/plugins/woocommerce-admin/client/error-boundary/index.tsx +++ b/plugins/woocommerce-admin/client/error-boundary/index.tsx @@ -4,6 +4,7 @@ import { Component, ReactNode, ErrorInfo } from 'react'; import { __ } from '@wordpress/i18n'; import { Button } from '@wordpress/components'; +import { captureException } from '@woocommerce/remote-logging'; /** * Internal dependencies */ @@ -34,9 +35,22 @@ export class ErrorBoundary extends Component< return { hasError: true, error }; } - componentDidCatch( _error: Error, errorInfo: ErrorInfo ) { + componentDidCatch( error: Error, errorInfo: ErrorInfo ) { this.setState( { errorInfo } ); - // TODO: Log error to error tracking service + + // Limit the component stack to 10 calls so we don't send too much data. + const componentStack = errorInfo.componentStack + .trim() + .split( '\n' ) + .slice( 0, 10 ) + .map( ( line ) => line.trim() ); + + captureException( error, { + severity: 'critical', + extra: { + componentStack, + }, + } ); } handleRefresh = () => { diff --git a/plugins/woocommerce-admin/client/index.js b/plugins/woocommerce-admin/client/index.js index 7b64b67829f..a40e0b37021 100644 --- a/plugins/woocommerce-admin/client/index.js +++ b/plugins/woocommerce-admin/client/index.js @@ -8,6 +8,12 @@ import { withCurrentUserHydration, withSettingsHydration, } from '@woocommerce/data'; +/** + * Internal dependencies + */ +import { initRemoteLogging } from './lib/init-remote-logging'; +// Initialize remote logging early to log any errors that occur during initialization. +initRemoteLogging(); /** * Internal dependencies diff --git a/plugins/woocommerce-admin/client/lib/init-remote-logging.ts b/plugins/woocommerce-admin/client/lib/init-remote-logging.ts new file mode 100644 index 00000000000..7b5f80bbf0c --- /dev/null +++ b/plugins/woocommerce-admin/client/lib/init-remote-logging.ts @@ -0,0 +1,10 @@ +/** + * External dependencies + */ +import { init } from '@woocommerce/remote-logging'; + +export const initRemoteLogging = () => { + init( { + errorRateLimitMs: 60000, // 1 minute + } ); +}; diff --git a/plugins/woocommerce-admin/package.json b/plugins/woocommerce-admin/package.json index 179409d6bd3..3106b273088 100644 --- a/plugins/woocommerce-admin/package.json +++ b/plugins/woocommerce-admin/package.json @@ -163,6 +163,7 @@ "@woocommerce/number": "workspace:*", "@woocommerce/onboarding": "workspace:*", "@woocommerce/product-editor": "workspace:*", + "@woocommerce/remote-logging": "workspace:*", "@woocommerce/tracks": "workspace:*", "@wordpress/babel-preset-default": "^6.17.0", "@wordpress/block-editor": "^9.8.0", @@ -305,6 +306,9 @@ "node_modules/@woocommerce/tracks/build", "node_modules/@woocommerce/tracks/build-module", "node_modules/@woocommerce/tracks/build-types", + "node_modules/@woocommerce/remote-logging/build", + "node_modules/@woocommerce/remote-logging/build-module", + "node_modules/@woocommerce/remote-logging/build-types", "node_modules/@woocommerce/product-editor/build", "node_modules/@woocommerce/product-editor/build-module", "node_modules/@woocommerce/product-editor/build-style", diff --git a/plugins/woocommerce-admin/webpack.config.js b/plugins/woocommerce-admin/webpack.config.js index f87c9b41fb1..7f3fa90597e 100644 --- a/plugins/woocommerce-admin/webpack.config.js +++ b/plugins/woocommerce-admin/webpack.config.js @@ -42,6 +42,7 @@ const wcAdminPackages = [ 'onboarding', 'block-templates', 'product-editor', + 'remote-logging', ]; // wpAdminScripts are loaded on wp-admin pages outside the context of WooCommerce Admin // See ./client/wp-admin-scripts/README.md for more details diff --git a/plugins/woocommerce/changelog/add-wcadmin-remote-logging b/plugins/woocommerce/changelog/add-wcadmin-remote-logging new file mode 100644 index 00000000000..98741a8c74c --- /dev/null +++ b/plugins/woocommerce/changelog/add-wcadmin-remote-logging @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Integrate JS remote logging package in WooCommerce Admin diff --git a/plugins/woocommerce/src/Blocks/Assets/AssetDataRegistry.php b/plugins/woocommerce/src/Blocks/Assets/AssetDataRegistry.php index c1c6824aeba..453417c338c 100644 --- a/plugins/woocommerce/src/Blocks/Assets/AssetDataRegistry.php +++ b/plugins/woocommerce/src/Blocks/Assets/AssetDataRegistry.php @@ -3,6 +3,7 @@ namespace Automattic\WooCommerce\Blocks\Assets; use Automattic\WooCommerce\Blocks\Package; use Automattic\WooCommerce\Blocks\Domain\Services\Hydration; +use Automattic\WooCommerce\Internal\Logging\RemoteLogger; use Exception; use InvalidArgumentException; @@ -89,6 +90,7 @@ class AssetDataRegistry { 'dateFormat' => wc_date_format(), 'homeUrl' => esc_url( home_url( '/' ) ), 'locale' => $this->get_locale_data(), + 'isRemoteLoggingEnabled' => wc_get_container()->get( RemoteLogger::class )->is_remote_logging_allowed(), 'dashboardUrl' => wc_get_account_endpoint_url( 'dashboard' ), 'orderStatuses' => $this->get_order_statuses(), 'placeholderImgSrc' => wc_placeholder_img_src(), diff --git a/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php b/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php index 11ac3e2893b..131c4f6c1e9 100644 --- a/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php +++ b/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php @@ -279,6 +279,7 @@ class WCAdminAssets { 'wc-navigation', 'wc-block-templates', 'wc-product-editor', + 'wc-remote-logging', ); $scripts_map = array( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35029ec5737..562f27b24d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3600,6 +3600,9 @@ importers: '@woocommerce/product-editor': specifier: workspace:* version: link:../../packages/js/product-editor + '@woocommerce/remote-logging': + specifier: workspace:* + version: link:../../packages/js/remote-logging '@woocommerce/tracks': specifier: workspace:* version: link:../../packages/js/tracks