84 public static function init()
86 if (!self::$shutdownRegistered) {
87 register_shutdown_function([__CLASS__,
'flush']);
88 register_shutdown_function([__CLASS__,
'flushAsyncQueue']);
89 self::$shutdownRegistered =
true;
120 self::$asyncEnabled =
false;
122 self::$logBufferSize = 10;
124 self::$asyncEnabled =
false;
126 self::$logBufferSize = 25;
129 }
catch (Exception $e) {
146 if (!is_dir(self::$asyncQueueDir)) {
147 @mkdir(self::$asyncQueueDir, 0755,
true);
149 }
catch (Exception $e) {
150 self::$asyncEnabled =
false;
166 private static function write($data, $type, $file =
null)
172 error_log(
'[' . $type .
'] ' . $data);
181 if ($file ===
null) {
182 $file = $type === self::ERROR
210 self::$logBuffer[] = [
212 'data' => str_replace([
"\r",
"\n"],
' ', (
string)$data),
216 self::$logStats[
'buffered']++;
221 if (self::$silentMode && $type !== self::ERROR) {
231 $shouldFlush = $type === self::ERROR ||
233 count(self::$logBuffer) >= self::$logBufferSize;
251 if (empty(self::$logBuffer)) {
259 foreach (self::$logBuffer as $log) {
260 error_log(
'[' . date(
'Y-m-d H:i:s', $log[
'time']) .
'] [' . $log[
'type'] .
'] ' . $log[
'data']);
262 self::$logStats[
'flushed'] += count(self::$logBuffer);
263 self::$logBuffer = [];
277 }
catch (Exception $e) {
283 foreach (self::$logBuffer as $log) {
284 $logsByFile[$log[
'file']][] = $log;
287 foreach ($logsByFile as $file => $logs) {
289 foreach ($logs as $log) {
290 $content .=
'[' . date(
'Y-m-d H:i:s', $log[
'time']) .
'] # ' .
292 $log[
'type'] .
': ' . $log[
'data'] . PHP_EOL;
296 if (!$forceSync && self::$asyncEnabled) {
302 self::$logStats[
'async']++;
303 self::$logStats[
'writes']++;
310 $written = @file_put_contents($file, $content, FILE_APPEND | LOCK_EX);
311 if ($written ===
false) {
313 foreach ($logs as $log) {
314 error_log(
'[' . $log[
'type'] .
'] ' . $log[
'data'] .
' (target: ' . $file .
')');
318 self::$logStats[
'writes']++;
321 self::$logStats[
'flushed'] += count(self::$logBuffer);
322 self::$logBuffer = [];
336 if (!self::$asyncEnabled || !is_dir(self::$asyncQueueDir)) {
341 if (strlen((
string)$content) > self::MAX_QUEUE_CONTENT_LENGTH) {
347 $queueFile = self::$asyncQueueDir .
'/' . uniqid(
'log_',
true) .
'.queue';
352 'content' => $content,
353 'timestamp' => time(),
357 $serialized = serialize($queueEntry);
358 $written = @file_put_contents($queueFile, $serialized, LOCK_EX);
360 return ($written !==
false);
361 }
catch (Exception $e) {
381 $file = (string)$file;
386 $normalized = str_replace(
'\\',
'/', $file);
387 if (strpos($normalized,
'..') !==
false) {
392 $logsPathNormalized = str_replace(
'\\',
'/', rtrim($logsPath,
'/\\')) .
'/';
395 if (strpos($normalized, $logsPathNormalized) !== 0) {
416 $iterationCap = 1000;
417 for ($i = 0; $i < $iterationCap; $i++) {
419 if ($processed <= 0) {
422 $totalProcessed += $processed;
425 return $totalProcessed;
436 if (!is_dir(self::$asyncQueueDir)) {
443 $queueFiles = glob(self::$asyncQueueDir .
'/*.queue');
444 if (!is_array($queueFiles) || empty($queueFiles)) {
451 $batchFiles = array_slice($queueFiles, 0, self::MAX_QUEUE_BATCH);
456 foreach ($batchFiles as $queueFile) {
459 $serialized = @file_get_contents($queueFile);
460 if ($serialized ===
false) {
464 $entry = @unserialize($serialized, [
'allowed_classes' =>
false]);
465 if ($entry ===
false || !isset($entry[
'file']) || !isset($entry[
'content'])) {
475 if ($targetFile ===
null) {
484 if (!isset($entriesByFile[$targetFile]) &&
485 count($entriesByFile) >= self::MAX_QUEUE_TARGETS) {
490 $content = (string)$entry[
'content'];
491 if (strlen($content) > self::MAX_QUEUE_CONTENT_LENGTH) {
496 if (!isset($entriesByFile[$targetFile])) {
497 $entriesByFile[$targetFile] = [];
500 $entriesByFile[$targetFile][] = $content;
505 }
catch (Exception $e) {
512 foreach ($entriesByFile as $targetFile => $contents) {
514 $combined = implode(
'', $contents);
515 @file_put_contents($targetFile, $combined, FILE_APPEND | LOCK_EX);
516 }
catch (Exception $e) {
518 error_log(
'Failed to write to ' . $targetFile);
524 }
catch (Exception $e) {
541 if (!is_dir(self::$asyncQueueDir)) {
549 $queueFiles = @glob(self::$asyncQueueDir .
'/*.queue');
551 if (!is_array($queueFiles)) {
555 foreach ($queueFiles as $queueFile) {
558 if ($now - @filemtime($queueFile) > $maxAge) {
562 }
catch (Exception $e) {
566 }
catch (Exception $e) {
581 self::$logBuffer = [];
582 self::$shutdownRegistered =
false;
600 self::$asyncEnabled = (bool)$enabled;
610 return self::$asyncEnabled;
620 return self::$asyncQueueDir;
630 if (!is_dir(self::$asyncQueueDir)) {
634 $files = @glob(self::$asyncQueueDir .
'/*.queue');
636 return is_array($files) ? count($files) : 0;
646 return self::$logStats;
658 if ($size > 0 && $size <= 1000) {
659 self::$logBufferSize = $size;
670 return self::$logBufferSize;
680 if (self::$silentMode) {
697 $separator =
'========================================================================================' . PHP_EOL;
698 foreach ($logs as $log) {
699 if (!file_exists($log)) {
702 $logContent = @file_get_contents($log);
703 if ($logContent !==
false && !str_ends_with($logContent, $separator)) {
704 @file_put_contents($log, $separator, FILE_APPEND);
715 public static function trace($data, $file =
null)
726 public static function debug($data, $file =
null)
737 public static function info($data, $file =
null)
748 public static function warning($data, $file =
null)
760 public static function error($data, $file =
null)
786 $className = get_class($classInstance);
788 if (isset(self::$initClassCounts[$className])) {
789 self::$initClassCounts[$className]++;
794 self::$initClassCounts[$className] = 1;
807 $className = get_class($classInstance);
809 if (isset(self::$reloadClassCounts[$className])) {
810 self::$reloadClassCounts[$className]++;
815 self::$reloadClassCounts[$className] = 1;
831 self::$silentMode =
true;
833 self::$logBuffer = [];
843 self::$silentMode =
false;
854 self::$silentMode =
false;
855 self::$logBuffer = [];
865 return self::$silentMode;
static queueAsyncWrite($file, $content)
static setBufferSize($size)
static info($data, $file=null)
static getAsyncQueueSize()
const MAX_QUEUE_CONTENT_LENGTH
static startSilentBuffer()
static rollbackSilentBuffer()
static debug($data, $file=null)
static initializeAsyncQueue()
static sanitizeQueueTarget($file)
static setAsyncEnabled($enabled)
static warning($data, $file=null)
static reloadClass($classInstance)
static processAsyncQueue()
static write($data, $type, $file=null)
static $maxAsyncQueueBatch
static $shutdownRegistered
static trace($data, $file=null)
static checkVerbosityAndAdjustAsync()
static error($data, $file=null)
static getAsyncQueueDir()
static $reloadClassCounts
static initClass($classInstance)
static cleanupStaleAsyncQueue($maxAge)
static commitSilentBuffer()
static getBatchLogFilePath($aetrayPath=false)
static getLogFilePath($aetrayPath=false)
static getStartupLogFilePath($aetrayPath=false)
static getServicesLogFilePath($aetrayPath=false)
static getLogsPath($aetrayPath=false)
static getRegistryLogFilePath($aetrayPath=false)
static getTmpPath($aetrayPath=false)
static getHomepageLogFilePath($aetrayPath=false)
static getErrorLogFilePath($aetrayPath=false)
static getWinbinderLogFilePath($aetrayPath=false)