PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 4.2.13
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v4.2.13
4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 3.12.1 All 134 releases
optimole-wp / inc / logger.php

logger.php in Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization 4.2.13, at inc/logger.php

259 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Optml_Logger
4 * Handles the logging operations.
5 */
6 class Optml_Logger {
7
8 const LOG_TYPE_OFFLOAD = 'offload';
9 const LOG_TYPE_ROLLBACK = 'rollback';
10
11 const LOG_SEPARATOR = '==========';
12
13 /**
14 * Upload directory.
15 *
16 * @var string
17 */
18 private $upload_dir;
19
20 /**
21 * Log filenames.
22 *
23 * @var array
24 */
25 private $log_filenames = [
26 self::LOG_TYPE_OFFLOAD => 'offload.log',
27 self::LOG_TYPE_ROLLBACK => 'rollback.log',
28 ];
29
30 /**
31 * The single instance of the class.
32 *
33 * @var Optml_Logger
34 */
35 private static $instance = null;
36
37 /**
38 * Ensures only one instance of the class is loaded.
39 *
40 * @return Optml_Logger An instance of the class.
41 */
42 public static function instance() {
43 if ( null === self::$instance ) {
44 self::$instance = new self();
45 }
46
47 return self::$instance;
48 }
49
50 /**
51 * Optml_Logger constructor.
52 * Initializes the upload directory.
53 *
54 * @return void
55 */
56 private function __construct() {
57 $upload_dir = wp_upload_dir();
58 $this->upload_dir = trailingslashit( $upload_dir['basedir'] ) . 'optimole-logs/';
59
60 if ( ! file_exists( $this->upload_dir ) ) {
61 if ( false === wp_mkdir_p( $this->upload_dir ) ) {
62 return;
63 }
64 }
65
66 add_action( 'wp_ajax_optml_fetch_logs', [ $this, 'fetch_logs_ajax_handler' ] );
67 }
68
69 /**
70 * Adds a log entry.
71 *
72 * @param string $type Type of log (offload/rollback).
73 * @param string $message Log message.
74 * @return bool Returns true on success, false on failure.
75 */
76 public function add_log( $type, $message ) {
77 if ( ! array_key_exists( $type, $this->log_filenames ) ) {
78 return false;
79 }
80
81 $this->send_log( $type, $message );
82
83 if ( ! $this->has_permission() ) {
84 return false;
85 }
86
87 $file_path = $this->upload_dir . $this->log_filenames[ $type ];
88 $handle = @fopen( $file_path, 'a' );
89
90 if ( $handle === false ) {
91 return false;
92 }
93
94 fwrite( $handle, '[' . gmdate( 'M d H:i:s' ) . '] ' . $message . "\n" );
95 fclose( $handle );
96 return true;
97 }
98
99 /**
100 * Retrieves the log.
101 *
102 * @param string $type Type of log (offload/rollback).
103 * @param int $lines Number of lines to return from the end of the log file.
104 * @param string $separator Separator line to start retrieving log entries from.
105 *
106 * @return false|string Returns log data or false on failure.
107 */
108 public function get_log( $type, $lines = 500, $separator = '' ) {
109 if ( ! array_key_exists( $type, $this->log_filenames ) ) {
110 return false;
111 }
112
113 $file_path = $this->upload_dir . $this->log_filenames[ $type ];
114
115 if ( $this->has_logs( $type ) ) {
116 $data = $this->tailCustom( $file_path, $lines, true, $separator );
117 return nl2br( esc_textarea( $data ) );
118 }
119
120 return false;
121 }
122
123 /**
124 * Checks if the log file exists.
125 *
126 * @param string $type Type of log (offload/rollback).
127 * @return bool Returns true if log file exists, false otherwise.
128 */
129 public function has_logs( $type ) {
130 if ( ! array_key_exists( $type, $this->log_filenames ) ) {
131 return false;
132 }
133
134 $file_path = $this->upload_dir . $this->log_filenames[ $type ];
135 return file_exists( $file_path );
136 }
137
138 /**
139 * Checks for permission to write to log file.
140 *
141 * @return bool Returns true if permission is granted, false otherwise.
142 */
143 public function has_permission() {
144 return is_writable( $this->upload_dir );
145 }
146
147 /**
148 * Reads the last n lines from a file, or from a specific point defined by a separator.
149 *
150 * @param string $filepath Path to the file.
151 * @param int $lines Number of lines to read from the end of the file.
152 * @param bool $adaptive Use adaptive buffers.
153 * @param string $separator Separator line to start retrieving log entries from.
154 * @return false|string Returns last n lines from the file or false on failure.
155 */
156 private function tailCustom( $filepath, $lines = 1, $adaptive = true, $separator = '' ) {
157 $f = @fopen( $filepath, 'rb' );
158
159 if ( $f === false ) {
160 return false;
161 }
162
163 if ( ! $adaptive ) {
164 $buffer = 4096;
165 } else {
166 $buffer = min( 64, filesize( $filepath ) );
167 }
168
169 fseek( $f, -1, SEEK_END );
170 $output = '';
171 $read = '';
172 $separator_found = false;
173
174 while ( $buffer > 0 && ftell( $f ) > 0 ) {
175 $seek = min( ftell( $f ), $buffer );
176 fseek( $f, -$seek, SEEK_CUR );
177 $output = ( $read = fread( $f, $seek ) ) . $output;
178 fseek( $f, -mb_strlen( $read, '8bit' ), SEEK_CUR );
179
180 if ( ! empty( $separator ) && strpos( $output, $separator ) !== false ) {
181 $separator_found = true;
182 break;
183 }
184
185 $buffer = min( $buffer * 2, 4096 );
186 }
187 $pos = -1;
188 if ( ! empty( $separator ) && $separator_found ) {
189 $pos = strrpos( $output, $separator );
190 if ( $pos !== false ) {
191 $output = substr( $output, $pos + strlen( $separator ) );
192 $pos = -1; // we reset the position to start from the beginning of the chunk when slicing.
193 }
194 }
195
196 // Extract the desired number of lines
197 $output_lines = explode( "\n", $output );
198 if ( empty( $separator ) || ! $separator_found ) {
199 $output = implode( "\n", array_slice( $output_lines, -$lines ) ); // fetch last n lines
200 } else {
201 // Calculate the start position for slicing
202 $start_pos = max( $pos + 1, count( $output_lines ) - $lines );
203
204 $output = implode( "\n", array_slice( $output_lines, $start_pos, $lines ) ); // fetch from separator
205 }
206
207 fclose( $f );
208 return trim( $output );
209 }
210
211 /**
212 * Fetches the logs via AJAX.
213 *
214 * @return void
215 */
216 public function fetch_logs_ajax_handler() {
217 // Check for nonce security
218 $nonce = $_REQUEST['nonce'];
219 if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
220 wp_die( __( 'Security check failed', 'optimole-wp' ) );
221 }
222
223 // Check for necessary parameters
224 if ( ! isset( $_REQUEST['type'] ) || ! in_array( $_REQUEST['type'], array_keys( $this->log_filenames ), true ) ) {
225 wp_die( __( 'Invalid log type', 'optimole-wp' ) );
226 }
227
228 $lines = 10000;
229
230 if ( isset( $_REQUEST['lines'] ) ) {
231 $lines = intval( $_REQUEST['lines'] );
232 }
233
234 // Get log type
235 $type = $_REQUEST['type'];
236
237 // Fetch the logs
238 $logs = $this->get_log( $type, $lines, self::LOG_SEPARATOR );
239
240 // Return the logs
241 echo $logs;
242
243 wp_die();
244 }
245
246 /**
247 * Send logs to Axiom.
248 *
249 * @param string $type Type of log (offload/rollback).
250 * @param string $message Log message.
251
252 * @return void
253 */
254 public function send_log( $type, $message ) {
255 $request = new Optml_Api();
256 $request->send_log( $type, $message );
257 }
258 }
259