public static function debug( $msg, $backtrace_limit = false ) {
if ( ! defined( 'LSCWP_LOG' ) ) {
if ( defined( 'LSCWP_DEBUG_EXC_STRINGS' ) && Utility::str_hit_array( $msg, LSCWP_DEBUG_EXC_STRINGS ) ) {
if ( false !== $backtrace_limit ) {
if ( ! is_numeric( $backtrace_limit ) ) {
$backtrace_limit = self::trim_longtext( $backtrace_limit );
if ( is_array( $backtrace_limit ) && 1 === count( $backtrace_limit ) && ! empty( $backtrace_limit[0] ) ) {
$msg .= ' --- ' . $backtrace_limit[0];
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
$msg .= ' --- ' . var_export( $backtrace_limit, true );
self::push( $msg, (int) $backtrace_limit + 1 );
* Trim strings inside arrays/object dumps to reasonable length.
* @param mixed $backtrace_limit Data to trim.
public static function trim_longtext( $backtrace_limit ) {
if ( is_array( $backtrace_limit ) ) {
$backtrace_limit = array_map( __CLASS__ . '::trim_longtext', $backtrace_limit );
if ( is_string( $backtrace_limit ) && strlen( $backtrace_limit ) > 500 ) {
$backtrace_limit = substr( $backtrace_limit, 0, 1000 ) . '...';
* Log a verbose debug message (requires O_DEBUG_LEVEL).
* @param string $msg Message.
* @param int|array $backtrace_limit Backtrace depth or payload to append.
public static function debug2( $msg, $backtrace_limit = false ) {
if ( ! defined( 'LSCWP_LOG_MORE' ) ) {
self::debug( $msg, $backtrace_limit );
* Append a message to the active log file.
* @param string $msg Message.
* @param int|bool $backtrace_limit Backtrace depth.
private static function push( $msg, $backtrace_limit = false ) {
if ( defined( 'LSCWP_LOG_MORE' ) && false !== $backtrace_limit ) {
$msg .= self::_backtrace_info( (int) $backtrace_limit );
File::append( self::$log_path, self::format_message( $msg ) );
* Create a compact backtrace string.
* @param int $backtrace_limit Depth.
private static function _backtrace_info( $backtrace_limit ) {
$limit = (int) $backtrace_limit;
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
$trace = debug_backtrace( false, $limit + 3 );
for ( $i = 2; $i <= $limit + 2; $i++ ) {
// 0 => _backtrace_info(), 1 => push().
if ( empty( $trace[ $i ]['class'] ) ) {
if ( empty( $trace[ $i ]['file'] ) ) {
$log = "\n" . $trace[ $i ]['file'];
if ( __CLASS__ === $trace[ $i ]['class'] ) {
if ( ! empty( $trace[ $i ]['args'] ) ) {
foreach ( $trace[ $i ]['args'] as $v ) {
if ( is_string( $v ) || is_numeric( $v ) ) {
$args = substr( $args, 0, strlen( $args ) > 100 ? 100 : -1 );
$log = str_replace( 'Core', 'LSC', $trace[ $i ]['class'] ) . $trace[ $i ]['type'] . $trace[ $i ]['function'] . '(' . $args . ')';
if ( ! empty( $trace[ $i - 1 ]['line'] ) ) {
$log .= '@' . $trace[ $i - 1 ]['line'];
* Clear all log files (debug|purge|crawler).
private function _clear_log() {
$logs = [ 'debug', 'purge', 'crawler' ];
foreach ( $logs as $log ) {
File::save( $this->path( $log ), '' );
* Handle requests routed to this class.
public function handler() {
$type = Router::verify_type();
case self::TYPE_CLEAR_LOG:
case self::TYPE_BETA_TEST: