Cloud Run services don’t send logs to Cloud Logging; Cloud Logging pulls them from Cloud Run’s execution environment.

Here’s how a Cloud Run service generates logs and how they end up in Cloud Logging:

When your Cloud Run service runs, any output written to stdout or stderr by your container is captured. Cloud Run’s infrastructure then automatically forwards these captured logs to Cloud Logging. This is the default behavior, so you don’t typically need to configure anything to start sending logs.

Let’s see this in action. Imagine you have a simple Python application in a main.py file:

import os
import time

def main():
    print("Starting my Cloud Run service...")
    for i in range(5):
        print(f"Processing step {i+1}...")
        time.sleep(2)
    print("Service finished successfully.")

if __name__ == "__main__":
    main()

You’d containerize this using a Dockerfile:

FROM python:3.9-slim

WORKDIR /app
COPY main.py .

RUN pip install --no-cache-dir -r requirements.txt # Assuming requirements.txt is empty for this example

CMD ["python", "main.py"]

After building and deploying this to Cloud Run, you can view its logs. Go to the Google Cloud Console, navigate to your Cloud Run service, and click on the "Logs" tab. You’ll see output like this:

2023-10-27T10:30:00.123Z stdout: Starting my Cloud Run service...
2023-10-27T10:30:02.456Z stdout: Processing step 1...
2023-10-27T10:30:04.789Z stdout: Processing step 2...
2023-10-27T10:30:07.111Z stdout: Processing step 3...
2023-10-27T10:30:09.444Z stdout: Processing step 4...
2023-10-27T10:30:11.777Z stdout: Processing step 5...
2023-10-27T10:30:13.999Z stdout: Service finished successfully.

Cloud Logging is the central nervous system for all your Google Cloud service logs. It ingests, stores, and indexes logs from various sources, including Cloud Run, Compute Engine, Kubernetes Engine, and more. For Cloud Run, it’s a managed service, meaning you don’t provision or manage any logging infrastructure yourself. The integration is seamless: container logs are automatically associated with the specific Cloud Run service and revision that generated them.

The problem this solves is observability: understanding what your application is doing, diagnosing errors, and monitoring performance. Without a centralized logging system, debugging issues across distributed services would be a nightmare. Cloud Logging provides a unified interface to search, filter, and analyze these logs.

Internally, Cloud Run uses the standard container logging mechanism. When a container starts, its stdout and stderr streams are captured. Cloud Run’s agent within the execution environment then pipes these streams to a logging agent that’s part of the Google Cloud infrastructure. This agent formats the logs (adding timestamps, log levels, and source information) and sends them to Cloud Logging via the Cloud Logging API.

The key levers you control are what your application writes to stdout and stderr. Structured logging, where you output JSON-formatted messages, is highly recommended. This allows Cloud Logging to parse fields automatically, making your logs more searchable and actionable. For example, instead of:

print("Request processed successfully.")

You’d write:

import json
log_data = {
    "message": "Request processed successfully.",
    "severity": "INFO",
    "httpRequest": {
        "requestMethod": "GET",
        "requestUrl": "/items/123",
        "status": 200
    },
    "trace": "projects/my-project/traces/my-trace-id"
}
print(json.dumps(log_data))

This structured log can be queried by httpRequest.status or message="Request processed successfully." directly in Cloud Logging.

To query these logs, you’ll use the Cloud Logging query interface. The basic structure looks like this:

resource.type="cloud_run_revision"
resource.labels.service_name="YOUR_SERVICE_NAME"
resource.labels.revision_name="YOUR_REVISION_NAME"
log_id="stdout" # or "stderr"

For example, to find all error logs from a specific Cloud Run service:

resource.type="cloud_run_revision"
resource.labels.service_name="my-app-service"
severity=ERROR

You can also search for specific messages or fields within structured logs:

resource.type="cloud_run_revision"
resource.labels.service_name="my-app-service"
jsonPayload.message:"User authentication failed"

The specific labels you use depend on how your Cloud Run service is configured. service_name is usually straightforward, but revision_name will change with each deployment. You can also filter by location if your service is deployed in a specific region.

When you’re debugging a Cloud Run service that’s crashing immediately on startup, the most common pitfall is that the logs themselves might not be making it to Cloud Logging if the container environment isn’t stable enough to initialize the logging agent. In such scenarios, your container might be exiting before its stdout/stderr can be reliably captured and sent. The solution is often to ensure your container’s entrypoint and application startup are robust, and if absolutely necessary, to configure a custom logging agent or sidecar pattern, although this is rarely needed for basic stdout/stderr logging.

Want structured learning?

Take the full Cloud-run course →