Skip to content

Commit 499b847

Browse files
authored
Merge pull request #997 from nextcloud/occ-log-printer-27
[27] allow printing log messages during occ
2 parents 990a6e2 + 274fdde commit 499b847

File tree

6 files changed

+175
-5
lines changed

6 files changed

+175
-5
lines changed

.github/workflows/psalm-matrix.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
# do not stop on another job's failure
2525
fail-fast: false
2626
matrix:
27-
ocp-version: [ 'dev-master', 'dev-stable25', 'dev-stable24' ]
27+
ocp-version: [ 'dev-stable27' ]
2828

2929
name: Nextcloud ${{ matrix.ocp-version }}
3030
steps:

appinfo/info.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
<namespace>LogReader</namespace>
1212
<default_enable/>
1313

14+
<types>
15+
<logging/>
16+
</types>
17+
1418
<category>tools</category>
1519
<website>https://github.com/nextcloud/logreader</website>
1620
<bugs>https://github.com/nextcloud/logreader/issues</bugs>

lib/AppInfo/Application.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,27 @@
2323

2424
namespace OCA\LogReader\AppInfo;
2525

26+
use OCA\LogReader\Listener\LogListener;
2627
use OCA\LogReader\Log\Formatter;
2728
use OCP\AppFramework\App;
2829
use OCP\AppFramework\Bootstrap\IBootContext;
2930
use OCP\AppFramework\Bootstrap\IBootstrap;
3031
use OCP\AppFramework\Bootstrap\IRegistrationContext;
31-
use OCP\AppFramework\IAppContainer;
32+
use OCP\Log\BeforeMessageLoggedEvent;
33+
use Psr\Container\ContainerInterface;
3234

3335
class Application extends App implements IBootstrap {
3436
public function __construct(array $urlParams = []) {
3537
parent::__construct('logreader', $urlParams);
3638
}
3739

3840
public function register(IRegistrationContext $context): void {
41+
$context->registerService(Formatter::class, function (ContainerInterface $c) {
42+
return new Formatter(\OC::$SERVERROOT);
43+
});
44+
$context->registerEventListener(BeforeMessageLoggedEvent::class, LogListener::class);
3945
}
4046

4147
public function boot(IBootContext $context): void {
42-
$context->getAppContainer()->registerService(Formatter::class, function (IAppContainer $c) {
43-
return new Formatter(\OC::$SERVERROOT);
44-
});
4548
}
4649
}

lib/Listener/LogListener.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2023 Robin Appelman <[email protected]>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as
11+
* published by the Free Software Foundation, either version 3 of the
12+
* License, or (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Affero General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Affero General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
24+
namespace OCA\LogReader\Listener;
25+
26+
use OC\SystemConfig;
27+
use OCA\LogReader\Log\Console;
28+
use OCA\LogReader\Log\Formatter;
29+
use OCP\EventDispatcher\Event;
30+
use OCP\EventDispatcher\IEventListener;
31+
use OCP\Log\BeforeMessageLoggedEvent;
32+
use Symfony\Component\Console\Terminal;
33+
34+
class LogListener implements IEventListener {
35+
private ?Console $console;
36+
37+
public function __construct(Formatter $formatter, SystemConfig $config) {
38+
if (defined('OC_CONSOLE') && \OC_CONSOLE) {
39+
$level = getenv('OCC_LOG');
40+
if ($level) {
41+
$terminal = new Terminal();
42+
$this->console = new Console($formatter, $config, $level, $terminal->getWidth());
43+
} else {
44+
$this->console = null;
45+
}
46+
} else {
47+
$this->console = null;
48+
}
49+
}
50+
51+
52+
public function handle(Event $event): void {
53+
if (!$event instanceof BeforeMessageLoggedEvent) {
54+
return;
55+
}
56+
57+
if ($this->console) {
58+
$this->console->log($event->getLevel(), $event->getApp(), $event->getMessage());
59+
}
60+
}
61+
}

lib/Log/Console.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2023 Robin Appelman <[email protected]>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as
11+
* published by the Free Software Foundation, either version 3 of the
12+
* License, or (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Affero General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Affero General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
24+
namespace OCA\LogReader\Log;
25+
26+
use OC\Log\LogDetails;
27+
use OC\SystemConfig;
28+
use OCA\LogReader\Command\Tail;
29+
30+
/**
31+
* Utility to write log messages to the console as they are emitted
32+
*/
33+
class Console extends LogDetails {
34+
private int $level;
35+
private int $terminalWidth;
36+
private Formatter $formatter;
37+
38+
public function __construct(Formatter $formatter, SystemConfig $config, string $level, int $terminalWidth) {
39+
parent::__construct($config);
40+
$this->formatter = $formatter;
41+
$this->level = self::parseLogLevel($level);
42+
$this->terminalWidth = $terminalWidth;
43+
}
44+
45+
public function log(int $level, string $app, array $entry) {
46+
if ($level >= $this->level) {
47+
$messageWidth = $this->terminalWidth - 8 - 18 - 6;
48+
49+
$entry = $this->logDetails($app, $entry, $level);
50+
51+
$lines = explode("\n", $this->formatter->formatMessage($entry, $messageWidth));
52+
$lines[0] = str_pad(Tail::LEVELS[$level], 8) . ' ' .
53+
str_pad(wordwrap($app, 18), 18) . ' ' .
54+
str_pad($lines[0], $messageWidth);
55+
56+
for ($i = 1; $i < count($lines); $i++) {
57+
$lines[$i] = str_repeat(' ', 8 + 18 + 2) . $lines[$i];
58+
}
59+
60+
foreach ($lines as $line) {
61+
fwrite(STDERR, $line . "\n");
62+
}
63+
fwrite(STDERR, "\n");
64+
}
65+
}
66+
67+
private static function parseLogLevel(string $level): int {
68+
if (is_numeric($level)) {
69+
return (int)$level;
70+
}
71+
72+
switch (strtoupper($level)) {
73+
case "DEBUG":
74+
return 0;
75+
case "INFO":
76+
return 1;
77+
case "WARN":
78+
return 2;
79+
case "ERROR":
80+
return 3;
81+
case "FATAL":
82+
return 4;
83+
default:
84+
throw new \Exception("Unknown log level $level");
85+
}
86+
}
87+
}

tests/stubs/stub.phpstub

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,18 @@ namespace OC\Core\Command {
3232
class InterruptedException extends \Exception {
3333
}
3434
}
35+
36+
namespace OC {
37+
class SystemConfig {
38+
}
39+
}
40+
41+
namespace OC\Log {
42+
use OC\SystemConfig;
43+
class LogDetails {
44+
public function __construct(SystemConfig $config) {
45+
}
46+
public function logDetails(string $app, $message, int $level): array {
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)