Skip to content

Commit cb5dd63

Browse files
authored
Merge pull request #1237 from nextcloud/backport/1235/stable29
[stable29] fix: LogIterator duplicates and drops log entries
2 parents 01be3f9 + b32d677 commit cb5dd63

File tree

2 files changed

+32
-11
lines changed

2 files changed

+32
-11
lines changed

lib/Log/LogIterator.php

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ class LogIterator implements \Iterator {
2929
* @var resource
3030
*/
3131
private $handle;
32+
private string $dateFormat;
33+
private \DateTimeZone $timezone;
34+
3235
private string $buffer = '';
36+
private string $lastLine = '';
3337
private int $position = 0;
34-
private string $lastLine;
35-
3638
private int $currentKey = -1;
37-
private string $dateFormat;
38-
private \DateTimeZone $timezone;
3939

4040
public const CHUNK_SIZE = 8192; // how many chars do we try at once to find a new line
4141

@@ -49,13 +49,15 @@ public function __construct($handle, string $dateFormat, string $timezone) {
4949
$this->dateFormat = $dateFormat;
5050
$this->timezone = new \DateTimeZone($timezone);
5151
$this->rewind();
52-
$this->next();
5352
}
5453

5554
public function rewind(): void {
5655
fseek($this->handle, 0, SEEK_END);
5756
$this->position = ftell($this->handle);
58-
$this->currentKey = 0;
57+
$this->lastLine = '';
58+
$this->buffer = '';
59+
$this->currentKey = -1;
60+
$this->next();
5961
}
6062

6163
/**
@@ -92,13 +94,19 @@ public function next(): void {
9294
while ($this->position >= 0) {
9395
$newlinePos = strrpos($this->buffer, "\n");
9496
if ($newlinePos !== false) {
97+
if ($newlinePos + 1 === strlen($this->buffer)) {
98+
// try again with truncated buffer if it ends with newline, i.e. on first call
99+
$this->buffer = substr($this->buffer, 0, $newlinePos);
100+
continue;
101+
}
95102
$this->lastLine = substr($this->buffer, $newlinePos + 1);
96103
$this->buffer = substr($this->buffer, 0, $newlinePos);
97104
$this->currentKey++;
98105
return;
99106
} elseif ($this->position === 0) {
100107
$this->lastLine = $this->buffer;
101108
$this->buffer = '';
109+
$this->currentKey++;
102110
return;
103111
} else {
104112
$this->fillBuffer();
@@ -111,11 +119,7 @@ public function valid(): bool {
111119
return false;
112120
}
113121

114-
if ($this->position > 0) {
115-
return true;
116-
}
117-
118-
if ($this->lastLine === '') {
122+
if ($this->lastLine === '' && $this->position === 0) {
119123
return false;
120124
}
121125

tests/Unit/Log/LogIteratorTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,21 @@ public function testGetLines() {
5252
$this->assertEquals(2, $entries[0]['reqId']);
5353
$this->assertEquals(1, $entries[1]['reqId']);
5454
}
55+
56+
public function testGetLinesTrailingNewLine() {
57+
$log = $this->getLogIterator(
58+
"{\"reqId\":\"1\",\"level\":3,\"time\":\"2019-11-04T18:50:57+00:00\",\"remoteAddr\":\"127.0.0.1\",\"user\":\"admin\",\"app\":\"comments\",\"method\":\"GET\",\"url\":\"/settings/admin/logging\",\"message\":{\"Exception\":\"RuntimeException\",\"Message\":\"App class OCA\\\\Comments\\\\AppInfo\\\\Application is not setup via query() but directly\",\"Code\":0,\"Trace\":[{\"file\":\"/srv/http/cloud/apps/comments/lib/AppInfo/Application.php\",\"line\":42,\"function\":\"__construct\",\"class\":\"OCP\\\\AppFramework\\\\App\",\"type\":\"->\",\"args\":[\"comments\",[]]},{\"file\":\"/srv/http/cloud/apps/comments/appinfo/app.php\",\"line\":24,\"function\":\"__construct\",\"class\":\"OCA\\\\Comments\\\\AppInfo\\\\Application\",\"type\":\"->\",\"args\":[]},{\"file\":\"/srv/http/cloud/lib/private/legacy/app.php\",\"line\":260,\"args\":[\"/srv/http/cloud/apps/comments/appinfo/app.php\"],\"function\":\"require_once\"},{\"file\":\"/srv/http/cloud/lib/private/legacy/app.php\",\"line\":154,\"function\":\"requireAppFile\",\"class\":\"OC_App\",\"type\":\"::\",\"args\":[\"comments\"]},{\"file\":\"/srv/http/cloud/lib/private/legacy/app.php\",\"line\":127,\"function\":\"loadApp\",\"class\":\"OC_App\",\"type\":\"::\",\"args\":[\"comments\"]},{\"file\":\"/srv/http/cloud/lib/base.php\",\"line\":991,\"function\":\"loadApps\",\"class\":\"OC_App\",\"type\":\"::\",\"args\":[]},{\"file\":\"/srv/http/cloud/index.php\",\"line\":42,\"function\":\"handleRequest\",\"class\":\"OC\",\"type\":\"::\",\"args\":[]}],\"File\":\"/srv/http/cloud/lib/public/AppFramework/App.php\",\"Line\":77,\"CustomMessage\":\"--\"},\"userAgent\":\"Mozilla\",\"version\":\"18.0.0.1\"}
59+
{\"reqId\":\"2\",\"level\":3,\"time\":\"2019-11-04T18:50:57+00:00\",\"remoteAddr\":\"127.0.0.1\",\"user\":\"admin\",\"app\":\"gallery\",\"method\":\"GET\",\"url\":\"/settings/admin/logging\",\"message\":{\"Exception\":\"RuntimeException\",\"Message\":\"App class OCA\\\\Gallery\\\\AppInfo\\\\Application is not setup via query() but directly\",\"Code\":0,\"Trace\":[{\"file\":\"/srv/http/cloud/apps/gallery/lib/AppInfo/Application.php\",\"line\":70,\"function\":\"__construct\",\"class\":\"OCP\\\\AppFramework\\\\App\",\"type\":\"->\",\"args\":[\"gallery\",[]]},{\"file\":\"/srv/http/cloud/apps/gallery/appinfo/app.php\",\"line\":19,\"function\":\"__construct\",\"class\":\"OCA\\\\Gallery\\\\AppInfo\\\\Application\",\"type\":\"->\",\"args\":[]},{\"file\":\"/srv/http/cloud/lib/private/legacy/app.php\",\"line\":260,\"args\":[\"/srv/http/cloud/apps/gallery/appinfo/app.php\"],\"function\":\"require_once\"},{\"file\":\"/srv/http/cloud/lib/private/legacy/app.php\",\"line\":154,\"function\":\"requireAppFile\",\"class\":\"OC_App\",\"type\":\"::\",\"args\":[\"gallery\"]},{\"file\":\"/srv/http/cloud/lib/private/legacy/app.php\",\"line\":127,\"function\":\"loadApp\",\"class\":\"OC_App\",\"type\":\"::\",\"args\":[\"gallery\"]},{\"file\":\"/srv/http/cloud/lib/base.php\",\"line\":991,\"function\":\"loadApps\",\"class\":\"OC_App\",\"type\":\"::\",\"args\":[]},{\"file\":\"/srv/http/cloud/index.php\",\"line\":42,\"function\":\"handleRequest\",\"class\":\"OC\",\"type\":\"::\",\"args\":[]}],\"File\":\"/srv/http/cloud/lib/public/AppFramework/App.php\",\"Line\":77,\"CustomMessage\":\"--\"},\"userAgent\":\"Mozilla\",\"version\":\"18.0.0.1\"}
60+
"
61+
);
62+
63+
/** @var array[] $entries */
64+
$entries = iterator_to_array($log);
65+
66+
$this->assertCount(2, $entries);
67+
68+
// sorted last first
69+
$this->assertEquals(2, $entries[0]['reqId']);
70+
$this->assertEquals(1, $entries[1]['reqId']);
71+
}
5572
}

0 commit comments

Comments
 (0)