manager = $manager; } /** * Log handler. * * @param Connector $connector Connector responsible for logging the event. * @param string $message sprintf-ready error message string. * @param array $args sprintf (and extra) arguments to use. * @param int $site_id Target site id. * @param string $context Context of the event. * @param string $action Action of the event. * @param int|null $state action status: null - N/A, 0 - failed, 1 - success. * @param int $user_id User responsible for the event. * * @return bool|WP_Error True if updated, otherwise false|WP_Error */ public function log( $connector, $message, $args, $site_id, $context, $action, $state = null, $user_id = null ) { //phpcs:ignore -- NOSONAR - compatible. if ( is_null( $user_id ) ) { $user_id = get_current_user_id(); } if ( is_null( $site_id ) ) { $site_id = 0; } $cron_tracking = apply_filters( 'mainwp_module_log_cron_tracking', true, $connector, $message, $args, $site_id, $context, $action, $user_id, $state ); $author = new Log_Author( $user_id ); $agent = $author->get_current_agent(); // WP Cron tracking requires opt-in and WP Cron to be enabled. if ( ! $cron_tracking && 'wp_cron' === $agent ) { return false; } $user = new \WP_User( $user_id ); $user_meta = array( 'user_login' => (string) ! empty( $user->user_login ) ? $user->user_login : '', 'agent' => (string) $agent, ); $system_user = ''; if ( 'wp_cli' === $agent ) { $system_user = 'wp_cli'; if ( is_callable( 'posix_getuid' ) && is_callable( 'posix_getpwuid' ) ) { $uid = posix_getuid(); $user_info = posix_getpwuid( $uid ); $user_meta['system_user_id'] = (int) $uid; $user_meta['system_user_name'] = (string) $user_info['name']; if ( ! empty( $user_meta['system_user_name'] ) ) { $system_user = $user_meta['system_user_name']; } } } elseif ( 'wp_cron' === $agent ) { $system_user = 'wp_cron'; } elseif ( 'wp_rest_api' === $agent ) { $system_user = 'wp_rest_api'; } $dura = isset( $args['duration'] ) ? floatval( $args['duration'] ) : $this->manager->executor->get_exec_time(); if ( isset( $args['duration'] ) ) { unset( $args['duration'] ); } $dura_bulk = 1; if ( isset( $args['duration_bulk'] ) ) { $dura_bulk = intval( $args['duration_bulk'] ); unset( $args['duration_bulk'] ); } if ( empty( $dura_bulk ) ) { $dura_bulk = 1; } $dura = $dura / $dura_bulk; // Prevent any meta with null values from being logged. $logs_meta = array_filter( $args, function ( $e ) { return ! is_null( $e ); } ); // To support searching user on meta. if ( empty( $logs_meta['user_login'] ) ) { if ( ! empty( $user_meta['user_login'] ) ) { $logs_meta['user_login'] = $user_meta['user_login']; } elseif ( ! empty( $system_user ) ) { $logs_meta['user_login'] = $system_user; } } // Add user meta to Log meta. $logs_meta['user_meta_json'] = wp_json_encode( $user_meta ); $recordarr = array( 'site_id' => (int) $site_id, 'user_id' => (int) $user_id, 'item' => (string) vsprintf( $message, $args ), 'connector' => (string) $connector, 'context' => (string) $context, 'action' => (string) $action, 'duration' => $dura, 'created' => time(), 'state' => $state, 'meta' => (array) $logs_meta, ); if ( 0 === $recordarr['site_id'] ) { unset( $recordarr['site_id'] ); } if ( null === $recordarr['state'] || '' === $recordarr['state'] ) { unset( $recordarr['state'] ); } // This is helpful in development environments. // error_log( $this->debug_backtrace( $recordarr ) ); //phpcs:ignore -- development. return $this->log_record( $recordarr ); } /** * Log record. * * @param array $recordarr sprintf (and extra) arguments to use. * * @return bool|WP_Error True if updated, otherwise false|WP_Error */ public function log_record( $recordarr ) { //phpcs:ignore -- NOSONAR - compatible. return $this->manager->db->insert( $recordarr ); } /** * Helper function to send a full backtrace of calls to the PHP error log for debugging. * * @param array $recordarr Record argument array. * * @return string $output MainWP Pro Reports backtrace. */ public function debug_backtrace( $recordarr ) { // Record details. $message = isset( $recordarr['item'] ) ? $recordarr['item'] : null; $author = isset( $recordarr['author'] ) ? $recordarr['author'] : null; $connector = isset( $recordarr['connector'] ) ? $recordarr['connector'] : null; $context = isset( $recordarr['context'] ) ? $recordarr['context'] : null; $action = isset( $recordarr['action'] ) ? $recordarr['action'] : null; // Log meta. $logs_meta = isset( $recordarr['meta'] ) ? $recordarr['meta'] : null; unset( $logs_meta['user_meta'] ); if ( $logs_meta ) { array_walk( $logs_meta, function ( &$value, $key ) { $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value ); } ); $logs_meta = implode( ', ', $logs_meta ); } // User meta. $user_meta = isset( $recordarr['meta']['user_meta'] ) ? $recordarr['meta']['user_meta'] : null; if ( $user_meta ) { array_walk( $user_meta, function ( &$value, $key ) { $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value ); } ); $user_meta = implode( ', ', $user_meta ); } // Debug backtrace. ob_start(); // @codingStandardsIgnoreStart debug_print_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // Option to ignore args requires PHP 5.3.6 // @codingStandardsIgnoreEnd $backtrace = ob_get_clean(); $backtrace = array_values( array_filter( explode( "\n", $backtrace ) ) ); return sprintf( "Pro Reports Debug Backtrace\n\n Summary | %s\n Author | %s\n Connector | %s\n Context | %s\n Action | %s\nReports Meta | %s\nAuthor Meta | %s\n\n%s\n", $message, $author, $connector, $context, $action, $logs_meta, $user_meta, implode( "\n", $backtrace ) ); } }