Skip to content

Commit 226ef97

Browse files
authored
[Setup] Log JS errors that occur on Setup page (#530)
1 parent ae758a3 commit 226ef97

File tree

6 files changed

+211
-6
lines changed

6 files changed

+211
-6
lines changed

app/code/Meta/BusinessExtension/Block/Adminhtml/Setup.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,16 @@ public function getCleanCacheAjaxRoute()
267267
return $this->fbeHelper->getUrl('fbeadmin/ajax/cleanCache');
268268
}
269269

270+
/**
271+
* Get the ajax route to report client errors.
272+
*
273+
* @return mixed
274+
*/
275+
public function getReportClientErrorRoute()
276+
{
277+
return $this->fbeHelper->getUrl('fbeadmin/ajax/reportClientError');
278+
}
279+
270280
/**
271281
* Get Delete Asset IDs Ajax Route
272282
*
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) Meta Platforms, Inc. and affiliates.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Meta\BusinessExtension\Controller\Adminhtml\Ajax;
22+
23+
use Magento\Backend\App\Action\Context;
24+
use Magento\Framework\Controller\Result\JsonFactory;
25+
use Meta\BusinessExtension\Helper\FBEHelper;
26+
use Meta\BusinessExtension\Model\System\Config as SystemConfig;
27+
28+
class ReportClientError extends AbstractAjax
29+
{
30+
private const JS_EXCEPTION_CODE = 100;
31+
32+
/**
33+
* @var FBEHelper
34+
*/
35+
private $fbeHelper;
36+
37+
/**
38+
* Construct
39+
*
40+
* @param Context $context
41+
* @param JsonFactory $resultJsonFactory
42+
* @param FBEHelper $fbeHelper
43+
*/
44+
public function __construct(
45+
Context $context,
46+
JsonFactory $resultJsonFactory,
47+
FBEHelper $fbeHelper,
48+
) {
49+
parent::__construct($context, $resultJsonFactory, $fbeHelper);
50+
$this->fbeHelper = $fbeHelper;
51+
}
52+
53+
/**
54+
* @inheritDoc
55+
*/
56+
public function executeForJson()
57+
{
58+
$storeID = $this->getRequest()->getParam('storeID');
59+
$message = $this->getRequest()->getParam('message');
60+
$stackTrace = $this->getRequest()->getParam('stackTrace');
61+
62+
$filename = $this->getRequest()->getParam('filename');
63+
$lineNumber = $this->getRequest()->getParam('line');
64+
$columnNumber = $this->getRequest()->getParam('column');
65+
66+
$loggingContext = [
67+
'event' => 'js_exception',
68+
'event_type' => 'error',
69+
'extra_data' => [
70+
'filename' => $filename,
71+
'line_number' => $lineNumber,
72+
'column_number' => $columnNumber,
73+
],
74+
'store_id' => $storeID,
75+
];
76+
77+
$this->fbeHelper->logExceptionDetailsImmediatelyToMeta(
78+
ReportClientError::JS_EXCEPTION_CODE,
79+
$message,
80+
$stackTrace,
81+
$loggingContext,
82+
);
83+
}
84+
}

app/code/Meta/BusinessExtension/Helper/FBEHelper.php

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,17 +304,23 @@ public function logException(Throwable $e, array $context = [])
304304
{
305305
$errorMessage = $e->getMessage();
306306
$exceptionTrace = $e->getTraceAsString();
307+
$exceptionCode = $e->getCode();
307308

309+
$this->logExceptionDetails($exceptionCode, $errorMessage, $exceptionTrace, $context);
310+
}
311+
312+
public function logExceptionDetails($code, $message, $traceAsString, array $context = [])
313+
{
308314
// If the log type is not set or Meta extension logging is not enabled just log the error message and trace.
309315
if (!isset($context['log_type']) || !$this->systemConfig->isMetaExceptionLoggingEnabled()) {
310-
$this->logger->error($errorMessage);
311-
$this->logger->error($exceptionTrace);
316+
$this->logger->error($message);
317+
$this->logger->error($traceAsString);
312318
return;
313319
}
314320

315-
$context['exception_message'] = $errorMessage;
316-
$context['exception_code'] = $e->getCode();
317-
$context['exception_trace'] = $exceptionTrace;
321+
$context['exception_message'] = $message;
322+
$context['exception_code'] = $code;
323+
$context['exception_trace'] = $traceAsString;
318324
$context['seller_platform_app_version'] = $this->getMagentoVersion();
319325

320326
$extraData = ['extension_version' => $this->systemConfig->getModuleVersion()];
@@ -330,7 +336,7 @@ public function logException(Throwable $e, array $context = [])
330336
$context['extra_data'] = $extraData;
331337
}
332338

333-
$this->logger->error($errorMessage, $context);
339+
$this->logger->error($message, $context);
334340
}
335341

336342
/**
@@ -345,6 +351,22 @@ public function logExceptionImmediatelyToMeta(Throwable $e, array $context = [])
345351
$this->logException($e, $context);
346352
}
347353

354+
/**
355+
* Log error details and persist immediately with Meta.
356+
*
357+
* `logExceptionImmediatelyToMeta` should be preferred whenever a /Throwable is available.
358+
*
359+
* @param int $code
360+
* @param string $message
361+
* @param string $traceAsString
362+
* @param array $context
363+
*/
364+
public function logExceptionDetailsImmediatelyToMeta($code, $message, $traceAsString, array $context = [])
365+
{
366+
$context['log_type'] = self::PERSIST_META_LOG_IMMEDIATELY;
367+
$this->logExceptionDetails($code, $message, $traceAsString, $context);
368+
}
369+
348370
/**
349371
* Log pixel event
350372
*

app/code/Meta/BusinessExtension/view/adminhtml/templates/setup.phtml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@
55
*/
66
?>
77

8+
<script>
9+
const errorEventsBeforeInit = [];
10+
const onEarlyError = (event) => {
11+
errorEventsBeforeInit.push(event);
12+
};
13+
window.addEventListener('error', onEarlyError);
14+
15+
require(['Meta_BusinessExtension/js/error_logging'], (errorLogging) => {
16+
window.removeEventListener('error', onEarlyError);
17+
18+
errorLogging.init(
19+
'<?=$escaper->escapeJs($block->getReportClientErrorRoute())?>',
20+
'<?=$escaper->escapeJs($block->getSelectedStoreId())?>',
21+
errorEventsBeforeInit,
22+
);
23+
});
24+
</script>
25+
826
<?php if ($block->getSelectedStoreId() === null): ?>
927
Create a Store to continue with setup.
1028
<?php else: ?>

app/code/Meta/BusinessExtension/view/adminhtml/web/js/commerce_extension.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the code directory.
8+
*/
9+
10+
'use strict';
11+
112
require(['jquery'], function (jQuery) {
213

314
const ajaxify = function (url) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the code directory.
8+
*/
9+
10+
'use strict';
11+
12+
define(['jquery'], function (jQuery) {
13+
14+
let _isInitialized = false;
15+
let _storeID = null;
16+
let _errorRoute = null;
17+
18+
const init = (errorRoute, storeID, errorEventsBeforeInit) => {
19+
if (_isInitialized) {
20+
throw new Error('error_logging.init() called multiple times');
21+
}
22+
23+
_errorRoute = errorRoute;
24+
_storeID = storeID;
25+
_isInitialized = true;
26+
27+
const ajaxParam = function (params) {
28+
if (window.FORM_KEY) {
29+
params.form_key = window.FORM_KEY;
30+
}
31+
return params;
32+
};
33+
34+
const onError = (event) => {
35+
const stackTrace = event.error && event.error.stack;
36+
const errorLog = {
37+
column: event.colno,
38+
filename: event.filename,
39+
line: event.lineno,
40+
message: event.message,
41+
stackTrace: stackTrace,
42+
storeID: _storeID,
43+
};
44+
45+
const ajaxURL = new URL(_errorRoute, document.baseURI);
46+
ajaxURL.searchParams.set('isAjax', 'true');
47+
48+
jQuery.ajax({
49+
type: 'post',
50+
data: ajaxParam(errorLog),
51+
url: ajaxURL,
52+
});
53+
};
54+
55+
window.addEventListener('error', onError);
56+
errorEventsBeforeInit.forEach(event => onError(event));
57+
};
58+
59+
return {init};
60+
});

0 commit comments

Comments
 (0)