or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
golangpkg:golang/cloud.google.com/go/logging@v1.13.1

docs

architecture.mdclient-logger.mdconstants.mderrors.mdhttp-request.mdindex.mdlogadmin.mdlogger-config.mdpayloads.mdseverity.mdstandard-logger.mdtracing.mdwriting-logs.md
tile.json

tessl/golang-cloud-google-com--go--logging

tessl install tessl/golang-cloud-google-com--go--logging@1.13.0

Cloud Logging client library for Go that enables writing log entries to Google Cloud Logging service with buffered asynchronous and synchronous logging capabilities.

tracing.mddocs/

Trace and Span Context Integration

This document describes how to integrate Cloud Logging with distributed tracing using OpenTelemetry, W3C Traceparent headers, and X-Cloud-Trace-Context headers.

Trace Fields in Entry

type Entry struct {
    // ...
    Trace        string
    SpanID       string
    TraceSampled bool
    // ...
}

Log entries can include trace context information to correlate logs with distributed traces:

Fields:

  • Trace string - Resource name of the trace associated with the log entry, if any. If it contains a relative resource name, the name is assumed to be relative to //tracing.googleapis.com.

  • SpanID string - ID of the span within the trace associated with the log entry. The ID is a 16-character hexadecimal encoding of an 8-byte array.

  • TraceSampled bool - If set, indicates that this request was sampled.

Automatic Trace Context Extraction

The logging library automatically extracts trace context from HTTP requests when an HTTPRequest is included in the log entry. It supports three methods in the following priority order:

  1. OpenTelemetry - Extracts from OpenTelemetry span context
  2. W3C Traceparent - Extracts from the Traceparent header
  3. X-Cloud-Trace-Context - Extracts from the legacy X-Cloud-Trace-Context header
func handler(w http.ResponseWriter, r *http.Request) {
    // Trace context is automatically extracted from r
    logger.Log(logging.Entry{
        Payload: "processing request",
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })
    // The Entry will automatically have Trace, SpanID, and TraceSampled
    // populated from the request's trace headers
}

OpenTelemetry Integration

When using OpenTelemetry for distributed tracing, the logging library automatically extracts trace information from the request context:

import (
    "context"
    "net/http"

    "cloud.google.com/go/logging"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/trace"
)

func handlerWithOTel(w http.ResponseWriter, r *http.Request) {
    // OpenTelemetry tracer
    tracer := otel.Tracer("my-service")

    // Start a span
    ctx, span := tracer.Start(r.Context(), "handle-request")
    defer span.End()

    // Create request with span context
    r = r.WithContext(ctx)

    // Log entry - trace context extracted automatically from OpenTelemetry
    logger.Log(logging.Entry{
        Payload: "handling request in span",
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })

    // The log entry will have:
    // - Trace: OpenTelemetry trace ID
    // - SpanID: OpenTelemetry span ID
    // - TraceSampled: Whether the trace is sampled
}

W3C Traceparent Header

The logging library supports the W3C Trace Context standard via the Traceparent header:

Traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
             |  |                                |                | |
             |  |                                |                | +-- Flags (sampled)
             |  |                                |                +---- Span ID
             |  |                                +--------------------- Trace ID
             |  +------------------------------------------------------ Version
             +--------------------------------------------------------- Format
func handlerWithW3CTraceContext(w http.ResponseWriter, r *http.Request) {
    // The Traceparent header is automatically parsed
    // Example: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"

    logger.Log(logging.Entry{
        Payload: "processing traced request",
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })

    // Trace field will be: "0af7651916cd43dd8448eb211c80319c"
    // SpanID field will be: "b7ad6b7169203331"
    // TraceSampled will be based on the flags
}

X-Cloud-Trace-Context Header (Legacy)

For compatibility with Google Cloud services, the library also supports the legacy X-Cloud-Trace-Context header:

X-Cloud-Trace-Context: TRACE_ID[/SPAN_ID][;o=TRACE_TRUE]
Example: 105445aa7843bc8bf206b12000100000/1;o=1
func handlerWithCloudTraceContext(w http.ResponseWriter, r *http.Request) {
    // The X-Cloud-Trace-Context header is automatically parsed
    // Example: "105445aa7843bc8bf206b12000100000/1;o=1"

    logger.Log(logging.Entry{
        Payload: "processing cloud trace request",
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })

    // Trace field will be: "105445aa7843bc8bf206b12000100000"
    // SpanID field will be: "0000000000000001" (converted to 16-char hex)
    // TraceSampled will be: true (from o=1)
}

Manual Trace Context

You can also manually set trace context without using HTTPRequest:

func manualTraceContext() {
    traceID := "0af7651916cd43dd8448eb211c80319c"
    spanID := "b7ad6b7169203331"

    logger.Log(logging.Entry{
        Payload:      "manual trace context",
        Severity:     logging.Info,
        Trace:        traceID,
        SpanID:       spanID,
        TraceSampled: true,
    })
}

Trace Context Format for Cloud Trace

When logging to Cloud Trace, the Trace field should be in one of these formats:

// Full resource name
trace := "projects/my-project/traces/0af7651916cd43dd8448eb211c80319c"

// Relative resource name (automatically expanded)
trace := "0af7651916cd43dd8448eb211c80319c"

logger.Log(logging.Entry{
    Payload:  "traced operation",
    Severity: logging.Info,
    Trace:    trace,
    SpanID:   "b7ad6b7169203331",
})

Correlating Logs with Traces

Use trace context to correlate logs with distributed traces in Cloud Trace:

func distributedOperation(w http.ResponseWriter, r *http.Request) {
    // Log 1: Request received
    logger.Log(logging.Entry{
        Payload: "request received",
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })

    // Call downstream service
    callDownstreamService(r)

    // Log 2: Request completed
    logger.Log(logging.Entry{
        Payload: "request completed",
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })

    // All logs will be correlated in Cloud Trace because they
    // share the same trace context from the HTTP request
}

Propagating Trace Context

When making downstream HTTP requests, propagate the trace context:

import (
    "net/http"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func callDownstreamService(r *http.Request) error {
    // Create downstream request with propagated trace context
    client := &http.Client{
        Transport: otelhttp.NewTransport(http.DefaultTransport),
    }

    req, _ := http.NewRequestWithContext(r.Context(), "GET", "http://downstream", nil)

    // Log before calling downstream
    logger.Log(logging.Entry{
        Payload: "calling downstream service",
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })

    resp, err := client.Do(req)
    if err != nil {
        logger.Log(logging.Entry{
            Payload: map[string]interface{}{
                "error": err.Error(),
            },
            Severity: logging.Error,
            HTTPRequest: &logging.HTTPRequest{
                Request: r,
            },
        })
        return err
    }
    defer resp.Body.Close()

    return nil
}

Sampling

The TraceSampled field indicates whether the trace should be recorded:

func sampledLogging(w http.ResponseWriter, r *http.Request) {
    entry := logging.Entry{
        Payload: "potentially sampled log",
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    }

    // After automatic trace extraction, check if sampled
    // (In practice, the library does this automatically)

    // You can also manually control sampling
    entry.TraceSampled = shouldSample() // Custom sampling logic

    logger.Log(entry)
}

func shouldSample() bool {
    // Custom sampling logic (e.g., 10% sampling)
    return rand.Float64() < 0.1
}

Complete Example with OpenTelemetry

package main

import (
    "context"
    "log"
    "net/http"

    "cloud.google.com/go/logging"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
    "go.opentelemetry.io/otel/sdk/trace"
    "go.opentelemetry.io/otel/propagation"
)

var logger *logging.Logger

func main() {
    ctx := context.Background()

    // Initialize Cloud Logging
    client, err := logging.NewClient(ctx, "my-project")
    if err != nil {
        log.Fatalf("failed to create logging client: %v", err)
    }
    defer client.Close()

    logger = client.Logger("trace-log")

    // Initialize OpenTelemetry
    exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
    if err != nil {
        log.Fatalf("failed to create exporter: %v", err)
    }

    tp := trace.NewTracerProvider(
        trace.WithBatcher(exporter),
    )
    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(propagation.TraceContext{})

    // Start HTTP server
    http.HandleFunc("/", tracedHandler)
    http.ListenAndServe(":8080", nil)
}

func tracedHandler(w http.ResponseWriter, r *http.Request) {
    tracer := otel.Tracer("my-service")

    // Start span
    ctx, span := tracer.Start(r.Context(), "handle-request")
    defer span.End()

    // Update request context
    r = r.WithContext(ctx)

    // Log with trace context
    logger.Log(logging.Entry{
        Payload: map[string]interface{}{
            "event": "request_started",
            "path":  r.URL.Path,
        },
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })

    // Simulate work
    performWork(ctx, r)

    // Log completion
    logger.Log(logging.Entry{
        Payload: map[string]interface{}{
            "event": "request_completed",
            "path":  r.URL.Path,
        },
        Severity: logging.Info,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
            Status:  http.StatusOK,
        },
    })

    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
}

func performWork(ctx context.Context, r *http.Request) {
    tracer := otel.Tracer("my-service")

    // Create child span
    _, span := tracer.Start(ctx, "perform-work")
    defer span.End()

    // Log within the span
    logger.Log(logging.Entry{
        Payload: "performing work",
        Severity: logging.Debug,
        HTTPRequest: &logging.HTTPRequest{
            Request: r,
        },
    })
}

Benefits of Trace Integration

  1. Distributed Tracing: Correlate logs across multiple services in a distributed system
  2. Performance Analysis: View logs alongside trace spans in Cloud Trace
  3. Request Flow: Follow the complete flow of a request through your system
  4. Error Investigation: Quickly find all logs related to failed traces
  5. Automatic Context: No manual trace ID management required when using OpenTelemetry
  6. Standard Compliance: Support for W3C Trace Context standard

Related Documentation

  • HTTP Request Metadata - Learn more about the HTTPRequest field
  • Writing Log Entries - Basic logging operations