Skip to content

Commit 24b558a

Browse files
authored
Merge pull request #235 from dtinit/feat/Cloud-Run-trace-correlation-contextvars
feat: Implement Cloud Run Logging trace correlation with contextvars
2 parents d2c276f + 448b312 commit 24b558a

File tree

3 files changed

+38
-149
lines changed

3 files changed

+38
-149
lines changed

testbed/core/utils/logging_filters.py

Lines changed: 0 additions & 77 deletions
This file was deleted.
Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,55 @@
11
"""
2-
This module provides functions for Google Cloud Logging handlers,
3-
properly isolates Cloud-specific dependencies to production/staging
2+
Uses Google's setup_logging() which automatically:
3+
- Detects Django framework
4+
- Extracts X-Cloud-Trace-Context headers from requests
5+
- Adds trace/spanId to all log entries
6+
- Groups logs by request in Cloud Logging
7+
8+
References:
9+
- https://cloud.google.com/python/docs/reference/logging/latest/auto-trace-span-extraction
10+
- https://cloud.google.com/trace/docs/trace-log-integration
11+
- https://docs.cloud.google.com/python/docs/reference/logging/latest/client
12+
- https://github.com/googleapis/python-logging/blob/main/google/cloud/logging_v2/handlers/handlers.py
413
"""
514

615
import os
716
import logging
817

18+
logger = logging.getLogger(__name__)
19+
920

10-
def get_cloud_logging_handler():
21+
def setup_cloud_logging():
1122
"""
12-
1. Checks if Cloud Logging is enabled (USE_GCLOUD_LOGGING env var)
13-
2. Imports google-cloud-logging packages ONLY if enabled
14-
3. Returns configured handler with trace correlation filter
15-
23+
Initialize Google Cloud Logging with automatic trace correlation.
24+
1625
Returns:
17-
logging.Handler: CloudLoggingHandler configured with custom logName and
18-
trace filter, or NullHandler if Cloud Logging is disabled
19-
26+
bool: True if Cloud Logging was successfully initialized, False if not
27+
2028
Environment Variables:
2129
USE_GCLOUD_LOGGING: Set to "1" to enable Cloud Logging
2230
GOOGLE_CLOUD_PROJECT: GCP project ID (auto-detected on Cloud Run)
2331
"""
24-
2532
if os.environ.get('USE_GCLOUD_LOGGING', '0') != '1':
26-
return logging.NullHandler()
27-
28-
# Import Google Cloud packages only when Cloud Logging is enabled
29-
# This ensures dev/CI/test environments never import these packages
33+
logger.info(
34+
"Cloud Logging disabled (USE_GCLOUD_LOGGING != 1)"
35+
)
36+
return False
37+
3038
try:
31-
from google.cloud.logging import Client as CloudLoggingClient
32-
from google.cloud.logging.handlers import CloudLoggingHandler
33-
from testbed.core.utils.logging_filters import CloudRunTraceFilter
34-
35-
client = CloudLoggingClient()
36-
37-
cloud_logging_handler = CloudLoggingHandler(
38-
client,
39-
name="testbed"
39+
import google.cloud.logging
40+
41+
client = google.cloud.logging.Client()
42+
43+
client.setup_logging(log_level=logging.INFO)
44+
45+
logger.info(
46+
"Cloud Logging initialized with automatic trace correlation"
4047
)
41-
42-
cloud_logging_handler.addFilter(CloudRunTraceFilter())
43-
44-
return cloud_logging_handler
45-
48+
return True
49+
4650
except Exception as e:
47-
logger = logging.getLogger(__name__)
4851
logger.warning(
4952
f"Failed to initialize Cloud Logging: {e}. "
50-
"Falling back to NullHandler. Logs will not appear in Cloud Logging."
53+
"Falling back to console logging."
5154
)
52-
return logging.NullHandler()
55+
return False

testbed/settings/production.py

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# ruff: noqa: F405, F403
2-
import sys
32
from google.oauth2 import service_account
43
from .base import *
54

@@ -58,43 +57,7 @@
5857
EMAIL_HOST_USER = "[email protected]"
5958
EMAIL_HOST_PASSWORD = env.str('EMAIL_HOST_PASSWORD')
6059

61-
# This section configures Google Cloud Logging for Cloud Run environments.
60+
# Google Cloud Logging with automatic trace correlation.
6261
# Enabled via USE_GCLOUD_LOGGING=1 environment variable.
63-
if os.environ.get('USE_GCLOUD_LOGGING', '0') == '1':
64-
65-
LOGGING["handlers"]["cloud_logging"] = {
66-
"()": "testbed.core.utils.logging_utils.get_cloud_logging_handler",
67-
}
68-
69-
LOGGING["root"] = {
70-
"handlers": ["cloud_logging"],
71-
"level": "INFO",
72-
}
73-
74-
LOGGING["loggers"]["django"]["handlers"] = ["cloud_logging"]
75-
LOGGING["loggers"]["django"]["propagate"] = False
76-
77-
LOGGING["loggers"]["testbed"]["handlers"] = ["cloud_logging"]
78-
LOGGING["loggers"]["testbed"]["propagate"] = False
79-
80-
LOGGING["loggers"]["django.request"] = {
81-
"handlers": ["cloud_logging"],
82-
"level": "INFO",
83-
"propagate": False,
84-
}
85-
86-
LOGGING["loggers"]["gunicorn"] = {
87-
"handlers": ["cloud_logging"],
88-
"level": "INFO",
89-
"propagate": False,
90-
}
91-
LOGGING["loggers"]["gunicorn.error"] = {
92-
"handlers": ["cloud_logging"],
93-
"level": "INFO",
94-
"propagate": False,
95-
}
96-
LOGGING["loggers"]["gunicorn.access"] = {
97-
"handlers": ["cloud_logging"],
98-
"level": "INFO",
99-
"propagate": False,
100-
}
62+
from testbed.core.utils.logging_utils import setup_cloud_logging
63+
setup_cloud_logging()

0 commit comments

Comments
 (0)