Skip to content

Commit

Permalink
Merge pull request #6 from slashtrace/feature/json-renderer
Browse files Browse the repository at this point in the history
Feature - JSON renderer
  • Loading branch information
victorstanciu committed Oct 9, 2018
2 parents f9283d1 + b9d5bf5 commit 6509c3b
Show file tree
Hide file tree
Showing 17 changed files with 388 additions and 18 deletions.
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

---

[![Screenshot](https://i.imgur.com/pt4jlYX.png)](https://slashtrace.com/demo.php)
[![Screenshot](https://slashtrace.com/demo.png)](https://slashtrace.com/demo.php)

---

SlashTrace is, at its core, an error and exception handler. You hook it into your error handling routine (or let it set itself up to catch all errors and exceptions), and it captures and displays a lot of nice information about your errors. It does this for normal browser requests, but also for [AJAX](https://i.imgur.com/BnvCp4N.png) and the [CLI](https://i.imgur.com/GA7tS0T.png).
SlashTrace is, at its core, an error and exception handler. You hook it into your error handling routine (or let it set itself up to catch all errors and exceptions), and it captures and displays a lot of nice information about your errors. It does this for normal browser requests, but also for [AJAX](https://slashtrace.com/demo-ajax.png), the [CLI](https://slashtrace.com/demo-cli.png), and [JSON APIs](https://slashtrace.com/demo.json).

When you're done with local debugging, you can configure SlashTrace to send errors to dedicated error reporting services, like [Sentry](https://sentry.io/), [Raygun](https://raygun.com/), and [Bugsnag](https://www.bugsnag.com/).

Expand Down Expand Up @@ -114,6 +114,25 @@ Tracker docs:
- [Raygun - Version numbers](https://raygun.com/docs/languages/php#php-version-number)
- Bugsnag - The release version cannot be explicitly set per event. Read the [Bugsnag docs](https://docs.bugsnag.com/platforms/php/other/#tracking-releases) for more details.

## Debug renderers

When you use the bundled debug handler, it tries to choose an appropiate output renderer based on the environment in which it's running, like this:
- The [CLI renderer](https://github.com/slashtrace/slashtrace/blob/master/src/DebugRenderer/DebugCliRenderer.php) when `php_sapi_name() === "cli`. [Example output](https://slashtrace.com/demo-cli.png).
- The [plain text renderer](https://github.com/slashtrace/slashtrace/blob/master/src/DebugRenderer/DebugTextRenderer.php), for AJAX requests (requests with the `X-Requested-With: XMLHttpRequest` header). [Example output](https://slashtrace.com/demo-ajax.png).
- The [JSON renderer](https://github.com/slashtrace/slashtrace/blob/master/src/DebugRenderer/DebugJsonRenderer.php), for requests with the `Accept: application/json` header. This takes precedence over the text renderer, in case both headers are present. [Example output](https://slashtrace.com/demo.json).
- The [Web renderer](https://github.com/slashtrace/slashtrace/blob/master/src/DebugRenderer/DebugWebRenderer.php), for all other requests. [Example output](https://slashtrace.com/demo.php).
Alternatively, you can force the debug handler to use a particular renderer:
```PHP
use SlashTrace\EventHandler\DebugHandler;
use SlashTrace\DebugRenderer\DebugJsonRenderer;
$handler = new DebugHandler();
$handler->setRenderer(new DebugJsonRenderer());
$slashtrace->addHandler($handler);
```
8 changes: 7 additions & 1 deletion src/Context/Breadcrumbs.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

namespace SlashTrace\Context;

use JsonSerializable;
use SlashTrace\Context\Breadcrumbs\Breadcrumb;
use SlashTrace\System\SystemProvider;
use DateTime;

class Breadcrumbs
class Breadcrumbs implements JsonSerializable
{
const MAX_SIZE = 25;

Expand Down Expand Up @@ -77,4 +78,9 @@ public function clear()
{
$this->crumbs = [];
}

public function jsonSerialize()
{
return $this->crumbs;
}
}
12 changes: 11 additions & 1 deletion src/Context/Breadcrumbs/Breadcrumb.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace SlashTrace\Context\Breadcrumbs;

use DateTime;
use JsonSerializable;

class Breadcrumb
class Breadcrumb implements JsonSerializable
{
/** @var string */
private $title;
Expand Down Expand Up @@ -50,4 +51,13 @@ public function getDateTime()
{
return $this->dateTime;
}

public function jsonSerialize()
{
return [
"title" => $this->getTitle(),
"data" => $this->getData(),
"time" => $this->getDateTime()->format(DATE_ATOM),
];
}
}
15 changes: 14 additions & 1 deletion src/Context/EventContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
namespace SlashTrace\Context;

use Exception;
use JsonSerializable;
use SlashTrace\Http\Request;

class EventContext
class EventContext implements JsonSerializable
{
/** @var string */
private $release;
Expand Down Expand Up @@ -128,4 +129,16 @@ public function hasCustomData()
}
return !is_null($this->getRelease()) || !is_null($this->getUser());
}

public function jsonSerialize()
{
return array_filter([
"request" => $this->getHTTPRequest(),
"server" => $this->getServer(),
"user" => $this->getUser(),
"breadcrumbs" => $this->getBreadcrumbs(),
"release" => $this->getRelease(),
"application_path" => $this->getApplicationPath(),
]);
}
}
12 changes: 11 additions & 1 deletion src/Context/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace SlashTrace\Context;

use InvalidArgumentException;
use JsonSerializable;

class User
class User implements JsonSerializable
{
/** @var string */
private $id;
Expand Down Expand Up @@ -65,4 +66,13 @@ public function setName($name)
{
$this->name = $name;
}

public function jsonSerialize()
{
return array_filter([
"id" => $this->getId(),
"email" => $this->getEmail(),
"name" => $this->getName(),
]);
}
}
21 changes: 21 additions & 0 deletions src/DebugRenderer/DebugJsonRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace SlashTrace\DebugRenderer;

use SlashTrace\Event;
use SlashTrace\System\HasSystemProvider;

class DebugJsonRenderer implements DebugRenderer
{
use HasSystemProvider;

public function render(Event $event)
{
$this->getSystem()->output(
json_encode(array_merge(
["success" => false],
$event->jsonSerialize()
))
);
}
}
12 changes: 10 additions & 2 deletions src/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

use SlashTrace\Context\EventContext;
use SlashTrace\Exception\ExceptionData;
use JsonSerializable;

class Event
class Event implements JsonSerializable
{

/** @var string */
private $level;

Expand Down Expand Up @@ -59,4 +59,12 @@ public function setContext(EventContext $context)
$this->context = $context;
}

public function jsonSerialize()
{
return [
"level" => $this->getLevel(),
"errors" => $this->getExceptions(),
"context" => $this->getContext(),
];
}
}
5 changes: 5 additions & 0 deletions src/EventHandler/DebugHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use SlashTrace\Context\Breadcrumbs;
use SlashTrace\Context\EventContext;
use SlashTrace\Context\User;
use SlashTrace\DebugRenderer\DebugJsonRenderer;
use SlashTrace\DebugRenderer\DebugRenderer;
use SlashTrace\DebugRenderer\DebugCliRenderer;
use SlashTrace\DebugRenderer\DebugWebRenderer;
Expand Down Expand Up @@ -75,6 +76,10 @@ private function createRenderer()
}

$request = $system->getHttpRequest();
if ($request->getHeader("Accept") === "application/json") {
return new DebugJsonRenderer();
}

if ($request->isXhr()) {
return new DebugTextRenderer();
}
Expand Down
12 changes: 11 additions & 1 deletion src/Exception/ExceptionData.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

namespace SlashTrace\Exception;

use JsonSerializable;
use SlashTrace\StackTrace\StackFrame;

class ExceptionData
class ExceptionData implements JsonSerializable
{
/** @var string */
private $message;
Expand Down Expand Up @@ -50,4 +51,13 @@ public function setStackTrace(array $stackTrace)
{
$this->stackTrace = $stackTrace;
}

public function jsonSerialize()
{
return array_filter([
"type" => $this->getType(),
"message" => $this->getMessage(),
"stacktrace" => $this->getStackTrace(),
]);
}
}
2 changes: 0 additions & 2 deletions src/Formatter/StackFrameCallTextFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

class StackFrameCallTextFormatter extends StackFrameCallFormatter
{

/** @var StackTraceTagFormatter */
private $tagFormatter;

Expand Down Expand Up @@ -39,5 +38,4 @@ private function tag($input, $tag)
}
return $this->tagFormatter->format($input, $tag);
}

}
18 changes: 16 additions & 2 deletions src/Http/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace SlashTrace\Http;

class Request
use JsonSerializable;

class Request implements JsonSerializable
{
protected $headers = [];

Expand Down Expand Up @@ -62,7 +64,7 @@ public function getHeaderName($serverKey)
return implode("-", $parts);
}

private function getHeader($header)
public function getHeader($header)
{
$headers = $this->getHeaders();
return isset($headers[$header]) ? $headers[$header] : null;
Expand Down Expand Up @@ -138,4 +140,16 @@ public function getIP()
}
return null;
}

public function jsonSerialize()
{
return [
"url" => $this->getUrl(),
"headers" => $this->getHeaders(),
"get" => $this->getGetData(),
"post" => $this->getPostData(),
"cookies" => $this->getCookies(),
"ip" => $this->getIP(),
];
}
}
20 changes: 18 additions & 2 deletions src/StackTrace/StackFrame.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

namespace SlashTrace\StackTrace;

class StackFrame
{
use JsonSerializable;
use SlashTrace\Formatter\StackFrameCallTextFormatter;
use SlashTrace\Serializer\Serializer;

class StackFrame implements JsonSerializable
{
const TYPE_METHOD = "->";
const TYPE_STATIC = "::";
const TYPE_FUNCTION = "";
Expand Down Expand Up @@ -110,4 +113,17 @@ public function getRelativeFile($rootPath = null)
return ltrim(substr($path, strlen($rootPath)), "/\\");
}

public function jsonSerialize()
{
$serializer = new Serializer();
$callFormatter = new StackFrameCallTextFormatter();
return array_filter([
"at" => $callFormatter->format($this),
"file" => $this->getFile(),
"line" => $this->getLine(),
"arguments" => array_map(function ($argument) use ($serializer) {
return $serializer->serialize($argument);
}, $this->getArguments()),
]);
}
}
46 changes: 46 additions & 0 deletions tests/DebugRenderer/DebugJsonRendererTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace SlashTrace\Tests\DebugRenderer;

use SlashTrace\DebugRenderer\DebugJsonRenderer;
use SlashTrace\Event;
use SlashTrace\Tests\Doubles\System\MockSystemProvider;
use SlashTrace\Tests\TestCase;

class DebugJsonRendererTest extends TestCase
{
/** @var DebugJsonRenderer */
private $renderer;

/** @var MockSystemProvider */
private $system;

protected function setUp()
{
parent::setUp();

$this->system = new MockSystemProvider();

$this->renderer = new DebugJsonRenderer();
$this->renderer->setSystem($this->system);
}

public function testOutput()
{
$event = $this->createMock(Event::class);
$event->expects($this->once())
->method("jsonSerialize")
->willReturn(["foo" => "bar"]);

/** @noinspection PhpParamsInspection */
$this->renderer->render($event);

$this->assertEquals(
json_encode([
"success" => false,
"foo" => "bar",
]),
$this->system->getOutput()[0]
);
}
}

0 comments on commit 6509c3b

Please sign in to comment.