Skip to content

Commit

Permalink
Add the StdOutLogger and Logging Trait
Browse files Browse the repository at this point in the history
  • Loading branch information
Hectorhammett committed Sep 4, 2024
1 parent 1277062 commit e3eb27e
Show file tree
Hide file tree
Showing 7 changed files with 576 additions and 1 deletion.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"guzzlehttp/guzzle": "^7.4.5",
"guzzlehttp/psr7": "^2.4.5",
"psr/http-message": "^1.1||^2.0",
"psr/cache": "^2.0||^3.0"
"psr/cache": "^2.0||^3.0",
"psr/log": "^3.0"
},
"require-dev": {
"guzzlehttp/promises": "^2.0",
Expand Down
121 changes: 121 additions & 0 deletions src/Logging/LogEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php
/**
* Copyright 2024 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Auth\Logging;

class LogEvent
{
/**
* Timestamp in format RFC3339 representing when this event ocurred
*
* @var null|string
*/
public null|string $timestamp = null;

/**
* Rest method type
*
* @var null|string
*/
public null|string $method = null;

/**
* URL representing the rest URL endpoint
*
* @var null|string
*/
public null|string $url = null;

/**
* An array that contains the headers for the response or request
*
* @var null|array<mixed>
*/
public null|array $headers = null;

/**
* An array representation of JSON for the response or request
*
* @var null|string|array
*/
public null|string|array $payload = null;

Check failure on line 55 in src/Logging/LogEvent.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis / PHPStan Static Analysis

Property Google\Auth\Logging\LogEvent::$payload type has no value type specified in iterable type array.

/**
* Status code for REST or gRPC methods
*
* @var null|int|string
*/
public null|int|string $status = null;

/**
* The latency in miliseconds
*
* @var null|int
*/
public null|int $latency = null;

/**
* The retry attempt number
*
* @var null|int
*/
public null|int $retryAttempt = null;

/**
* The name of the gRPC method being called
*
* @var null|string
*/
public null|string $rpcName = null;

/**
* The Service Name of the gRPC
*
* @var null|string $serviceName
*/
public null|string $serviceName;

/**
* The Client Id for easy trace
*
* @var int $clientId
*/
public int $clientId;

/**
* The Request id for easy trace
*
* @var in $requestId;
*/
public int $requestId;

Check failure on line 104 in src/Logging/LogEvent.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis / PHPStan Static Analysis

PHPDoc tag @var for property Google\Auth\Logging\LogEvent::$requestId with type Google\Auth\Logging\in is incompatible with native type int.

Check failure on line 104 in src/Logging/LogEvent.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis / PHPStan Static Analysis

Property Google\Auth\Logging\LogEvent::$requestId has unknown class Google\Auth\Logging\in as its type.

/**
* Creates an object with all the fields required for logging
* Passing a string representation of a timestamp calculates the difference between
* these two times and sets the latency field with the result.
*
* @param null|string $startTime (Optional) Parameter to calculate the latency
*/
public function __construct(null|string $startTime = null)
{
$this->timestamp = date(DATE_RFC3339);

if ($startTime) {
$this->latency = (int) strtotime($this->timestamp) - strtotime($startTime);
}
}
}
94 changes: 94 additions & 0 deletions src/Logging/LoggingTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
/**
* Copyright 2024 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Auth\Logging;

use Psr\Log\LogLevel;

trait LoggingTrait
{
private function logRequest(LogEvent $event): void
{
$debugEvent = [
'timestamp' => $event->timestamp,
'severity' => strtoupper(LogLevel::DEBUG),
'clientId' => $event->clientId,
'requestId' => $event->requestId,
];

$jsonPayload = [
'request.method' => $event->method,
'request.url' => $event->url,
'request.headers' => $event->headers,
'request.payload' => $event->payload,
'request.jwt' => $this->getJwtToken($event->headers),
'retryAttempt' => $event->retryAttempt
];

// Filter out the falsey values
$jsonPayload = array_filter($jsonPayload);
$debugEvent['jsonPayload'] = $jsonPayload;

$this->logger->debug((string) json_encode($debugEvent));
}

private function logResponse(LogEvent $event): void
{
$debugEvent = [
'timestamp' => $event->timestamp,
'severity' => strtoupper(LogLevel::DEBUG),
'clientId' => $event->clientId,
'requestId' => $event->requestId,
'jsonPayload' => [
'response.headers' => $event->headers,
'response.payload' => $event->payload,
'latency' => $event->latency,
]
];

$this->logger->debug((string) json_encode($debugEvent));

$infoEvent = [
'timestamp' => $event->timestamp,
'severity' => LogLevel::INFO,
'clientId' => $event->clientId,
'requestId' => $event->requestId,
'jsonPayload' => [
'response.status' => $event->status
]
];

$this->logger->info((string) json_encode($infoEvent));
}

private function getJwtToken(array $headers): null|array
{
$tokenHeader = $headers['Authorization'] ?? '';
$token = str_replace('Bearer ', '', $tokenHeader);

if (substr_count($token, '.') !== 2) {
return null;
}

[$header, $token, $_] = explode('.', $token);

return [
'header' => base64_decode($header),
'token' => base64_decode($token)
];
}
}
107 changes: 107 additions & 0 deletions src/Logging/StdOutLogger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php
/**
* Copyright 2024 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Auth\Logging;

use Exception;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Stringable;

/**
* A basic logger class to log into stdOut for GCP logging
*/
class StdOutLogger implements LoggerInterface
{
/**
* @var array<string,int>
*/
private array $levelMapping = [
LogLevel::EMERGENCY => 7,
LogLevel::ALERT => 6,
LogLevel::CRITICAL => 5,
LogLevel::ERROR => 4,
LogLevel::WARNING => 3,
LogLevel::NOTICE => 2,
LogLevel::INFO => 1,
LogLevel::DEBUG => 0,
];
private int $level;

public function __construct(string $level = LogLevel::DEBUG)
{
$this->level = $this->getLevelMap($level);
}

public function emergency(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::EMERGENCY, $message);
}

public function alert(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::ALERT, $message);
}

public function critical(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::CRITICAL, $message);
}

public function error(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::ERROR, $message);
}

public function warning(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::WARNING, $message);
}

public function notice(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::NOTICE, $message);
}

public function info(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::INFO, $message);
}

public function debug(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::DEBUG, $message);
}

public function log($level, string|Stringable $message, array $context = []): void

Check failure on line 90 in src/Logging/StdOutLogger.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis / PHPStan Static Analysis

Method Google\Auth\Logging\StdOutLogger::log() has parameter $level with no type specified.
{
if ($this->getLevelMap($level) < $this->level) {
return;
}

print($message . "\n");
}

private function getLevelMap(string $levelName): int
{
if (!array_key_exists($levelName, $this->levelMapping)) {
throw new Exception('The level supplied to the Logger is not valid');
}

return $this->levelMapping[$levelName];
}
}
42 changes: 42 additions & 0 deletions tests/Logging/LogEventTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
/**
* Copyright 2024 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Auth\Tests\Logging;

use Google\Auth\Logging\LogEvent;
use Google\Auth\Tests\BaseTest;

class LogEventTest extends BaseTest
{
public function testInstanceAddsTimestamp()
{
$item = new LogEvent();
$this->assertNotNull($item->timestamp);
}

public function testConstructorWithoutParameterHasNoLatency()
{
$item = new LogEvent();
$this->assertNull($item->latency);
}

public function testConstructorWithParameterHasLatencySet()
{
$item = new LogEvent(date(DATE_RFC3339));
$this->assertNotNull($item->latency);
}
}
Loading

0 comments on commit e3eb27e

Please sign in to comment.