sunholo 0.129.0__py3-none-any.whl → 0.129.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sunholo/custom_logging.py +182 -26
- {sunholo-0.129.0.dist-info → sunholo-0.129.1.dist-info}/METADATA +1 -1
- {sunholo-0.129.0.dist-info → sunholo-0.129.1.dist-info}/RECORD +7 -8
- sunholo/traced_logging.py +0 -70
- {sunholo-0.129.0.dist-info → sunholo-0.129.1.dist-info}/WHEEL +0 -0
- {sunholo-0.129.0.dist-info → sunholo-0.129.1.dist-info}/entry_points.txt +0 -0
- {sunholo-0.129.0.dist-info → sunholo-0.129.1.dist-info}/licenses/LICENSE.txt +0 -0
- {sunholo-0.129.0.dist-info → sunholo-0.129.1.dist-info}/top_level.txt +0 -0
sunholo/custom_logging.py
CHANGED
@@ -89,53 +89,100 @@ class GoogleCloudLogging:
|
|
89
89
|
}
|
90
90
|
return None
|
91
91
|
|
92
|
+
def update_trace_id(self, trace_id):
|
93
|
+
"""
|
94
|
+
Updates the trace ID to be included in all logs.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
trace_id (str): The trace ID to add to all logs.
|
98
|
+
"""
|
99
|
+
self.trace_id = trace_id
|
100
|
+
|
101
|
+
def _append_trace_id(self, log_text):
|
102
|
+
"""
|
103
|
+
Appends trace ID to log text if available.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
log_text (str): The log message.
|
107
|
+
|
108
|
+
Returns:
|
109
|
+
str: Log message with trace ID prefixed if available.
|
110
|
+
"""
|
111
|
+
if hasattr(self, 'trace_id') and self.trace_id:
|
112
|
+
return f"{self.logger_name}-{self.trace_id} {log_text}"
|
113
|
+
return log_text
|
114
|
+
|
115
|
+
def _add_trace_to_struct(self, log_struct):
|
116
|
+
"""
|
117
|
+
Adds trace ID to structured log data if available.
|
118
|
+
|
119
|
+
Args:
|
120
|
+
log_struct (dict): The structured log data.
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
dict: Log structure with trace ID added if available.
|
124
|
+
"""
|
125
|
+
if not isinstance(log_struct, dict):
|
126
|
+
return log_struct
|
127
|
+
|
128
|
+
if hasattr(self, 'trace_id') and self.trace_id:
|
129
|
+
return {**log_struct, "trace_id": self.trace_id}
|
130
|
+
return log_struct
|
131
|
+
|
92
132
|
def structured_log(self, log_text=None, log_struct=None, logger_name=None, severity="INFO"):
|
93
133
|
"""
|
94
134
|
Writes log entries to the specified logger as either text or structured data.
|
95
|
-
|
135
|
+
|
96
136
|
Args:
|
97
137
|
log_text (str, optional): The log message as a text string. Defaults to None.
|
98
138
|
log_struct (dict, optional): The log message as a dictionary for structured log. Defaults to None.
|
99
|
-
logger_name (str, optional): The name of the logger to which to write the log entries.
|
100
|
-
|
139
|
+
logger_name (str, optional): The name of the logger to which to write the log entries.
|
140
|
+
e.g. logName="run.googleapis.com%2Fstderr"
|
101
141
|
severity (str, optional): The severity level of the log entry. Defaults to "INFO".
|
102
142
|
"""
|
103
|
-
|
104
143
|
from .utils.version import sunholo_version
|
105
|
-
|
106
|
-
log_text
|
107
|
-
|
144
|
+
|
145
|
+
if log_text is not None:
|
146
|
+
log_text = f"[{sunholo_version()}] {log_text}"
|
147
|
+
# Apply trace ID to log text
|
148
|
+
log_text = self._append_trace_id(log_text)
|
149
|
+
|
150
|
+
if log_struct is not None:
|
151
|
+
# Add trace ID to structured log
|
152
|
+
log_struct = self._add_trace_to_struct(log_struct)
|
153
|
+
|
108
154
|
if not logger_name and not self.logger_name:
|
109
155
|
raise ValueError("Must provide a logger name e.g. 'run.googleapis.com%2Fstderr'")
|
110
156
|
|
111
157
|
from .utils.gcp import is_running_on_gcp, is_gcp_logged_in
|
112
158
|
if not is_running_on_gcp() and not is_gcp_logged_in():
|
159
|
+
import logging as log
|
113
160
|
log.basicConfig(level=log.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
114
161
|
if log_text:
|
115
162
|
log.info(f"[{severity}][{logger_name or self.logger_name}][{self.version}] - {log_text}")
|
116
163
|
elif log_struct:
|
117
164
|
log.info(f"[{severity}][{logger_name or self.logger_name}][{self.version}] - {str(log_struct)}")
|
118
|
-
|
165
|
+
return
|
166
|
+
|
119
167
|
logger = self.client.logger(logger_name or self.logger_name)
|
120
|
-
|
168
|
+
|
121
169
|
caller_info = self._get_caller_info()
|
122
|
-
|
170
|
+
|
123
171
|
if log_text:
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
172
|
+
try:
|
173
|
+
turn_to_text = str(log_text)
|
174
|
+
logger.log_text(turn_to_text, severity=severity, source_location=caller_info)
|
175
|
+
except Exception as err:
|
176
|
+
print(f"Could not log this: {log_text=} - {str(err)}")
|
177
|
+
|
178
|
+
if log_struct:
|
179
|
+
if not isinstance(log_struct, dict):
|
180
|
+
print(f"Warning: log_struct must be a dictionary, got {type(log_struct)}")
|
128
181
|
else:
|
129
182
|
try:
|
130
|
-
|
131
|
-
logger.log_text(turn_to_text, severity=severity, source_location=caller_info)
|
183
|
+
logger.log_struct(log_struct, severity=severity, source_location=caller_info)
|
132
184
|
except Exception as err:
|
133
|
-
print(f"Could not log
|
134
|
-
|
135
|
-
elif log_struct:
|
136
|
-
if not isinstance(log_struct, dict):
|
137
|
-
raise ValueError("log_struct must be a dictionary.")
|
138
|
-
logger.log_struct(log_struct, severity=severity, source_location=caller_info)
|
185
|
+
print(f"Could not log struct: {log_struct=} - {str(err)}")
|
139
186
|
|
140
187
|
def debug(self, log_text=None, log_struct=None):
|
141
188
|
|
@@ -217,6 +264,53 @@ def setup_logging(logger_name=None, log_level=logging.INFO, project_id=None):
|
|
217
264
|
# Now you can use Python's logging module as usual
|
218
265
|
import logging
|
219
266
|
log.info('This is an info message that will be sent to Google Cloud log.')
|
267
|
+
|
268
|
+
# Basic structured logging
|
269
|
+
log.info(log_struct={"action": "user_login", "user_id": "12345"})
|
270
|
+
|
271
|
+
# Structured logging with trace ID
|
272
|
+
log.update_trace_id("abc-123")
|
273
|
+
log.info(log_struct={"action": "process_started", "file_count": 42})
|
274
|
+
# This will include trace_id: "abc-123" in the logged structure
|
275
|
+
|
276
|
+
# Logging with both text and structure
|
277
|
+
log.info(
|
278
|
+
log_text="Processing completed successfully",
|
279
|
+
log_struct={"duration_ms": 1234, "items_processed": 100}
|
280
|
+
)
|
281
|
+
|
282
|
+
# Logging error with structured context
|
283
|
+
try:
|
284
|
+
# Some operation
|
285
|
+
process_data()
|
286
|
+
except Exception as e:
|
287
|
+
log.error(
|
288
|
+
log_text=f"Error processing data: {str(e)}",
|
289
|
+
log_struct={
|
290
|
+
"error_type": type(e).__name__,
|
291
|
+
"file_name": "example.csv",
|
292
|
+
"line_number": 42
|
293
|
+
}
|
294
|
+
)
|
295
|
+
|
296
|
+
# More complex structured logging
|
297
|
+
log.info(log_struct={
|
298
|
+
"request": {
|
299
|
+
"method": "POST",
|
300
|
+
"path": "/api/data",
|
301
|
+
"user_agent": "Mozilla/5.0...",
|
302
|
+
"ip": "192.168.1.1"
|
303
|
+
},
|
304
|
+
"response": {
|
305
|
+
"status_code": 200,
|
306
|
+
"processing_time_ms": 345,
|
307
|
+
"bytes_sent": 1024
|
308
|
+
},
|
309
|
+
"metadata": {
|
310
|
+
"version": "1.2.3",
|
311
|
+
"environment": "production"
|
312
|
+
}
|
313
|
+
})
|
220
314
|
|
221
315
|
Note:
|
222
316
|
This function requires that the 'google-cloud-logging' library is installed and
|
@@ -252,9 +346,22 @@ def setup_logging(logger_name=None, log_level=logging.INFO, project_id=None):
|
|
252
346
|
|
253
347
|
return log
|
254
348
|
|
255
|
-
|
256
|
-
|
257
|
-
|
349
|
+
# for debugging
|
350
|
+
def safe_log_struct(log, severity, message, struct):
|
351
|
+
try:
|
352
|
+
if severity == "INFO":
|
353
|
+
log.info(log_text=message, log_struct=struct)
|
354
|
+
elif severity == "ERROR":
|
355
|
+
log.error(log_text=message, log_struct=struct)
|
356
|
+
# Add other severity levels as needed
|
357
|
+
except Exception as e:
|
358
|
+
print(f"Logging error: {e}")
|
359
|
+
print(f"Failed to log structure: {struct}")
|
360
|
+
# Fallback to simple text logging
|
361
|
+
if severity == "INFO":
|
362
|
+
log.info(f"{message} (struct logging failed)")
|
363
|
+
elif severity == "ERROR":
|
364
|
+
log.error(f"{message} (struct logging failed)")
|
258
365
|
|
259
366
|
def log_folder_location(folder_name):
|
260
367
|
# Get the current working directory
|
@@ -277,4 +384,53 @@ def get_logger():
|
|
277
384
|
_logger = setup_logging("sunholo")
|
278
385
|
return _logger
|
279
386
|
|
280
|
-
log = get_logger()
|
387
|
+
log = get_logger()
|
388
|
+
|
389
|
+
"""
|
390
|
+
# Basic structured logging
|
391
|
+
log.info(log_struct={"action": "user_login", "user_id": "12345"})
|
392
|
+
|
393
|
+
# Structured logging with trace ID
|
394
|
+
log.update_trace_id("abc-123")
|
395
|
+
log.info(log_struct={"action": "process_started", "file_count": 42})
|
396
|
+
# This will include trace_id: "abc-123" in the logged structure
|
397
|
+
|
398
|
+
# Logging with both text and structure
|
399
|
+
log.info(
|
400
|
+
log_text="Processing completed successfully",
|
401
|
+
log_struct={"duration_ms": 1234, "items_processed": 100}
|
402
|
+
)
|
403
|
+
|
404
|
+
# Logging error with structured context
|
405
|
+
try:
|
406
|
+
# Some operation
|
407
|
+
process_data()
|
408
|
+
except Exception as e:
|
409
|
+
log.error(
|
410
|
+
log_text=f"Error processing data: {str(e)}",
|
411
|
+
log_struct={
|
412
|
+
"error_type": type(e).__name__,
|
413
|
+
"file_name": "example.csv",
|
414
|
+
"line_number": 42
|
415
|
+
}
|
416
|
+
)
|
417
|
+
|
418
|
+
# More complex structured logging
|
419
|
+
log.info(log_struct={
|
420
|
+
"request": {
|
421
|
+
"method": "POST",
|
422
|
+
"path": "/api/data",
|
423
|
+
"user_agent": "Mozilla/5.0...",
|
424
|
+
"ip": "192.168.1.1"
|
425
|
+
},
|
426
|
+
"response": {
|
427
|
+
"status_code": 200,
|
428
|
+
"processing_time_ms": 345,
|
429
|
+
"bytes_sent": 1024
|
430
|
+
},
|
431
|
+
"metadata": {
|
432
|
+
"version": "1.2.3",
|
433
|
+
"environment": "production"
|
434
|
+
}
|
435
|
+
})
|
436
|
+
"""
|
@@ -1,7 +1,6 @@
|
|
1
1
|
sunholo/__init__.py,sha256=InRbX4V0-qdNHo9zYH3GEye7ASLR6LX8-SMvPV4Jsaw,1212
|
2
|
-
sunholo/custom_logging.py,sha256=
|
2
|
+
sunholo/custom_logging.py,sha256=37oyAGc8PWqxelXX_LB298HgiwBlYfJrHyk4R4_li6o,17080
|
3
3
|
sunholo/langchain_types.py,sha256=uZ4zvgej_f7pLqjtu4YP7qMC_eZD5ym_5x4pyvA1Ih4,1834
|
4
|
-
sunholo/traced_logging.py,sha256=TaAK9gFQ63h700dXfCK-nLcyMvh00N-oZ-xlz4VAZ_4,2891
|
5
4
|
sunholo/agents/__init__.py,sha256=X2I3pPkGeKWjc3d0QgSpkTyqD8J8JtrEWqwrumf1MMc,391
|
6
5
|
sunholo/agents/chat_history.py,sha256=Gph_CdlP2otYnNdR1q1Umyyyvcad2F6K3LxU5yBQ9l0,5387
|
7
6
|
sunholo/agents/dispatch_to_qa.py,sha256=NHihwAoCJ5_Lk11e_jZnucVUGQyZHCB-YpkfMHBCpQk,8882
|
@@ -169,9 +168,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
|
|
169
168
|
sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
|
170
169
|
sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
|
171
170
|
sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
|
172
|
-
sunholo-0.129.
|
173
|
-
sunholo-0.129.
|
174
|
-
sunholo-0.129.
|
175
|
-
sunholo-0.129.
|
176
|
-
sunholo-0.129.
|
177
|
-
sunholo-0.129.
|
171
|
+
sunholo-0.129.1.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
172
|
+
sunholo-0.129.1.dist-info/METADATA,sha256=1fAuA2lDi15g88z6xm0RR7vKAnhEJTCpr6nOEJ9Ly9I,10084
|
173
|
+
sunholo-0.129.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
174
|
+
sunholo-0.129.1.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
175
|
+
sunholo-0.129.1.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
176
|
+
sunholo-0.129.1.dist-info/RECORD,,
|
sunholo/traced_logging.py
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
from .custom_logging import setup_logging, GoogleCloudLogging
|
2
|
-
|
3
|
-
class TracedGoogleCloudLogging(GoogleCloudLogging):
|
4
|
-
"""Extends GoogleCloudLogging with trace ID functionality."""
|
5
|
-
|
6
|
-
def __init__(self, project_id=None, log_level=None, logger_name=None, trace_id=None):
|
7
|
-
super().__init__(project_id, log_level, logger_name)
|
8
|
-
self.trace_id = trace_id
|
9
|
-
|
10
|
-
def update_trace_id(self, trace_id):
|
11
|
-
"""Update the trace ID to be included in all logs."""
|
12
|
-
self.trace_id = trace_id
|
13
|
-
|
14
|
-
def _get_trace_prefix(self):
|
15
|
-
"""Get the trace prefix if a trace ID is set."""
|
16
|
-
return f"aitana-{self.trace_id} " if self.trace_id else ""
|
17
|
-
|
18
|
-
def structured_log(self, log_text=None, log_struct=None, logger_name=None, severity="INFO"):
|
19
|
-
"""Override to add trace ID to logs."""
|
20
|
-
if log_text and self.trace_id:
|
21
|
-
log_text = f"{self._get_trace_prefix()}{log_text}"
|
22
|
-
|
23
|
-
if log_struct and self.trace_id:
|
24
|
-
if isinstance(log_struct, dict):
|
25
|
-
log_struct = {**log_struct, "trace_id": self.trace_id}
|
26
|
-
|
27
|
-
# Call the parent method with the modified parameters
|
28
|
-
super().structured_log(log_text, log_struct, logger_name, severity)
|
29
|
-
|
30
|
-
def setup_traced_logging(logger_name=None, log_level=None, project_id=None, trace_id=None):
|
31
|
-
"""Sets up traced logging that includes a trace ID in all logs."""
|
32
|
-
# First get the base logger from the original setup_logging
|
33
|
-
base_logger = setup_logging(logger_name, log_level, project_id)
|
34
|
-
|
35
|
-
# If it's a GoogleCloudLogging instance, wrap it with our traced version
|
36
|
-
if isinstance(base_logger, GoogleCloudLogging):
|
37
|
-
traced_logger = TracedGoogleCloudLogging(
|
38
|
-
project_id=base_logger.project_id,
|
39
|
-
log_level=base_logger.log_level,
|
40
|
-
logger_name=base_logger.logger_name,
|
41
|
-
trace_id=trace_id
|
42
|
-
)
|
43
|
-
traced_logger.client = base_logger.client # Reuse the client
|
44
|
-
return traced_logger
|
45
|
-
|
46
|
-
# For standard Python logging, we can add a filter
|
47
|
-
import logging
|
48
|
-
class TraceFilter(logging.Filter):
|
49
|
-
def __init__(self, trace_id=None):
|
50
|
-
super().__init__()
|
51
|
-
self.trace_id = trace_id
|
52
|
-
|
53
|
-
def update_trace_id(self, trace_id):
|
54
|
-
self.trace_id = trace_id
|
55
|
-
|
56
|
-
def filter(self, record):
|
57
|
-
if self.trace_id:
|
58
|
-
prefix = f"aitana-{self.trace_id} "
|
59
|
-
if not record.msg.startswith(prefix):
|
60
|
-
record.msg = f"{prefix}{record.msg}"
|
61
|
-
return True
|
62
|
-
|
63
|
-
# Add the trace filter to the logger
|
64
|
-
trace_filter = TraceFilter(trace_id)
|
65
|
-
base_logger.addFilter(trace_filter)
|
66
|
-
|
67
|
-
# Add the update method to the standard logger
|
68
|
-
base_logger.update_trace_id = trace_filter.update_trace_id
|
69
|
-
|
70
|
-
return base_logger
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|