-
Notifications
You must be signed in to change notification settings - Fork 32
Expand file tree
/
Copy pathLogController.php
More file actions
143 lines (125 loc) Β· 4.26 KB
/
LogController.php
File metadata and controls
143 lines (125 loc) Β· 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<?php
/**
* SPDX-FileCopyrightText: 2016-2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\LogReader\Controller;
use OCA\LogReader\Log\LogIteratorFactory;
use OCA\LogReader\Log\SearchFilter;
use OCA\LogReader\Service\SettingsService;
use OCA\LogReader\Settings\Admin;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use Psr\Log\LoggerInterface;
/**
* Class LogController
*
* @package OCA\LogReader\Controller
*/
class LogController extends Controller {
public function __construct(
$appName,
IRequest $request,
private LogIteratorFactory $logIteratorFactory,
private SettingsService $settingsService,
private LoggerInterface $logger,
) {
parent::__construct($appName, $request);
}
/**
* @param string $query
* @param int $count
* @param int $offset
* @return JSONResponse
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
public function get($query = '', $count = 50, $offset = 0): JSONResponse {
$logType = $this->settingsService->getLoggingType();
// we only support web access when `log_type` is set to `file` (the default)
if ($logType !== 'file') {
$this->logger->debug('File-based logging must be enabled to access logs from the Web UI.');
return new JSONResponse([], Http::STATUS_FAILED_DEPENDENCY);
}
$iterator = $this->logIteratorFactory->getLogIterator($this->settingsService->getShownLevels());
if ($query !== '') {
$iterator = new \LimitIterator($iterator, 0, 100000); // limit the number of message we search to avoid huge search times
$iterator->rewind();
$iterator = new SearchFilter($iterator, $query);
$iterator->rewind();
return $this->responseFromIterator($iterator, $count, $offset);
}
return $this->responseFromIterator($iterator, $count, $offset);
}
/**
* @brief Gets the last item in the log, bypassing any cache.
* @return mixed
*/
private function getLastItem() {
$iterator = $this->logIteratorFactory->getLogIterator($this->settingsService->getShownLevels());
return $iterator->current();
}
/**
* @brief Use to poll for new log messages since $lastReqId.
*
* @note There is a possible race condition, when the user loads the
* logging page when a request isn't finished and this specific request
* is the last request in the log, then new messages of this request
* won't be polled. This is because there is no reliable way to identify
* a log message, so we have to use the reqid:
* - the key of the iterator will change when a new message is saved
* - a combination of reqid and counting the messages for that specific reqid
* will work in some cases but not when there are more than 50 messages of that
* request.
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
public function poll(string $lastReqId): JSONResponse {
$logType = $this->settingsService->getLoggingType();
// we only support web access when `log_type` is set to `file` (the default)
if ($logType !== 'file') {
$this->logger->debug('File-based logging must be enabled to access logs from the Web UI.');
return new JSONResponse([], Http::STATUS_FAILED_DEPENDENCY);
}
$lastItem = $this->getLastItem();
if ($lastItem === null || $lastItem['reqId'] === $lastReqId) {
return new JSONResponse([]);
}
$iterator = $this->logIteratorFactory->getLogIterator($this->settingsService->getShownLevels());
$iterator->next();
$data = [];
while ($iterator->valid()) {
$line = $iterator->current();
if ($line['reqId'] === $lastReqId) {
break;
}
if (!is_null($line)) {
$line['id'] = uniqid();
$data[] = $line;
}
$iterator->next();
}
return new JSONResponse($data);
}
protected function responseFromIterator(\Iterator $iterator, $count, $offset): JSONResponse {
$iterator->rewind();
for ($i = 0; $i < $offset; $i++) {
$iterator->next();
}
$data = [];
for ($i = 0; $i < $count && $iterator->valid(); $i++) {
$line = $iterator->current();
if (!is_null($line)) {
$line['id'] = uniqid();
$data[] = $line;
}
$iterator->next();
}
return new JSONResponse([
'data' => $data,
'remain' => $iterator->valid()
]);
}
}