Skip to content

Commit

Permalink
Merge pull request #32985 from nextcloud/backport/32242/stable23
Browse files Browse the repository at this point in the history
[stable23] Fix logging data context to file
  • Loading branch information
blizzz authored Sep 1, 2022
2 parents 314ed26 + 54b0b53 commit 55f07a4
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 24 deletions.
34 changes: 21 additions & 13 deletions lib/private/Log.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* @author Olivier Paroz <[email protected]>
* @author Robin Appelman <[email protected]>
* @author Roeland Jago Douma <[email protected]>
* @author Thomas Citharel <[email protected]>
* @author Thomas Müller <[email protected]>
* @author Victor Dubiniuk <[email protected]>
*
Expand Down Expand Up @@ -207,11 +208,11 @@ public function log(int $level, string $message, array $context = []) {
array_walk($context, [$this->normalizer, 'format']);

$app = $context['app'] ?? 'no app in context';
$message = $this->interpolateMessage($context, $message);
$entry = $this->interpolateMessage($context, $message);

try {
if ($level >= $minLevel) {
$this->writeLog($app, $message, $level);
$this->writeLog($app, $entry, $level);

if ($this->crashReporters !== null) {
$messageContext = array_merge(
Expand All @@ -220,11 +221,11 @@ public function log(int $level, string $message, array $context = []) {
'level' => $level
]
);
$this->crashReporters->delegateMessage($message, $messageContext);
$this->crashReporters->delegateMessage($entry['message'], $messageContext);
}
} else {
if ($this->crashReporters !== null) {
$this->crashReporters->delegateBreadcrumb($message, 'log', $context);
$this->crashReporters->delegateBreadcrumb($entry['message'], 'log', $context);
}
}
} catch (\Throwable $e) {
Expand Down Expand Up @@ -309,8 +310,11 @@ public function logException(\Throwable $exception, array $context = []) {
$level = $context['level'] ?? ILogger::ERROR;

$serializer = new ExceptionSerializer($this->config);
$data = $serializer->serializeException($exception);
$data['CustomMessage'] = $this->interpolateMessage($context, $context['message'] ?? '--');
$data = $context;
unset($data['app']);
unset($data['level']);
$data = array_merge($serializer->serializeException($exception), $data);
$data = $this->interpolateMessage($data, $context['message'] ?? '--', 'CustomMessage');

$minLevel = $this->getLogLevel($context);

Expand Down Expand Up @@ -375,16 +379,20 @@ public function getLogPath():string {
/**
* Interpolate $message as defined in PSR-3
*
* @param array $context
* @param string $message
*
* @return string
* Returns an array containing the context without the interpolated
* parameters placeholders and the message as the 'message' - or
* user-defined - key.
*/
private function interpolateMessage(array $context, string $message): string {
private function interpolateMessage(array $context, string $message, string $messageKey = 'message'): array {
$replace = [];
$usedContextKeys = [];
foreach ($context as $key => $val) {
$replace['{' . $key . '}'] = $val;
$fullKey = '{' . $key . '}';
$replace[$fullKey] = $val;
if (strpos($message, $fullKey) !== false) {
$usedContextKeys[$key] = true;
}
}
return strtr($message, $replace);
return array_merge(array_diff_key($context, $usedContextKeys), [$messageKey => strtr($message, $replace)]);
}
}
4 changes: 3 additions & 1 deletion lib/private/Log/LogDetails.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* @author Arthur Schiwon <[email protected]>
* @author Christoph Wurst <[email protected]>
* @author Julius Härtl <[email protected]>
* @author Thomas Citharel <[email protected]>
*
* @license GNU AGPL version 3 or any later version
*
Expand Down Expand Up @@ -90,8 +91,9 @@ public function logDetails(string $app, $message, int $level): array {
$entry['exception'] = $message;
$entry['message'] = $message['CustomMessage'] !== '--' ? $message['CustomMessage'] : $message['Message'];
} else {
$entry['data'] = $message;
$entry['message'] = $message['message'] ?? '(no message provided)';
unset($message['message']);
$entry['data'] = $message;
}
}

Expand Down
30 changes: 27 additions & 3 deletions tests/lib/Log/FileTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php
/**
*
* @author Thomas Citharel <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
Expand All @@ -18,6 +20,7 @@
namespace Test\Log;

use OC\Log\File;
use OCP\IConfig;
use OCP\ILogger;
use Test\TestCase;

Expand All @@ -36,7 +39,7 @@ protected function setUp(): void {
$config = \OC::$server->getSystemConfig();
$this->restore_logfile = $config->getValue("logfile");
$this->restore_logdateformat = $config->getValue('logdateformat');

$config->setValue("logfile", $config->getValue('datadirectory') . "/logtest.log");
$this->logFile = new File($config->getValue('datadirectory') . '/logtest.log', '', $config);
}
Expand All @@ -55,7 +58,28 @@ protected function tearDown(): void {
$this->logFile = new File($this->restore_logfile, '', $config);
parent::tearDown();
}


public function testLogging() {
$config = \OC::$server->get(IConfig::class);
# delete old logfile
unlink($config->getSystemValue('logfile'));

# set format & write log line
$config->setSystemValue('logdateformat', 'u');
$this->logFile->write('code', ['something' => 'extra', 'message' => 'Testing logging'], ILogger::ERROR);

# read log line
$handle = @fopen($config->getSystemValue('logfile'), 'r');
$line = fread($handle, 1000);
fclose($handle);

# check log has data content
$values = (array) json_decode($line, true);
$this->assertArrayNotHasKey('message', $values['data']);
$this->assertEquals('extra', $values['data']['something']);
$this->assertEquals('Testing logging', $values['message']);
}

public function testMicrosecondsLogTimestamp() {
$config = \OC::$server->getConfig();
# delete old logfile
Expand All @@ -69,7 +93,7 @@ public function testMicrosecondsLogTimestamp() {
$handle = @fopen($config->getSystemValue('logfile'), 'r');
$line = fread($handle, 1000);
fclose($handle);

# check timestamp has microseconds part
$values = (array) json_decode($line);
$microseconds = $values['time'];
Expand Down
31 changes: 24 additions & 7 deletions tests/lib/LoggerTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php
/**
* Copyright (c) 2014 Thomas Müller <[email protected]>
*
* @author Thomas Citharel <[email protected]>
*
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
Expand All @@ -9,18 +12,21 @@
namespace Test;

use OC\Log;
use OC\SystemConfig;
use OCP\ILogger;
use OCP\Log\IWriter;
use OCP\Support\CrashReport\IRegistry;
use PHPUnit\Framework\MockObject\MockObject;

class LoggerTest extends TestCase implements IWriter {

/** @var \OC\SystemConfig|\PHPUnit\Framework\MockObject\MockObject */
/** @var SystemConfig|MockObject */
private $config;

/** @var \OCP\Support\CrashReport\IRegistry|\PHPUnit\Framework\MockObject\MockObject */
/** @var IRegistry|MockObject */
private $registry;

/** @var \OCP\ILogger */
/** @var ILogger */
private $logger;

/** @var array */
Expand All @@ -30,8 +36,8 @@ protected function setUp(): void {
parent::setUp();

$this->logs = [];
$this->config = $this->createMock(\OC\SystemConfig::class);
$this->registry = $this->createMock(\OCP\Support\CrashReport\IRegistry::class);
$this->config = $this->createMock(SystemConfig::class);
$this->registry = $this->createMock(IRegistry::class);
$this->logger = new Log($this, $this->config, null, $this->registry);
}

Expand Down Expand Up @@ -63,12 +69,23 @@ public function testAppCondition() {
$this->assertEquals($expected, $this->getLogs());
}

private function getLogs() {
public function testLoggingWithDataArray(): void {
$writerMock = $this->createMock(IWriter::class);
$logFile = new Log($writerMock, $this->config);
$writerMock->expects($this->once())->method('write')->with('no app in context', ['something' => 'extra', 'message' => 'Testing logging with john']);
$logFile->error('Testing logging with {user}', ['something' => 'extra', 'user' => 'john']);
}

private function getLogs(): array {
return $this->logs;
}

public function write(string $app, $message, int $level) {
$this->logs[] = "$level $message";
$textMessage = $message;
if (is_array($message)) {
$textMessage = $message['message'];
}
$this->logs[] = $level . " " . $textMessage;
}

public function userAndPasswordData(): array {
Expand Down

0 comments on commit 55f07a4

Please sign in to comment.