Files
pathfinder-websocket/app/Log/Store.php
Mark Friedrich cbca53ad73 - improved logging
- fixed multiple problem with map sync
- added some "garbage collection" for unsubscribed connection data
- updated README.md
2019-07-01 19:59:52 +02:00

220 lines
6.5 KiB
PHP

<?php
namespace Exodus4D\Socket\Log;
class Store {
/**
* default for: unique store name
*/
const DEFAULT_NAME = 'store';
/**
* default for: echo log data in terminal
*/
const DEFAULT_LOG_TO_STDOUT = true;
/**
* default for: max cached log entries
*/
const DEFAULT_LOG_STORE_SIZE = 50;
/**
* @see Store::DEFAULT_NAME
* @var string
*/
private $name = self::DEFAULT_NAME;
/**
* log store for log entries
* -> store size should be limited for memory reasons
* @var array
*/
private $store = [];
/**
* all valid types for custom log events
* if value is false, logs for this type are ignored
* @var array
*/
protected $logTypes = [
'error' => true,
'info' => true,
'debug' => true
];
/**
* if Store is locked, current state can not be changed
* @var bool
*/
protected $locked = false;
/**
* @var ShellColors
*/
static $colors;
/**
* Store constructor.
* @param string $name
*/
public function __construct(string $name){
$this->name = $name;
}
/**
* get all stored log entries
* @return array
*/
public function getStore() : array {
return $this->store;
}
/**
* @param bool $locked
*/
public function setLocked(bool $locked){
$this->locked = $locked;
}
/**
* @return bool
*/
public function isLocked() : bool {
return $this->locked;
}
/**
* @param int $logLevel
*/
public function setLogLevel(int $logLevel){
switch($logLevel){
case 3:
$this->logTypes['error'] = true;
$this->logTypes['info'] = true;
$this->logTypes['debug'] = true;
break;
case 2:
$this->logTypes['error'] = true;
$this->logTypes['info'] = true;
$this->logTypes['debug'] = false;
break;
case 1:
$this->logTypes['error'] = true;
$this->logTypes['info'] = false;
$this->logTypes['debug'] = false;
break;
case 0:
default:
$this->setLocked(true); // no logging
}
}
/**
* this is used for custom log events like 'error', 'debug',...
* works as dispatcher method that calls individual log*() methods
* @param $logTypes
* @param string|null $remoteAddress
* @param int|null $resourceId
* @param string $action
* @param string $message
*/
public function log($logTypes, ?string $remoteAddress, ?int $resourceId, string $action, string $message = '') : void {
if(!$this->isLocked()){
// filter out logTypes that should not be logged
$logTypes = array_filter((array)$logTypes, function(string $type) : bool {
return array_key_exists($type, $this->logTypes) && $this->logTypes[$type];
});
if($logTypes){
// get log entry data
$logData = $this->getLogData($logTypes, $remoteAddress, $resourceId, $action, $message);
if(self::DEFAULT_LOG_TO_STDOUT){
$this->echoLog($logData);
}
// add entry to local store and check size limit for store
$this->store[] = $logData;
$this->store = array_slice($this->store, self::DEFAULT_LOG_STORE_SIZE * -1);
}
}
}
/**
* get log data as array for a custom log entry
* @param array $logTypes
* @param string|null $remoteAddress
* @param int|null $resourceId
* @param string $action
* @param string $message
* @return array
*/
private function getLogData(array $logTypes, ?string $remoteAddress, ?int $resourceId, string $action, string $message = '') : array {
$file = null;
$lineNum = null;
$function = null;
$traceIndex = 4;
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $traceIndex);
if(count($backtrace) == $traceIndex){
$caller = $backtrace[$traceIndex - 2];
$callerOrig = $backtrace[$traceIndex - 1];
$file = substr($caller['file'], strlen(dirname(dirname(dirname($caller['file'])))) + 1);
$lineNum = $caller['line'];
$function = $callerOrig['function'];
}
$microTime = microtime(true);
$logTime = self::getDateTimeFromMicrotime($microTime);
return [
'store' => $this->name,
'mTime' => $microTime,
'mTimeFormat1' => $logTime->format('Y-m-d H:i:s.u'),
'mTimeFormat2' => $logTime->format('H:i:s'),
'logTypes' => $logTypes,
'remoteAddress' => $remoteAddress,
'resourceId' => $resourceId,
'fileName' => $file,
'lineNumber' => $lineNum,
'function' => $function,
'action' => $action,
'message' => $message
];
}
/**
* echo log data to stdout -> terminal
* @param array $logData
*/
private function echoLog(array $logData) : void {
if(!self::$colors){
self::$colors = new ShellColors();
}
$data = [
self::$colors->getColoredString($logData['mTimeFormat1'], 'dark_gray'),
self::$colors->getColoredString($logData['store'], $logData['store'] == 'webSock' ? 'brown' : 'cyan'),
$logData['remoteAddress'] . ($logData['resourceId'] ? ' #' . $logData['resourceId'] : ''),
self::$colors->getColoredString($logData['fileName'] . ' line ' . $logData['lineNumber'], 'dark_gray'),
self::$colors->getColoredString($logData['function'] . '()' . (($logData['function'] !== $logData['action']) ? ' [' . $logData['action'] . ']' : ''), 'dark_gray'),
implode((array)$logData['logTypes'], ','),
self::$colors->getColoredString($logData['message'], 'light_purple')
];
echo implode(array_filter($data), ' | ') . PHP_EOL;
}
/**
* @see https://stackoverflow.com/a/29598719/4329969
* @param float $mTime
* @return \DateTime
*/
public static function getDateTimeFromMicrotime(float $mTime) : \DateTime {
return \DateTime::createFromFormat('U.u', number_format($mTime, 6, '.', ''));
}
}