migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\LogReader\Log;
use OC\Log\LogDetails;
use OC\SystemConfig;
use OCA\LogReader\Command\Tail;
/**
* Utility to write log messages to the console as they are emitted
*/
class Console extends LogDetails {
private int $level;
private int $terminalWidth;
private Formatter $formatter;
public function __construct(Formatter $formatter, SystemConfig $config, string $level, int $terminalWidth) {
parent::__construct($config);
$this->formatter = $formatter;
$this->level = self::parseLogLevel($level);
$this->terminalWidth = $terminalWidth;
}
public function log(int $level, string $app, array $entry) {
if ($level >= $this->level) {
$messageWidth = $this->terminalWidth - 8 - 18 - 6;
$entry = $this->logDetails($app, $entry, $level);
$lines = explode("\n", $this->formatter->formatMessage($entry, $messageWidth));
$lines[0] = str_pad(Tail::LEVELS[$level], 8) . ' ' .
str_pad(wordwrap($app, 18), 18) . ' ' .
str_pad($lines[0], $messageWidth);
for ($i = 1; $i < count($lines); $i++) {
$lines[$i] = str_repeat(' ', 8 + 18 + 2) . $lines[$i];
}
foreach ($lines as $line) {
fwrite(STDERR, $line . "\n");
}
fwrite(STDERR, "\n");
}
}
private static function parseLogLevel(string $level): int {
if (is_numeric($level)) {
return (int)$level;
}
switch (strtoupper($level)) {
case "DEBUG":
return 0;
case "INFO":
return 1;
case "WARN":
return 2;
case "ERROR":
return 3;
case "FATAL":
return 4;
default:
throw new \Exception("Unknown log level $level");
}
}
}
@@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\LogReader\Log;
class Formatter {
private $root;
public function __construct(string $root) {
$this->root = $root;
}
public function formatMessage($logItem, int $width) {
if (isset($logItem['exception'])) {
return $this->formatException($logItem['exception'], $width);
}
if (is_array($logItem['message']) && isset($logItem['message']['Exception'])) {
return $this->formatException($logItem['message'], $width);
}
return wordwrap($logItem['message'], $width);
}
private function formatException(array $e, int $width) {
$largestIndex = count($e['Trace']) - 1;
$largestIndexWidth = strlen((string)$largestIndex);
$message = wordwrap($e['Exception'] . ': ' . $e['Message'] . ' at ' . $this->getFileAndLine($e), $width) . "\n\n";
$message .= implode("\n", array_map(function ($index, $trace) use ($largestIndexWidth, $width) {
return $this->formatTraceLine($index, $trace, $largestIndexWidth, $width);
}, array_keys($e['Trace']), array_values($e['Trace'])));
if (isset($e['Previous'])) {
$message .= "\n\n" . 'Caused by ' . $this->formatException($e['Previous'], $width);
}
return $message;
}
private function formatTraceLine(int $index, array $trace, int $largestIndexWidth, int $width): string {
$whiteSpace = str_repeat(' ', $largestIndexWidth - strlen((string)$index));
$method = ($trace['class'] ?? '') . ($trace['type'] ?? '') . $trace['function'];
$argumentWidth = $width - $largestIndexWidth - strlen($method) - 5;
$arguments = array_map(function ($arg) use ($argumentWidth) {
$base = str_replace("\n", '', $this->formatArgument($arg, 0));
$showInline = strlen($base) < $argumentWidth;
return $showInline ? $base : substr($base, 0, $argumentWidth - 8) . ' ... ' . substr($base, -2);
}, $trace['args'] ?? []);
$argumentsString = implode(', ', $arguments);
$argumentWhiteSpace = str_repeat(' ', $largestIndexWidth + 2);
if ($argumentsString && strlen($argumentsString) < $argumentWidth) {
return $whiteSpace . $index . '. ' . $this->getFileAndLine($trace, $argumentWidth) . "\n" .
$argumentWhiteSpace . $method . '(' .
$argumentsString . ')';
} else {
return $whiteSpace . $index . '. ' . $this->getFileAndLine($trace, $argumentWidth) . "\n" .
$argumentWhiteSpace . $method . "(\n" .
implode(",\n", array_map(function ($argumentLine) use ($argumentWhiteSpace) {
return $argumentWhiteSpace . ' ' . trim($argumentLine);
}, $arguments)) . "\n" .
$argumentWhiteSpace . ")";
}
}
private function formatArgument($argument, int $whiteSpace, int $depth = 0, bool $forceObject = false): string {
$leadingSpace = str_repeat(' ', $whiteSpace * $depth);
$glue = ($whiteSpace) ? ",\n" : ',';
if (is_array($argument) && isset($argument['__class__'])) {
$className = $argument['__class__'];
unset($argument['__class__']);
return $leadingSpace . $className . ' ' . trim($this->formatArgument($argument, $whiteSpace, $depth, true));
} elseif (is_array($argument)) {
if (count($argument) === 0) {
return $leadingSpace . ($forceObject ? '{}' : '[]');
}
$isObject = $forceObject || array_keys($argument) !== range(0, count($argument) - 1);
if ($isObject) {
$keyWhitespace = str_repeat(' ', $whiteSpace * ($depth + 1));
return $leadingSpace . "{\n" .
implode($glue, array_map(function ($key, $value) use ($whiteSpace, $depth, $keyWhitespace) {
return $keyWhitespace . $key . ':' . trim($this->formatArgument($value, $whiteSpace, $depth + 1));
}, array_keys($argument), array_values($argument))) . ($whiteSpace ? "\n" : '') . $leadingSpace . '}';
} else {
return $leadingSpace . "[\n" .
implode($glue, array_map(function ($value) use ($whiteSpace, $depth) {
return $this->formatArgument($value, $whiteSpace, $depth + 1);
}, $argument)) . ($whiteSpace ? "\n" : '') . $leadingSpace . ']';
}
} else {
$value = json_encode($argument, $whiteSpace ? JSON_PRETTY_PRINT : 0);
return $leadingSpace . $value;
}
}
private function getFileAndLine(array $item, int $width = 64): string {
$file = $item['file'] ?? $item['File'] ?? null;
$line = $item['line'] ?? $item['Line'] ?? null;
if ($file && $line) {
if (substr($file, 0, strlen($this->root) + 1) === $this->root . '/') {
$file = substr($file, strlen($this->root) + 1);
}
$postFix = ' line ' . $line;
$fileWidth = $width - strlen($postFix) - 4;
$prefix = '';
$count = 0;
while (strlen($file) > $fileWidth && strpos($file, '/') !== false && $count < 20) {
$file = substr($file, strpos($file, '/') + 1);
$prefix = '.../';
$count++;
}
return $prefix . $file . $postFix;
} else {
return '<<closure>>';
}
}
}
@@ -0,0 +1,144 @@
<?php
/**
* @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\LogReader\Log;
/**
* @template-implements \Iterator<int,array>
*/
class LogIterator implements \Iterator {
/**
* @var resource
*/
private $handle;
/**
* @var int
*/
private $position = 0;
/**
* @var string
*/
private $lastLine;
/**
* @var string
*/
private $currentLine = '';
private $currentKey = -1;
/**
* @var string
*/
private $dateFormat;
private $timezone;
public const CHUNK_SIZE = 100; // how many chars do we try at once to find a new line
/**
* @param resource $handle
* @param string $dateFormat
* @param string $timezone
*/
public function __construct($handle, $dateFormat, $timezone) {
$this->handle = $handle;
$this->dateFormat = $dateFormat;
$this->timezone = new \DateTimeZone($timezone);
$this->rewind();
$this->next();
}
public function rewind(): void {
fseek($this->handle, 0, SEEK_END);
$this->position = ftell($this->handle) - self::CHUNK_SIZE;
$this->currentKey = 0;
}
/**
* @return array
*/
#[\ReturnTypeWillChange]
public function current() {
$entry = json_decode($this->lastLine, true);
if ($this->dateFormat !== \DateTime::ATOM) {
if (isset($entry['time'])) {
$time = \DateTime::createFromFormat($this->dateFormat, $entry['time'], $this->timezone);
if ($time) {
$entry['time'] = $time->format(\DateTime::ATOM);
}
}
}
return $entry;
}
public function key(): int {
return $this->currentKey;
}
public function next(): void {
$this->currentLine = '';
// Loop through each character of the file looking for new lines
while ($this->position > 0) {
fseek($this->handle, $this->position);
$chars = fread($this->handle, self::CHUNK_SIZE);
$newlinePos = strrpos($chars, "\n");
if ($newlinePos !== false) {
$this->currentLine = substr($chars, $newlinePos + 1) . $this->currentLine;
$this->lastLine = $this->currentLine;
$this->currentKey++;
$this->position -= (self::CHUNK_SIZE - $newlinePos);
return;
} else {
$this->currentLine = $chars . $this->currentLine;
if ($this->position >= self::CHUNK_SIZE) {
$this->position -= self::CHUNK_SIZE;
} else {
$remaining = $this->position;
fseek($this->handle, 0);
$chars = fread($this->handle, $remaining);
$this->currentLine = $chars . $this->currentLine;
$this->lastLine = $this->currentLine;
$this->position = 0;
}
}
}
}
public function valid(): bool {
if (!is_resource($this->handle)) {
return false;
}
if ($this->position > 0) {
return true;
}
if ($this->currentLine === '') {
return false;
}
return true;
}
}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\LogReader\Log;
use OCP\IConfig;
use OCP\Log\IFileBased;
use OCP\Log\ILogFactory;
class LogIteratorFactory {
private $config;
private $logFactory;
public function __construct(IConfig $config, ILogFactory $logFactory) {
$this->config = $config;
$this->logFactory = $logFactory;
}
/**
* @return \Iterator
* @param int[] $levels Array of levels to show
* @throws \Exception
*/
public function getLogIterator(array $levels) {
$dateFormat = $this->config->getSystemValue('logdateformat', \DateTime::ATOM);
$timezone = $this->config->getSystemValue('logtimezone', 'UTC');
$log = $this->logFactory->get('file');
if ($log instanceof IFileBased) {
$handle = fopen($log->getLogFilePath(), 'rb');
if ($handle) {
$iterator = new LogIterator($handle, $dateFormat, $timezone);
return new \CallbackFilterIterator($iterator, function ($logItem) use ($levels) {
return $logItem && in_array($logItem['level'], $levels);
});
} else {
throw new \Exception("Error while opening " . $log->getLogFilePath());
}
}
throw new \Exception('Can\'t find log class');
}
}
@@ -0,0 +1,71 @@
<?php
/**
* @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\LogReader\Log;
/**
* @template-extends \FilterIterator<int,array,\Iterator<int,array>>
*/
class SearchFilter extends \FilterIterator {
/**
* @var string
*/
private $query;
/**
* @var string[]
*/
private $levels;
public function __construct(\Iterator $iterator, string $query) {
parent::__construct($iterator);
$this->rewind();
$this->query = strtolower($query);
$this->levels = ['Debug', 'Info', 'Warning', 'Error', 'Fatal'];
}
private function formatLevel(int $level): string {
return isset($this->levels[$level]) ? $this->levels[$level] : 'Unknown';
}
public function accept(): bool {
if (!$this->query) {
return true;
}
$value = $this->current();
return $this->inMessage($value['message'] ?? '', $this->query)
|| stripos($value['app'] ?? '', $this->query) !== false
|| stripos($value['reqId'] ?? '', $this->query) !== false
|| stripos($value['user'] ?? '', $this->query) !== false
|| stripos($value['url'] ?? '', $this->query) !== false
|| stripos($this->formatLevel($value['level'] ?? -1), $this->query) !== false;
}
private function inMessage($message, string $query): bool {
if (is_string($message)) {
return stripos($message, $query) !== false;
} elseif (isset($message['Exception'])) {
return stripos($message['Exception'], $query) !== false
|| stripos($message['Message'] ?? '', $query) !== false;
}
return false;
}
}