Skip to content

Local Development Logging

When developing the Hub or Runtime Broker locally, you often need to test the logging pipeline and verify log output before deploying to GCP. This guide covers how to configure structured logging for local development, including GCP Cloud Logging format testing.

Set the SCION_LOG_GCP environment variable to output logs in GCP Cloud Logging format:

Terminal window
# Run hub with GCP-formatted logs
SCION_LOG_GCP=true scion server start --enable-hub
# Run broker with GCP-formatted logs
SCION_LOG_GCP=true scion server start --enable-runtime-broker
# Run both with debug logging enabled
SCION_LOG_GCP=true SCION_LOG_LEVEL=debug scion server start --enable-hub --enable-runtime-broker

Without SCION_LOG_GCP, logs use standard JSON format:

{
"time": "2025-02-09T12:34:56Z",
"level": "INFO",
"msg": "Server started",
"component": "scion-hub",
"port": 9810
}

With SCION_LOG_GCP=true, logs use GCP’s expected format:

{
"severity": "INFO",
"message": "Server started",
"timestamp": "2025-02-09T12:34:56Z",
"logging.googleapis.com/labels": {
"component": "scion-hub",
"hostname": "dev-machine"
},
"logging.googleapis.com/sourceLocation": {
"file": "/path/to/server.go",
"line": "172",
"function": "cmd.runServerStart"
},
"port": 9810
}
VariableDescriptionDefault
SCION_LOG_GCPEnable GCP Cloud Logging JSON format on stdoutfalse
SCION_LOG_LEVELLog level (debug, info, warn, error)info
K_SERVICEAuto-enables GCP logging format (set by Cloud Run)-
SCION_CLOUD_LOGGINGSend logs directly to Cloud Logging via client libraryfalse
SCION_CLOUD_LOGGING_LOG_IDLog name in Cloud Loggingscion
SCION_GCP_PROJECT_IDGCP project ID (priority 1 for Cloud Logging)auto-detect
GOOGLE_CLOUD_PROJECTGCP project ID (priority 2 for Cloud Logging)-
SCION_SERVER_REQUEST_LOG_PATHWrite HTTP request logs to a file at this path(disabled)

For development, you can pipe logs directly to GCP Cloud Logging using the gcloud CLI.

  1. Install Google Cloud SDK
  2. Authenticate: gcloud auth login
  3. Set your project: gcloud config set project YOUR_PROJECT_ID
Terminal window
# Create a custom log stream for development
SCION_LOG_GCP=true scion server start --enable-hub 2>&1 | \
while read line; do
echo "$line" | gcloud logging write scion-dev-hub --payload-type=json
done

For long-running development sessions:

Terminal window
# Create a named pipe
mkfifo /tmp/scion-logs
# Start log forwarder in background
cat /tmp/scion-logs | while read line; do
echo "$line" | gcloud logging write scion-dev-hub --payload-type=json
done &
# Run server with logs to pipe
SCION_LOG_GCP=true scion server start --enable-hub > /tmp/scion-logs 2>&1

Navigate to Logging > Logs Explorer in the GCP Console and filter by:

logName="projects/YOUR_PROJECT/logs/scion-dev-hub"

Instead of piping stdout to gcloud or running a full OTel pipeline, you can send logs directly to Google Cloud Logging using the built-in client library. This works for both Hub and Runtime Broker servers.

  1. Authenticate with Application Default Credentials:

    Terminal window
    gcloud auth application-default login
  2. Set the required environment variables and start the server:

    Terminal window
    export SCION_CLOUD_LOGGING=true
    export SCION_GCP_PROJECT_ID="your-project-id"
    # Start Hub with direct Cloud Logging
    scion server start --enable-hub
    # Or start Broker
    scion server start --enable-runtime-broker
    # Or both
    scion server start --enable-hub --enable-runtime-broker
  3. Optionally customize the log name (defaults to scion):

    Terminal window
    export SCION_CLOUD_LOGGING_LOG_ID="scion-dev"

When SCION_CLOUD_LOGGING=true, the server creates a cloud.google.com/go/logging client that sends structured log entries to Cloud Logging as a background handler. Logs continue to appear on stdout as normal — Cloud Logging is additive, not a replacement.

The project ID is resolved in this order:

  1. SCION_GCP_PROJECT_ID environment variable
  2. GOOGLE_CLOUD_PROJECT environment variable
  3. Auto-detected from the environment (e.g., metadata server on GCE/Cloud Run)

Logs appear in Logging > Logs Explorer in the GCP Console. Filter by log name:

logName="projects/YOUR_PROJECT/logs/scion-server"

Or filter by component label:

logName="projects/YOUR_PROJECT/logs/scion-server"
labels.component="scion-hub"

Direct Cloud Logging can be used alongside other logging backends:

Terminal window
# Cloud Logging + GCP-formatted stdout + debug level
SCION_CLOUD_LOGGING=true \
SCION_GCP_PROJECT_ID="your-project-id" \
SCION_LOG_GCP=true \
SCION_LOG_LEVEL=debug \
scion server start --enable-hub

All three backends (stdout, OTel, Cloud Logging) operate independently and can be enabled simultaneously.

For more advanced setups, you can export logs via OpenTelemetry to GCP:

Terminal window
# Enable OTel log bridging
export SCION_OTEL_LOG_ENABLED=true
export SCION_OTEL_ENDPOINT="monitoring.googleapis.com:443"
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
scion server start --enable-hub

See the Observability guide for full OTel configuration.

Use --debug flag or SCION_LOG_LEVEL=debug for verbose output during development:

Terminal window
# Via flag
scion server start --enable-hub --debug
# Via environment
SCION_LOG_LEVEL=debug scion server start --enable-hub

Debug logging includes:

  • Request/response details
  • Internal state transitions
  • Detailed error context

The log component field reflects the server mode:

ModeComponent
Hub onlyscion-hub
Broker onlyscion-broker
Bothscion-server

In addition to component, each log entry includes a subsystem field that identifies the internal subsystem (e.g., hub.notifications, broker.agent-lifecycle). Use this for fine-grained filtering when debugging a specific area:

Terminal window
# Filter local GCP-formatted logs by subsystem with jq
SCION_LOG_GCP=true scion server start --enable-hub 2>&1 | \
jq 'select(.subsystem == "hub.auth")'
# Show all subsystem values in the log stream
SCION_LOG_GCP=true scion server start --enable-hub --enable-runtime-broker 2>&1 | \
jq -r '.subsystem // empty' | sort -u

See the Observability guide for the full list of subsystems and Cloud Logging query examples.

HTTP requests to the Hub, Runtime Broker, and Web server are logged as a dedicated structured stream using the google.logging.type.HttpRequest format. This stream is separate from application logs, making it easy to filter, query, and alert on request traffic.

Request logs are routed based on the server’s configuration:

ConditionDestination
SCION_SERVER_REQUEST_LOG_PATH is setJSON lines written to the specified file
SCION_CLOUD_LOGGING=trueSent to Cloud Logging under log name scion_request_log (separate from application logs in scion-server)
Background / piped mode (no file, no cloud)Written to stdout as JSON
--foreground mode (no file, no cloud)Suppressed — request logs do not appear on stdout in foreground mode to reduce noise

You can combine file and Cloud Logging output. When --foreground is set, file and Cloud Logging targets are still active — only stdout output is suppressed.

To write request logs to a file:

Terminal window
SCION_SERVER_REQUEST_LOG_PATH=/var/log/scion/requests.log scion server start

Each line is a JSON object:

{
"time": "2026-03-07T12:00:00.000Z",
"level": "INFO",
"msg": "Request completed",
"httpRequest": {
"requestMethod": "GET",
"requestUrl": "/api/v1/groves/my-project/agents",
"status": 200,
"responseSize": 1234,
"userAgent": "scion-cli/0.1.0",
"remoteIp": "192.168.1.1:54321",
"latency": "0.045s",
"protocol": "HTTP/1.1"
},
"component": "hub",
"grove_id": "my-project",
"agent_id": "",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
FieldDescription
httpRequest.requestMethodHTTP method (GET, POST, etc.)
httpRequest.requestUrlFull request URL
httpRequest.statusHTTP status code
httpRequest.responseSizeResponse body size in bytes
httpRequest.userAgentClient User-Agent header
httpRequest.remoteIpClient IP address and port
httpRequest.latencyRequest duration as "Xs" (e.g. "0.045s")
httpRequest.protocolHTTP protocol version
componentServer component: hub, broker, or web
grove_idGrove ID extracted from the URL path (if applicable)
agent_idAgent ID extracted from the URL path (if applicable)
request_idGenerated UUID for correlating logs within a request
trace_idTrace header value from X-Cloud-Trace-Context, traceparent, or X-Trace-ID (if present)

The request logging middleware generates a unique request_id (UUID) for every request. If the client sends a trace header (X-Cloud-Trace-Context, traceparent, or X-Trace-ID), it is also captured as trace_id.

Both request_id and trace_id are automatically attached to all application logs emitted during the request when using logging.Logger(ctx):

// In any handler, Logger(ctx) automatically includes request_id, trace_id, grove_id, agent_id
log := logging.Logger(r.Context())
log.Info("Processing agent", "name", agentName)
// Output includes: request_id=..., trace_id=..., grove_id=..., agent_id=...

When Cloud Logging is enabled, request logs appear under a separate log name (scion_request_log) from application logs (scion-server). This allows independent filtering:

-- All HTTP request logs
logName="projects/YOUR_PROJECT/logs/scion_request_log"
-- Slow requests (latency > 1s)
logName="projects/YOUR_PROJECT/logs/scion_request_log"
httpRequest.latency > "1s"
-- Failed requests to a specific grove
logName="projects/YOUR_PROJECT/logs/scion_request_log"
httpRequest.status >= 400
labels.grove_id = "my-grove"
-- Correlate a request with its application logs
logName="projects/YOUR_PROJECT/logs/scion-server" OR logName="projects/YOUR_PROJECT/logs/scion_request_log"
jsonPayload.request_id = "550e8400-e29b-41d4-a716-446655440000"
Terminal window
# Pretty-print request logs from file
cat /var/log/scion/requests.log | jq .
# Show only failed requests
cat /var/log/scion/requests.log | jq 'select(.httpRequest.status >= 400)'
# Top endpoints by request count
cat /var/log/scion/requests.log | jq -r '.httpRequest.requestUrl' | sort | uniq -c | sort -rn | head
# Average latency per endpoint
cat /var/log/scion/requests.log | jq -r '[.httpRequest.requestUrl, .httpRequest.latency] | @tsv'

To verify your logging configuration without sending to GCP:

Terminal window
# Pretty-print GCP-formatted logs with jq
SCION_LOG_GCP=true scion server start --enable-hub 2>&1 | jq .
# Filter for specific severity
SCION_LOG_GCP=true scion server start --enable-hub 2>&1 | \
jq 'select(.severity == "ERROR")'
# Extract just messages
SCION_LOG_GCP=true scion server start --enable-hub 2>&1 | \
jq -r '.message'