Skip to content

Commit b7216a4

Browse files
committed
add missing file
1 parent 918032d commit b7216a4

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed

api/src/utils/logging_utils.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import logging
2+
import os
3+
import threading
4+
5+
from google.cloud.logging.handlers import CloudLoggingHandler
6+
import google.cloud.logging
7+
from google.cloud.logging_v2 import Client
8+
9+
from middleware.request_context import get_request_context
10+
from utils.config import get_config, PROJECT_ID
11+
12+
13+
def get_env_logging_level():
14+
"""
15+
Get the logging level from the environment via OS variable LOGGING_LEVEL. Returns INFO if not set.
16+
"""
17+
return os.getenv("LOGGING_LEVEL", "INFO")
18+
19+
20+
def is_local_env():
21+
return os.getenv("K_SERVICE") is None
22+
23+
lock = threading.Lock()
24+
class Logger:
25+
"""
26+
GCP-friendly logger: structured JSON output, works locally or in production.
27+
"""
28+
29+
def __init__(self, name: str):
30+
self.logger = logging.getLogger(name)
31+
# self.logger.setLevel(get_env_logging_level())
32+
# self.logger.handlers.clear()
33+
34+
# formatter = jsonlogger.JsonFormatter(
35+
# '%(asctime)s %(levelname)s %(name)s %(message)s'
36+
# )
37+
# logging.basicConfig(level=get_env_logging_level())
38+
# if not is_local_env():
39+
# handler = logging.StreamHandler()
40+
# else:
41+
# try:
42+
# client = google.cloud.logging.Client()
43+
# client.setup_logging()
44+
# # self.logger.info("GCP logging client initialized")
45+
# # handler = CloudLoggingHandler(client)
46+
# except Exception as e:
47+
# # fallback to stdout if cloud client fails
48+
# # self.logger.error(f"GCP logging failed, using fallback: {e}")
49+
# handler = logging.StreamHandler()
50+
51+
# handler.setFormatter(formatter)
52+
# self.logger.addHandler(handler)
53+
54+
# Also configure SQLAlchemy to use this logger
55+
# self.setup_sqlalchemy_logger(handler)
56+
# self.logger.info("Logger initialized")
57+
58+
@staticmethod
59+
def init_logger():
60+
"""
61+
Initializes the logger
62+
"""
63+
with lock:
64+
if hasattr(Logger, "initialized"):
65+
return
66+
logging.basicConfig(level=get_env_logging_level())
67+
if is_local_env():
68+
# Use the default logging handler
69+
logging.info("Using default logging handler")
70+
return
71+
try:
72+
client = google.cloud.logging_v2.Client()
73+
client.get_default_handler()
74+
client.setup_logging()
75+
logging.info("GCP logging client initialized")
76+
except Exception as error:
77+
# This might happen when the GCP authorization credentials are not available.
78+
# Example, when running the tests locally
79+
logging.error(f"Error initializing the logger: {error}")
80+
Logger.initialized = True
81+
82+
def get_logger(self):
83+
return self.logger
84+
85+
86+
class TraceLogger:
87+
def __init__(self, logger: logging.Logger, project_id: str, trace_id: str, span_id: str):
88+
self._logger = logger
89+
self._trace_id = None
90+
self._span_id = None
91+
self._project_id = project_id
92+
self._trace_id = trace_id
93+
self._span_id = span_id
94+
95+
def _inject_trace(self, extra):
96+
if not self._trace_id or not self._span_id:
97+
return extra or {}
98+
99+
trace_fields = {
100+
"logging.googleapis.com/trace": f"projects/{self._project_id}/traces/{self._trace_id}",
101+
"logging.googleapis.com/spanId": self._span_id,
102+
"logging.googleapis.com/trace_sampled": True,
103+
}
104+
if extra:
105+
trace_fields.update(extra)
106+
return trace_fields
107+
108+
def info(self, msg, *args, extra=None, **kwargs):
109+
return self._logger.info(msg, *args, extra=self._inject_trace(extra), **kwargs)
110+
111+
def error(self, msg, *args, extra=None, **kwargs):
112+
return self._logger.error(msg, *args, extra=self._inject_trace(extra), **kwargs)
113+
114+
def warning(self, msg, *args, extra=None, **kwargs):
115+
return self._logger.warning(msg, *args, extra=self._inject_trace(extra), **kwargs)
116+
117+
def debug(self, msg, *args, extra=None, **kwargs):
118+
return self._logger.debug(msg, *args, extra=self._inject_trace(extra), **kwargs)
119+
120+
def exception(self, msg, *args, extra=None, **kwargs):
121+
return self._logger.exception(msg, *args, extra=self._inject_trace(extra), **kwargs)
122+
123+
124+
def new_logger(name: str):
125+
"""
126+
Create a new logger with the given name.
127+
"""
128+
Logger.init_logger()
129+
logger = logging.getLogger(name)
130+
logger.setLevel(get_env_logging_level())
131+
request_context = get_request_context()
132+
# if not request_context:
133+
# return logger
134+
return TraceLogger(logger, get_config(PROJECT_ID), request_context.get("trace_id"), request_context.get("span_id"))
135+
136+

0 commit comments

Comments
 (0)