Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions src/Logger/Adapter/Bugsnag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php

namespace Utopia\Logger\Adapter;

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

// Reference Material
// https://bugsnagerrorreportingapi.docs.apiary.io/#reference/0/notify/send-error-reports

class Bugsnag extends Adapter
{
/**
* @var string (required, can be found in Bugsnag -> Project -> Project settings -> Notifier API key)
*/
protected string $apiKey;

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

/**
* Push log to external provider
*
* @param Log $log
* @return int
* @throws Exception
*/
public function push(Log $log): int
{
$line = isset($log->getExtra()['line']) ? $log->getExtra()['line'] : '';
$file = isset($log->getExtra()['file']) ? $log->getExtra()['file'] : '';
$id = empty($log->getUser()) ? null : $log->getUser()->getId();
$email = empty($log->getUser()) ? null : $log->getUser()->getEmail();
$username = empty($log->getUser()) ? null : $log->getUser()->getUsername();

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

foreach ($breadcrumbsObject as $breadcrumb) {
\array_push($breadcrumbsArray, [
'timestamp' => date('c', \intval($breadcrumb->getTimestamp())),
'name' => $breadcrumb->getMessage(),
'type' => 'manual',
'metadata' => [
'type' => $breadcrumb->getType(),
'category' => $breadcrumb->getCategory()
]
]);
}

$stackFrames = [];

if (isset($log->getExtra()['detailedTrace'])) {
foreach ($log->getExtra()['detailedTrace'] as $trace) {
\array_push($stackFrames, [
'file' => $trace['file'],
'lineNumber' => $trace['line'],
'method' => $trace['function'],
]);
}
}

// prepare log (request body)
$requestBody = [
'payloadVersion' => '5',
'notifier' => [
'name'=> 'utopia-logger',
'version' => $log->getVersion(),
'url' => 'https://github.com/utopia-php/logger',
],
'events' => array(
'exceptions' => array(
'errorClass' => $log->getType(),
'message' => $log->getMessage(),
'stacktrace' => $stackFrames,
'type' => 'php'
),
'breadcrumbs' => $breadcrumbsArray,
'context' => $log->getAction(),
'groupingHash' => $log->getNamespace(),
'user' => [
'id' => $id,
'email' => $email,
'name' => $username
],
'app' => [
'releaseStage' => $log->getEnvironment()
],
'device' => [
'hostname' => $log->getServer(),
'time' => date('c', \intval($log->getTimestamp()))
],
'metaData' => $log->getTags()
)
];

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

// define options
$optArray = array(
CURLOPT_URL => 'https://notify.bugsnag.com/',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => \json_encode($requestBody),
CURLOPT_HEADEROPT => \CURLHEADER_UNIFIED,
CURLOPT_HTTPHEADER => array('Content-Type: application/json', 'Bugsnag-Api-Key: ' . $this->apiKey, 'Bugsnag-Payload-Version: 5')
);

// 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;
}

/**
* Bugsnag 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',
'bugsnag',
];

/**
Expand Down
8 changes: 8 additions & 0 deletions tests/LoggerTest.php
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\Bugsnag;
use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
Expand Down Expand Up @@ -181,5 +182,12 @@ public function testAdapters(): void
$logger = new Logger($adapter);
$response = $logger->addLog($log);
$this->assertEquals(200, $response);

// Test Bugsnag
$bugsnagKey = \getenv('TEST_BUGSNAG_KEY');
$adapter = new Bugsnag($bugsnagKey ? $bugsnagKey : '');
$logger = new Logger($adapter);
$response = $logger->addLog($log);
$this->assertEquals(200, $response);
Copy link
Contributor

Choose a reason for hiding this comment

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

I tried running this test and it passed, but I don't see any new errors in bugsnag. Were you able to see something in bugsnag?

Copy link
Author

Choose a reason for hiding this comment

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

@stnguyen90 I didn't see anything in Bugsnag, as I don't recall sending any errors to Bugsnag. Do you know how we can send errors to Bugsnag to validate these changes? I'm willing to help in any way I can.

Copy link
Contributor

Choose a reason for hiding this comment

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

@michizhou, you can sign up for a trial at BugSnag.

}
}