17.1 Structured Logs, Trace Correlation, and Queries
Key Takeaways
Structured fields are parsed, but not every JSON field is automatically indexed.
Use the project-qualified trace resource and a hexadecimal span ID from the active context.
Keep sensitive data out of logs and use precise resource/time filters when investigating.
Structured Application Logging & Log Sinks with Cloud Logging
Centralized observability in enterprise microservices requires structured telemetry that machines can parse, index, and correlate at high scale. The Google Cloud Professional Cloud Developer exam tests your understanding of structured logging patterns, special reserved JSON fields recognized by the Cloud Logging agent, the Logging Query Language (LQL), and Log Router architectures—including export sinks to BigQuery, Cloud Storage, and Pub/Sub, as well as cost-control exclusion filters.
Structured Logging vs. Unstructured Text Logs
When applications write unstructured strings to standard output (stdout) or standard error (stderr), Cloud Logging ingests each line as a plain-text textPayload. In contrast, when an application serializes its log entries as JSON objects to stdout, the underlying logging agent (such as the Cloud Logging agent, Fluent Bit in GKE, or the Cloud Run container supervisor) automatically deserializes the entry into a typed jsonPayload.
The Operational Hazards of Unstructured Logs
Unstructured logging poses severe operational liabilities in production:
- Opaque Payloads: Filtering by user ID, tenant ID, or HTTP response latency requires expensive, brittle regular expression searches across raw string messages.
- Loss of Metric Granularity: Log-based metrics can extract values from text with regular expressions, but structured numeric fields are easier to maintain and less fragile.
- Lack of Trace Association: Distributed trace spans cannot be linked automatically to log lines, forcing engineers to manually search across thousands of log streams during production incidents.
Structured vs. Unstructured Comparison
| Dimension | Unstructured Logging (textPayload) | Structured Logging (jsonPayload) |
|---|---|---|
| Payload Structure | Freeform single-line or multi-line string | Strongly typed, hierarchical JSON key-value object |
| Search Efficiency | Full-text substring search and regex evaluation | Typed field queries (index coverage depends on configuration) (e.g., jsonPayload.userId = "u123") |
| Severity Detection | Inferred from string prefix or defaults to INFO | Explicitly declared via the reserved severity field |
| Distributed Tracing | Requires manual search for request IDs in message strings | Natively correlated in Cloud Trace waterfall views via trace identifiers |
| Log-Based Metrics | Limited to pattern matching and regex extraction | Direct field extraction and mathematical distribution computation |
Cloud Logging Special Reserved JSON Fields
Google Cloud Logging recognizes several top-level reserved fields in your JSON payload. When present, the logging pipeline strips or promotes these fields into first-class metadata properties on the resulting LogEntry resource rather than nesting them generically within jsonPayload.
Core Reserved Fields
severity: Specifies the log severity level. Valid values areDEFAULT,DEBUG,INFO,NOTICE,WARNING,ERROR,CRITICAL,ALERT, andEMERGENCY. Set severity explicitly; defaults and stream-based handling depend on the collector/runtime.message: Supplies the primary human-readable log message. In the Cloud Logging Logs Explorer and Cloud Error Reporting consoles, the string inmessageserves as the primary headline and stack trace payload.timeortimestamp: Overrides the ingestion timestamp with the exact moment the event occurred at the application layer. Accepted formats include an RFC 3339 string (e.g.,"2026-10-18T15:04:05.123456Z") or a JSON object withsecondsandnanos({"seconds": 1760800000, "nanos": 0}). If omitted, the timestamp defaults to the time the log was ingested by the Google Cloud collector.logging.googleapis.com/trace: Correlates the log entry with a distributed trace in Cloud Trace. Crucial Exam Rule: The trace field must be formatted as the complete resource path:projects/[PROJECT_ID]/traces/[TRACE_ID]. Passing only[TRACE_ID]will prevent Cloud Trace and Cloud Logging from linking the records.logging.googleapis.com/spanId: Identifies the specific sub-operation within the trace. Must be a 16-character hexadecimal string representing a 64-bit integer (e.g.,"000000000000004a").logging.googleapis.com/trace_sampled: A boolean (trueorfalse) indicating whether the trace was sampled for storage in Cloud Trace.logging.googleapis.com/sourceLocation: A JSON object pinpointing the exact source code origin. Containsfile(string),line(integer or string), andfunction(string).logging.googleapis.com/labels: A map of string key-value pairs added toLogEntry.labels, available as structured labels; indexing and cross-project access require appropriate configuration.httpRequest: A nested object representing an HTTP transaction, adhering to Google's standard schema:requestMethod,requestUrl,status,userAgent,remoteIp,referer,latency,cacheLookup, andresponseSize.
Concrete Example: Emitting Structured JSON to Standard Output
The following Go microservice handler demonstrates how to format and emit structured JSON to stdout without external third-party SDK dependencies, using an active OpenTelemetry span context. The legacy X-Cloud-Trace-Context span number is decimal and must not be copied unchanged into the hexadecimal spanId field:
// Handler excerpt: tracing middleware must already extract the incoming context.
// Imports: encoding/json, fmt, net/http, os, time, go.opentelemetry.io/otel/trace.
func orderHandler(w http.ResponseWriter, r *http.Request) {
sc := trace.SpanContextFromContext(r.Context())
entry := map[string]any{
"severity": "INFO", "message": "Order accepted",
"time": time.Now().UTC().Format(time.RFC3339Nano),
}
if sc.IsValid() {
entry["logging.googleapis.com/trace"] = fmt.Sprintf(
"projects/%s/traces/%s", os.Getenv("GOOGLE_CLOUD_PROJECT"), sc.TraceID().String())
entry["logging.googleapis.com/spanId"] = sc.SpanID().String()
entry["logging.googleapis.com/trace_sampled"] = sc.IsSampled()
}
if err := json.NewEncoder(os.Stdout).Encode(entry); err != nil {
http.Error(w, "log serialization failed", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusAccepted)
}
Querying Telemetry with Logging Query Language (LQL)
The Logs Explorer uses the Logging Query Language (LQL) to filter, inspect, and analyze logs across services and resource boundaries.
LQL Syntax Fundamentals
- Exact Equality:
resource.type = "cloud_run_revision" - Field In-Hierarchy:
jsonPayload.tenantId = "acme-corp" - Text Has / Substring Matching:
jsonPayload.message : "timeout"(matches any token containing "timeout") - Regular Expression Matching:
jsonPayload.userId =~ "^usr_[0-9]{5}$" - Numerical and Severity Comparisons:
severity >= WARNING,httpRequest.latency > "2.5s",httpRequest.status >= 500 - Logical Operators:
AND,OR, andNOT(or-). When multiple clauses appear on separate lines,ANDis evaluated implicitly.
Complex LQL Query Example
To find all failed transactions on Cloud Run for a specific tenant within a precise time window:
resource.type = "cloud_run_revision"
resource.labels.service_name = "checkout-service"
severity >= ERROR
jsonPayload.tenantId = "acme-corp"
timestamp >= "2026-10-18T10:00:00Z" AND timestamp <= "2026-10-18T12:00:00Z"
NOT jsonPayload.message : "circuit_breaker_test"
A developer writes a microservice deployed on Cloud Run. The service outputs JSON logs to stdout including the trace ID extracted from the X-Cloud-Trace-Context header. However, when inspecting slow requests in the Cloud Trace console, the Trace Waterfall pane displays 'No logs found for this span'. What is the root cause of this failure?
The trace field was populated with only the 32-character trace ID rather than the full resource path: projects/[PROJECT_ID]/traces/[TRACE_ID]
Cloud Run does not support structured JSON logging from standard output without the Cloud Logging agent installed in a sidecar container
The log entry severity was set to INFO, and Cloud Trace only indexes log entries with severity WARNING or higher
The application failed to generate a span ID in UUIDv4 format inside the logging.googleapis.com/spanId field
Sections you finish are checked off in the contents.