55from dataclasses import dataclass
66from typing import TYPE_CHECKING
77
8+ from aws_durable_execution_sdk_python .lambda_service import OperationType
89from aws_durable_execution_sdk_python .types import LoggerInterface
910
1011if TYPE_CHECKING :
11- from collections .abc import Mapping , MutableMapping
12+ from collections .abc import Callable , Mapping , MutableMapping
1213
14+ from aws_durable_execution_sdk_python .context import ExecutionState
1315 from aws_durable_execution_sdk_python .identifier import OperationIdentifier
1416
1517
1618@dataclass (frozen = True )
1719class LogInfo :
18- execution_arn : str
20+ execution_state : ExecutionState
1921 parent_id : str | None = None
2022 operation_id : str | None = None
2123 name : str | None = None
2224 attempt : int | None = None
2325
2426 @classmethod
2527 def from_operation_identifier (
26- cls , execution_arn : str , op_id : OperationIdentifier , attempt : int | None = None
28+ cls ,
29+ execution_state : ExecutionState ,
30+ op_id : OperationIdentifier ,
31+ attempt : int | None = None ,
2732 ) -> LogInfo :
2833 """Create new log info from an execution arn, OperationIdentifier and attempt."""
2934 return cls (
30- execution_arn = execution_arn ,
35+ execution_state = execution_state ,
3136 parent_id = op_id .parent_id ,
3237 operation_id = op_id .operation_id ,
3338 name = op_id .name ,
@@ -37,7 +42,7 @@ def from_operation_identifier(
3742 def with_parent_id (self , parent_id : str ) -> LogInfo :
3843 """Clone the log info with a new parent id."""
3944 return LogInfo (
40- execution_arn = self .execution_arn ,
45+ execution_state = self .execution_state ,
4146 parent_id = parent_id ,
4247 operation_id = self .operation_id ,
4348 name = self .name ,
@@ -47,15 +52,23 @@ def with_parent_id(self, parent_id: str) -> LogInfo:
4752
4853class Logger (LoggerInterface ):
4954 def __init__ (
50- self , logger : LoggerInterface , default_extra : Mapping [str , object ]
55+ self ,
56+ logger : LoggerInterface ,
57+ default_extra : Mapping [str , object ],
58+ execution_state : ExecutionState ,
59+ visited_operations : set [str ] | None = None ,
5160 ) -> None :
5261 self ._logger = logger
5362 self ._default_extra = default_extra
63+ self ._execution_state = execution_state
64+ self ._visited_operations = visited_operations or set ()
5465
5566 @classmethod
5667 def from_log_info (cls , logger : LoggerInterface , info : LogInfo ) -> Logger :
5768 """Create a new logger with the given LogInfo."""
58- extra : MutableMapping [str , object ] = {"execution_arn" : info .execution_arn }
69+ extra : MutableMapping [str , object ] = {
70+ "execution_arn" : info .execution_state .durable_execution_arn
71+ }
5972 if info .parent_id :
6073 extra ["parent_id" ] = info .parent_id
6174 if info .name :
@@ -65,7 +78,9 @@ def from_log_info(cls, logger: LoggerInterface, info: LogInfo) -> Logger:
6578 extra ["attempt" ] = info .attempt + 1
6679 if info .operation_id :
6780 extra ["operation_id" ] = info .operation_id
68- return cls (logger , extra )
81+ return cls (
82+ logger = logger , default_extra = extra , execution_state = info .execution_state
83+ )
6984
7085 def with_log_info (self , info : LogInfo ) -> Logger :
7186 """Clone the existing logger with new LogInfo."""
@@ -81,29 +96,52 @@ def get_logger(self) -> LoggerInterface:
8196 def debug (
8297 self , msg : object , * args : object , extra : Mapping [str , object ] | None = None
8398 ) -> None :
84- merged_extra = {** self ._default_extra , ** (extra or {})}
85- self ._logger .debug (msg , * args , extra = merged_extra )
99+ self ._log (self ._logger .debug , msg , * args , extra = extra )
86100
87101 def info (
88102 self , msg : object , * args : object , extra : Mapping [str , object ] | None = None
89103 ) -> None :
90- merged_extra = {** self ._default_extra , ** (extra or {})}
91- self ._logger .info (msg , * args , extra = merged_extra )
104+ self ._log (self ._logger .info , msg , * args , extra = extra )
92105
93106 def warning (
94107 self , msg : object , * args : object , extra : Mapping [str , object ] | None = None
95108 ) -> None :
96- merged_extra = {** self ._default_extra , ** (extra or {})}
97- self ._logger .warning (msg , * args , extra = merged_extra )
109+ self ._log (self ._logger .warning , msg , * args , extra = extra )
98110
99111 def error (
100112 self , msg : object , * args : object , extra : Mapping [str , object ] | None = None
101113 ) -> None :
102- merged_extra = {** self ._default_extra , ** (extra or {})}
103- self ._logger .error (msg , * args , extra = merged_extra )
114+ self ._log (self ._logger .error , msg , * args , extra = extra )
104115
105116 def exception (
106117 self , msg : object , * args : object , extra : Mapping [str , object ] | None = None
107118 ) -> None :
119+ self ._log (self ._logger .exception , msg , * args , extra = extra )
120+
121+ def _log (
122+ self ,
123+ log_func : Callable ,
124+ msg : object ,
125+ * args : object ,
126+ extra : Mapping [str , object ] | None = None ,
127+ ):
128+ if not self ._should_log ():
129+ return
108130 merged_extra = {** self ._default_extra , ** (extra or {})}
109- self ._logger .exception (msg , * args , extra = merged_extra )
131+ log_func (msg , * args , extra = merged_extra )
132+
133+ def visit_operation (self , operation_id : str ):
134+ self ._visited_operations .add (operation_id )
135+
136+ def _is_replay (self ) -> bool :
137+ if not self ._execution_state or not self ._execution_state .operations :
138+ return False
139+
140+ return any (
141+ operation_id in self ._visited_operations
142+ for operation_id , operation in self ._execution_state .operations .items ()
143+ if operation .operation_type != OperationType .EXECUTION
144+ )
145+
146+ def _should_log (self ) -> bool :
147+ return not self ._is_replay ()
0 commit comments