Merge branch 'develop'
This commit is contained in:
5
Dockerfile
Normal file
5
Dockerfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
FROM php:8-fpm
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY ./php-errors.ini /usr/local/etc/php/conf.d/docker-php-errors.ini
|
16
app/common/Controller/Base.php
Normal file
16
app/common/Controller/Base.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Common\Controller;
|
||||||
|
|
||||||
|
use ProVM\Common\Service\Logs;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Slim\Views\Blade as View;
|
||||||
|
|
||||||
|
class Base
|
||||||
|
{
|
||||||
|
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, View $view, Logs $service): ResponseInterface
|
||||||
|
{
|
||||||
|
$files = $service->getFiles();
|
||||||
|
return $view->render($response, 'home', compact('files'));
|
||||||
|
}
|
||||||
|
}
|
25
app/common/Controller/Logs.php
Normal file
25
app/common/Controller/Logs.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Common\Controller;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Slim\Views\Blade as View;
|
||||||
|
use ProVM\Common\Service\Logs as Service;
|
||||||
|
use ProVM\Logview\Log;
|
||||||
|
|
||||||
|
class Logs
|
||||||
|
{
|
||||||
|
public function get(ServerRequestInterface $request, ResponseInterface $response, View $view, Service $service, string $log_file): ResponseInterface
|
||||||
|
{
|
||||||
|
$log = $service->get($log_file);
|
||||||
|
|
||||||
|
$levels = [];
|
||||||
|
foreach (Log::LEVELS as $level) {
|
||||||
|
$levels[strtolower($level)] = (object) [
|
||||||
|
'text' => Log::COLORS[$level],
|
||||||
|
'background' => Log::BACKGROUNDS[$level],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $view->render($response, 'logs.show', compact('log', 'levels'));
|
||||||
|
}
|
||||||
|
}
|
44
app/common/Service/Logs.php
Normal file
44
app/common/Service/Logs.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Common\Service;
|
||||||
|
|
||||||
|
use ProVM\Logview\Log\File;
|
||||||
|
|
||||||
|
class Logs
|
||||||
|
{
|
||||||
|
public function __construct(string $folder)
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setFolder($folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string $folder;
|
||||||
|
|
||||||
|
public function getFolder(): string
|
||||||
|
{
|
||||||
|
return $this->folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFolder(string $folder): Logs
|
||||||
|
{
|
||||||
|
$this->folder = $folder;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFiles(): array
|
||||||
|
{
|
||||||
|
$files = new \FilesystemIterator($this->getFolder());
|
||||||
|
$output = [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$output []= $file;
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
public function get(string $log_file): File
|
||||||
|
{
|
||||||
|
$content = \Safe\file_get_contents(implode(DIRECTORY_SEPARATOR, [$this->getFolder(), $log_file]));
|
||||||
|
return (new File())->setFilename($log_file)->setContent($content);
|
||||||
|
}
|
||||||
|
}
|
34
app/composer.json
Normal file
34
app/composer.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "provm/logview",
|
||||||
|
"description": "Monolog log file viewer",
|
||||||
|
"type": "project",
|
||||||
|
"require": {
|
||||||
|
"berrnd/slim-blade-view": "^1.0",
|
||||||
|
"monolog/monolog": "^3.3",
|
||||||
|
"nyholm/psr7": "^1.5",
|
||||||
|
"nyholm/psr7-server": "^1.0",
|
||||||
|
"php-di/php-di": "^7.0",
|
||||||
|
"php-di/slim-bridge": "^3.3",
|
||||||
|
"slim/slim": "^4.11",
|
||||||
|
"thecodingmachine/safe": "^2.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^10.0",
|
||||||
|
"kint-php/kint": "^5.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"ProVM\\Logview\\": "src/",
|
||||||
|
"ProVM\\Common\\": "common/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Aldarien",
|
||||||
|
"email": "aldarien85@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true
|
||||||
|
}
|
||||||
|
}
|
16
app/public/index.php
Normal file
16
app/public/index.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
$app = require_once implode(DIRECTORY_SEPARATOR, [
|
||||||
|
dirname(__FILE__, 2),
|
||||||
|
'setup',
|
||||||
|
'app.php'
|
||||||
|
]);
|
||||||
|
Monolog\ErrorHandler::register($app->getContainer()->get(LoggerInterface::class));
|
||||||
|
try {
|
||||||
|
$app->run();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$app->getContainer()->get(LoggerInterface::class)->alert($e);
|
||||||
|
} catch (Error $e) {
|
||||||
|
$app->getContainer()->get(LoggerInterface::class)->error($e);
|
||||||
|
}
|
9
app/resources/routes/01_logs.php
Normal file
9
app/resources/routes/01_logs.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
use ProVM\Common\Controller\Logs;
|
||||||
|
|
||||||
|
$app->group('/logs', function($app) {
|
||||||
|
$app->get('[/]', Logs::class);
|
||||||
|
});
|
||||||
|
$app->group('/log/{log_file}', function($app) {
|
||||||
|
$app->get('[/]', [Logs::class, 'get']);
|
||||||
|
});
|
4
app/resources/routes/99_base.php
Normal file
4
app/resources/routes/99_base.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
use ProVM\Common\Controller\Base;
|
||||||
|
|
||||||
|
$app->get('[/]', Base::class);
|
11
app/resources/views/home.blade.php
Normal file
11
app/resources/views/home.blade.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
@extends('layout.base')
|
||||||
|
|
||||||
|
@section('page_content')
|
||||||
|
<div class="ui container">
|
||||||
|
<div class="ui list">
|
||||||
|
@foreach ($files as $file)
|
||||||
|
<a class="item" href="{{$urls->base}}/log/{{urlencode($file->getBasename())}}">{{$file->getBasename()}}</a>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
5
app/resources/views/layout/base.blade.php
Normal file
5
app/resources/views/layout/base.blade.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
@include('layout.head')
|
||||||
|
@include('layout.body')
|
||||||
|
</html>
|
6
app/resources/views/layout/body.blade.php
Normal file
6
app/resources/views/layout/body.blade.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<body>
|
||||||
|
@include('layout.body.header')
|
||||||
|
@yield('page_content')
|
||||||
|
@include('layout.body.footer')
|
||||||
|
@include('layout.body.scripts')
|
||||||
|
</body>
|
0
app/resources/views/layout/body/footer.blade.php
Normal file
0
app/resources/views/layout/body/footer.blade.php
Normal file
5
app/resources/views/layout/body/header.blade.php
Normal file
5
app/resources/views/layout/body/header.blade.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<nav class="ui menu">
|
||||||
|
<div class="ui container">
|
||||||
|
<a class="item" href="/">Home</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
4
app/resources/views/layout/body/scripts.blade.php
Normal file
4
app/resources/views/layout/body/scripts.blade.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.2/semantic.min.js" integrity="sha512-5cguXwRllb+6bcc2pogwIeQmQPXEzn2ddsqAexIBhh7FO1z5Hkek1J9mrK2+rmZCTU6b6pERxI7acnp1MpAg4Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
|
||||||
|
@stack('page_scripts')
|
10
app/resources/views/layout/head.blade.php
Normal file
10
app/resources/views/layout/head.blade.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<head>
|
||||||
|
<meta charset="utf8" />
|
||||||
|
@hasSection('page_title')
|
||||||
|
<title>Logs - @yield('page_title')</title>
|
||||||
|
@else
|
||||||
|
<title>Logs</title>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@include('layout.head.styles')
|
||||||
|
</head>
|
3
app/resources/views/layout/head/styles.blade.php
Normal file
3
app/resources/views/layout/head/styles.blade.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.2/semantic.min.css" integrity="sha512-n//BDM4vMPvyca4bJjZPDh7hlqsQ7hqbP9RH18GF2hTXBY5amBwM2501M0GPiwCU/v9Tor2m13GOTFjk00tkQA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
|
||||||
|
@stack('page_styles')
|
40
app/setup/app.php
Normal file
40
app/setup/app.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
use DI\ContainerBuilder;
|
||||||
|
use DI\Bridge\Slim\Bridge;
|
||||||
|
|
||||||
|
require_once 'composer.php';
|
||||||
|
|
||||||
|
$builder = new ContainerBuilder();
|
||||||
|
|
||||||
|
$folders = [
|
||||||
|
'settings',
|
||||||
|
'setups'
|
||||||
|
];
|
||||||
|
foreach ($folders as $f) {
|
||||||
|
$folder = implode(DIRECTORY_SEPARATOR, [__DIR__, $f]);
|
||||||
|
if (!file_exists($folder)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files = new FilesystemIterator($folder);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$builder->addDefinitions($file->getRealPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$app = Bridge::create($builder->build());
|
||||||
|
|
||||||
|
$folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'middlewares']);
|
||||||
|
if (file_exists($folder)) {
|
||||||
|
$files = new FilesystemIterator($folder);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
include_once $file->getRealPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $app;
|
6
app/setup/composer.php
Normal file
6
app/setup/composer.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
require_once implode(DIRECTORY_SEPARATOR, [
|
||||||
|
dirname(__FILE__, 2),
|
||||||
|
'vendor',
|
||||||
|
'autoload.php'
|
||||||
|
]);
|
9
app/setup/middlewares/99_routes.php
Normal file
9
app/setup/middlewares/99_routes.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
$folder = $app->getContainer()->get('folders')->get('routes');
|
||||||
|
$files = new FilesystemIterator($folder);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
include_once $file->getRealPath();
|
||||||
|
}
|
4
app/setup/settings/01_env.php
Normal file
4
app/setup/settings/01_env.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'logs_folder' => $_ENV['LOGS_PATH'] ?? '/logs'
|
||||||
|
];
|
22
app/setup/settings/02_folders.php
Normal file
22
app/setup/settings/02_folders.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'folders' => function() {
|
||||||
|
return new DI\Container([
|
||||||
|
'base' => dirname(__FILE__, 3),
|
||||||
|
'resources' => DI\String('{base}/resources'),
|
||||||
|
'routes' => DI\String('{resources}/routes'),
|
||||||
|
'cache' => DI\String('{base}/cache'),
|
||||||
|
'templates' => DI\String('{resources}/views')
|
||||||
|
]);
|
||||||
|
/*$arr = ['base' => dirname(__FILE__, 3)];
|
||||||
|
$arr['resources'] = implode(DIRECTORY_SEPARATOR, [
|
||||||
|
$arr['base'],
|
||||||
|
'resources'
|
||||||
|
]);
|
||||||
|
$arr['routes'] = implode(DIRECTORY_SEPARATOR, [
|
||||||
|
$arr['resources'],
|
||||||
|
'routes'
|
||||||
|
]);
|
||||||
|
return (object) $arr;*/
|
||||||
|
}
|
||||||
|
];
|
11
app/setup/settings/03_urls.php
Normal file
11
app/setup/settings/03_urls.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'urls' => function() {
|
||||||
|
$arr = ['base' => $_ENV['WEB_URL'] ?? 'http://localhost:' . ($_ENV['WEB_PORT'] ?? 8030)];
|
||||||
|
$arr['assets'] = implode('/', [
|
||||||
|
$arr['base'],
|
||||||
|
'assets'
|
||||||
|
]);
|
||||||
|
return (object) $arr;
|
||||||
|
}
|
||||||
|
];
|
16
app/setup/setups/01_logs.php
Normal file
16
app/setup/setups/01_logs.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
Psr\Log\LoggerInterface::class => function(ContainerInterface $container) {
|
||||||
|
$logger = new Monolog\Logger('logger');
|
||||||
|
$logger->pushHandler(
|
||||||
|
new Monolog\Handler\RotatingFileHandler(
|
||||||
|
implode(DIRECTORY_SEPARATOR, [
|
||||||
|
$container->get('logs_folder'), 'php.log'
|
||||||
|
])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return $logger;
|
||||||
|
}
|
||||||
|
];
|
15
app/setup/setups/02_view.php
Normal file
15
app/setup/setups/02_view.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
Slim\Views\Blade::class => function(ContainerInterface $container) {
|
||||||
|
return new Slim\Views\Blade(
|
||||||
|
$container->get('folders')->get('templates'),
|
||||||
|
$container->get('folders')->get('cache'),
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
'urls' => $container->get('urls')
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
];
|
10
app/setup/setups/03_services.php
Normal file
10
app/setup/setups/03_services.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
ProVM\Common\Service\Logs::class => function(ContainerInterface $container) {
|
||||||
|
return new ProVM\Common\Service\Logs(
|
||||||
|
$container->get('logs_folder')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
];
|
156
app/src/Log.php
Normal file
156
app/src/Log.php
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Logview;
|
||||||
|
|
||||||
|
use DateTimeInterface;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
class Log
|
||||||
|
{
|
||||||
|
protected DateTimeInterface $dateTime;
|
||||||
|
protected string $channel;
|
||||||
|
protected string $severity;
|
||||||
|
protected string $message;
|
||||||
|
protected array $stack;
|
||||||
|
protected string $context;
|
||||||
|
protected string $extra;
|
||||||
|
|
||||||
|
public function getDate(): DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->dateTime;
|
||||||
|
}
|
||||||
|
public function getChannel(): string
|
||||||
|
{
|
||||||
|
return $this->channel;
|
||||||
|
}
|
||||||
|
public function getSeverity(): string
|
||||||
|
{
|
||||||
|
return $this->severity;
|
||||||
|
}
|
||||||
|
public function getMessage(): string
|
||||||
|
{
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
public function getStack(): array
|
||||||
|
{
|
||||||
|
return $this->stack ?? [];
|
||||||
|
}
|
||||||
|
public function getContext(): string
|
||||||
|
{
|
||||||
|
return $this->context;
|
||||||
|
}
|
||||||
|
public function getExtra(): string
|
||||||
|
{
|
||||||
|
return $this->extra ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDate(DateTimeInterface $dateTime): Log
|
||||||
|
{
|
||||||
|
$this->dateTime = $dateTime;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function setChannel(string $channel): Log
|
||||||
|
{
|
||||||
|
$this->channel = $channel;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function setSeverity(string $severity): Log
|
||||||
|
{
|
||||||
|
$this->severity = $severity;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function setMessage(string $message): Log
|
||||||
|
{
|
||||||
|
$this->message = $message;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function setStack(array $stack): Log
|
||||||
|
{
|
||||||
|
$this->stack = $stack;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function setContext(string $context): Log
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function setExtra(string $extra): Log
|
||||||
|
{
|
||||||
|
$this->extra = $extra;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasStack(): bool
|
||||||
|
{
|
||||||
|
return isset($this->stack);
|
||||||
|
}
|
||||||
|
public function hasContext(): bool
|
||||||
|
{
|
||||||
|
return $this->context !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColor(): string
|
||||||
|
{
|
||||||
|
return self::COLORS[strtoupper($this->getSeverity())];
|
||||||
|
}
|
||||||
|
public function getBackgroundColor(): string
|
||||||
|
{
|
||||||
|
return self::BACKGROUNDS[strtoupper($this->getSeverity())];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function parse(string $content): Log
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
|
||||||
|
$regex = "/\[(?P<date>.*)\]\s(?<channel>\w*)\.(?<severity>\w*):\s(?<message>.*)\s[\[|\{](?<context>.*)[\]|\}]\s\[(?<extra>.*)\]/";
|
||||||
|
preg_match($regex, $content, $matches);
|
||||||
|
$log->setDate(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.uP', $matches['date']));
|
||||||
|
$log->setChannel($matches['channel']);
|
||||||
|
$log->setSeverity($matches['severity']);
|
||||||
|
$message = $matches['message'];
|
||||||
|
if (str_contains($message, 'Stack trace')) {
|
||||||
|
list($msg, $data) = explode('Stack trace:', $message);
|
||||||
|
$message = trim($msg);
|
||||||
|
$regex = '/\s#\d+\s/';
|
||||||
|
$lines = preg_split($regex, $data);
|
||||||
|
array_shift($lines);
|
||||||
|
$log->setStack($lines);
|
||||||
|
}
|
||||||
|
$log->setMessage($message);
|
||||||
|
$log->setContext($matches['context']);
|
||||||
|
if (isset($matches['extra'])) {
|
||||||
|
$log->setExtra($matches['extra']);
|
||||||
|
}
|
||||||
|
return $log;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LEVELS = [
|
||||||
|
'DEBUG',
|
||||||
|
'INFO',
|
||||||
|
'NOTICE',
|
||||||
|
'WARNING',
|
||||||
|
'ERROR',
|
||||||
|
'CRITICAL',
|
||||||
|
'ALERT',
|
||||||
|
'EMERGENCY',
|
||||||
|
];
|
||||||
|
const COLORS = [
|
||||||
|
'DEBUG' => '#000',
|
||||||
|
'INFO' => '#000',
|
||||||
|
'NOTICE' => '#fff',
|
||||||
|
'WARNING' => '#000',
|
||||||
|
'ERROR' => '#fff',
|
||||||
|
'CRITICAL' => '#fff',
|
||||||
|
'ALERT' => '#fff',
|
||||||
|
'EMERGENCY' => '#fff',
|
||||||
|
];
|
||||||
|
const BACKGROUNDS = [
|
||||||
|
'DEBUG' => '#fff',
|
||||||
|
'INFO' => '#00f',
|
||||||
|
'NOTICE' => '#55f',
|
||||||
|
'WARNING' => '#dd5',
|
||||||
|
'ERROR' => '#555',
|
||||||
|
'CRITICAL' => '#f00',
|
||||||
|
'ALERT' => '#f55',
|
||||||
|
'EMERGENCY' => '#f55',
|
||||||
|
];
|
||||||
|
}
|
44
app/src/Log/File.php
Normal file
44
app/src/Log/File.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Logview\Log;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use ProVM\Logview\Log;
|
||||||
|
|
||||||
|
class File
|
||||||
|
{
|
||||||
|
protected string $filename;
|
||||||
|
protected string $content;
|
||||||
|
|
||||||
|
public function getFilename(): string
|
||||||
|
{
|
||||||
|
return $this->filename;
|
||||||
|
}
|
||||||
|
public function getContent(): string
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFilename(string $filename): File
|
||||||
|
{
|
||||||
|
$this->filename = $filename;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function setContent(string $content): File
|
||||||
|
{
|
||||||
|
$this->content = $content;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogs(): array
|
||||||
|
{
|
||||||
|
$lines = explode(PHP_EOL, $this->getContent());
|
||||||
|
$logs = [];
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (trim($line) === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$logs []= Log::parse($line);
|
||||||
|
}
|
||||||
|
return array_reverse($logs);
|
||||||
|
}
|
||||||
|
}
|
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
proxy:
|
||||||
|
container_name: lv_proxy
|
||||||
|
profiles:
|
||||||
|
- app
|
||||||
|
image: nginx
|
||||||
|
ports:
|
||||||
|
- "${WEB_PORT:-8030}:80"
|
||||||
|
volumes:
|
||||||
|
- "./nginx.conf:/etc/nginx/conf.d/default.conf"
|
||||||
|
- "./src:/app"
|
||||||
|
- "./logs:/logs"
|
||||||
|
|
||||||
|
php:
|
||||||
|
container_name: lv_app
|
||||||
|
profiles:
|
||||||
|
- app
|
||||||
|
build: .
|
||||||
|
volumes:
|
||||||
|
- "./src:/app"
|
||||||
|
- "./logs:/logs"
|
22
nginx.conf
Normal file
22
nginx.conf
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
error_log /logs/error.log;
|
||||||
|
access_log /logs/access.log;
|
||||||
|
root /app/public;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri /index.php$is_args$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php {
|
||||||
|
try_files $uri =404;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_read_timeout 3600;
|
||||||
|
fastcgi_pass php:9000;
|
||||||
|
}
|
||||||
|
}
|
3
php-errors.ini
Normal file
3
php-errors.ini
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
display_errors=no
|
||||||
|
log_errors=yes
|
||||||
|
error_log=/logs/php_errors.log
|
Reference in New Issue
Block a user