diff --git a/logging/logging.go b/logging/logging.go index 7543bb0aeaf..66fe7b27b95 100644 --- a/logging/logging.go +++ b/logging/logging.go @@ -873,33 +873,31 @@ func (l *Logger) writeLogEntries(entries []*logpb.LogEntry) { // (for example by calling SetFlags or SetPrefix). func (l *Logger) StandardLogger(s Severity) *log.Logger { return l.stdLoggers[s] } -var reCloudTraceContext = regexp.MustCompile(`([a-f\d]+)/([a-f\d]+);o=(\d)`) +var reCloudTraceContext = regexp.MustCompile( + // Matches on "TRACE_ID" + `([a-f\d]+)?` + + // Matches on "/SPAN_ID" + `(?:/([a-f\d]+))?` + + // Matches on ";0=TRACE_TRUE" + `(?:;o=(\d))?`) func deconstructXCloudTraceContext(s string) (traceID, spanID string, traceSampled bool) { - // As per the format described at https://cloud.google.com/trace/docs/troubleshooting#force-trace + // As per the format described at https://cloud.google.com/trace/docs/setup#force-trace // "X-Cloud-Trace-Context: TRACE_ID/SPAN_ID;o=TRACE_TRUE" // for example: - // "X-Cloud-Trace-Context: 105445aa7843bc8bf206b120001000/0;o=1" + // "X-Cloud-Trace-Context: 105445aa7843bc8bf206b120001000/1;o=1" // // We expect: - // * traceID: "105445aa7843bc8bf206b120001000" - // * spanID: "" - // * traceSampled: true - matches := reCloudTraceContext.FindAllStringSubmatch(s, -1) - if len(matches) != 1 { - return - } + // * traceID (optional): "105445aa7843bc8bf206b120001000" + // * spanID (optional): "1" + // * traceSampled (optional): true + matches := reCloudTraceContext.FindStringSubmatch(s) - sub := matches[0] - if len(sub) != 4 { - return - } + traceID, spanID, traceSampled = matches[1], matches[2], matches[3] == "1" - traceID, spanID = sub[1], sub[2] if spanID == "0" { spanID = "" } - traceSampled = sub[3] == "1" return } diff --git a/logging/logging_unexported_test.go b/logging/logging_unexported_test.go index b764688462a..2a426252de0 100644 --- a/logging/logging_unexported_test.go +++ b/logging/logging_unexported_test.go @@ -288,18 +288,54 @@ func TestToLogEntryTrace(t *testing.T) { }, logging.LogEntry{Trace: "projects/P/traces/105445aa7843bc8bf206b120001000", SpanId: "000000000000004a", TraceSampled: true}, }, + { + "X-Trace-Context header with blank trace", + Entry{ + HTTPRequest: &HTTPRequest{ + Request: &http.Request{ + URL: u, + Header: http.Header{"X-Cloud-Trace-Context": {"/0;o=1"}}, + }, + }, + }, + logging.LogEntry{TraceSampled: true}, + }, { "X-Trace-Context header with blank span", Entry{ HTTPRequest: &HTTPRequest{ Request: &http.Request{ URL: u, - Header: http.Header{"X-Cloud-Trace-Context": {"105445aa7843bc8bf206b120001000/0;o=0"}}, + Header: http.Header{"X-Cloud-Trace-Context": {"105445aa7843bc8bf206b120001000/;o=0"}}, }, }, }, logging.LogEntry{Trace: "projects/P/traces/105445aa7843bc8bf206b120001000"}, }, + { + "X-Trace-Context header with missing traceSampled aka ?o=*", + Entry{ + HTTPRequest: &HTTPRequest{ + Request: &http.Request{ + URL: u, + Header: http.Header{"X-Cloud-Trace-Context": {"105445aa7843bc8bf206b120001000/0"}}, + }, + }, + }, + logging.LogEntry{Trace: "projects/P/traces/105445aa7843bc8bf206b120001000"}, + }, + { + "X-Trace-Context header with all blank fields", + Entry{ + HTTPRequest: &HTTPRequest{ + Request: &http.Request{ + URL: u, + Header: http.Header{"X-Cloud-Trace-Context": {""}}, + }, + }, + }, + logging.LogEntry{}, + }, { "Invalid X-Trace-Context header but already set TraceID", Entry{