Skip to content

Instantly share code, notes, and snippets.

@juniorb2ss
Last active April 6, 2019 15:12
Show Gist options
  • Save juniorb2ss/f875f9bb211480c3650e61d54f568427 to your computer and use it in GitHub Desktop.
Save juniorb2ss/f875f9bb211480c3650e61d54f568427 to your computer and use it in GitHub Desktop.
Laravel Logging All Guzzle Request/Response to Elasticsearch
<?php
namespace App\Service\Tray\Provider;
use Carbon\Carbon;
use Monolog\Logger;
use GuzzleHttp\Middleware;
use GuzzleHttp\HandlerStack;
use App\Library\Configurations;
use Elastica\Client as EClient;
use GuzzleHttp\MessageFormatter;
use juniorb2ss\EloquentUuid\Uuid;
use Illuminate\Container\Container;
use Illuminate\Support\ServiceProvider;
use Monolog\Formatter\ElasticaFormatter;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\ElasticSearchHandler;
use App\Helper\GuzzleLoggerMessageFormatterHelper;
use App\Service\Tray\Transporter\GuzzleHttpClient;
use Illuminate\Contracts\Support\DeferrableProvider;
use App\Service\Tray\Transporter\GuzzleHttpTransporter;
use App\Service\Tray\Transporter\HttpTransporterInterface;
use App\Service\Tray\Transporter\TrayGuzzleHttpClientInterface;
class TraySdkServiceProvider extends ServiceProvider implements DeferrableProvider
{
const TIMEOUT = 20;
/**
* @var Logger
*/
private $logger;
/**
* @var string
*/
private $requestId;
/**
* All of the container bindings that should be registered.
*
* @var array
*/
public $bindings = [
TrayGuzzleHttpClientInterface::class => GuzzleHttpClient::class,
HttpTransporterInterface::class => GuzzleHttpTransporter::class,
];
/**
* Bootstrap any application services.
*/
public function boot()
{
}
/**
* @return string
*/
private function getCurrentRequestId(): string
{
if (! $this->requestId) {
$this->requestId = (string) Uuid::generate();
}
return $this->requestId;
}
/**
* Register any application services.
*/
public function register()
{
$this->app->bind(GuzzleHttpClient::class, function (Container $app) {
$configurations = $app->get(Configurations::class);
$clientConfig = [
'timeout' => $configurations->get('tray.sdk.timeout', self::TIMEOUT),
'allow_redirects' => true,
'http_errors' => $configurations->get('tray.sdk.http_errors', true),
'debug' => $configurations->get('tray.sdk.debug', false),
'proxy' => [
'http' => $configurations->get('tray.sdk.proxy.http', null),
'https' => $configurations->get('tray.sdk.proxy.https', null),
'no' => $configurations->get('tray.sdk.proxy.no', []),
],
'headers' => [
'Accept' => 'application/json',
'x-sdk' => $configurations->get('tray.sdk.headers.version', 1.0),
'x-origin' => $configurations->get('tray.sdk.headers.origin', 'hubseg'),
'x-timezone' => $configurations->get('tray.sdk.headers.timezone', 'America/Sao_Paulo'),
'requestId' => $this->getCurrentRequestId(),
],
];
if ($configurations->get('tray.client.logging.enabled', false)) {
$stack = $this->createLoggingHandlerStack([
GuzzleLoggerMessageFormatterHelper::getRequestLoggerString(), // {request}
GuzzleLoggerMessageFormatterHelper::getResponseLoggerString(), // {response}
]);
$clientConfig['handler'] = $stack;
}
$client = new GuzzleHttpClient($clientConfig);
return $client;
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [GuzzleHttpClient::class];
}
/**
* @param array $messageFormats
*
* @return HandlerStack
*/
private function createLoggingHandlerStack(array $messageFormats)
{
$stack = HandlerStack::create();
collect($messageFormats)->each(function ($messageFormat) use ($stack) {
// We'll use unshift instead of push, to add the middleware to the bottom of the stack, not the top
$stack->unshift(
$this->createGuzzleLoggingMiddleware($messageFormat)
);
});
return $stack;
}
/**
* @return mixed
*/
private function getLogger()
{
$client = new EClient([
'servers' => [
[
'host' => config('tray.client.logging.host'),
'port' => config('tray.client.logging.port'),
],
],
]);
try {
// timeout to prevent loopback
$client->setConfigValue('timeout', 1);
// try to get current elasticsearch version, to test connection
$client->getVersion();
$handler = new ElasticSearchHandler($client);
$handler->setFormatter(
new ElasticaFormatter(
config('tray.client.logging.index'),
config('tray.client.logging.type')
)
);
} catch (\Exception $exception) {
$handler = new RotatingFileHandler(storage_path('logs/api-consumer.log'));
}
if (! $this->logger) {
$this->logger = with(new Logger('api-consumer'))->pushHandler($handler);
$this->logger->pushProcessor(function ($record) {
$record['extra'] = [
'hostname' => gethostname(),
'sdk' => config('tray.sdk.headers.version', 1.0),
'requestAt' => Carbon::now()->timestamp,
'requestId' => $this->getCurrentRequestId(),
];
return $record;
});
}
return $this->logger;
}
/**
* @param string $messageFormat
*
* @return callable
*/
private function createGuzzleLoggingMiddleware(string $messageFormat)
{
$message = new MessageFormatter($messageFormat);
return Middleware::log(
$this->getLogger(),
$message
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment