11#!/usr/bin/env python
22
3- # Copyright 2023 Philipp Schillinger, Team ViGIR, Christopher Newport University
3+ # Copyright 2024 Philipp Schillinger, Team ViGIR, Christopher Newport University
44#
55# Redistribution and use in source and binary forms, with or without
66# modification, are permitted provided that the following conditions are met:
3131
3232"""Realize behavior-specific logging."""
3333
34+ from rclpy .exceptions import ParameterNotDeclaredException
3435from rclpy .node import Node
36+ from rclpy .duration import Duration
3537
3638from flexbe_msgs .msg import BehaviorLog
3739
@@ -47,13 +49,37 @@ class Logger:
4749
4850 LOGGING_TOPIC = 'flexbe/log'
4951
52+ # max number of items in last logged dict (used for log_throttle)
53+ MAX_LAST_LOGGED_SIZE = 1024
54+ LAST_LOGGED_CLEARING_RATIO = 0.2
55+
5056 _pub = None
5157 _node = None
5258
5359 @staticmethod
5460 def initialize (node : Node ):
5561 Logger ._node = node
5662 Logger ._pub = node .create_publisher (BehaviorLog , Logger .LOGGING_TOPIC , 100 )
63+ Logger ._last_logged = {}
64+
65+ # Optional parameters that can be defined
66+ try :
67+ size_param = node .get_parameter ("max_throttle_logging_size" )
68+ if size_param .type_ == size_param .Type .INTEGER :
69+ Logger .MAX_LAST_LOGGED_SIZE = size_param .value
70+ except ParameterNotDeclaredException as exc :
71+ pass
72+
73+ try :
74+ clear_param = node .get_parameter ("throttle_logging_clear_ratio" )
75+ if clear_param .type_ in (clear_param .Type .INTEGER , clear_param .Type .DOUBLE ):
76+ Logger .LAST_LOGGED_CLEARING_RATIO = clear_param .value
77+ except ParameterNotDeclaredException as exc :
78+ pass
79+
80+ Logger ._node .get_logger ().debug (f"Enable throttle logging option with "
81+ f"max size={ Logger .MAX_LAST_LOGGED_SIZE } "
82+ f"clear ratio={ Logger .LAST_LOGGED_CLEARING_RATIO } " )
5783
5884 @staticmethod
5985 def log (text : str , severity : int ):
@@ -67,6 +93,25 @@ def log(text: str, severity: int):
6793 # also log locally
6894 Logger .local (text , severity )
6995
96+ @staticmethod
97+ def log_throttle (period : float , text : str , severity : int ):
98+ # create unique identifier for each logging message
99+ log_id = f"{ severity } _{ text } "
100+ time_now = Logger ._node .get_clock ().now ()
101+ # only log when it's the first time or period time has passed for the logging message
102+ if not log_id in Logger ._last_logged .keys () or \
103+ time_now - Logger ._last_logged [log_id ] > Duration (seconds = period ):
104+ Logger .log (text , severity )
105+ Logger ._last_logged .update ({log_id : time_now })
106+
107+ if len (Logger ._last_logged ) > Logger .MAX_LAST_LOGGED_SIZE :
108+ # iterate through last logged items, sorted by the timestamp (oldest last)
109+ clear_size = Logger .MAX_LAST_LOGGED_SIZE * (1 - Logger .LAST_LOGGED_CLEARING_RATIO )
110+ for i , log in enumerate (sorted (Logger ._last_logged .items (), key = lambda item : item [1 ], reverse = True )):
111+ # remove defined percentage of oldest items
112+ if i > clear_size :
113+ Logger ._last_logged .pop (log [0 ])
114+
70115 @staticmethod
71116 def local (text : str , severity : int ):
72117 if Logger ._node is None :
@@ -87,57 +132,81 @@ def local(text: str, severity: int):
87132 # NOTE: Below text strings can only have single % symbols if they are being treated
88133 # as format strings with appropriate arguments (otherwise replace with %% for simple string without args)
89134 @staticmethod
90- def logdebug (text , * args ):
135+ def logdebug (text : str , * args ):
91136 Logger .log (text % args , Logger .REPORT_DEBUG )
92137
93138 @staticmethod
94- def loginfo (text , * args ):
139+ def loginfo (text : str , * args ):
95140 Logger .log (text % args , Logger .REPORT_INFO )
96141
97142 @staticmethod
98- def logwarn (text , * args ):
143+ def logwarn (text : str , * args ):
99144 Logger .log (text % args , Logger .REPORT_WARN )
100145
101146 @staticmethod
102- def loghint (text , * args ):
147+ def loghint (text : str , * args ):
103148 Logger .log (text % args , Logger .REPORT_HINT )
104149
105150 @staticmethod
106- def logerr (text , * args ):
151+ def logerr (text : str , * args ):
107152 Logger .log (text % args , Logger .REPORT_ERROR )
108153
109154 @staticmethod
110- def localdebug (text , * args ):
155+ def logdebug_throttle (period : float , text : str , * args ):
156+ Logger .log_throttle (period , text % args , Logger .REPORT_DEBUG )
157+
158+ @staticmethod
159+ def loginfo_throttle (period : float , text : str , * args ):
160+ Logger .log_throttle (period , text % args , Logger .REPORT_INFO )
161+
162+ @staticmethod
163+ def logwarn_throttle (period : float , text : str , * args ):
164+ Logger .log_throttle (period , text % args , Logger .REPORT_WARN )
165+
166+ @staticmethod
167+ def loghint_throttle (period : float , text : str , * args ):
168+ Logger .log_throttle (period , text % args , Logger .REPORT_HINT )
169+
170+ @staticmethod
171+ def logerr_throttle (period : float , text : str , * args ):
172+ Logger .log_throttle (period , text % args , Logger .REPORT_ERROR )
173+
174+ @staticmethod
175+ def localdebug (text : str , * args ):
111176 Logger .local (text % args , Logger .REPORT_DEBUG )
112177
113178 @staticmethod
114- def localinfo (text , * args ):
179+ def localinfo (text : str , * args ):
115180 Logger .local (text % args , Logger .REPORT_INFO )
116181
117182 @staticmethod
118- def localwarn (text , * args ):
183+ def localwarn (text : str , * args ):
119184 Logger .local (text % args , Logger .REPORT_WARN )
120185
121186 @staticmethod
122- def localerr (text , * args ):
187+ def localhint (text : str , * args ):
188+ Logger .local (text % args , Logger .REPORT_HINT )
189+
190+ @staticmethod
191+ def localerr (text : str , * args ):
123192 Logger .local (text % args , Logger .REPORT_ERROR )
124193
125194 @staticmethod
126- def debug (text , * args ):
195+ def debug (text : str , * args ):
127196 Logger .logdebug (text , * args )
128197
129198 @staticmethod
130- def info (text , * args ):
199+ def info (text : str , * args ):
131200 Logger .loginfo (text , * args )
132201
133202 @staticmethod
134- def warning (text , * args ):
203+ def warning (text : str , * args ):
135204 Logger .logwarn (text , * args )
136205
137206 @staticmethod
138- def hint (text , * args ):
207+ def hint (text : str , * args ):
139208 Logger .loghint (text , * args )
140209
141210 @staticmethod
142- def error (text , * args ):
211+ def error (text : str , * args ):
143212 Logger .logerr (text , * args )
0 commit comments