Skip to content
4 changes: 2 additions & 2 deletions docs/tutorials/add-logger-adapter.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Adding a new logger adapter 💾

This document is part of the Utopia contributors' guide. Before you continue reading this document make sure you have read the [Code of Conduct](https://raw.githubusercontent.com/utopia-php/logger/main/CODE_OF_CONDUCT.md) and the [Contributing Guide](https://raw.githubusercontent.com/utopia-php/logger/main/CONTRIBUTING.md).
This document is part of the Utopia contributors' guide. Before you continue reading this document make sure you have read the [Code of Conduct](../../CODE_OF_CONDUCT.md) and the [Contributing Guide](../../CONTRIBUTING.md).

## Getting started

Expand Down Expand Up @@ -139,7 +139,7 @@ In `src/Logger/Logger.php` update variable `const PROVIDERS` to include your pro

## 3. Test your adapter

After you finished adding your new adapter, you should write a proper test for it. To do that, you enter `tests/LoggerTests.php` and take a look at `testAdapters()` method. In there, we already build a whole log object and all you need to do is to push the log using your provider. Take a look at how test for already existign adapter looks or use template below:
After you finished adding your new adapter, you should write a proper test for it. To do that, you enter `tests/LoggerTests.php` and take a look at `testAdapters()` method. In there, we already build a whole log object and all you need to do is to push the log using your provider. Take a look at how test for already existing adapter looks or use the template below:

```php
// Test [ADAPTER_NAME]
Expand Down
176 changes: 176 additions & 0 deletions src/Logger/Adapter/HoneyBadger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

namespace Utopia\Logger\Adapter;

use Exception;
use Utopia\Logger\Adapter;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;

// Reference Material
// https://docs.honeybadger.io/api/reporting-exceptions

class HoneyBadger extends Adapter
{
protected string $apiKey;

/**
* Return unique adapter name
*
* @return string
*/
public static function getName(): string
{
return 'honeyBadger';
}

/**
* Push log to external provider
*
* @param Log $log
* @return int
*/
public function push(Log $log): int
{
$params = [];

foreach ($log->getExtra() as $paramKey => $paramValue) {
$params[$paramKey] = var_export($paramValue, true);
}

$breadcrumbsObject = $log->getBreadcrumbs();
$breadcrumbsArray = [];

foreach ($breadcrumbsObject as $breadcrumb) {
\array_push($breadcrumbsArray, [
'category' => $breadcrumb->getCategory(),
'timestamp' => \intval($breadcrumb->getTimestamp()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this recently, but got an error in Honeybadger because they expect this to be a string like 2020-01-27T12:37:31.616-08:00. Would you please update this to be a ISO-8601 formatted timestamp in UTC?

'message' => $breadcrumb->getMessage(),
'metadata' => [
'exception' => $breadcrumb->getType(),
],
]);
}

$stackFrames = [];

if (isset($log->getExtra()['detailedTrace'])) {
$detailedTrace = $log->getExtra()['detailedTrace'];
if (! is_array($detailedTrace)) {
throw new Exception('detailedTrace must be an array');
}
foreach ($detailedTrace as $trace) {
if (! is_array($trace)) {
throw new Exception('detailedTrace must be an array of arrays');
}
\array_push($stackFrames, [
'filename' => $trace['file'] ?? '',
'lineno' => $trace['line'] ?? '',
'function' => $trace['function'] ?? '',
]);
}
}

$requestBody = [
'notifier' => [
'name' => 'utopia-logger',
'url' => 'https://github.com/utopia-php/logger',
'tags' => $log->getTags(),
'version' => $log->getVersion(),
],
'error' => [
'class' => $log->getType(),
'message' => $log->getMessage(),
'backtrace' => $stackFrames,
],
'breadcrumbs' => [
'enabled' => true,
'trail' => $breadcrumbsArray,
],

'request' => [
'params' => $params,
'action' => $log->getAction(),
'context' => empty($log->getUser()) ? null : [
'user_id' => $log->getUser()->getId(),
'user_email' => $log->getUser()->getEmail(),
],

],
'server' => [
'project_root' => $log->getServer(),
'environment_name' => $log->getEnvironment(),
'hostname' => $log->getServer(),
],

];

// init curl object
$ch = \curl_init();

// define options
$optArray = [
CURLOPT_URL => 'https://api.honeybadger.io/v1/notices',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => \json_encode($requestBody),
CURLOPT_HEADEROPT => \CURLHEADER_UNIFIED,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-API-Key: '.$this->apiKey, 'Accept: application/json', 'User-Agent: utopia-logger/'.Logger::LIBRARY_VERSION],
];

// apply those options
\curl_setopt_array($ch, $optArray);

// execute request and get response
$result = \curl_exec($ch);
$response = \curl_getinfo($ch, \CURLINFO_HTTP_CODE);

if (! $result && $response >= 400) {
throw new Exception('Log could not be pushed with status code '.$response.': '.\curl_error($ch));
}

\curl_close($ch);

return $response;
}

/**
* HoneyBadger constructor.
*
* @param string $configKey
*/
public function __construct(string $configKey)
{
$this->apiKey = $configKey;
}

public function getSupportedTypes(): array
{
return [
Log::TYPE_INFO,
Log::TYPE_DEBUG,
Log::TYPE_VERBOSE,
Log::TYPE_WARNING,
Log::TYPE_ERROR,
];
}

public function getSupportedEnvironments(): array
{
return [
Log::ENVIRONMENT_STAGING,
Log::ENVIRONMENT_PRODUCTION,
];
}

public function getSupportedBreadcrumbTypes(): array
{
return [
Log::TYPE_INFO,
Log::TYPE_DEBUG,
Log::TYPE_VERBOSE,
Log::TYPE_WARNING,
Log::TYPE_ERROR,
];
}
}
1 change: 1 addition & 0 deletions src/Logger/Logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Logger
'sentry',
'appSignal',
'logOwl',
'honeyBadger',
];

/**
Expand Down
11 changes: 10 additions & 1 deletion tests/LoggerTest.php
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use PHPUnit\Framework\TestCase;
use Utopia\Logger\Adapter\AppSignal;
use Utopia\Logger\Adapter\HoneyBadger;
use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
Expand Down Expand Up @@ -138,7 +139,7 @@ public function testAdapters(): void
$log->setType(Log::TYPE_ERROR);
$log->setVersion('0.11.5');
$log->setMessage('Document efgh5678 not found');
$log->setUser(new User('efgh5678'));
$log->setUser(new User('efgh5678', 'abc@test.com', 'John Doe'));
$log->addBreadcrumb(new Breadcrumb(Log::TYPE_DEBUG, 'http', 'DELETE /api/v1/database/abcd1234/efgh5678', \microtime(true) - 500));
$log->addBreadcrumb(new Breadcrumb(Log::TYPE_DEBUG, 'auth', 'Using API key', \microtime(true) - 400));
$log->addBreadcrumb(new Breadcrumb(Log::TYPE_INFO, 'auth', 'Authenticated with * Using API Key', \microtime(true) - 350));
Expand All @@ -154,6 +155,7 @@ public function testAdapters(): void
$log->addExtra('isExpected', true);
$log->addExtra('file', '/User/example/server/src/server/server.js');
$log->addExtra('line', '15');
$log->addExtra('stackTrace', [['number' => 15, 'file' => 'User/example/server/src/server/server.js', 'method' => 'runtime_error']]);

// Test Sentry
$adapter = new Sentry(\getenv('TEST_SENTRY_KEY').';'.\getenv('TEST_SENTRY_PROJECT_ID').';'.\getenv('TEST_SENTRY_HOST'));
Expand Down Expand Up @@ -181,5 +183,12 @@ public function testAdapters(): void
$logger = new Logger($adapter);
$response = $logger->addLog($log);
$this->assertEquals(200, $response);

// Test HoneyBadger
$honebadgerkey = \getenv('TEST_HONEYBADGER_KEY');
$adapter = new HoneyBadger($honebadgerkey ? $honebadgerkey : '');
$logger = new Logger($adapter);
$response = $logger->addLog($log);
$this->assertEquals(201, $response);
}
}