27 Commits
Author SHA1 Message Date
Dave Mc Nicoll 72e50fbe50 - Added getenvonce() method which was missing 2026-07-14 20:13:20 +00:00
mcndave 27bfdd5902 - Fixed client bug in js source and also on negundo server side 2026-07-14 19:07:31 +00:00
mcndave 063aba6040 - Request method needed to be lowerscase and specified arguments in constructor) 2026-07-07 19:56:14 +00:00
mcndave 956ccbba1a - Added new Picea's Di extension for client_bugs 2026-07-06 16:29:50 +00:00
Dave Mc Nicoll af0e9f291d - Fixed some mistyped variables 2026-06-17 21:00:58 +00:00
Dev bc8d831b6c - Added noop if no software data were given 2026-06-09 19:18:42 +00:00
Dave Mc Nicoll 2df4ea15e2 - Removed curl_close() as it is deprecated 2026-06-01 15:12:34 +00:00
Dave Mc Nicoll 0694b50eb6 - Fixed null value declaration 2026-05-22 19:54:16 +00:00
Dave Mc Nicoll 72f5341dff - Fixed ndump() source code 2026-05-22 18:49:22 +00:00
Dave Mc Nicoll b88602a98b - Fixed double slash in client url 2025-07-18 13:02:54 +00:00
mcndave f1e9dddf5e - Minors fixes 2025-07-17 18:36:38 +00:00
Dave Mc Nicoll 8de8aafa84 - WIP on tasks 2025-02-04 08:36:14 -05:00
Dave Mc Nicoll 7b5a1bcf97 - WIP on TaskReport 2024-11-08 09:14:56 -05:00
Dave Mc Nicoll 6227321045 Merge branch 'master' of https://git.mcnd.ca/mcndave/negundo-client 2024-11-01 16:10:30 -04:00
Dave Mc Nicoll 38df94fc2a - WIP on a new TaksReport object 2024-11-01 16:10:18 -04:00
Dave Mc Nicoll 36d4a6971e - WIP on http method from which a request is sent 2024-10-14 12:57:58 +00:00
Dave Mc Nicoll 3474cc4840 - Added Http Method 2024-10-10 19:37:00 +00:00
mcndave 5a737c06c4 - WIP on Task scheduling interface 2024-04-22 13:55:36 +00:00
Nicolas Demers 586f2344df update bug catcher on client side 2024-04-18 08:50:24 -04:00
mcndave 29a780ac6a - Added a new view 2024-03-15 14:14:20 -04:00
mcndave e85925ace7 - Added a view to ease integration of JS client lib 2024-03-13 09:58:53 -04:00
mcndave ef43c7831f - Added JS debug lib 2024-03-13 09:08:54 -04:00
Dave Mc Nicoll 3590993925 - Added a process to native handler 2024-03-01 16:48:01 +00:00
Dave Mc Nicoll d94ee83230 - Fixed nullable errors after forced typing 2023-11-07 11:12:20 -05:00
mcndave a552a6675e Merge branch 'master' of https://git.mcnd.ca/mcndave/negundo-client 2023-11-03 20:03:49 -04:00
mcndave 5e44f36dbc - Forced variable typing 2023-11-03 20:03:34 -04:00
Dave Mc Nicoll 4484f1c997 - Added some more type checking 2023-11-01 11:29:08 -04:00
20 changed files with 442 additions and 108 deletions

No files matched your search

+109
View File
@@ -0,0 +1,109 @@
class ErrorHandler {
constructor(options) {
if (!options) throw new Error("Options not provided");
if (!options.url) throw new Error("URL is required");
if (!options.apikey) throw new Error("API key is required");
this.url = options.url;
this.apikey = options.apikey;
this.catchError();
}
getErrorPosition(script) {
let totalLines = 0;
let element = script.previousElementSibling;
while (element) {
totalLines += (element.outerHTML || element.textContent).split("\n").length;
element = element.previousElementSibling;
}
return {
startLine: totalLines + 1,
endLine: totalLines + script.textContent.split("\n").length
};
}
catchError() {
window.onerror = (message, url, lineNumber, column, error) => {
let type = 'JavaScript Error';
if (message.indexOf(':')) {
type = message.substring(0, message.indexOf(':'));
message = message.substring(message.indexOf(':') + 1).trim();
}
this.reportError(message, url, lineNumber, column, error.stack, type);
return false;
};
window.addEventListener('unhandledrejection', event => {
this.reportError(event.reason.toString(), document.location.href, 0, 0, event.reason.stack, 'Promise Rejection');
});
this.createErrorAgain();
}
async reportError(message, url, line, column, stack, type = 'JavaScript Error') {
await new Promise(r => setTimeout(r, 2000));
fetch(this.url ? `${this.url}/${this.apikey}` : window.location.href, {
method: "POST",
headers: {
'Accept': "application/json",
'Content-Type': "application/json"
},
body: JSON.stringify({
type,
message,
url,
line,
code: column,
backtrace: stack ? stack.split("\n").filter((e) => !!e).map((e) => this.parseErrorLine(e)) : [],
file: window.location.href,
source: await this.getSourceCode(url, line),
})
}).then(response => response.json()).then(console.info);
}
async getSourceCode(url, errorLineNumber) {
try {
const response = await fetch(url);
const originalHtml = await response.text();
const lines = originalHtml.split('\n');
const start = Math.max(0, errorLineNumber - 5);
const end = Math.min(lines.length, errorLineNumber + 5);
return lines.slice(start, end).join('\n');
}
catch (error) {
console.error('Failed to fetch original HTML:', error);
}
}
parseErrorLine(errorLine) {
const obj = {};
// Pattern for: functionName@url:line:column
const pattern = /^(.*)@(.+?):(\d+):(\d+)$/;
const match = errorLine.match(pattern);
if (match) {
obj.function = match[1];
obj.file = match[2];
obj.line = match[3];
// column would be match[4] if needed
console.log(match);
}
// Remove undefined/empty values
Object.keys(obj).forEach(key => {
if (!obj[key]) delete obj[key];
});
return obj;
}
}
+18
View File
@@ -2,6 +2,7 @@
"name": "mcnd/negundo-client",
"description": "Negundo client which allow sending dump(), error and tasks reports",
"keywords": ["negundo","dev","debug","psr15","middleware"],
"type": "library",
"license": "MIT",
"authors": [
{
@@ -13,5 +14,22 @@
"psr-4": {
"Negundo\\Client\\": "src/"
}
},
"require": {
"php": "^8.2",
"ext-curl": "*",
"ext-json": "*"
},
"extra" : {
"lean" : {
"autoload": {
"definitions" : [
"meta/negundo.php"
],
"config": [
"meta/config.php"
]
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
<?php
return [
'lean' => [
'autoload' => [
'negundo.client'
]
],
];
+47
View File
@@ -0,0 +1,47 @@
<?php
use Negundo\Client\{ SoftwareConfig, Dump, Task, NegundoMiddleware };
use function DI\{ autowire, create, get, add };
if (! function_exists('getenvonce')) {
function getenvonce(string $name) : array|string|false
{
$value = getenv($name);
unset($_ENV[$name], $_SERVER[$name]);
putenv($name . "=");
return $value;
}
}
return [
SoftwareConfig::class => create(SoftwareConfig::class)->constructor(
serverUrl: getenvonce('NEGUNDO_SERVER'),
softwareHash: getenvonce('NEGUNDO_HASH'),
),
NegundoMiddleware::class => autowire(NegundoMiddleware::class),
Dump::class => autowire(Dump::class),
Task::class => autowire(Task::class),
'lean.autoload' => add([
Dump::class,
Task::class,
]),
'negundo.client' => [
'picea' => [
'asset' => [
[
'path' => implode(DIRECTORY_SEPARATOR, [ dirname(__DIR__), "asset", '' ]),
'order' => 10
],
],
'view' => [
[
'path' => implode(DIRECTORY_SEPARATOR, [ dirname(__DIR__), "view", '' ]),
'order' => 99,
],
],
],
],
];
+11 -9
View File
@@ -2,19 +2,19 @@
namespace Negundo\Client {
class Dump {
public static /* array */ $instances = [];
public static array $instances = [];
# public /*string*/ $serverUrl = "http://dev.cslsj.qc.ca/debug/dump/report/%s";
# public string $serverUrl = "http://dev.cslsj.qc.ca/debug/dump/report/%s";
protected /* SoftwareConfig */ $config;
protected SoftwareConfig $config;
protected /*array*/ $sent = [];
protected array $sent = [];
protected /* TransportInterface */ $transport;
protected Transport\TransportInterface $transport;
protected /* Util\DumpHandler */ $dumpHandler;
protected Util\DumpHandler $dumpHandler;
public function __construct(SoftwareConfig $config, ? DataInterface $dataManipulator = null, Transport\TransportInterface $transport = null)
public function __construct(SoftwareConfig $config, ? DataInterface $dataManipulator = null, ? Transport\TransportInterface $transport = null)
{
$this->config = $config;
$this->transport = $transport ?: new Transport\Curl();
@@ -22,7 +22,7 @@ namespace Negundo\Client {
static::$instances[] = $this;
}
public function pushData(...$content) : ? object
public function pushData(...$content) : object|null|bool
{
$data = $this->dumpHandler->dumpData(...$content);
@@ -35,7 +35,9 @@ namespace Negundo\Client {
$this->sent[$dumpHash] = true;
return $this->transport->push($this->config->url('dump/report'), $data);
$url = $this->config->url('dump/report');
return $url ? $this->transport->push($url, $data) : false;
}
}
}
+15 -13
View File
@@ -5,25 +5,25 @@ namespace Negundo\Client;
use Closure;
abstract class Handler {
protected /* SoftwareConfig */ $config;
protected SoftwareConfig $config;
public /* bool */ $registerErrorHandler = true;
public bool $registerErrorHandler = true;
public /* bool */ $registerExceptionHandler = true;
public bool $registerExceptionHandler = true;
public /* bool */ $registerFatalErrorHandler = true;
public bool $registerFatalErrorHandler = true;
protected /*Closure*/ $callback;
protected ? Closure $callback;
protected /*array*/ $sent = [];
protected /* TransportInterface */ $transport;
protected Transport\TransportInterface $transport;
protected /* Util\ExceptionHandler */ $exceptionHandler;
protected Util\ExceptionHandler $exceptionHandler;
protected array $sent = [];
public abstract function handleException(\Throwable $ex) : array;
public function __construct(SoftwareConfig $config, ? DataInterface $dataManipulator = null, ? Closure $callback = null, Transport\TransportInterface $transport = null)
public function __construct(SoftwareConfig $config, ? DataInterface $dataManipulator = null, ? Closure $callback = null, ? Transport\TransportInterface $transport = null)
{
$this->config = $config;
@@ -33,7 +33,7 @@ abstract class Handler {
$this->registerHandlers();
}
public function registerHandlers()
public function registerHandlers() : void
{
$this->registerExceptionHandler && set_exception_handler(function(\Throwable $ex) {
$this->pushData($ex);
@@ -55,7 +55,7 @@ abstract class Handler {
});
}
public function pushData(\Throwable $ex) : ? object
public function pushData(\Throwable $ex) : null|object|bool
{
// Make sure not to spam the server if an ErrorMessage or Exception was already sent (like inside a loop)
$exceptionHash = $this->exceptionHandler->hash($ex);
@@ -68,6 +68,8 @@ abstract class Handler {
$data = $this->handleException($ex);
return $this->transport->push($this->config->url('bug/report'), $data);
$url = $this->config->url('bug/report');
return $url ? $this->transport->push($url, $data) : false;
}
}
+18 -1
View File
@@ -8,5 +8,22 @@ class NativeHandler extends Handler {
{
return $this->exceptionHandler->extractExceptionData($ex, $_SERVER, $_POST);
}
public function process(callable $callback) : mixed
{
try {
return $callback();
}
catch (\Throwable $ex)
{
$this->pushData($ex);
if ( $this->callback ?? false ) {
return call_user_func_array($this->callback, [ $ex ] );
}
else {
throw $ex;
}
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ class NegundoMiddleware extends Handler implements MiddlewareInterface {
protected ServerRequestInterface $request;
public $registerExceptionHandler = false;
public bool $registerExceptionHandler = false;
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
+7 -19
View File
@@ -4,29 +4,17 @@ namespace Negundo\Client;
class SoftwareConfig
{
public /*string*/ $serverUrl;
public function __construct(
public string $serverUrl,
public string $softwareHash,
) {}
public /* string */ $softwareHash;
public function __construct(string $softwareHash, string $serverUrl)
public function url(string $path) : string|null
{
if (! $softwareHash ) {
throw new \Exception("[Negundo] - You must provide a software hash to match a registered application on the plateform.");
}
else {
$this->softwareHash = $softwareHash;
if (empty($this->serverUrl) || empty($this->softwareHash)) {
return null;
}
if (! $serverUrl ) {
throw new \Exception("[Negundo] - You must provide a valid Negundo server url endpoint.");
}
else {
$this->serverUrl = $serverUrl;
}
}
public function url(string $path) : string
{
return sprintf("%s/%s/%s", rtrim($this->serverUrl, '/'), $path, $this->softwareHash);
}
}
+29 -14
View File
@@ -4,17 +4,17 @@ namespace Negundo\Client {
class Task {
public static /* array */ $instances = [];
public static array $instances = [];
protected /*array*/ $sent = [];
protected array $sent = [];
protected /* TransportInterface */ $transport;
protected Transport\TransportInterface $transport;
protected /* Util\TaskHandler */ $taskHandler;
protected Util\TaskHandler $taskHandler;
protected /* SoftwareConfig */ $config;
protected SoftwareConfig $config;
public function __construct(SoftwareConfig $config, ? DataInterface $dataManipulator = null, Transport\TransportInterface $transport = null)
public function __construct(SoftwareConfig $config, ? DataInterface $dataManipulator = null, ? Transport\TransportInterface $transport = null)
{
$this->config = $config;
@@ -25,18 +25,18 @@ namespace Negundo\Client {
static::$instances[] = $this;
}
public function newReport(string $message, ? string $title = null, ? array $data = []) : ? object
public function newReport(string $message, ? string $title = null, ? array $data = [], ? array $events = []) : object|null|bool
{
$report = $this->taskHandler->sendReport($message, $title, $data);
$report = $this->taskHandler->sendReport($message, $title, $data, $events);
// Make sure not to spam the server if an ErrorMessage or Exception was already sent (like inside a loop)
$dumpHash = $this->taskHandler->hash($report);
$taskHash = $this->taskHandler->hash($report);
if ( $this->sent[$dumpHash] ?? false ) {
if ( $this->sent[$taskHash] ?? false ) {
return null;
}
$this->sent[$dumpHash] = true;
$this->sent[$taskHash] = true;
return $this->transport->push($this->config->url('task/report'), $report);
}
@@ -44,11 +44,26 @@ namespace Negundo\Client {
}
namespace {
use Negundo\Client\Task\StatusEnum;
use Negundo\Client\Task\TaskReport;
if (! function_exists('ntask') ) {
function ntask(string $message, ? string $title = null, ? array $data) {
function ntask(string $message, ? string $title = null, ? array $data = null, ? Negundo\Client\Task\StatusEnum $status = null, array $events = []) {
foreach (\Negundo\Client\Task::$instances as $instance) {
$instance->newReport($message, $title, $data);
$sent = $instance->newReport($message, $title, $data, $events);
if (! $sent ) {
throw new \Exception(sprintf('Could not send report titled `%s`.', $title));
}
}
}
function nreport(TaskReport $report)
{
if ($report->status !== StatusEnum::NothingToDo || $report->getEvents()) {
ntask($report->getMessage(), $report->getTitle(), $report->getData(), $report->getStatus(), $report->getEvents());
}
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace Negundo\Client\Task;
enum StatusEnum : string
{
case New = "new";
case Failed = "failed";
case Warning = "warning";
case Completed = "completed";
case Empty = "empty";
case NothingToDo = "nothing-to-do";
}
+84
View File
@@ -0,0 +1,84 @@
<?php
namespace Negundo\Client\Task;
class TaskReport implements \JsonSerializable
{
protected array $events = [];
public function __construct(
public string|array $message,
public string $title,
public ? StatusEnum $status = null,
public array $data = [],
) {}
public function addData(string $name, array $data) : static
{
$this->data[$name][] = $data;
return $this;
}
public function addEvent(string $key, ? StatusEnum $status = null, array $data = []) : static
{
$this->events[] = [
'key' => $key,
'status' => $status ? $status->value : null,
'data' => $data
];
return $this;
}
public function addMessage(string $message) : static
{
if ( is_string($this->message) ) {
$this->message = [
$this->message, $message
];
}
else {
$this->message[] = $message;
}
return $this;
}
public function getMessage() : string
{
return implode(PHP_EOL, (array) $this->message);
}
public function getTitle() : string
{
return $this->title;
}
public function getData() : array
{
return $this->data;
}
public function getEvents(?StatusEnum $filterType = null) : array
{
return $filterType ? array_filter($this->events, fn($e) => StatusEnum::tryFrom($e['status']) === $filterType) : $this->events;
}
public function getStatus() : StatusEnum
{
return $this->status;
}
public function jsonSerialize(): mixed
{
return [
'title' => $this->getTitle(),
'message' => $this->getMessage(),
'status' => $this->getStatus(),
'events' => $this->getEvents(),
'data' => $this->getData(),
];
}
}
+45 -18
View File
@@ -4,36 +4,63 @@ namespace Negundo\Client\Transport;
class Curl implements TransportInterface {
public $timeout = 1;
public $throwErrors = false;
public $timeout = 4;
public $throwErrors = true;
public $verifySsl = false;
public $headers = [];
public function push(string $url, array $data) : ? object
public function push(string $url, array $data) : object|null|bool
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers );
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data, '', '&'));
curl_setopt($ch, CURLOPT_TIMEOUT_MS, $this->timeout * 200);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verifySsl);
if ( ( false === curl_exec($ch) ) && $this->throwErrors ) {
$errno = curl_errno($ch);
$errors = array_filter([ curl_error($ch) , static::CURL_ERROR[$errno] ?? null ]);
throw new CurlException(implode(PHP_EOL, $errors), $errno);
$exec = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($this->throwErrors) {
if ( false === $exec) {
$errno = curl_errno($ch);
throw new CurlException(implode(PHP_EOL, array_filter([ curl_error($ch) , static::CURL_ERROR[$errno] ?? null ])), $errno);
}
elseif ($code >= 400) {
throw new \Exception(sprintf("HTTP code received : $code with page content : %s", $exec));
}
}
curl_close($ch);
return null;
return $exec;
}
public function get(string $url) : ? object
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers );
curl_setopt($ch, CURLOPT_TIMEOUT_MS, $this->timeout * 200);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verifySsl);
$execute = curl_exec($ch);
if ( ( false === $execute ) && $this->throwErrors ) {
$errno = curl_errno($ch);
throw new CurlException(implode(PHP_EOL, array_filter([ curl_error($ch) , static::CURL_ERROR[$errno] ?? null ])), $errno);
}
return $execute ? (object) $execute : null;
}
const CURL_ERROR = [
0 => 'CURLE_OK',
1 => 'CURLE_UNSUPPORTED_PROTOCOL',
@@ -159,7 +186,7 @@ class Curl implements TransportInterface {
35 => "A problem occurred somewhere in the SSL/TLS handshake. You really want the error buffer and read the message there as it pinpoints the problem slightly more. Could be certificates (file formats, paths, permissions), passwords, and others.",
36 => "The download could not be resumed because the specified offset was out of the file boundary.",
37 => "A file given with FILE:// couldn't be opened. Most likely because the file path doesn't identify an existing file. Did you check file permissions? ",
35 => "LDAP cannot bind. LDAP bind operation failed.",
38 => "LDAP cannot bind. LDAP bind operation failed.",
39 => "LDAP search failed.",
41 => "Function not found. A required zlib function was not found.",
42 => "Aborted by callback. A callback returned \"abort\" to libcurl.",
+1 -1
View File
@@ -12,7 +12,7 @@ class GuzzleClient implements TransportInterface {
public array $headers = [];
public function push(string $url, array $data) : ? object
public function push(string $url, array $data) : object|null|bool
{
return ( new Client([
'timeout' => $this->timeout,
+1 -1
View File
@@ -3,5 +3,5 @@
namespace Negundo\Client\Transport;
interface TransportInterface {
public function push(string $url, array $data) : ? object;
public function push(string $url, array $data) : object|null|bool;
}
+4 -7
View File
@@ -5,13 +5,10 @@ namespace Negundo\Client\Util;
use Negundo\Client\DataInterface;
class DumpHandler {
public /*DataInterface*/ $dataManipulator;
public function __construct(DataInterface $dataManipulator = null)
{
$this->dataManipulator = $dataManipulator;
}
public function __construct(
public ? DataInterface $dataManipulator = null
) {}
public function dumpData(...$content) : array
{
+9 -10
View File
@@ -6,12 +6,9 @@ use Negundo\Client\DataInterface;
class ExceptionHandler {
public /*DataInterface*/ $dataManipulator;
public function __construct(DataInterface $dataManipulator = null)
{
$this->dataManipulator = $dataManipulator;
}
public function __construct(
public ? DataInterface $dataManipulator = null
) {}
public function extractExceptionData(\Throwable $ex, array $serverData, array $postData, $dataManipulator = null) : array
{
@@ -19,10 +16,11 @@ class ExceptionHandler {
'HTTPS' => $serverData['HTTPS'] ?? false,
'HTTP_HOST' => $serverData['HTTP_HOST'] ?? "",
'REQUEST_URI' => $serverData['REQUEST_URI'] ?? "",
'HTTP_USER_AGENT' => $serverData['HTTP_USER_AGENT'] ?? null,
'REMOTE_ADDR' => $serverData['REMOTE_ADDR'] ?? null,
'HTTP_USER_AGENT' => $serverData['HTTP_USER_AGENT'] ?? "# UNKNOWN #",
'REMOTE_ADDR' => $server['HTTP_X_FORWARDED_FOR'] ?? $serverData['REMOTE_ADDR'] ?? null,
'REQUEST_METHOD' => $serverData['HTTP_X_HTTP_METHOD'] ?? $serverData['REQUEST_METHOD'] ?? null,
];
$post = [
'code' => $ex->getCode(),
'file' => $ex->getFile(),
@@ -30,11 +28,12 @@ class ExceptionHandler {
'type' => $ex::class,
'message' => $ex->getMessage(),
'url' => ( ( 'on' === $serverData['HTTPS'] ) ? 'https' : 'http' ) . '://' . $serverData['HTTP_HOST'] . $serverData["REQUEST_URI"],
'http_method' => strtolower($serverData['REQUEST_METHOD']),
'backtrace' => json_encode($ex->getTrace()),
'backtrace_string' => $ex->getTraceAsString(),
'source' => ( new SourceCodeFormatter() )->generateFromException($ex),
'hits' => [
'user_agent' => $serverData['HTTP_USER_AGENT'] ?? "???",
'user_agent' => $serverData['HTTP_USER_AGENT'],
'sent_at' => date('Y-m-d H:i:s'),
'request_body_vars' => array_keys($postData),
'remote_addr' => $serverData['REMOTE_ADDR'],
+3 -4
View File
@@ -41,10 +41,9 @@ class SourceCodeFormatter {
catch(\Throwable $e) {
$content = $fileSpl->current();
}
# Get code between <code></code> tags
$content = substr($content, strpos($content, "<code>") + 6, strrpos($content, "</code>") - 6);
$content = strip_tags($content, '<span>');
# Removing the added <?php tag
$content = str_replace("&lt;?php", "", $content);
+8 -10
View File
@@ -6,22 +6,19 @@ use Negundo\Client\DataInterface;
class TaskHandler {
public /*DataInterface*/ $dataManipulator;
public function __construct(
public ? DataInterface $dataManipulator = null
) {}
public function __construct(DataInterface $dataManipulator = null)
{
$this->dataManipulator = $dataManipulator;
}
public function sendReport(string $message, ? string $title = null, array $data = []) : array
public function sendReport(string $message, ? string $title = null, array $data = [], array $events = []) : array
{
$backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
array_shift($backtrace);
array_shift($backtrace);
$trace = $backtrace[0] ?? [
"line" => -1,
"file" => "unknown",
"line" => -1,
];
$post = [
@@ -36,6 +33,7 @@ class TaskHandler {
'request_body' => json_encode($_POST),
'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? null,
],
'events' => $events,
];
if ( $this->dataManipulator ?? false ) {
@@ -44,11 +42,11 @@ class TaskHandler {
$this->dataManipulator->run($post);
}
$post['data'] = json_encode($post['data'] ?? null);
$post['data'] = json_encode($post['data'] ?? null, JSON_INVALID_UTF8_IGNORE);
return $post;
}
public function hash(array $content) : string
{
return md5(implode('-', [ $content['file'], $content['line'], $content['title'] ]));
+10
View File
@@ -0,0 +1,10 @@
{% php $softwareConfig = di_get(\Negundo\Client\SoftwareConfig::class) %}
<script src="{% asset 'static/negundo/js/debug.js' %}"></script>
<script>
new ErrorHandler({
url: "{{ rtrim($softwareConfig->serverUrl, '/') }}/bug/client/report",
apikey: "{{ $softwareConfig->softwareHash }}"
});
</script>