ddeutil-workflow 0.0.72__py3-none-any.whl → 0.0.74__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.
- ddeutil/workflow/__about__.py +1 -1
- ddeutil/workflow/__cron.py +14 -8
- ddeutil/workflow/__init__.py +119 -10
- ddeutil/workflow/__types.py +53 -41
- ddeutil/workflow/api/__init__.py +74 -3
- ddeutil/workflow/api/routes/job.py +15 -29
- ddeutil/workflow/api/routes/logs.py +9 -9
- ddeutil/workflow/api/routes/workflows.py +3 -3
- ddeutil/workflow/audits.py +70 -55
- ddeutil/workflow/cli.py +1 -15
- ddeutil/workflow/conf.py +78 -26
- ddeutil/workflow/errors.py +86 -19
- ddeutil/workflow/event.py +268 -169
- ddeutil/workflow/job.py +331 -192
- ddeutil/workflow/params.py +37 -7
- ddeutil/workflow/result.py +96 -70
- ddeutil/workflow/reusables.py +56 -6
- ddeutil/workflow/stages.py +1088 -575
- ddeutil/workflow/traces.py +218 -128
- ddeutil/workflow/utils.py +60 -8
- ddeutil/workflow/workflow.py +424 -290
- {ddeutil_workflow-0.0.72.dist-info → ddeutil_workflow-0.0.74.dist-info}/METADATA +27 -17
- ddeutil_workflow-0.0.74.dist-info/RECORD +30 -0
- ddeutil_workflow-0.0.72.dist-info/RECORD +0 -30
- {ddeutil_workflow-0.0.72.dist-info → ddeutil_workflow-0.0.74.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.72.dist-info → ddeutil_workflow-0.0.74.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.72.dist-info → ddeutil_workflow-0.0.74.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.72.dist-info → ddeutil_workflow-0.0.74.dist-info}/top_level.txt +0 -0
ddeutil/workflow/traces.py
CHANGED
@@ -3,7 +3,36 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
-
|
6
|
+
"""Tracing and Logging Module for Workflow Execution.
|
7
|
+
|
8
|
+
This module provides comprehensive tracing and logging capabilities for workflow
|
9
|
+
execution monitoring. It supports multiple trace backends including console output,
|
10
|
+
file-based logging, and SQLite database storage.
|
11
|
+
|
12
|
+
The tracing system captures detailed execution metadata including process IDs,
|
13
|
+
thread identifiers, timestamps, and contextual information for debugging and
|
14
|
+
monitoring workflow executions.
|
15
|
+
|
16
|
+
Classes:
|
17
|
+
Message: Log message model with prefix parsing
|
18
|
+
TraceMeta: Metadata model for execution context
|
19
|
+
TraceData: Container for trace information
|
20
|
+
BaseTrace: Abstract base class for trace implementations
|
21
|
+
ConsoleTrace: Console-based trace output
|
22
|
+
FileTrace: File-based trace storage
|
23
|
+
SQLiteTrace: Database-based trace storage
|
24
|
+
|
25
|
+
Functions:
|
26
|
+
set_logging: Configure logger with custom formatting
|
27
|
+
get_trace: Factory function for trace instances
|
28
|
+
|
29
|
+
Example:
|
30
|
+
>>> from ddeutil.workflow.traces import get_trace
|
31
|
+
>>> # Create file-based trace
|
32
|
+
>>> trace = get_trace("running-id-101", parent_run_id="workflow-001")
|
33
|
+
>>> trace.info("Workflow execution started")
|
34
|
+
>>> trace.debug("Processing stage 1")
|
35
|
+
"""
|
7
36
|
from __future__ import annotations
|
8
37
|
|
9
38
|
import json
|
@@ -17,9 +46,12 @@ from inspect import Traceback, currentframe, getframeinfo
|
|
17
46
|
from pathlib import Path
|
18
47
|
from threading import get_ident
|
19
48
|
from types import FrameType
|
20
|
-
from typing import ClassVar, Final, Literal, Optional,
|
49
|
+
from typing import ClassVar, Final, Literal, Optional, Union
|
50
|
+
from urllib.parse import ParseResult, unquote_plus, urlparse
|
21
51
|
|
22
52
|
from pydantic import BaseModel, ConfigDict, Field
|
53
|
+
from pydantic.functional_serializers import field_serializer
|
54
|
+
from pydantic.functional_validators import field_validator
|
23
55
|
from typing_extensions import Self
|
24
56
|
|
25
57
|
from .__types import DictData
|
@@ -32,12 +64,23 @@ logger = logging.getLogger("ddeutil.workflow")
|
|
32
64
|
|
33
65
|
@lru_cache
|
34
66
|
def set_logging(name: str) -> logging.Logger:
|
35
|
-
"""
|
36
|
-
custom handler and formatter from this package config.
|
67
|
+
"""Configure logger with custom formatting and handlers.
|
37
68
|
|
38
|
-
|
69
|
+
Creates and configures a logger instance with the custom formatter and
|
70
|
+
handlers defined in the package configuration. The logger includes both
|
71
|
+
console output and proper formatting for workflow execution tracking.
|
39
72
|
|
40
|
-
:
|
73
|
+
Args:
|
74
|
+
name: Module name to create logger for
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
logging.Logger: Configured logger instance with custom formatting
|
78
|
+
|
79
|
+
Example:
|
80
|
+
```python
|
81
|
+
logger = set_logging("ddeutil.workflow.stages")
|
82
|
+
logger.info("Stage execution started")
|
83
|
+
```
|
41
84
|
"""
|
42
85
|
_logger = logging.getLogger(name)
|
43
86
|
|
@@ -76,7 +119,7 @@ PREFIX_LOGS_REGEX: re.Pattern[str] = re.compile(
|
|
76
119
|
) # pragma: no cov
|
77
120
|
|
78
121
|
|
79
|
-
class
|
122
|
+
class Message(BaseModel):
|
80
123
|
"""Prefix Message model for receive grouping dict from searching prefix data
|
81
124
|
from logging message.
|
82
125
|
"""
|
@@ -92,19 +135,21 @@ class PrefixMsg(BaseModel):
|
|
92
135
|
msg (str): A message that want to extract.
|
93
136
|
|
94
137
|
Returns:
|
95
|
-
|
138
|
+
Message: the validated model from a string message.
|
96
139
|
"""
|
97
|
-
return
|
140
|
+
return Message.model_validate(
|
98
141
|
obj=PREFIX_LOGS_REGEX.search(msg).groupdict()
|
99
142
|
)
|
100
143
|
|
101
144
|
def prepare(self, extras: Optional[DictData] = None) -> str:
|
102
145
|
"""Prepare message with force add prefix before writing trace log.
|
103
146
|
|
104
|
-
:
|
105
|
-
|
147
|
+
Args:
|
148
|
+
extras: An extra parameter that want to get the
|
149
|
+
`log_add_emoji` flag.
|
106
150
|
|
107
|
-
:
|
151
|
+
Returns:
|
152
|
+
str: The prepared message with prefix and optional emoji.
|
108
153
|
"""
|
109
154
|
name: str = self.name or PREFIX_DEFAULT
|
110
155
|
emoji: str = (
|
@@ -126,6 +171,9 @@ class TraceMeta(BaseModel): # pragma: no cov
|
|
126
171
|
process: int = Field(description="A process ID.")
|
127
172
|
thread: int = Field(description="A thread ID.")
|
128
173
|
message: str = Field(description="A message log.")
|
174
|
+
cut_id: Optional[str] = Field(
|
175
|
+
default=None, description="A cutting of running ID."
|
176
|
+
)
|
129
177
|
filename: str = Field(description="A filename of this log.")
|
130
178
|
lineno: int = Field(description="A line number of this log.")
|
131
179
|
|
@@ -136,9 +184,13 @@ class TraceMeta(BaseModel): # pragma: no cov
|
|
136
184
|
"""Dynamic Frame information base on the `logs_trace_frame_layer` config
|
137
185
|
value that was set from the extra parameter.
|
138
186
|
|
139
|
-
:
|
140
|
-
|
141
|
-
|
187
|
+
Args:
|
188
|
+
frame: The current frame that want to dynamic.
|
189
|
+
extras: An extra parameter that want to get the
|
190
|
+
`logs_trace_frame_layer` config value.
|
191
|
+
|
192
|
+
Returns:
|
193
|
+
Traceback: The frame information at the specified layer.
|
142
194
|
"""
|
143
195
|
extras: DictData = extras or {}
|
144
196
|
layer: int = extras.get("logs_trace_frame_layer", 4)
|
@@ -157,19 +209,23 @@ class TraceMeta(BaseModel): # pragma: no cov
|
|
157
209
|
mode: Literal["stdout", "stderr"],
|
158
210
|
message: str,
|
159
211
|
level: str,
|
212
|
+
cutting_id: str,
|
160
213
|
*,
|
161
214
|
extras: Optional[DictData] = None,
|
162
215
|
) -> Self:
|
163
216
|
"""Make the current metric for contract this TraceMeta model instance
|
164
217
|
that will catch local states like PID, thread identity.
|
165
218
|
|
166
|
-
:
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
219
|
+
Args:
|
220
|
+
mode: A metadata mode.
|
221
|
+
message: A message.
|
222
|
+
level: A log level.
|
223
|
+
cutting_id: A cutting ID string.
|
224
|
+
extras: An extra parameter that want to override core
|
225
|
+
config values.
|
171
226
|
|
172
|
-
:
|
227
|
+
Returns:
|
228
|
+
Self: The constructed TraceMeta instance.
|
173
229
|
"""
|
174
230
|
frame: FrameType = currentframe()
|
175
231
|
frame_info: Traceback = cls.dynamic_frame(frame, extras=extras)
|
@@ -185,6 +241,7 @@ class TraceMeta(BaseModel): # pragma: no cov
|
|
185
241
|
process=os.getpid(),
|
186
242
|
thread=get_ident(),
|
187
243
|
message=message,
|
244
|
+
cut_id=cutting_id,
|
188
245
|
filename=frame_info.filename.split(os.path.sep)[-1],
|
189
246
|
lineno=frame_info.lineno,
|
190
247
|
)
|
@@ -246,43 +303,6 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
246
303
|
description="A parent running ID",
|
247
304
|
)
|
248
305
|
|
249
|
-
@classmethod
|
250
|
-
@abstractmethod
|
251
|
-
def find_traces(
|
252
|
-
cls,
|
253
|
-
path: Optional[Path] = None,
|
254
|
-
extras: Optional[DictData] = None,
|
255
|
-
) -> Iterator[TraceData]: # pragma: no cov
|
256
|
-
"""Return iterator of TraceData models from the target pointer.
|
257
|
-
|
258
|
-
Args:
|
259
|
-
path (:obj:`Path`, optional): A pointer path that want to override.
|
260
|
-
extras (:obj:`DictData`, optional): An extras parameter that want to
|
261
|
-
override default engine config.
|
262
|
-
|
263
|
-
Returns:
|
264
|
-
Iterator[TracData]: An iterator object that generate a TracData
|
265
|
-
model.
|
266
|
-
"""
|
267
|
-
raise NotImplementedError(
|
268
|
-
"Trace dataclass should implement `find_traces` class-method."
|
269
|
-
)
|
270
|
-
|
271
|
-
@classmethod
|
272
|
-
@abstractmethod
|
273
|
-
def find_trace_with_id(
|
274
|
-
cls,
|
275
|
-
run_id: str,
|
276
|
-
force_raise: bool = True,
|
277
|
-
*,
|
278
|
-
path: Optional[Path] = None,
|
279
|
-
extras: Optional[DictData] = None,
|
280
|
-
) -> TraceData:
|
281
|
-
raise NotImplementedError(
|
282
|
-
"Trace dataclass should implement `find_trace_with_id` "
|
283
|
-
"class-method."
|
284
|
-
)
|
285
|
-
|
286
306
|
@abstractmethod
|
287
307
|
def writer(
|
288
308
|
self,
|
@@ -334,7 +354,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
334
354
|
)
|
335
355
|
|
336
356
|
@abstractmethod
|
337
|
-
def
|
357
|
+
def emit(
|
338
358
|
self,
|
339
359
|
message: str,
|
340
360
|
mode: str,
|
@@ -358,7 +378,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
358
378
|
|
359
379
|
:param message: (str) A message that want to log.
|
360
380
|
"""
|
361
|
-
self.
|
381
|
+
self.emit(message, mode="debug")
|
362
382
|
|
363
383
|
def info(self, message: str) -> None:
|
364
384
|
"""Write trace log with append mode and logging this message with the
|
@@ -366,7 +386,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
366
386
|
|
367
387
|
:param message: (str) A message that want to log.
|
368
388
|
"""
|
369
|
-
self.
|
389
|
+
self.emit(message, mode="info")
|
370
390
|
|
371
391
|
def warning(self, message: str) -> None:
|
372
392
|
"""Write trace log with append mode and logging this message with the
|
@@ -374,7 +394,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
374
394
|
|
375
395
|
:param message: (str) A message that want to log.
|
376
396
|
"""
|
377
|
-
self.
|
397
|
+
self.emit(message, mode="warning")
|
378
398
|
|
379
399
|
def error(self, message: str) -> None:
|
380
400
|
"""Write trace log with append mode and logging this message with the
|
@@ -382,7 +402,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
382
402
|
|
383
403
|
:param message: (str) A message that want to log.
|
384
404
|
"""
|
385
|
-
self.
|
405
|
+
self.emit(message, mode="error", is_err=True)
|
386
406
|
|
387
407
|
def exception(self, message: str) -> None:
|
388
408
|
"""Write trace log with append mode and logging this message with the
|
@@ -390,10 +410,10 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
390
410
|
|
391
411
|
:param message: (str) A message that want to log.
|
392
412
|
"""
|
393
|
-
self.
|
413
|
+
self.emit(message, mode="exception", is_err=True)
|
394
414
|
|
395
415
|
@abstractmethod
|
396
|
-
async def
|
416
|
+
async def amit(
|
397
417
|
self,
|
398
418
|
message: str,
|
399
419
|
mode: str,
|
@@ -417,7 +437,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
417
437
|
|
418
438
|
:param message: (str) A message that want to log.
|
419
439
|
"""
|
420
|
-
await self.
|
440
|
+
await self.amit(message, mode="debug")
|
421
441
|
|
422
442
|
async def ainfo(self, message: str) -> None: # pragma: no cov
|
423
443
|
"""Async write trace log with append mode and logging this message with
|
@@ -425,7 +445,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
425
445
|
|
426
446
|
:param message: (str) A message that want to log.
|
427
447
|
"""
|
428
|
-
await self.
|
448
|
+
await self.amit(message, mode="info")
|
429
449
|
|
430
450
|
async def awarning(self, message: str) -> None: # pragma: no cov
|
431
451
|
"""Async write trace log with append mode and logging this message with
|
@@ -433,7 +453,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
433
453
|
|
434
454
|
:param message: (str) A message that want to log.
|
435
455
|
"""
|
436
|
-
await self.
|
456
|
+
await self.amit(message, mode="warning")
|
437
457
|
|
438
458
|
async def aerror(self, message: str) -> None: # pragma: no cov
|
439
459
|
"""Async write trace log with append mode and logging this message with
|
@@ -441,7 +461,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
441
461
|
|
442
462
|
:param message: (str) A message that want to log.
|
443
463
|
"""
|
444
|
-
await self.
|
464
|
+
await self.amit(message, mode="error", is_err=True)
|
445
465
|
|
446
466
|
async def aexception(self, message: str) -> None: # pragma: no cov
|
447
467
|
"""Async write trace log with append mode and logging this message with
|
@@ -449,36 +469,12 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
|
|
449
469
|
|
450
470
|
:param message: (str) A message that want to log.
|
451
471
|
"""
|
452
|
-
await self.
|
472
|
+
await self.amit(message, mode="exception", is_err=True)
|
453
473
|
|
454
474
|
|
455
475
|
class ConsoleTrace(BaseTrace): # pragma: no cov
|
456
476
|
"""Console Trace log model."""
|
457
477
|
|
458
|
-
@classmethod
|
459
|
-
def find_traces(
|
460
|
-
cls,
|
461
|
-
path: Optional[Path] = None,
|
462
|
-
extras: Optional[DictData] = None,
|
463
|
-
) -> Iterator[TraceData]: # pragma: no cov
|
464
|
-
raise NotImplementedError(
|
465
|
-
"Console Trace does not support to find history traces data."
|
466
|
-
)
|
467
|
-
|
468
|
-
@classmethod
|
469
|
-
def find_trace_with_id(
|
470
|
-
cls,
|
471
|
-
run_id: str,
|
472
|
-
force_raise: bool = True,
|
473
|
-
*,
|
474
|
-
path: Optional[Path] = None,
|
475
|
-
extras: Optional[DictData] = None,
|
476
|
-
) -> TraceData:
|
477
|
-
raise NotImplementedError(
|
478
|
-
"Console Trace does not support to find history traces data with "
|
479
|
-
"the specific running ID."
|
480
|
-
)
|
481
|
-
|
482
478
|
def writer(
|
483
479
|
self,
|
484
480
|
message: str,
|
@@ -529,18 +525,15 @@ class ConsoleTrace(BaseTrace): # pragma: no cov
|
|
529
525
|
|
530
526
|
:rtype: str
|
531
527
|
"""
|
532
|
-
return prepare_newline(
|
533
|
-
f"({self.cut_id}) "
|
534
|
-
f"{PrefixMsg.from_str(message).prepare(self.extras)}"
|
535
|
-
)
|
528
|
+
return prepare_newline(Message.from_str(message).prepare(self.extras))
|
536
529
|
|
537
|
-
def
|
538
|
-
self, message: str, mode: str, *, is_err: bool = False
|
539
|
-
) -> None:
|
530
|
+
def emit(self, message: str, mode: str, *, is_err: bool = False) -> None:
|
540
531
|
"""Write trace log with append mode and logging this message with any
|
541
532
|
logging level.
|
542
533
|
|
543
534
|
:param message: (str) A message that want to log.
|
535
|
+
:param mode: (str)
|
536
|
+
:param is_err: (bool)
|
544
537
|
"""
|
545
538
|
msg: str = self.make_message(message)
|
546
539
|
|
@@ -551,13 +544,15 @@ class ConsoleTrace(BaseTrace): # pragma: no cov
|
|
551
544
|
|
552
545
|
getattr(logger, mode)(msg, stacklevel=3, extra={"cut_id": self.cut_id})
|
553
546
|
|
554
|
-
async def
|
547
|
+
async def amit(
|
555
548
|
self, message: str, mode: str, *, is_err: bool = False
|
556
549
|
) -> None:
|
557
550
|
"""Write trace log with append mode and logging this message with any
|
558
551
|
logging level.
|
559
552
|
|
560
553
|
:param message: (str) A message that want to log.
|
554
|
+
:param mode: (str)
|
555
|
+
:param is_err: (bool)
|
561
556
|
"""
|
562
557
|
msg: str = self.make_message(message)
|
563
558
|
|
@@ -569,7 +564,62 @@ class ConsoleTrace(BaseTrace): # pragma: no cov
|
|
569
564
|
getattr(logger, mode)(msg, stacklevel=3, extra={"cut_id": self.cut_id})
|
570
565
|
|
571
566
|
|
572
|
-
class
|
567
|
+
class OutsideTrace(ConsoleTrace, ABC):
|
568
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
569
|
+
|
570
|
+
url: ParseResult = Field(description="An URL for create pointer.")
|
571
|
+
|
572
|
+
@field_validator(
|
573
|
+
"url", mode="before", json_schema_input_type=Union[ParseResult, str]
|
574
|
+
)
|
575
|
+
def __parse_url(cls, value: Union[ParseResult, str]) -> ParseResult:
|
576
|
+
if isinstance(value, str):
|
577
|
+
return urlparse(value)
|
578
|
+
return value
|
579
|
+
|
580
|
+
@field_serializer("url")
|
581
|
+
def __serialize_url(self, value: ParseResult) -> str:
|
582
|
+
return value.geturl()
|
583
|
+
|
584
|
+
@classmethod
|
585
|
+
@abstractmethod
|
586
|
+
def find_traces(
|
587
|
+
cls,
|
588
|
+
path: Optional[Path] = None,
|
589
|
+
extras: Optional[DictData] = None,
|
590
|
+
) -> Iterator[TraceData]: # pragma: no cov
|
591
|
+
"""Return iterator of TraceData models from the target pointer.
|
592
|
+
|
593
|
+
Args:
|
594
|
+
path (:obj:`Path`, optional): A pointer path that want to override.
|
595
|
+
extras (:obj:`DictData`, optional): An extras parameter that want to
|
596
|
+
override default engine config.
|
597
|
+
|
598
|
+
Returns:
|
599
|
+
Iterator[TracData]: An iterator object that generate a TracData
|
600
|
+
model.
|
601
|
+
"""
|
602
|
+
raise NotImplementedError(
|
603
|
+
"Trace dataclass should implement `find_traces` class-method."
|
604
|
+
)
|
605
|
+
|
606
|
+
@classmethod
|
607
|
+
@abstractmethod
|
608
|
+
def find_trace_with_id(
|
609
|
+
cls,
|
610
|
+
run_id: str,
|
611
|
+
force_raise: bool = True,
|
612
|
+
*,
|
613
|
+
path: Optional[Path] = None,
|
614
|
+
extras: Optional[DictData] = None,
|
615
|
+
) -> TraceData:
|
616
|
+
raise NotImplementedError(
|
617
|
+
"Trace dataclass should implement `find_trace_with_id` "
|
618
|
+
"class-method."
|
619
|
+
)
|
620
|
+
|
621
|
+
|
622
|
+
class FileTrace(OutsideTrace): # pragma: no cov
|
573
623
|
"""File Trace dataclass that write file to the local storage."""
|
574
624
|
|
575
625
|
@classmethod
|
@@ -584,7 +634,9 @@ class FileTrace(ConsoleTrace): # pragma: no cov
|
|
584
634
|
:param extras: An extra parameter that want to override core config.
|
585
635
|
"""
|
586
636
|
for file in sorted(
|
587
|
-
(path or dynamic("
|
637
|
+
(path or Path(dynamic("trace_url", extras=extras).path)).glob(
|
638
|
+
"./run_id=*"
|
639
|
+
),
|
588
640
|
key=lambda f: f.lstat().st_mtime,
|
589
641
|
):
|
590
642
|
yield TraceData.from_path(file)
|
@@ -605,7 +657,7 @@ class FileTrace(ConsoleTrace): # pragma: no cov
|
|
605
657
|
:param path: (Path)
|
606
658
|
:param extras: An extra parameter that want to override core config.
|
607
659
|
"""
|
608
|
-
base_path: Path = path or dynamic("
|
660
|
+
base_path: Path = path or Path(dynamic("trace_url", extras=extras).path)
|
609
661
|
file: Path = base_path / f"run_id={run_id}"
|
610
662
|
if file.exists():
|
611
663
|
return TraceData.from_path(file)
|
@@ -621,10 +673,14 @@ class FileTrace(ConsoleTrace): # pragma: no cov
|
|
621
673
|
"""Pointer of the target path that use to writing trace log or searching
|
622
674
|
trace log.
|
623
675
|
|
676
|
+
This running ID folder that use to keeping trace log data will use
|
677
|
+
a parent running ID first. If it does not set, it will use running ID
|
678
|
+
instead.
|
679
|
+
|
624
680
|
:rtype: Path
|
625
681
|
"""
|
626
682
|
log_file: Path = (
|
627
|
-
|
683
|
+
Path(unquote_plus(self.url.path))
|
628
684
|
/ f"run_id={self.parent_run_id or self.run_id}"
|
629
685
|
)
|
630
686
|
if not log_file.exists():
|
@@ -655,7 +711,11 @@ class FileTrace(ConsoleTrace): # pragma: no cov
|
|
655
711
|
|
656
712
|
mode: Literal["stdout", "stderr"] = "stderr" if is_err else "stdout"
|
657
713
|
trace_meta: TraceMeta = TraceMeta.make(
|
658
|
-
mode=mode,
|
714
|
+
mode=mode,
|
715
|
+
level=level,
|
716
|
+
message=message,
|
717
|
+
cutting_id=self.cut_id,
|
718
|
+
extras=self.extras,
|
659
719
|
)
|
660
720
|
|
661
721
|
with (self.pointer / f"{mode}.txt").open(
|
@@ -684,7 +744,11 @@ class FileTrace(ConsoleTrace): # pragma: no cov
|
|
684
744
|
|
685
745
|
mode: Literal["stdout", "stderr"] = "stderr" if is_err else "stdout"
|
686
746
|
trace_meta: TraceMeta = TraceMeta.make(
|
687
|
-
mode=mode,
|
747
|
+
mode=mode,
|
748
|
+
level=level,
|
749
|
+
message=message,
|
750
|
+
cutting_id=self.cut_id,
|
751
|
+
extras=self.extras,
|
688
752
|
)
|
689
753
|
|
690
754
|
async with aiofiles.open(
|
@@ -699,17 +763,20 @@ class FileTrace(ConsoleTrace): # pragma: no cov
|
|
699
763
|
await f.write(trace_meta.model_dump_json() + "\n")
|
700
764
|
|
701
765
|
|
702
|
-
class SQLiteTrace(
|
766
|
+
class SQLiteTrace(OutsideTrace): # pragma: no cov
|
703
767
|
"""SQLite Trace dataclass that write trace log to the SQLite database file."""
|
704
768
|
|
705
769
|
table_name: ClassVar[str] = "audits"
|
706
770
|
schemas: ClassVar[
|
707
771
|
str
|
708
772
|
] = """
|
709
|
-
run_id
|
710
|
-
|
711
|
-
|
712
|
-
|
773
|
+
run_id str
|
774
|
+
, parent_run_id str
|
775
|
+
, type str
|
776
|
+
, text str
|
777
|
+
, metadata JSON
|
778
|
+
, created_at datetime
|
779
|
+
, updated_at datetime
|
713
780
|
primary key ( run_id )
|
714
781
|
"""
|
715
782
|
|
@@ -718,7 +785,8 @@ class SQLiteTrace(ConsoleTrace): # pragma: no cov
|
|
718
785
|
cls,
|
719
786
|
path: Optional[Path] = None,
|
720
787
|
extras: Optional[DictData] = None,
|
721
|
-
) -> Iterator[TraceData]:
|
788
|
+
) -> Iterator[TraceData]:
|
789
|
+
raise NotImplementedError("SQLiteTrace does not implement yet.")
|
722
790
|
|
723
791
|
@classmethod
|
724
792
|
def find_trace_with_id(
|
@@ -728,30 +796,33 @@ class SQLiteTrace(ConsoleTrace): # pragma: no cov
|
|
728
796
|
*,
|
729
797
|
path: Optional[Path] = None,
|
730
798
|
extras: Optional[DictData] = None,
|
731
|
-
) -> TraceData:
|
799
|
+
) -> TraceData:
|
800
|
+
raise NotImplementedError("SQLiteTrace does not implement yet.")
|
732
801
|
|
733
|
-
def make_message(self, message: str) -> str:
|
802
|
+
def make_message(self, message: str) -> str:
|
803
|
+
raise NotImplementedError("SQLiteTrace does not implement yet.")
|
734
804
|
|
735
805
|
def writer(
|
736
806
|
self,
|
737
807
|
message: str,
|
738
808
|
level: str,
|
739
809
|
is_err: bool = False,
|
740
|
-
) -> None:
|
810
|
+
) -> None:
|
811
|
+
raise NotImplementedError("SQLiteTrace does not implement yet.")
|
741
812
|
|
742
813
|
def awriter(
|
743
814
|
self,
|
744
815
|
message: str,
|
745
816
|
level: str,
|
746
817
|
is_err: bool = False,
|
747
|
-
) -> None:
|
818
|
+
) -> None:
|
819
|
+
raise NotImplementedError("SQLiteTrace does not implement yet.")
|
748
820
|
|
749
821
|
|
750
|
-
Trace =
|
751
|
-
TraceModel = Union[
|
752
|
-
ConsoleTrace,
|
822
|
+
Trace = Union[
|
753
823
|
FileTrace,
|
754
824
|
SQLiteTrace,
|
825
|
+
OutsideTrace,
|
755
826
|
]
|
756
827
|
|
757
828
|
|
@@ -760,7 +831,7 @@ def get_trace(
|
|
760
831
|
*,
|
761
832
|
parent_run_id: Optional[str] = None,
|
762
833
|
extras: Optional[DictData] = None,
|
763
|
-
) ->
|
834
|
+
) -> Trace: # pragma: no cov
|
764
835
|
"""Get dynamic Trace instance from the core config (it can override by an
|
765
836
|
extras argument) that passing running ID and parent running ID.
|
766
837
|
|
@@ -769,12 +840,31 @@ def get_trace(
|
|
769
840
|
:param extras: (DictData) An extra parameter that want to override the core
|
770
841
|
config values.
|
771
842
|
|
772
|
-
:rtype:
|
843
|
+
:rtype: Trace
|
773
844
|
"""
|
774
|
-
|
775
|
-
|
776
|
-
|
845
|
+
# NOTE: Allow you to override trace model by the extra parameter.
|
846
|
+
map_trace_models: dict[str, type[Trace]] = extras.get(
|
847
|
+
"trace_model_mapping", {}
|
848
|
+
)
|
849
|
+
url: ParseResult
|
850
|
+
if (url := dynamic("trace_url", extras=extras)).scheme and (
|
851
|
+
url.scheme == "sqlite"
|
852
|
+
or (url.scheme == "file" and Path(url.path).is_file())
|
853
|
+
):
|
854
|
+
return map_trace_models.get("sqlite", SQLiteTrace)(
|
855
|
+
url=url,
|
856
|
+
run_id=run_id,
|
857
|
+
parent_run_id=parent_run_id,
|
858
|
+
extras=(extras or {}),
|
859
|
+
)
|
860
|
+
elif url.scheme and url.scheme != "file":
|
861
|
+
raise NotImplementedError(
|
862
|
+
f"Does not implement the outside trace model support for URL: {url}"
|
777
863
|
)
|
778
|
-
|
779
|
-
|
864
|
+
|
865
|
+
return map_trace_models.get("file", FileTrace)(
|
866
|
+
url=url,
|
867
|
+
run_id=run_id,
|
868
|
+
parent_run_id=parent_run_id,
|
869
|
+
extras=(extras or {}),
|
780
870
|
)
|