Files
ops-Gazelle/app/Manager/ErrorLog.php

180 lines
5.7 KiB
PHP

<?php
namespace Gazelle\Manager;
use Gazelle\BaseManager;
class ErrorLog extends BaseManager {
protected string $filter;
protected function digest(string $trace, array $errorList): string {
return hash('sha256', $trace . json_encode($errorList));
}
public function create(
string $uri,
int $userId,
float $duration,
int $memory,
int $nrQuery,
int $nrCache,
string $trace,
array $request,
array $errorList,
): \Gazelle\ErrorLog {
$len = strlen($trace);
if ($len > 4000) {
$clipped = $len - 4000;
$trace = substr($trace, 0, 4000) . "[...$clipped chars clipped]";
}
$id = $this->pg()->scalar("
merge into error_log using (
select ? as uri,
?::int as id_user,
?::double precision as duration,
?::bigint as memory,
?::int as nr_query,
?::int as nr_cache,
? as trace,
?::jsonb as request,
?::jsonb as error_list,
?::bytea as digest
) as i on i.digest = error_log.digest
when not matched then
insert ( uri, id_user, duration, memory, nr_query, nr_cache, trace, request, error_list, digest)
values (i.uri, i.id_user, i.duration, i.memory, i.nr_query, i.nr_cache, i.trace, i.request, i.error_list, i.digest)
when matched then
update set
seen = error_log.seen + 1,
updated = now()
returning id_error_log;
", substr($uri, 0, 255), $userId, $duration, $memory, $nrQuery, $nrCache,
$trace, json_encode($request), json_encode($errorList),
$this->digest($trace, $errorList),
);
return new \Gazelle\ErrorLog($id);
}
/**
* Get an error log based on its ID
*/
public function findById(int $id): ?\Gazelle\ErrorLog {
$errorId = (int)$this->pg()->scalar("
select id_error_log FROM error_log where id_error_log = ?
", $id
);
return $errorId ? new \Gazelle\ErrorLog($errorId) : null;
}
/**
* Get an error log based on its digest
*/
public function findByDigest(string $trace, array $errorList): ?\Gazelle\ErrorLog {
$id = (int)$this->pg()->scalar("
select id_error_log FROM error_log where digest = ?
", $this->digest($trace, $errorList),
);
return $id ? new \Gazelle\ErrorLog($id) : null;
}
public function findByPrev(int $errorId): ?\Gazelle\ErrorLog {
$id = (int)$this->pg()->scalar("
select id_error_log
from error_log
where updated > (select updated from error_log where id_error_log = ?)
order by updated asc
limit 1
", $errorId
);
return $id ? new \Gazelle\ErrorLog($id) : null;
}
public function findByNext(int $errorId): ?\Gazelle\ErrorLog {
$id = (int)$this->pg()->scalar("
select id_error_log
from error_log
where updated < (select updated from error_log where id_error_log = ?)
order by updated desc
limit 1
", $errorId
);
return $id ? new \Gazelle\ErrorLog($id) : null;
}
public function setFilter(string $filter): static {
$this->filter = $filter;
return $this;
}
public function total(): int {
$args = [];
if (!isset($this->filter)) {
$where = '';
} else {
$where = "where uri ~ ?";
$args[] = $this->filter;
}
return (int)$this->pg()->scalar("
select count(*) from error_log $where
", ...$args
);
}
public function list(string $orderBy, string $dir, int $limit, int $offset): array {
$args = [];
if (!isset($this->filter)) {
$where = '';
} else {
$where = "where uri ~ ?";
$args[] = $this->filter;
}
array_push($args, $limit, $offset);
/* In theory this could simply fetch the ids and then hydrate the
* individual ErrorLog objects, but as they are uncached, it would
* require n+1 queries overall.
*/
$result = $this->pg()->all("
select id_error_log as id,
duration,
memory,
nr_cache,
nr_query,
seen,
created,
updated,
uri,
trace,
request,
error_list,
logged_var
from error_log $where
order by {$orderBy} {$dir}
limit ? offset ?
", ...$args
);
$list = [];
foreach ($result as $item) {
$item['trace'] = explode("\n", $item['trace']);
$item['request'] = json_decode($item['request'], true);
$item['error_list'] = json_decode($item['error_list'], true);
$item['logged_var'] = json_decode($item['logged_var'], true);
$list[] = $item;
}
return $list;
}
public function removeList(array $list): int {
return $this->pg()->prepared_query("
delete from error_log where id_error_log in (
" . placeholders($list) . ")", ...$list
);
}
public function removeSlow(float $duration): int {
return $this->pg()->prepared_query("
delete from error_log where duration >= ?
", $duration
);
}
}