@@ -31,13 +31,22 @@ def pymodbus_apply_logging_config(
3131 level = level .upper ()
3232 Log .apply_logging_config (level , log_file_name )
3333
34+ def pymodbus_get_last_frames () -> str :
35+ """Prepare and return last frames, for automatic debugging."""
36+ return Log .get_last_frames ()
3437
3538class Log :
3639 """Class to hide logging complexity.
3740
3841 :meta private:
3942 """
4043
44+ SEND_DATA = "send"
45+ RECV_DATA = "recv"
46+ EXTRA_DATA = "extra"
47+ MAX_FRAMES = 20
48+ frame_dump : list [tuple ] = []
49+
4150 _logger = logging .getLogger (__name__ )
4251 last_log_text = ""
4352 repeat_log = False
@@ -66,6 +75,50 @@ def setLevel(cls, level):
6675 """Apply basic logging level."""
6776 cls ._logger .setLevel (level )
6877
78+ @classmethod
79+ def build_frame_log_line (cls , data_type , data , old_data ):
80+ """Change frame into log line."""
81+ if data_type == cls .SEND_DATA :
82+ log_text = cls .build_msg ("send: {}" , data , ":hex" )
83+ elif data_type == cls .RECV_DATA :
84+ log_text = cls .build_msg (
85+ "recv: {} extra data: {}" ,
86+ data ,
87+ ":hex" ,
88+ old_data ,
89+ ":hex" ,
90+ )
91+ else :
92+ log_text = cls .build_msg (
93+ "extra: {} unexpected data: {}" ,
94+ data ,
95+ ":hex" ,
96+ old_data ,
97+ ":hex" ,
98+ )
99+ return log_text
100+
101+ @classmethod
102+ def get_last_frames (cls ):
103+ """Prepare and return last frames, for automatic debugging."""
104+ log_text = ""
105+ for (data_type , data , old_data ) in cls .frame_dump :
106+ log_text += f"\n >>>>> { cls .build_frame_log_line (data_type , data , old_data )} "
107+ cls .frame_dump = []
108+ return log_text
109+
110+ @classmethod
111+ def transport_dump (cls , data_type , data , old_data ):
112+ """Debug transport data."""
113+ if not cls ._logger .isEnabledFor (logging .DEBUG ):
114+ cls .frame_dump .append ((data_type , data , old_data ))
115+ if len (cls .frame_dump ) > cls .MAX_FRAMES :
116+ del cls .frame_dump [0 ]
117+ return
118+
119+ cls .frame_dump = []
120+ cls ._logger .debug (cls .build_frame_log_line (data_type , data , old_data ), stacklevel = 2 )
121+
69122 @classmethod
70123 def build_msg (cls , txt , * args ):
71124 """Build message."""
@@ -97,7 +150,7 @@ def build_msg(cls, txt, *args):
97150 if not cls .repeat_log :
98151 cls .repeat_log = True
99152 return "Repeating...."
100- return None
153+ return cls . last_log_text
101154
102155 @classmethod
103156 def info (cls , txt , * args ):
@@ -125,10 +178,14 @@ def error(cls, txt, *args):
125178 """Log error messages."""
126179 if cls ._logger .isEnabledFor (logging .ERROR ):
127180 if (log_text := cls .build_msg (txt , * args )):
181+ if not cls ._logger .isEnabledFor (logging .DEBUG ):
182+ log_text += cls .get_last_frames ()
128183 cls ._logger .error (log_text , stacklevel = 2 )
129184
130185 @classmethod
131186 def critical (cls , txt , * args ):
132187 """Log critical messages."""
133188 if (log_text := cls .build_msg (txt , * args )):
189+ if not cls ._logger .isEnabledFor (logging .DEBUG ):
190+ log_text += cls .get_last_frames ()
134191 cls ._logger .critical (log_text , stacklevel = 2 )
0 commit comments