Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
+252
-102
@@ -11,50 +11,127 @@
|
||||
namespace IPS;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DateInterval;
|
||||
use DirectoryIterator;
|
||||
use Error;
|
||||
use Exception;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use RuntimeException;
|
||||
use function defined;
|
||||
use function file_put_contents;
|
||||
use function get_called_class;
|
||||
use function get_class;
|
||||
use function is_array;
|
||||
use function str_replace;
|
||||
use function ucwords;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ($_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0') . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log Class
|
||||
*/
|
||||
class _Log extends \IPS\Patterns\ActiveRecord
|
||||
class Log extends ActiveRecord
|
||||
{
|
||||
const DEFAULT_LOG_LEVEL = 3;
|
||||
const MAX_LOG_LEVEL = 5;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'core_log';
|
||||
|
||||
public static ?string $databaseTable = 'core_log';
|
||||
|
||||
/**
|
||||
* Set Default Values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultValues()
|
||||
public function setDefaultValues() : void
|
||||
{
|
||||
$this->time = time();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Track if we are currently logging to prevent recursion for database issues
|
||||
*/
|
||||
static protected $currentlyLogging = FALSE;
|
||||
static protected bool $currentlyLogging = FALSE;
|
||||
|
||||
/***
|
||||
* Default log levels for different categories
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static array $categoryLogLevels = [
|
||||
'content_debug' => 1,
|
||||
'redis_writes' => 1,
|
||||
'redis_exception' => 1,
|
||||
'upgrade' => 2,
|
||||
'auto_upgrade' => 2,
|
||||
'paypal' => 2,
|
||||
'duplicate_comment' => 3,
|
||||
'searchResults' => 3,
|
||||
'profile_completion' => 3,
|
||||
'digestBuild' => 3,
|
||||
'index_single_item' => 3,
|
||||
'spam-service' => 4,
|
||||
'group_promotion' => 4,
|
||||
'birthday_error' => 5,
|
||||
'cookie' => 5,
|
||||
'views' => 5,
|
||||
'cicloud' => 5,
|
||||
'facebook_oembed' => 5,
|
||||
'achievements' => 5,
|
||||
'embed_fail' => 5,
|
||||
'template_store_building' => 5,
|
||||
'deleteFilesTask' => 5,
|
||||
'warn_fix' => 5
|
||||
];
|
||||
|
||||
/**
|
||||
* Log categories that are only logged when we enable
|
||||
* the DEBUG_LOG constant
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static array $excludeFromSettings = [
|
||||
'ranges',
|
||||
'runQueue_log',
|
||||
'rebuildAttachmentThumbnails',
|
||||
'rebuildItemCounts',
|
||||
'rebuildContentImages',
|
||||
'rebuildImageProxy',
|
||||
'rebuildLazyLoad',
|
||||
'rebuildSearchIndex',
|
||||
'rebuildSitemap',
|
||||
'RebuildUrlRels',
|
||||
'RestoreBrokenAttachments',
|
||||
'rebuildCategories',
|
||||
'rebuildItems',
|
||||
'rebuildPosts',
|
||||
'rebuildTagCache',
|
||||
'ICrebuildPosts',
|
||||
'fixDeletionLog',
|
||||
'UpdateMemberSeoNames',
|
||||
'Upgrade46FeaturedContent'
|
||||
];
|
||||
|
||||
/**
|
||||
* Write a log message
|
||||
*
|
||||
* @param \Exception|string $message An Exception object or a generic message to log
|
||||
* @param string|null $category An optional string identifying the type of log (for example "upgrade")
|
||||
* @return \IPS\Log|null
|
||||
* @param Exception|string|array $message An Exception object or a generic message to log
|
||||
* @param string|null $category An optional string identifying the type of log (for example "upgrade")
|
||||
* @return Log|null
|
||||
*/
|
||||
public static function log( $message, $category = NULL )
|
||||
public static function log( Exception|string|array $message, string $category = NULL ): ?Log
|
||||
{
|
||||
/* Anything to log? */
|
||||
if( !$message )
|
||||
@@ -67,17 +144,17 @@ class _Log extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
if( static::$currentlyLogging === TRUE )
|
||||
{
|
||||
throw new \RuntimeException;
|
||||
throw new RuntimeException;
|
||||
}
|
||||
|
||||
static::$currentlyLogging = TRUE;
|
||||
|
||||
$log = new static;
|
||||
|
||||
if ( $message instanceof \Exception )
|
||||
$log = new Log();
|
||||
|
||||
if ( $message instanceof Exception )
|
||||
{
|
||||
$log->exception_class = \get_class( $message );
|
||||
$log->exception_code = $message->getCode();
|
||||
$log->exceptionClass = get_class( $message );
|
||||
$log->exceptionCode = $message->getCode();
|
||||
|
||||
if ( method_exists( $message, 'extraLogData' ) AND $extraData = $message->extraLogData() )
|
||||
{
|
||||
@@ -92,142 +169,240 @@ class _Log extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( \is_array( $message ) )
|
||||
if ( is_array( $message ) )
|
||||
{
|
||||
$message = var_export( $message, TRUE );
|
||||
}
|
||||
$log->message = $message;
|
||||
$log->backtrace = ( new \Exception )->getTraceAsString();
|
||||
$log->backtrace = ( new Exception )->getTraceAsString();
|
||||
}
|
||||
|
||||
/* If this is an actual request and not command line-invoked */
|
||||
if( \IPS\Dispatcher::hasInstance() AND mb_strpos( php_sapi_name(), 'cli' ) !== 0 )
|
||||
if( Dispatcher::hasInstance() AND mb_strpos( php_sapi_name(), 'cli' ) !== 0 )
|
||||
{
|
||||
$log->url = \IPS\Request::i()->url();
|
||||
try
|
||||
{
|
||||
// Early exceptions may occur before the logged in object is set up.
|
||||
$log->member_id = \IPS\Member::loggedIn()->member_id ?: 0;
|
||||
$log->member_id = Member::loggedIn()->member_id ?: 0;
|
||||
}
|
||||
catch( \Error $e )
|
||||
catch( Error $e )
|
||||
{
|
||||
$log->member_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( \IPS\Application::load('core')->long_version >= 106149 ) #Column was added in 106149
|
||||
{
|
||||
$log->loaded_hooks = json_encode( \IPS\IPS::$loadedHooks );
|
||||
}
|
||||
$log->category = $category;
|
||||
$log->save();
|
||||
|
||||
static::$currentlyLogging = FALSE;
|
||||
|
||||
|
||||
return $log;
|
||||
}
|
||||
/* If that fails, log to disk */
|
||||
catch ( \Exception $e )
|
||||
catch ( Exception $e )
|
||||
{
|
||||
if ( !\IPS\NO_WRITES and !\IPS\CIC )
|
||||
if ( !NO_WRITES and !CIC)
|
||||
{
|
||||
/* What are we writing? */
|
||||
$date = date('r');
|
||||
if ( $message instanceof \Exception )
|
||||
if ( $message instanceof Exception )
|
||||
{
|
||||
$messageToLog = $date . "\n" . \get_class( $message ) . '::' . $message->getCode() . "\n" . $message->getMessage() . "\n" . $message->getTraceAsString();
|
||||
$messageToLog = $date . "\n" . get_class( $message ) . '::' . $message->getCode() . "\n" . $message->getMessage() . "\n" . $message->getTraceAsString();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( \is_array( $message ) )
|
||||
if ( is_array( $message ) )
|
||||
{
|
||||
$message = var_export( $message, TRUE );
|
||||
}
|
||||
$messageToLog = $date . "\n" . $message . "\n" . ( new \Exception )->getTraceAsString();
|
||||
$messageToLog = $date . "\n" . $message . "\n" . ( new Exception )->getTraceAsString();
|
||||
}
|
||||
|
||||
|
||||
/* Where are we writing it? */
|
||||
$dir = str_replace( '{root}', \IPS\ROOT_PATH, \IPS\LOG_FALLBACK_DIR );
|
||||
$dir = str_replace( '{root}', ROOT_PATH, LOG_FALLBACK_DIR);
|
||||
if ( !is_dir( $dir ) )
|
||||
{
|
||||
if ( !@mkdir( $dir ) or !@chmod( $dir, \IPS\IPS_FOLDER_PERMISSION ) )
|
||||
if ( !@mkdir( $dir ) or !@chmod( $dir, IPS_FOLDER_PERMISSION) )
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Write it */
|
||||
$header = "<?php exit; ?>\n\n";
|
||||
$file = $dir . '/' . date( 'Y' ) . '_' . date( 'm' ) . '_' . date('d') . '_' . ( $category ?: 'nocategory' ) . '.php';
|
||||
if ( file_exists( $file ) )
|
||||
{
|
||||
@\file_put_contents( $file, "\n\n-------------\n\n" . $messageToLog, FILE_APPEND );
|
||||
@file_put_contents( $file, "\n\n-------------\n\n" . $messageToLog, FILE_APPEND );
|
||||
}
|
||||
else
|
||||
{
|
||||
@\file_put_contents( $file, $header . $messageToLog );
|
||||
@file_put_contents( $file, $header . $messageToLog );
|
||||
}
|
||||
@chmod( $file, \IPS\IPS_FILE_PERMISSION );
|
||||
@chmod( $file, IPS_FILE_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write a debug message
|
||||
*
|
||||
* @param \Exception|string $message An Exception object or a generic message to log
|
||||
* @param string|null $category An optional string identifying the type of log (for example "upgrade")
|
||||
* @return \IPS\Log|null
|
||||
* @param Exception|string $message An Exception object or a generic message to log
|
||||
* @param string|null $category An optional string identifying the type of log (for example "upgrade")
|
||||
* @param int|null $logLevel An optional number to identify the logging level for this message. The lower the level,
|
||||
* the higher the priority
|
||||
* @return Log|null
|
||||
*/
|
||||
public static function debug( $message, $category = NULL )
|
||||
public static function debug( Exception|string $message, string $category = NULL, ?int $logLevel=null ): ?Log
|
||||
{
|
||||
if ( \defined('\IPS\DEBUG_LOG') and \IPS\DEBUG_LOG )
|
||||
/* If the constant is enabled, we log everything */
|
||||
if ( defined('\IPS\DEBUG_LOG') and DEBUG_LOG )
|
||||
{
|
||||
return static::log( $message, $category );
|
||||
}
|
||||
|
||||
/* If the category is in the excluded settings, stop here */
|
||||
if( $category === null or in_array( $category, static::$excludeFromSettings ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Check if we have a custom logging method for this category */
|
||||
$method = '_debug' . str_replace( " ", "", ucwords( str_replace( '_', ' ', $category ) ) );
|
||||
if( method_exists( get_called_class(), $method ) )
|
||||
{
|
||||
return static::$method( $message, $category );
|
||||
}
|
||||
|
||||
/* If we didn't specify a log level, check if we have a default */
|
||||
if( $logLevel === null )
|
||||
{
|
||||
$logLevel = static::$categoryLogLevels[ $category ] ?? static::DEFAULT_LOG_LEVEL;
|
||||
}
|
||||
elseif( $logLevel > static::MAX_LOG_LEVEL )
|
||||
{
|
||||
/* Make sure we didn't go over the max */
|
||||
$logLevel = static::MAX_LOG_LEVEL;
|
||||
}
|
||||
|
||||
/* Check settings - only log messages that at or below the current value */
|
||||
if( Settings::i()->debug_log_level and Settings::i()->debug_log_level <= $logLevel )
|
||||
{
|
||||
return static::log( $message, $category );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Exception|string|array $message
|
||||
* @param string|NULL $category
|
||||
* @return Log|null
|
||||
*/
|
||||
protected static function _debugRequest( Exception|string|array $message, string $category = NULL ): ?Log
|
||||
{
|
||||
if( !empty( Settings::i()->debug_log_requests ) )
|
||||
{
|
||||
/* This setting would have URLs or URL fragments that we should check */
|
||||
foreach( json_decode( Settings::i()->debug_log_requests, true ) as $string )
|
||||
{
|
||||
if( strpos( $message, $string ) !== false )
|
||||
{
|
||||
return static::log( $message, $category );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception|string|array $message
|
||||
* @param string|NULL $category
|
||||
* @return Log|null
|
||||
*/
|
||||
protected static function _debugWebhookFireCall( Exception|string|array $message, string $category = NULL ): ?Log
|
||||
{
|
||||
if( !empty( Settings::i()->debug_log_webhooks ) )
|
||||
{
|
||||
/* This setting would have URLs or URL fragments that we should check */
|
||||
foreach( json_decode( Settings::i()->debug_log_webhooks, true ) as $string )
|
||||
{
|
||||
$stringToCheck = '[event] => ' . $string;
|
||||
if( strpos( $message, $stringToCheck ) !== false )
|
||||
{
|
||||
return static::log( $message, $category );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception|string|array $message
|
||||
* @param string|NULL $category
|
||||
* @return Log|null
|
||||
*/
|
||||
protected static function _debugEventFire( Exception|string|array $message, string $category = NULL ): ?Log
|
||||
{
|
||||
if( !empty( Settings::i()->debug_log_events ) )
|
||||
{
|
||||
/* This setting would have URLs or URL fragments that we should check */
|
||||
foreach( json_decode( Settings::i()->debug_log_events, true ) as $string )
|
||||
{
|
||||
$stringToCheck = '[event] => ' . $string;
|
||||
if( strpos( $message, $stringToCheck ) !== false )
|
||||
{
|
||||
return static::log( $message, $category );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fallback directory
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function fallbackDir()
|
||||
public static function fallbackDir(): ?string
|
||||
{
|
||||
if ( \IPS\CIC )
|
||||
if (CIC)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return str_replace( '{root}', \IPS\ROOT_PATH, \IPS\LOG_FALLBACK_DIR );
|
||||
return str_replace( '{root}', ROOT_PATH, LOG_FALLBACK_DIR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prune logs
|
||||
*
|
||||
* @param int $days Older than (days) to prune
|
||||
* @return void
|
||||
*
|
||||
* @param int $days Older than (days) to prune
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function pruneLogs( $days )
|
||||
public static function pruneLogs( int $days ) : void
|
||||
{
|
||||
\IPS\Db::i()->delete( static::$databaseTable, array( 'time<?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $days . 'D' ) )->getTimestamp() ) );
|
||||
|
||||
if ( !\IPS\NO_WRITES )
|
||||
Db::i()->delete( static::$databaseTable, array( 'time<?', DateTime::create()->sub( new DateInterval( 'P' . $days . 'D' ) )->getTimestamp() ) );
|
||||
|
||||
if ( !NO_WRITES)
|
||||
{
|
||||
$dir = static::fallbackDir();
|
||||
if ( is_dir( $dir ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$it = new \DirectoryIterator( $dir );
|
||||
$it = new DirectoryIterator( $dir );
|
||||
}
|
||||
catch ( Exception $e )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
foreach( $it as $file )
|
||||
{
|
||||
try
|
||||
@@ -236,8 +411,8 @@ class _Log extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( preg_match( "#.cgi$#", $file->getFilename(), $matches ) or ( preg_match( "#.php$#", $file->getFilename(), $matches ) and $file->getMTime() < ( time() - ( 60 * 60 * 24 * (int) $days ) ) ) )
|
||||
|
||||
if( preg_match( "#.cgi$#", $file->getFilename(), $matches ) or ( preg_match( "#.php$#", $file->getFilename(), $matches ) and $file->getMTime() < ( time() - ( 60 * 60 * 24 * $days ) ) ) )
|
||||
{
|
||||
@unlink( $file->getPathname() );
|
||||
}
|
||||
@@ -247,47 +422,22 @@ class _Log extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Loaded Hooks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_loaded_hooks()
|
||||
{
|
||||
return ( $this->_data['loaded_hooks'] ) ? json_decode( $this->_data['loaded_hooks'] ) : array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Hook details
|
||||
*
|
||||
* @param string $file filename of loaded hook
|
||||
*
|
||||
* @param string $file filename of loaded hook
|
||||
* @return array
|
||||
*/
|
||||
public static function hookDetails( $file )
|
||||
public static function hookDetails( string $file ): array
|
||||
{
|
||||
$bits = explode( '/', $file );
|
||||
|
||||
|
||||
$return = array();
|
||||
|
||||
/* Plugins are always third party */
|
||||
if ( $bits[0] === 'plugins' )
|
||||
{
|
||||
$plugin = NULL;
|
||||
try
|
||||
{
|
||||
$plugin = \IPS\Plugin::getPluginFromPath( "/" . $file );
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
|
||||
$return['type'] = 'plugin';
|
||||
$return['id'] = ( $plugin ) ? $plugin->id : NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$return['type'] = 'app';
|
||||
$return['id'] = $bits[1];
|
||||
}
|
||||
|
||||
$return['type'] = 'app';
|
||||
$return['id'] = $bits[1];
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user