24
24
*/
25
25
final class GoogleCloudLoggingFormatter extends JsonFormatter
26
26
{
27
+ const CONTEXT_HEADER_FORMAT = '/([0-9a-fA-F]{32})(?:\/(\d+))?(?:;o=(\d+))?/ ' ;
28
+
29
+ private static ?string $ traceID = null ;
30
+
27
31
protected function normalizeRecord (LogRecord $ record ): array
28
32
{
29
33
$ normalized = parent ::normalizeRecord ($ record );
@@ -32,9 +36,49 @@ protected function normalizeRecord(LogRecord $record): array
32
36
$ normalized ['severity ' ] = $ normalized ['level_name ' ];
33
37
$ normalized ['time ' ] = $ record ->datetime ->format (DateTimeInterface::RFC3339_EXTENDED );
34
38
39
+ // Tag with Trace ID for request attribution
40
+ $ normalized ['logging.googleapis.com/trace ' ] = $ this ->getTraceID ();
41
+
35
42
// Remove keys that are not used by GCP
36
43
unset($ normalized ['level ' ], $ normalized ['level_name ' ], $ normalized ['datetime ' ]);
37
44
38
45
return $ normalized ;
39
46
}
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
+ }
40
84
}
0 commit comments