You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
5.7 KiB
209 lines
5.7 KiB
5 years ago
|
<?php
|
||
|
/**
|
||
|
* @author Robin Appelman <icewind@owncloud.com>
|
||
|
*
|
||
|
* @copyright Copyright (c) 2015, ownCloud, Inc.
|
||
|
* @license AGPL-3.0
|
||
|
*
|
||
|
* This code is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Affero General Public License, version 3,
|
||
|
* as published by the Free Software Foundation.
|
||
|
*
|
||
|
* 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, version 3,
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
namespace OCA\LogReader\Controller;
|
||
|
|
||
|
use OCA\LogReader\Log\LogIterator;
|
||
|
use OCA\LogReader\Log\LogIteratorFactory;
|
||
|
use OCA\LogReader\Log\SearchFilter;
|
||
|
use OCP\AppFramework\Controller;
|
||
|
use OCP\AppFramework\Http\JSONResponse;
|
||
|
use OCP\AppFramework\Http\TemplateResponse;
|
||
|
use OCP\IConfig;
|
||
|
use OCP\IRequest;
|
||
|
use OCP\Log\IFileBased;
|
||
|
use OCP\Log\ILogFactory;
|
||
|
|
||
|
/**
|
||
|
* Class LogController
|
||
|
*
|
||
|
* @package OCA\LogReader\Controller
|
||
|
*/
|
||
|
class LogController extends Controller {
|
||
|
private $logIteratorFactory;
|
||
|
private $config;
|
||
|
|
||
|
public function __construct($appName,
|
||
|
IRequest $request,
|
||
|
IConfig $config,
|
||
|
LogIteratorFactory $logIteratorFactory
|
||
|
) {
|
||
|
parent::__construct($appName, $request);
|
||
|
$this->logIteratorFactory = $logIteratorFactory;
|
||
|
$this->config = $config;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $count
|
||
|
* @param int $offset
|
||
|
* @param string $levels
|
||
|
* @return TemplateResponse
|
||
|
*/
|
||
|
public function get($count = 50, $offset = 0, $levels = '11111') {
|
||
|
$iterator = $this->logIteratorFactory->getLogIterator($levels);
|
||
|
return $this->responseFromIterator($iterator, $count, $offset);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Gets the last item in the log, bypassing any cache.
|
||
|
* @return mixed
|
||
|
*/
|
||
|
private function getLastItem($levels) {
|
||
|
$iterator = $this->logIteratorFactory->getLogIterator($levels);
|
||
|
$iterator->next();
|
||
|
return $iterator->current();
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief polls for a new log message since $lastReqId.
|
||
|
* This method will sleep for maximum 20 seconds before returning an empty
|
||
|
* result.
|
||
|
*
|
||
|
* Note that there is a race condition possible: 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.
|
||
|
* @param $lastReqId
|
||
|
* @param string $levels
|
||
|
* @return JSONResponse
|
||
|
*/
|
||
|
public function poll($lastReqId, $levels = '11111') {
|
||
|
|
||
|
$cycles = 0;
|
||
|
$maxCycles = 20;
|
||
|
|
||
|
while ($this->getLastItem($levels)['reqId'] === $lastReqId) {
|
||
|
sleep(1);
|
||
|
$cycles++;
|
||
|
if ($cycles === $maxCycles) {
|
||
|
return new JSONResponse([]);
|
||
|
}
|
||
|
}
|
||
|
$iterator = $this->logIteratorFactory->getLogIterator($levels);
|
||
|
$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);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $query
|
||
|
* @param int $count
|
||
|
* @param int $offset
|
||
|
* @param string $levels
|
||
|
* @return TemplateResponse
|
||
|
*
|
||
|
* @NoCSRFRequired
|
||
|
*/
|
||
|
public function search($query = '', $count = 50, $offset = 0, $levels = '11111') {
|
||
|
$iterator = $this->logIteratorFactory->getLogIterator($levels);
|
||
|
$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);
|
||
|
}
|
||
|
|
||
|
public function getLevels() {
|
||
|
return new JSONResponse($this->config->getAppValue('logreader', 'levels', '11111'));
|
||
|
}
|
||
|
|
||
|
public function getSettings() {
|
||
|
return new JSONResponse([
|
||
|
'levels' => $this->config->getAppValue('logreader', 'levels', '11111'),
|
||
|
'dateformat' => $this->config->getSystemValue('logdateformat', \DateTime::ISO8601),
|
||
|
'timezone' => $this->config->getSystemValue('logtimezone', 'UTC'),
|
||
|
'relativedates' => (bool)$this->config->getAppValue('logreader', 'relativedates', false),
|
||
|
'live' => (bool)$this->config->getAppValue('logreader', 'live', true),
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param bool $relative
|
||
|
*/
|
||
|
public function setRelative($relative) {
|
||
|
$this->config->setAppValue('logreader', 'relativedates', $relative);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param bool $live
|
||
|
*/
|
||
|
public function setLive($live) {
|
||
|
$this->config->setAppValue('logreader', 'live', $live);
|
||
|
}
|
||
|
|
||
|
public function setLevels($levels) {
|
||
|
$intLevels = array_map('intval', str_split($levels));
|
||
|
$minLevel = 4;
|
||
|
foreach ($intLevels as $level => $log) {
|
||
|
if ($log) {
|
||
|
$minLevel = $level;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
$this->config->setAppValue('logreader', 'levels', $levels);
|
||
|
return $minLevel;
|
||
|
}
|
||
|
|
||
|
protected function responseFromIterator(\Iterator $iterator, $count, $offset) {
|
||
|
|
||
|
$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()
|
||
|
]);
|
||
|
}
|
||
|
}
|