Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Components\Logger\Context;
|
||||
|
||||
use DateTime;
|
||||
use Throwable;
|
||||
use Xentral\Components\Http\Request;
|
||||
use Xentral\Components\Logger\LoggerInterface;
|
||||
use Xentral\Components\Util\StringUtil;
|
||||
|
||||
final class ContextHelper
|
||||
{
|
||||
/** @var Request $request */
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(
|
||||
Request $request
|
||||
) {
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $contextArray
|
||||
*
|
||||
* @return ContextInterface
|
||||
*/
|
||||
public function createContext(array $contextArray): ContextInterface
|
||||
{
|
||||
//time context
|
||||
$time = new DateTime('now');
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
|
||||
//calling code line
|
||||
$invocation = $this->findInvocation($backtrace);
|
||||
|
||||
//origin of the call
|
||||
$instigation = $this->createOrigin($backtrace);
|
||||
|
||||
//possible exception
|
||||
$exceptionEntry = null;
|
||||
if (array_key_exists('exception', $contextArray)) {
|
||||
$exceptionEntry = $contextArray['exception'];
|
||||
unset($contextArray['exception']);
|
||||
}
|
||||
$exception = null;
|
||||
if (is_subclass_of($exceptionEntry, Throwable::class)) {
|
||||
$exception = $exceptionEntry;
|
||||
}
|
||||
|
||||
//possible dump
|
||||
if (count($contextArray) === 0) {
|
||||
$dump = null;
|
||||
} else {
|
||||
$dump = $contextArray;
|
||||
}
|
||||
|
||||
return new LoggerContext(
|
||||
$time,
|
||||
$invocation,
|
||||
$instigation,
|
||||
$exception,
|
||||
$dump
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function interpolateMessage(string $message, array &$context): string
|
||||
{
|
||||
$keys = array_keys($context);
|
||||
$interpolated = $message;
|
||||
foreach ($keys as $key) {
|
||||
if ($key === 'exception') {
|
||||
continue;
|
||||
}
|
||||
if (!preg_match('/^\w+$/', (string)$key)) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match(sprintf('/\{%s\}/', $key), $interpolated)) {
|
||||
$pattern = sprintf('{%s}', $key);
|
||||
try {
|
||||
$interpolated = str_replace($pattern, (string)$context[$key], $interpolated);
|
||||
} catch (Throwable $e) {
|
||||
continue;
|
||||
}
|
||||
unset($context[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $interpolated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $backtrace
|
||||
*
|
||||
* @return Invocation
|
||||
*/
|
||||
public function findInvocation(array $backtrace): Invocation
|
||||
{
|
||||
$lastLine = 0;
|
||||
$lastFile = '';
|
||||
foreach ($backtrace as $trace) {
|
||||
if (isset($trace['class'])) {
|
||||
$class = $trace['class'];
|
||||
if (
|
||||
$class !== self::class
|
||||
&& !in_array(LoggerInterface::class, class_implements($class), true)
|
||||
) {
|
||||
return new Invocation(
|
||||
$trace['class'],
|
||||
$trace['function'],
|
||||
$lastLine,
|
||||
$lastFile
|
||||
);
|
||||
}
|
||||
if (array_key_exists('line', $trace)) {
|
||||
$lastLine = $trace['line'];
|
||||
$lastFile = $trace['file'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Invocation('unknown', 'unknown', 0, 'unknown');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $backtrace
|
||||
*
|
||||
* @return OriginInterface
|
||||
*/
|
||||
public function createOrigin(array $backtrace): OriginInterface
|
||||
{
|
||||
try {
|
||||
$instigation = $this->tryCreateFrontendOrigin();
|
||||
if ($instigation !== null) {
|
||||
return $instigation;
|
||||
}
|
||||
$instigation = $this->tryCreateSchedulerJobOrigin($backtrace);
|
||||
if ($instigation !== null) {
|
||||
return $instigation;
|
||||
}
|
||||
$instigation = $this->tryCreateRestApiOrigin();
|
||||
if ($instigation !== null) {
|
||||
return $instigation;
|
||||
}
|
||||
$instigation = $this->tryCreateLegacyApiOrigin();
|
||||
if ($instigation !== null) {
|
||||
return $instigation;
|
||||
}
|
||||
$instigation = $this->tryCreateCliOrigin();
|
||||
if ($instigation !== null) {
|
||||
return $instigation;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
|
||||
return new Origin(OriginInterface::TYPE_UNKNOWN, 'unknown');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OriginInterface|null
|
||||
*/
|
||||
private function tryCreateFrontendOrigin(): ?OriginInterface
|
||||
{
|
||||
$module = $this->request->get->get('module', null);
|
||||
if ($this->request === null || $module === null || $module === 'api') {
|
||||
return null;
|
||||
}
|
||||
$action = $this->request->get->get('action', null);
|
||||
$cmd = $this->request->get->get('cmd', null);
|
||||
$payload = [strtoupper($this->request->getMethod())];
|
||||
if ($module !== null) {
|
||||
$payload[] = sprintf('module=%s', $module);
|
||||
}
|
||||
if ($action !== null) {
|
||||
$payload[] = sprintf('action=%s', $action);
|
||||
}
|
||||
if ($cmd !== null) {
|
||||
$payload[] = sprintf('cmd=%s', $cmd);
|
||||
}
|
||||
|
||||
return new Origin(OriginInterface::TYPE_FRONTEND, implode(' ', $payload));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OriginInterface|null
|
||||
*/
|
||||
private function tryCreateLegacyApiOrigin(): ?OriginInterface
|
||||
{
|
||||
if ($this->request->get->get('module', '') === 'api') {
|
||||
return new Origin(
|
||||
OriginInterface::TYPE_LEGACY_API,
|
||||
sprintf(
|
||||
'%s action=%s',
|
||||
$this->request->getMethod(),
|
||||
$this->request->get->get('action', '')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OriginInterface|null
|
||||
*/
|
||||
private function tryCreateRestApiOrigin(): ?OriginInterface
|
||||
{
|
||||
$url = $this->request->getFullUrl();
|
||||
$match = preg_match('/api\/(v\d.?\/[^?]+)\??/', $url, $matches);
|
||||
if ($match && count($matches) > 1) {
|
||||
return new Origin(
|
||||
OriginInterface::TYPE_REST_API,
|
||||
sprintf(
|
||||
'%s endpoint=%s',
|
||||
$this->request->getMethod(),
|
||||
$matches[1]
|
||||
)
|
||||
);
|
||||
}
|
||||
$path = $this->request->get->get('path', '');
|
||||
if (preg_match('/(v\d.?\/[^?]+)/', $path)
|
||||
&& preg_match('/api\/index.php\?/', $url)
|
||||
) {
|
||||
return new Origin(
|
||||
OriginInterface::TYPE_REST_API,
|
||||
sprintf(
|
||||
'%s path=%s',
|
||||
$this->request->getMethod(),
|
||||
$path
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $backtrace
|
||||
*
|
||||
* @return OriginInterface|null
|
||||
*/
|
||||
private function tryCreateSchedulerJobOrigin(array $backtrace): ?OriginInterface
|
||||
{
|
||||
if (count($backtrace) === 0) {
|
||||
return null;
|
||||
}
|
||||
$trace = $backtrace[count($backtrace) - 1];
|
||||
$file = $trace['file'] ?? '';
|
||||
if (!StringUtil::endsWith($file, '/cronjobs/command.php')) {
|
||||
return null;
|
||||
}
|
||||
$jobFile = $trace['args'][0] ?? '';
|
||||
$payload = $jobFile;
|
||||
$matchresult = [];
|
||||
preg_match('/^.+\/cronjobs\/(.+)$/', $jobFile, $matchresult);
|
||||
if (count($matchresult) > 1) {
|
||||
$payload = sprintf('job=%s', $matchresult[1]);
|
||||
}
|
||||
|
||||
return new Origin(OriginInterface::TYPE_SCHEDULER_JOB, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OriginInterface|null
|
||||
*/
|
||||
private function tryCreateCliOrigin(): ?OriginInterface
|
||||
{
|
||||
if ($this->request->server->getInt('argc', 0) < 1) {
|
||||
return null;
|
||||
}
|
||||
$file = $this->request->server->get('argv', [''])[0];
|
||||
$payload = sprintf('script=%s', $file);
|
||||
|
||||
return new Origin(OriginInterface::TYPE_CLI, $payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Components\Logger\Context;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Throwable;
|
||||
|
||||
interface ContextInterface
|
||||
{
|
||||
public function getTime(): DateTimeInterface;
|
||||
|
||||
public function getClass(): ?string;
|
||||
|
||||
public function getFunction(): ?string;
|
||||
|
||||
public function getLine(): int;
|
||||
|
||||
public function hasOrigin(): bool;
|
||||
|
||||
public function getOriginType(): ?string;
|
||||
|
||||
public function getOriginDetail(): ?string;
|
||||
|
||||
public function hasException(): bool;
|
||||
|
||||
public function getException(): ?Throwable;
|
||||
|
||||
public function hasDump(): bool;
|
||||
|
||||
public function getDump(): array;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Components\Logger\Context;
|
||||
|
||||
final class Invocation
|
||||
{
|
||||
/** @var string $class */
|
||||
private $class;
|
||||
|
||||
/** @var string $function */
|
||||
private $function;
|
||||
|
||||
/** @var int $line */
|
||||
private $line;
|
||||
|
||||
/** @var string $file */
|
||||
private $file;
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param string $function
|
||||
* @param int $line
|
||||
* @param string $class
|
||||
*/
|
||||
public function __construct(string $class, string $function, int $line, string $file)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->function = $function;
|
||||
$this->line = $line;
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFunction(): string
|
||||
{
|
||||
return $this->function;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLine(): int
|
||||
{
|
||||
return $this->line;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFile(): string
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Components\Logger\Context;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
final class LoggerContext implements ContextInterface
|
||||
{
|
||||
/** @var DateTimeInterface $dateTime */
|
||||
private $dateTime;
|
||||
|
||||
/** @var Invocation $invocation */
|
||||
private $invocation;
|
||||
|
||||
/** @var OriginInterface $origin */
|
||||
private $origin;
|
||||
|
||||
/** @var Exception $exception */
|
||||
private $exception;
|
||||
|
||||
/** @var array $dump */
|
||||
private $dump;
|
||||
|
||||
/**
|
||||
* @param DateTimeInterface $dateTime
|
||||
* @param Invocation|null $invocation
|
||||
* @param OriginInterface|null $origin
|
||||
* @param Throwable|null $exception
|
||||
* @param array|null $dump
|
||||
*/
|
||||
public function __construct(
|
||||
DateTimeInterface $dateTime,
|
||||
Invocation $invocation = null,
|
||||
OriginInterface $origin = null,
|
||||
Throwable $exception = null,
|
||||
array $dump = null
|
||||
) {
|
||||
$this->dateTime = $dateTime;
|
||||
$this->invocation = $invocation;
|
||||
$this->origin = $origin;
|
||||
$this->exception = $exception;
|
||||
$this->dump = $dump;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateTimeInterface
|
||||
*/
|
||||
public function getTime(): DateTimeInterface
|
||||
{
|
||||
return $this->dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getClass(): ?string
|
||||
{
|
||||
if ($this->invocation === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->invocation->getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFunction(): ?string
|
||||
{
|
||||
if ($this->invocation === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->invocation->getFunction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLine(): int
|
||||
{
|
||||
if ($this->invocation === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->invocation->getLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOrigin(): bool
|
||||
{
|
||||
return $this->origin !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasException(): bool
|
||||
{
|
||||
return $this->exception !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Exception|null
|
||||
*/
|
||||
public function getException(): ?Throwable
|
||||
{
|
||||
return $this->exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDump(): bool
|
||||
{
|
||||
return is_array($this->dump) && count($this->dump) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDump(): array
|
||||
{
|
||||
if (!is_array($this->dump)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->dump;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOriginType(): ?string
|
||||
{
|
||||
if ($this->origin === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->origin->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOriginDetail(): ?string
|
||||
{
|
||||
if ($this->origin === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->origin->getDetail();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Components\Logger\Context;
|
||||
|
||||
final class Origin implements OriginInterface
|
||||
{
|
||||
/** @var string $type */
|
||||
private $type;
|
||||
|
||||
/** @var string $payload */
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $payload
|
||||
*/
|
||||
public function __construct(string $type, string $payload)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->payload = $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getDetail(): string
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Components\Logger\Context;
|
||||
|
||||
interface OriginInterface
|
||||
{
|
||||
/** @var string FRONTEND */
|
||||
public const TYPE_FRONTEND = 'frontend_request';
|
||||
|
||||
/** @var string LEGACY_API */
|
||||
public const TYPE_LEGACY_API = 'legacy_api';
|
||||
|
||||
/** @var string TYPE_REST_API */
|
||||
public const TYPE_REST_API = 'rest_api';
|
||||
|
||||
/** @var string TYPE_CALDAV */
|
||||
public const TYPE_CALDAV = 'caldav_request';
|
||||
|
||||
/** @var string TYPE_WEBDAV */
|
||||
public const TYPE_WEBDAV = 'webdav_request';
|
||||
|
||||
/** @var string TYPE_DEVICE */
|
||||
public const TYPE_DEVICE = 'device_request';
|
||||
|
||||
/** @var string TYPE_SCHEDULER_JOB */
|
||||
public const TYPE_SCHEDULER_JOB = 'scheduler_job';
|
||||
|
||||
/** @var string TYPE_CLI */
|
||||
public const TYPE_CLI = 'cli_command';
|
||||
|
||||
/** @var string TYPE_UNKNOWN */
|
||||
public const TYPE_UNKNOWN = 'unknown';
|
||||
|
||||
/**
|
||||
* Gets the origin type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string;
|
||||
|
||||
/**
|
||||
* Gets the origin payload.
|
||||
*
|
||||
* e.g. web request GET params, cronjob name, REST-API endpoint etc...
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDetail(): string;
|
||||
}
|
||||
Reference in New Issue
Block a user