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.
@@ -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
- # [x] Use fix config for `set_logging`, and Model initialize step.
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, TypeVar, Union
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
- """Return logger object with an input module name that already implement the
36
- custom handler and formatter from this package config.
67
+ """Configure logger with custom formatting and handlers.
37
68
 
38
- :param name: (str) A module name that want to log.
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
- :rtype: logging.Logger
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 PrefixMsg(BaseModel):
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
- PrefixMsg: the validated model from a string message.
138
+ Message: the validated model from a string message.
96
139
  """
97
- return PrefixMsg.model_validate(
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
- :param extras: (DictData) An extra parameter that want to get the
105
- `log_add_emoji` flag.
147
+ Args:
148
+ extras: An extra parameter that want to get the
149
+ `log_add_emoji` flag.
106
150
 
107
- :rtype: str
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
- :param frame: (FrameType) The current frame that want to dynamic.
140
- :param extras: (DictData) An extra parameter that want to get the
141
- `logs_trace_frame_layer` config value.
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
- :param mode: (Literal["stdout", "stderr"]) A metadata mode.
167
- :param message: (str) A message.
168
- :param level: (str) A log level.
169
- :param extras: (DictData) An extra parameter that want to override core
170
- config values.
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
- :rtype: Self
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 _logging(
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._logging(message, mode="debug")
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._logging(message, mode="info")
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._logging(message, mode="warning")
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._logging(message, mode="error", is_err=True)
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._logging(message, mode="exception", is_err=True)
413
+ self.emit(message, mode="exception", is_err=True)
394
414
 
395
415
  @abstractmethod
396
- async def _alogging(
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._alogging(message, mode="debug")
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._alogging(message, mode="info")
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._alogging(message, mode="warning")
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._alogging(message, mode="error", is_err=True)
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._alogging(message, mode="exception", is_err=True)
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 _logging(
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 _alogging(
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 FileTrace(ConsoleTrace): # pragma: no cov
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("trace_path", extras=extras)).glob("./run_id=*"),
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("trace_path", extras=extras)
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
- dynamic("trace_path", extras=self.extras)
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, level=level, message=message, extras=self.extras
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, level=level, message=message, extras=self.extras
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(ConsoleTrace): # pragma: no cov
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 int,
710
- stdout str,
711
- stderr str,
712
- update datetime
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 = TypeVar("Trace", bound=BaseTrace)
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
- ) -> TraceModel: # pragma: no cov
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: TraceLog
843
+ :rtype: Trace
773
844
  """
774
- if dynamic("trace_path", extras=extras).is_file():
775
- return SQLiteTrace(
776
- run_id=run_id, parent_run_id=parent_run_id, extras=(extras or {})
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
- return FileTrace(
779
- run_id=run_id, parent_run_id=parent_run_id, extras=(extras or {})
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
  )