Skip to content

Commit

Permalink
Merge pull request #873 from nextcloud/occ-log-printer
Browse files Browse the repository at this point in the history
allow printing log messages during occ
  • Loading branch information
icewind1991 authored Sep 18, 2023
2 parents d6d56b6 + 1cf6534 commit fc17c6c
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/psalm-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
# do not stop on another job's failure
fail-fast: false
matrix:
ocp-version: [ 'dev-master', 'dev-stable27', 'dev-stable26', 'dev-stable25']
ocp-version: [ 'dev-master' ]

name: Nextcloud ${{ matrix.ocp-version }}
steps:
Expand Down
4 changes: 4 additions & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
<namespace>LogReader</namespace>
<default_enable/>

<types>
<logging/>
</types>

<category>tools</category>
<website>https://github.com/nextcloud/logreader</website>
<bugs>https://github.com/nextcloud/logreader/issues</bugs>
Expand Down
3 changes: 3 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@

namespace OCA\LogReader\AppInfo;

use OCA\LogReader\Listener\LogListener;
use OCA\LogReader\Log\Formatter;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Log\BeforeMessageLoggedEvent;
use Psr\Container\ContainerInterface;

class Application extends App implements IBootstrap {
Expand All @@ -36,6 +38,7 @@ public function __construct(array $urlParams = []) {
}

public function register(IRegistrationContext $context): void {
$context->registerEventListener(BeforeMessageLoggedEvent::class, LogListener::class);
$context->registerService(Formatter::class, function (ContainerInterface $c) {
return new Formatter(\OC::$SERVERROOT);
});
Expand Down
61 changes: 61 additions & 0 deletions lib/Listener/LogListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Robin Appelman <[email protected]>
*
* @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\Listener;

use OC\SystemConfig;
use OCA\LogReader\Log\Console;
use OCA\LogReader\Log\Formatter;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Log\BeforeMessageLoggedEvent;
use Symfony\Component\Console\Terminal;

class LogListener implements IEventListener {
private ?Console $console;

public function __construct(Formatter $formatter, SystemConfig $config) {
if (defined('OC_CONSOLE') && \OC_CONSOLE) {
$level = getenv('OCC_LOG');
if ($level) {
$terminal = new Terminal();
$this->console = new Console($formatter, $config, $level, $terminal->getWidth());
} else {
$this->console = null;
}
} else {
$this->console = null;
}
}


public function handle(Event $event): void {
if (!$event instanceof BeforeMessageLoggedEvent) {
return;
}

if ($this->console) {
$this->console->log($event->getLevel(), $event->getApp(), $event->getMessage());
}
}
}
87 changes: 87 additions & 0 deletions lib/Log/Console.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Robin Appelman <[email protected]>
*
* @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");
}
}
}
15 changes: 15 additions & 0 deletions tests/stubs/stub.phpstub
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,18 @@ namespace OC\Core\Command {
class InterruptedException extends \Exception {
}
}

namespace OC {
class SystemConfig {
}
}

namespace OC\Log {
use OC\SystemConfig;
class LogDetails {
public function __construct(SystemConfig $config) {
}
public function logDetails(string $app, $message, int $level): array {
}
}
}

0 comments on commit fc17c6c

Please sign in to comment.