Template API: Server-side logging (#41316)

This commit is contained in:
Matt Sherman 2023-11-15 11:07:11 -05:00 committed by GitHub
commit f4596deddb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 729 additions and 94 deletions

View File

@ -58,4 +58,4 @@ require 'features/features.php';
require 'rest-api-filters/class-wca-test-helper-rest-api-filters.php';
require 'rest-api-filters/hook.php';
require 'live-branches/manifest.php';
require 'live-branches/install.php';
require 'tools/set-block-template-logging-threshold.php';

View File

@ -0,0 +1,102 @@
<?php
defined( 'ABSPATH' ) || exit;
register_woocommerce_admin_test_helper_rest_route(
'/tools/get-logging-levels/v1',
'tools_get_logging_levels',
array(
'methods' => 'GET',
)
);
register_woocommerce_admin_test_helper_rest_route(
'/tools/get-block-template-logging-threshold/v1',
'tools_get_block_template_logging_threshold',
array(
'methods' => 'GET',
)
);
register_woocommerce_admin_test_helper_rest_route(
'/tools/update-block-template-logging-threshold/v1',
'tools_update_block_template_logging_threshold',
array(
'methods' => 'POST',
'args' => array(
'threshold' => array(
'description' => 'Logging threshold',
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
),
)
);
/**
* Get the list of logging levels.
*/
function tools_get_logging_levels() {
$levels = array(
array(
'label' => 'Emergency',
'value' => \WC_Log_Levels::EMERGENCY,
),
array(
'label' => 'Alert',
'value' => \WC_Log_Levels::ALERT,
),
array(
'label' => 'Critical',
'value' => \WC_Log_Levels::CRITICAL,
),
array(
'label' => 'Error',
'value' => \WC_Log_Levels::ERROR,
),
array(
'label' => 'Warning',
'value' => \WC_Log_Levels::WARNING,
),
array(
'label' => 'Notice',
'value' => \WC_Log_Levels::NOTICE,
),
array(
'label' => 'Info',
'value' => \WC_Log_Levels::INFO,
),
array(
'label' => 'Debug',
'value' => \WC_Log_Levels::DEBUG,
),
);
return new WP_REST_Response( $levels, 200 );
}
/**
* Get the block template logging threshold.
*/
function tools_get_block_template_logging_threshold() {
$threshold = get_option( 'woocommerce_block_template_logging_threshold', \WC_Log_Levels::WARNING );
return new WP_REST_Response( $threshold, 200 );
}
/**
* Update the block template logging threshold.
*
* @param WP_REST_Request $request The full request data.
*/
function tools_update_block_template_logging_threshold( $request ) {
$threshold = $request->get_param( 'threshold' );
if ( ! isset( $threshold ) || ! \WC_Log_Levels::is_valid_level( $threshold ) ) {
return new WP_REST_Response( 'Invalid threshold', 400 );
}
update_option( 'woocommerce_block_template_logging_threshold', $threshold );
return new WP_REST_Response( $threshold, 200 );
}

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add ability to update block templates logging threshold.

View File

@ -91,8 +91,8 @@
&.command {
white-space: nowrap;
}
.trigger-cron-job {
width: 40%;
.select-description {
width: 60%;
padding-top: 4px;
.components-base-control__field {
margin-bottom: 0;

View File

@ -3,6 +3,10 @@
*/
import { TriggerCronJob, TRIGGER_CRON_ACTION_NAME } from './trigger-cron';
import { DisableEmail } from './disable-email';
import {
UpdateBlockTemplateLoggingThreshold,
UPDATE_BLOCK_TEMPLATE_LOGGING_THRESHOLD_ACTION_NAME,
} from './update-block-template-logging-threshold';
import {
TriggerUpdateCallbacks,
TRIGGER_UPDATE_CALLBACKS_ACTION_NAME,
@ -69,4 +73,9 @@ export default [
description: 'Resets Customize Your Store changes.',
action: 'resetCustomizeYourStore',
},
{
command: 'Update block template logging threshold',
description: <UpdateBlockTemplateLoggingThreshold />,
action: UPDATE_BLOCK_TEMPLATE_LOGGING_THRESHOLD_ACTION_NAME,
},
];

View File

@ -32,7 +32,7 @@ export const TriggerCronJob = () => {
}
return (
<div className="trigger-cron-job">
<div className="select-description">
{ ! cronList ? (
<p>Loading ...</p>
) : (

View File

@ -35,7 +35,7 @@ export const TriggerUpdateCallbacks = () => {
}
return (
<div className="trigger-cron-job">
<div className="select-description">
{ ! dbUpdateVersions ? (
<p>Loading ...</p>
) : (

View File

@ -0,0 +1,91 @@
/**
* External dependencies
*/
import { SelectControl } from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore no types
// eslint-disable-next-line @woocommerce/dependency-group
import { useSelect, useDispatch } from '@wordpress/data';
/**
* Internal dependencies
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore no types
// eslint-disable-next-line @woocommerce/dependency-group
import { STORE_KEY } from '../data/constants';
export const UPDATE_BLOCK_TEMPLATE_LOGGING_THRESHOLD_ACTION_NAME =
'updateBlockTemplateLoggingThreshold';
interface LoggingLevel {
label: string;
value: string;
}
export const UpdateBlockTemplateLoggingThreshold = () => {
const { loggingLevels, threshold, isLoading } = useSelect(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore no types
( select ) => {
const { getLoggingLevels, getBlockTemplateLoggingThreshold } =
select( STORE_KEY );
const retrievedLoggingLevels = getLoggingLevels();
const retrievedThreshold = getBlockTemplateLoggingThreshold();
return {
loggingLevels: retrievedLoggingLevels,
threshold: retrievedThreshold,
isLoading: ! retrievedLoggingLevels || ! retrievedThreshold,
};
}
);
const [ newThreshold, setNewThreshold ] = useState( threshold );
const { updateCommandParams } = useDispatch( STORE_KEY );
function onThresholdChange( selectedThreshold: string ) {
setNewThreshold( selectedThreshold );
updateCommandParams(
UPDATE_BLOCK_TEMPLATE_LOGGING_THRESHOLD_ACTION_NAME,
{
threshold: selectedThreshold,
}
);
}
function getOptions() {
return loggingLevels.map( ( loggingLevel: LoggingLevel ) => {
return {
label: loggingLevel.label,
value: loggingLevel.value,
};
} );
}
useEffect( () => {
setNewThreshold( threshold );
}, [ threshold ] );
return (
<div className="select-description">
{ isLoading ? (
<p>Loading...</p>
) : (
<SelectControl
label="Threshold"
onChange={ onThresholdChange }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore labelPosition prop exists
labelPosition="side"
options={ getOptions() }
value={ newThreshold }
/>
) }
</div>
);
};

View File

@ -8,6 +8,11 @@ const TYPES = {
SET_CRON_JOBS: 'SET_CRON_JOBS',
IS_EMAIL_DISABLED: 'IS_EMAIL_DISABLED',
SET_DB_UPDATE_VERSIONS: 'SET_DB_UPDATE_VERSIONS',
SET_LOGGING_LEVELS: 'SET_LOGGING_LEVELS',
SET_BLOCK_TEMPLATE_LOGGING_THRESHOLD:
'SET_BLOCK_TEMPLATE_LOGGING_THRESHOLD',
UPDATE_BLOCK_TEMPLATE_LOGGING_THRESHOLD:
'UPDATE_BLOCK_TEMPLATE_LOGGING_THRESHOLD',
};
export default TYPES;

View File

@ -229,3 +229,31 @@ export function* resetCustomizeYourStore() {
} );
} );
}
export function setLoggingLevels( loggingLevels ) {
return {
type: TYPES.SET_LOGGING_LEVELS,
loggingLevels,
};
}
export function setBlockTemplateLoggingThreshold(
blockTemplateLoggingThreshold
) {
return {
type: TYPES.SET_BLOCK_TEMPLATE_LOGGING_THRESHOLD,
blockTemplateLoggingThreshold,
};
}
export function* updateBlockTemplateLoggingThreshold( params ) {
yield runCommand( 'Update block template logging threshold', function* () {
yield apiFetch( {
path:
API_NAMESPACE +
'/tools/update-block-template-logging-threshold/v1',
method: 'POST',
data: params,
} );
} );
}

View File

@ -12,6 +12,8 @@ const DEFAULT_STATE = {
params: [],
status: '',
dbUpdateVersions: [],
loggingLevels: null,
blockTemplateLoggingThreshold: null,
};
const reducer = ( state = DEFAULT_STATE, action ) => {
@ -80,6 +82,17 @@ const reducer = ( state = DEFAULT_STATE, action ) => {
...state,
dbUpdateVersions: action.versions,
};
case TYPES.SET_LOGGING_LEVELS:
return {
...state,
loggingLevels: action.loggingLevels,
};
case TYPES.SET_BLOCK_TEMPLATE_LOGGING_THRESHOLD:
return {
...state,
blockTemplateLoggingThreshold:
action.blockTemplateLoggingThreshold,
};
default:
return state;
}

View File

@ -8,9 +8,11 @@ import { apiFetch } from '@wordpress/data-controls';
*/
import { API_NAMESPACE } from './constants';
import {
setBlockTemplateLoggingThreshold,
setCronJobs,
setDBUpdateVersions,
setIsEmailDisabled,
setLoggingLevels,
} from './actions';
export function* getCronJobs() {
@ -55,3 +57,31 @@ export function* getIsEmailDisabled() {
throw new Error( error );
}
}
export function* getLoggingLevels() {
const path = `${ API_NAMESPACE }/tools/get-logging-levels/v1`;
try {
const response = yield apiFetch( {
path,
method: 'GET',
} );
yield setLoggingLevels( response );
} catch ( error ) {
throw new Error( error );
}
}
export function* getBlockTemplateLoggingThreshold() {
const path = `${ API_NAMESPACE }/tools/get-block-template-logging-threshold/v1`;
try {
const response = yield apiFetch( {
path,
method: 'GET',
} );
yield setBlockTemplateLoggingThreshold( response );
} catch ( error ) {
throw new Error( error );
}
}

View File

@ -25,3 +25,11 @@ export function getIsEmailDisabled( state ) {
export function getDBUpdateVersions( state ) {
return state.dbUpdateVersions;
}
export function getLoggingLevels( state ) {
return state.loggingLevels;
}
export function getBlockTemplateLoggingThreshold( state ) {
return state.blockTemplateLoggingThreshold;
}

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Improve block template API logging.

View File

@ -10,6 +10,8 @@ use Automattic\WooCommerce\Internal\Admin\Features\ProductBlockEditor\ProductTem
use Automattic\WooCommerce\Internal\Admin\Features\ProductBlockEditor\ProductTemplates\ProductVariationTemplate;
use Automattic\WooCommerce\Admin\PageController;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplateRegistry;
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\Block;
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplateLogger;
use WP_Block_Editor_Context;
/**
@ -62,6 +64,9 @@ class Init {
$tracks = new Tracks();
$tracks->init();
// Make sure the block template logger is initialized before any templates are created.
BlockTemplateLogger::get_instance();
}
}
@ -199,13 +204,22 @@ class Init {
private function get_product_editor_settings() {
$editor_settings = array();
$template_registry = wc_get_container()->get( BlockTemplateRegistry::class );
$template_registry = wc_get_container()->get( BlockTemplateRegistry::class );
$block_template_logger = BlockTemplateLogger::get_instance();
$block_template_logger->log_template_events_to_file( 'simple-product' );
$block_template_logger->log_template_events_to_file( 'product-variation' );
$editor_settings['templates'] = array(
'product' => $template_registry->get_registered( 'simple-product' )->get_formatted_template(),
'product_variation' => $template_registry->get_registered( 'product-variation' )->get_formatted_template(),
);
$editor_settings['templateEvents'] = array(
'product' => $block_template_logger->get_formatted_template_events( 'simple-product' ),
'product_variation' => $block_template_logger->get_formatted_template_events( 'product-variation' ),
);
$block_editor_context = new WP_Block_Editor_Context( array( 'name' => self::EDITOR_CONTEXT_NAME ) );
return get_block_editor_settings( $editor_settings, $block_editor_context );

View File

@ -252,6 +252,15 @@ class AbstractBlock implements BlockInterface {
'expression' => $expression,
);
/**
* Action called after a hide condition is added to a block.
*
* @param BlockInterface $block The block.
*
* @since 8.4.0
*/
do_action( 'woocommerce_block_template_after_add_hide_condition', $this );
return $key;
}
@ -262,6 +271,15 @@ class AbstractBlock implements BlockInterface {
*/
public function remove_hide_condition( string $key ) {
unset( $this->hide_conditions[ $key ] );
/**
* Action called after a hide condition is removed from a block.
*
* @param BlockInterface $block The block.
*
* @since 8.4.0
*/
do_action( 'woocommerce_block_template_after_remove_hide_condition', $this );
}
/**

View File

@ -18,7 +18,7 @@ trait BlockContainerTrait {
*
* @var BlockInterface[]
*/
private $inner_blocks = [];
private $inner_blocks = array();
// phpcs doesn't take into account exceptions thrown by called methods.
// phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
@ -43,16 +43,7 @@ trait BlockContainerTrait {
}
$is_detached = method_exists( $this, 'is_detached' ) && $this->is_detached();
if ( $is_detached ) {
BlockTemplateLogger::get_instance()->warning(
'Block added to detached container. Block will not be included in the template, since the container will not be included in the template.',
[
'block' => $block,
'container' => $this,
'template' => $this->get_root_template(),
]
);
} else {
if ( ! $is_detached ) {
$this->get_root_template()->cache_block( $block );
}
@ -169,14 +160,6 @@ trait BlockContainerTrait {
}
);
BlockTemplateLogger::get_instance()->info(
'Block removed from template.',
[
'block' => $block,
'template' => $root_template,
]
);
$this->do_after_remove_block_action( $block );
$this->do_after_remove_specific_block_action( $block );
}
@ -237,12 +220,7 @@ trait BlockContainerTrait {
*/
do_action( 'woocommerce_block_template_after_add_block', $block );
} catch ( \Exception $e ) {
$this->handle_exception_doing_action(
'Error after adding block to template.',
'woocommerce_block_template_after_add_block',
$block,
$e
);
$this->do_after_add_block_error_action( $block, 'woocommerce_block_template_after_add_block', $e );
}
}
@ -266,15 +244,35 @@ trait BlockContainerTrait {
*/
do_action( "woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_add_block_{$block->get_id()}", $block );
} catch ( \Exception $e ) {
$this->handle_exception_doing_action(
'Error after adding block to template.',
"woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_add_block_{$block->get_id()}",
$block,
$e
);
$this->do_after_add_block_error_action( $block, "woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_add_block_{$block->get_id()}", $e );
}
}
/**
* Do the `woocommerce_block_after_add_block_error` action.
*
* @param BlockInterface $block The block.
* @param string $action The action that threw the exception.
* @param \Exception $e The exception.
*/
private function do_after_add_block_error_action( BlockInterface $block, string $action, \Exception $e ) {
/**
* Action called after an exception is thrown by a `woocommerce_block_template_after_add_block` action hook.
*
* @param BlockInterface $block The block.
* @param string $action The action that threw the exception.
* @param \Exception $exception The exception.
*
* @since 8.4.0
*/
do_action(
'woocommerce_block_template_after_add_block_error',
$block,
$action,
$e,
);
}
/**
* Do the `woocommerce_block_template_after_remove_block` action.
* Handle exceptions thrown by the action.
@ -295,12 +293,7 @@ trait BlockContainerTrait {
*/
do_action( 'woocommerce_block_template_after_remove_block', $block );
} catch ( \Exception $e ) {
$this->handle_exception_doing_action(
'Error after removing block from template.',
'woocommerce_block_template_after_remove_block',
$block,
$e
);
$this->do_after_remove_block_error_action( $block, 'woocommerce_block_template_after_remove_block', $e );
}
}
@ -324,33 +317,32 @@ trait BlockContainerTrait {
*/
do_action( "woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_remove_block_{$block->get_id()}", $block );
} catch ( \Exception $e ) {
$this->handle_exception_doing_action(
'Error after removing block from template.',
"woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_remove_block_{$block->get_id()}",
$block,
$e
);
$this->do_after_remove_block_error_action( $block, "woocommerce_block_template_area_{$this->get_root_template()->get_area()}_after_remove_block_{$block->get_id()}", $e );
}
}
/**
* Handle an exception thrown by an action.
* Do the `woocommerce_block_after_remove_block_error` action.
*
* @param string $message The message.
* @param string $action_tag The action tag.
* @param BlockInterface $block The block.
* @param \Exception $e The exception.
* @param BlockInterface $block The block.
* @param string $action The action that threw the exception.
* @param \Exception $e The exception.
*/
private function handle_exception_doing_action( string $message, string $action_tag, BlockInterface $block, \Exception $e ) {
BlockTemplateLogger::get_instance()->error(
$message,
[
'exception' => $e,
'action' => $action_tag,
'container' => $this,
'block' => $block,
'template' => $this->get_root_template(),
],
private function do_after_remove_block_error_action( BlockInterface $block, string $action, \Exception $e ) {
/**
* Action called after an exception is thrown by a `woocommerce_block_template_after_remove_block` action hook.
*
* @param BlockInterface $block The block.
* @param string $action The action that threw the exception.
* @param \Exception $exception The exception.
*
* @since 8.4.0
*/
do_action(
'woocommerce_block_template_after_remove_block_error',
$block,
$action,
$e,
);
}
}

View File

@ -5,11 +5,68 @@ namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockContainerInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
use Automattic\WooCommerce\Admin\BlockTemplates\ContainerInterface;
/**
* Logger for block template modifications.
*/
class BlockTemplateLogger {
const BLOCK_ADDED = 'block_added';
const BLOCK_REMOVED = 'block_removed';
const BLOCK_MODIFIED = 'block_modified';
const BLOCK_ADDED_TO_DETACHED_CONTAINER = 'block_added_to_detached_container';
const HIDE_CONDITION_ADDED = 'hide_condition_added';
const HIDE_CONDITION_REMOVED = 'hide_condition_removed';
const HIDE_CONDITION_ADDED_TO_DETACHED_BLOCK = 'hide_condition_added_to_detached_block';
const ERROR_AFTER_BLOCK_ADDED = 'error_after_block_added';
const ERROR_AFTER_BLOCK_REMOVED = 'error_after_block_removed';
const LOG_HASH_TRANSIENT_BASE_NAME = 'wc_block_template_events_log_hash_';
/**
* Event types.
*
* @var array
*/
public static $event_types = array(
self::BLOCK_ADDED => array(
'level' => \WC_Log_Levels::DEBUG,
'message' => 'Block added to template.',
),
self::BLOCK_REMOVED => array(
'level' => \WC_Log_Levels::NOTICE,
'message' => 'Block removed from template.',
),
self::BLOCK_MODIFIED => array(
'level' => \WC_Log_Levels::NOTICE,
'message' => 'Block modified in template.',
),
self::BLOCK_ADDED_TO_DETACHED_CONTAINER => array(
'level' => \WC_Log_Levels::WARNING,
'message' => 'Block added to detached container. Block will not be included in the template, since the container will not be included in the template.',
),
self::HIDE_CONDITION_ADDED => array(
'level' => \WC_Log_Levels::NOTICE,
'message' => 'Hide condition added to block.',
),
self::HIDE_CONDITION_REMOVED => array(
'level' => \WC_Log_Levels::NOTICE,
'message' => 'Hide condition removed from block.',
),
self::HIDE_CONDITION_ADDED_TO_DETACHED_BLOCK => array(
'level' => \WC_Log_Levels::WARNING,
'message' => 'Hide condition added to detached block. Block will not be included in the template, so the hide condition is not needed.',
),
self::ERROR_AFTER_BLOCK_ADDED => array(
'level' => \WC_Log_Levels::WARNING,
'message' => 'Error after block added to template.',
),
self::ERROR_AFTER_BLOCK_REMOVED => array(
'level' => \WC_Log_Levels::WARNING,
'message' => 'Error after block removed from template.',
),
);
/**
* Singleton instance.
*
@ -24,6 +81,27 @@ class BlockTemplateLogger {
*/
protected $logger = null;
/**
* All template events.
*
* @var array
*/
private $all_template_events = array();
/**
* Templates.
*
* @var array
*/
private $templates = array();
/**
* Threshold severity.
*
* @var int
*/
private $threshold_severity = null;
/**
* Get the singleton instance.
*/
@ -40,44 +118,283 @@ class BlockTemplateLogger {
*/
protected function __construct() {
$this->logger = wc_get_logger();
}
/**
* Log an informational message.
*
* @param string $message Message to log.
* @param array $info Additional info to log.
*/
public function info( string $message, array $info = [] ) {
$this->logger->info(
$this->format_message( $message, $info ),
[ 'source' => 'block_template' ]
$threshold = get_option( 'woocommerce_block_template_logging_threshold', \WC_Log_Levels::WARNING );
if ( ! \WC_Log_Levels::is_valid_level( $threshold ) ) {
$threshold = \WC_Log_Levels::INFO;
}
$this->threshold_severity = \WC_Log_Levels::get_level_severity( $threshold );
add_action(
'woocommerce_block_template_after_add_block',
function ( BlockInterface $block ) {
$is_detached = method_exists( $block->get_parent(), 'is_detached' ) && $block->get_parent()->is_detached();
$this->log(
$is_detached
? $this::BLOCK_ADDED_TO_DETACHED_CONTAINER
: $this::BLOCK_ADDED,
$block,
);
},
0,
);
add_action(
'woocommerce_block_template_after_remove_block',
function ( BlockInterface $block ) {
$this->log(
$this::BLOCK_REMOVED,
$block,
);
},
0,
);
add_action(
'woocommerce_block_template_after_add_hide_condition',
function ( BlockInterface $block ) {
$this->log(
$block->is_detached()
? $this::HIDE_CONDITION_ADDED_TO_DETACHED_BLOCK
: $this::HIDE_CONDITION_ADDED,
$block,
);
},
0
);
add_action(
'woocommerce_block_template_after_remove_hide_condition',
function ( BlockInterface $block ) {
$this->log(
$this::HIDE_CONDITION_REMOVED,
$block,
);
},
0
);
add_action(
'woocommerce_block_template_after_add_block_error',
function ( BlockInterface $block, string $action, \Exception $exception ) {
$this->log(
$this::ERROR_AFTER_BLOCK_ADDED,
$block,
array(
'action' => $action,
'exception' => $exception,
),
);
},
0,
3
);
add_action(
'woocommerce_block_template_after_remove_block_error',
function ( BlockInterface $block, string $action, \Exception $exception ) {
$this->log(
$this::ERROR_AFTER_BLOCK_REMOVED,
$block,
array(
'action' => $action,
'exception' => $exception,
),
);
},
0,
3
);
}
/**
* Log a warning message.
* Get all template events for a given template.
*
* @param string $message Message to log.
* @param array $info Additional info to log.
* @param string $template_id Template ID.
*/
public function warning( string $message, array $info = [] ) {
$this->logger->warning(
$this->format_message( $message, $info ),
[ 'source' => 'block_template' ]
);
public function get_formatted_template_events( string $template_id ): array {
if ( ! isset( $this->all_template_events[ $template_id ] ) ) {
return array();
}
$template_events = $this->all_template_events[ $template_id ];
$template = $this->templates[ $template_id ];
$formatted_template_events = array();
foreach ( $template_events as $template_event ) {
$container = $template_event['container'];
$block = $template_event['block'];
$formatted_template_events[] = array(
'level' => $template_event['level'],
'event_type' => $template_event['event_type'],
'message' => $template_event['message'],
'container' => $container instanceof BlockInterface
? array(
'id' => $container->get_id(),
'name' => $container->get_name(),
)
: null,
'block' => array(
'id' => $block->get_id(),
'name' => $block->get_name(),
),
'additional_info' => $this->format_info( $template_event['additional_info'] ),
);
}
return $formatted_template_events;
}
/**
* Log an error message.
* Log all template events for a given template to the log file.
*
* @param string $message Message to log.
* @param array $info Additional info to log.
* @param string $template_id Template ID.
*/
public function error( string $message, array $info = [] ) {
$this->logger->error(
$this->format_message( $message, $info ),
[ 'source' => 'block_template' ]
public function log_template_events_to_file( string $template_id ) {
if ( ! isset( $this->all_template_events[ $template_id ] ) ) {
return;
}
$template_events = $this->all_template_events[ $template_id ];
$hash = $this->generate_template_events_hash( $template_events );
if ( ! $this->has_template_events_changed( $template_id, $hash ) ) {
// Nothing has changed since the last time this was logged,
// so don't log it again.
return;
}
$this->set_template_events_log_hash( $template_id, $hash );
$template = $this->templates[ $template_id ];
foreach ( $template_events as $template_event ) {
$info = array_merge(
array(
'template' => $template,
'container' => $template_event['container'],
'block' => $template_event['block'],
),
$template_event['additional_info']
);
$message = $this->format_message( $template_event['message'], $info );
$this->logger->log(
$template_event['level'],
$message,
array( 'source' => 'block_template' )
);
}
}
/**
* Has the template events changed since the last time they were logged?
*
* @param string $template_id Template ID.
* @param string $events_hash Events hash.
*/
private function has_template_events_changed( string $template_id, string $events_hash ) {
$previous_hash = get_transient( self::LOG_HASH_TRANSIENT_BASE_NAME . $template_id );
return $previous_hash !== $events_hash;
}
/**
* Generate a hash for a given set of template events.
*
* @param array $template_events Template events.
*/
private function generate_template_events_hash( array $template_events ): string {
return md5( wp_json_encode( $template_events ) );
}
/**
* Set the template events hash for a given template.
*
* @param string $template_id Template ID.
* @param string $hash Hash of template events.
*/
private function set_template_events_log_hash( string $template_id, string $hash ) {
set_transient( self::LOG_HASH_TRANSIENT_BASE_NAME . $template_id, $hash );
}
/**
* Log an event.
*
* @param string $event_type Event type.
* @param BlockInterface $block Block.
* @param array $additional_info Additional info.
*/
private function log( string $event_type, BlockInterface $block, $additional_info = array() ) {
if ( ! isset( self::$event_types[ $event_type ] ) ) {
/* translators: 1: WC_Logger::log 2: level */
wc_doing_it_wrong( __METHOD__, sprintf( __( '%1$s was called with an invalid event type "%2$s".', 'woocommerce' ), '<code>BlockTemplateLogger::log</code>', $event_type ), '8.4' );
}
$event_type_info = isset( self::$event_types[ $event_type ] )
? array_merge(
self::$event_types[ $event_type ],
array(
'event_type' => $event_type,
)
)
: array(
'level' => \WC_Log_Levels::ERROR,
'event_type' => $event_type,
'message' => 'Unknown error.',
);
if ( ! $this->should_handle( $event_type_info['level'] ) ) {
return;
}
$template = $block->get_root_template();
$container = $block->get_parent();
$this->add_template_event( $event_type_info, $template, $container, $block, $additional_info );
}
/**
* Should the logger handle a given level?
*
* @param int $level Level to check.
*/
private function should_handle( $level ) {
return $this->threshold_severity <= \WC_Log_Levels::get_level_severity( $level );
}
/**
* Add a template event.
*
* @param array $event_type_info Event type info.
* @param BlockTemplateInterface $template Template.
* @param ContainerInterface $container Container.
* @param BlockInterface $block Block.
* @param array $additional_info Additional info.
*/
private function add_template_event( array $event_type_info, BlockTemplateInterface $template, ContainerInterface $container, BlockInterface $block, array $additional_info = array() ) {
$template_id = $template->get_id();
if ( ! isset( $this->all_template_events[ $template_id ] ) ) {
$this->all_template_events[ $template_id ] = array();
$this->templates[ $template_id ] = $template;
}
$template_events = &$this->all_template_events[ $template_id ];
$template_events[] = array(
'level' => $event_type_info['level'],
'event_type' => $event_type_info['event_type'],
'message' => $event_type_info['message'],
'container' => $container,
'block' => $block,
'additional_info' => $additional_info,
);
}
@ -87,7 +404,7 @@ class BlockTemplateLogger {
* @param string $message Message to log.
* @param array $info Additional info to log.
*/
private function format_message( string $message, array $info = [] ): string {
private function format_message( string $message, array $info = array() ): string {
$formatted_message = sprintf(
"%s\n%s",
$message,
@ -137,12 +454,12 @@ class BlockTemplateLogger {
* @param \Exception $exception Exception to format.
*/
private function format_exception( \Exception $exception ): array {
return [
return array(
'message' => $exception->getMessage(),
'source' => "{$exception->getFile()}: {$exception->getLine()}",
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
'trace' => print_r( $this->format_exception_trace( $exception->getTrace() ), true ),
];
);
}
/**
@ -151,7 +468,7 @@ class BlockTemplateLogger {
* @param array $trace Exception trace to format.
*/
private function format_exception_trace( array $trace ): array {
$formatted_trace = [];
$formatted_trace = array();
foreach ( $trace as $source ) {
$formatted_trace[] = "{$source['file']}: {$source['line']}";