Cloud Logging¶
Validibot uses Google Cloud Logging for centralized log management in production. Cloud Run automatically captures stdout/stderr and sends it to Cloud Logging, where logs are indexed, searchable, and retained for 30 days by default.
How It Works¶
When Django runs on Cloud Run, all output to stdout is captured by Cloud Logging. We use structured JSON logging so that individual fields (severity, module, message, etc.) become queryable in the Cloud Console.
The flow:
Without JSON formatting, logs appear as plain text strings. With JSON formatting, Cloud Logging parses the JSON and indexes each field, enabling powerful filtering.
What We Log¶
Automatic Logs¶
Cloud Run automatically logs:
- Request logs: Every HTTP request with status code, latency, URL
- Container lifecycle: Startup, shutdown, cold starts
- System errors: OOM, crashes, timeouts
Application Logs¶
Our Django app logs:
- User actions: Login, logout, organization changes
- Validation runs: Creation, status changes, completion
- API requests: Authenticated API calls
- Errors: Exceptions with stack traces (also sent to Sentry)
Viewing Logs¶
Cloud Console¶
The easiest way to view logs is the Cloud Console:
- Go to Cloud Logging
- Select your project
- Use the query editor to filter
CLI Quick Commands¶
# Recent logs from web service
just gcp logs
# Or directly with gcloud
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=$GCP_APP_NAME" --limit=50
# Logs from worker service
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=$GCP_APP_NAME-worker" --limit=50
Searching Logs¶
Cloud Logging uses a query language to filter logs. Here are common queries:
By Severity¶
By Service¶
By Module/Logger¶
By Message Content¶
By Time Range¶
timestamp>="2025-12-01T00:00:00Z"
timestamp>="2025-12-01T00:00:00Z" timestamp<="2025-12-02T00:00:00Z"
Combined Queries¶
Find validation errors in the last hour:
resource.labels.service_name="$GCP_APP_NAME-worker"
severity>=ERROR
jsonPayload.name:"validations"
timestamp>="-1h"
Find requests for a specific validation run:
Structured Logging in Code¶
When logging in application code, include structured data for better searchability:
import logging
logger = logging.getLogger(__name__)
# Basic logging
logger.info("Validation started")
# With extra fields (become queryable in Cloud Logging)
logger.info(
"Validation started",
extra={
"run_id": str(run.id),
"workflow_id": str(run.workflow_id),
"user_id": str(run.user_id),
}
)
The extra fields are merged into the JSON output and become searchable:
Log Levels¶
| Level | Usage |
|---|---|
DEBUG |
Detailed diagnostic info (disabled in production) |
INFO |
Normal operations, milestones |
WARNING |
Unexpected but handled conditions |
ERROR |
Errors that need attention |
CRITICAL |
System failures |
In production, the root logger is set to INFO. Individual loggers can be adjusted in production.py.
Retention and Costs¶
- Default retention: 30 days
- Cost: Cloud Logging has a generous free tier (50 GB/month)
- Export: Logs can be exported to Cloud Storage or BigQuery for longer retention
Debugging a Failed Validation¶
When a validation fails, here's how to investigate:
-
Find the error in Cloud Logging:
-
Get the run_id from the error
-
Find all logs for that run:
-
Check Cloud Run Job logs (for validator execution):