Skip to content

Commit a0b5b3c

Browse files
committed
GoogleCloudLoggingFormatter trace context
1 parent 479c936 commit a0b5b3c

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

composer.json

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"aws/aws-sdk-php": "^3.0",
2222
"doctrine/couchdb": "~1.0@dev",
2323
"elasticsearch/elasticsearch": "^7 || ^8",
24+
"friendsofphp/php-cs-fixer": "^3.52",
2425
"graylog2/gelf-php": "^1.4.2 || ^2.0",
2526
"guzzlehttp/guzzle": "^7.4.5",
2627
"guzzlehttp/psr7": "^2.2",
@@ -45,6 +46,7 @@
4546
"ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
4647
"mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
4748
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
49+
"google/cloud": "Allow inclusion of additional contextual information on GCP",
4850
"rollbar/rollbar": "Allow sending log messages to Rollbar",
4951
"ext-mbstring": "Allow to work properly with unicode symbols",
5052
"ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",

src/Monolog/Formatter/GoogleCloudLoggingFormatter.php

+44
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
*/
2525
final class GoogleCloudLoggingFormatter extends JsonFormatter
2626
{
27+
const CONTEXT_HEADER_FORMAT = '/([0-9a-fA-F]{32})(?:\/(\d+))?(?:;o=(\d+))?/';
28+
29+
private static ?string $traceID = null;
30+
2731
protected function normalizeRecord(LogRecord $record): array
2832
{
2933
$normalized = parent::normalizeRecord($record);
@@ -32,9 +36,49 @@ protected function normalizeRecord(LogRecord $record): array
3236
$normalized['severity'] = $normalized['level_name'];
3337
$normalized['time'] = $record->datetime->format(DateTimeInterface::RFC3339_EXTENDED);
3438

39+
// Tag with Trace ID for request attribution
40+
$normalized['logging.googleapis.com/trace'] = $this->getTraceID();
41+
3542
// Remove keys that are not used by GCP
3643
unset($normalized['level'], $normalized['level_name'], $normalized['datetime']);
3744

3845
return $normalized;
3946
}
47+
48+
private function getTraceID(): ?string
49+
{
50+
if (empty($this->traceID) && !empty($_SERVER['HTTP_X_CLOUD_TRACE_CONTEXT'])) {
51+
$matched = preg_match(
52+
self::CONTEXT_HEADER_FORMAT,
53+
$_SERVER['HTTP_X_CLOUD_TRACE_CONTEXT'] ?? '',
54+
$matches,
55+
);
56+
57+
if (!$matched) {
58+
return null;
59+
}
60+
61+
$projectID = $this->getProjectID();
62+
if (empty($projectID)) {
63+
return null;
64+
}
65+
66+
$this->traceID = 'projects/'.$projectID.'/traces/'.strtolower($matches[1]);
67+
}
68+
69+
return $this->traceID;
70+
}
71+
72+
private function getProjectID(): ?string
73+
{
74+
if (isset($_SERVER['GOOGLE_CLOUD_PROJECT'])) {
75+
return $_SERVER['GOOGLE_CLOUD_PROJECT'];
76+
}
77+
78+
if (class_exists('\Google\Cloud\Core\Compute\Metadata')) {
79+
return (new \Google\Cloud\Core\Compute\Metadata())->getProjectId();
80+
}
81+
82+
return null;
83+
}
4084
}

0 commit comments

Comments
 (0)