9 from functools
import wraps, partial
11 from std_msgs.msg
import String
16 Realizes logging of active states. 20 _serialize_impl =
'yaml' 24 log_folder = os.path.expanduser(rospy.get_param(
"~log_folder",
"~/.flexbe_logs"))
26 if log_folder ==
"" or not rospy.get_param(
"~log_enabled",
False):
27 StateLogger.enabled =
False 29 StateLogger.enabled =
True 31 if not os.path.exists(log_folder):
32 os.makedirs(log_folder)
35 if be_name
is not None:
36 name = be_name.replace(
" ",
"_").replace(
",",
"_").replace(
".",
"_").replace(
"/",
"_").lower()
38 StateLogger._serialize_impl = rospy.get_param(
'~log_serialize',
'yaml')
40 logger_config = dict({
42 'disable_existing_loggers':
False,
43 'formatters': {
'yaml': {
'()':
'flexbe_core.state_logger.YamlFormatter'}},
46 'class':
'logging.FileHandler',
47 'filename':
'%(log_folder)s/%(behavior)s_%(timestamp)s.yaml',
51 'class':
'flexbe_core.state_logger.PublishBehaviorLogMessage',
52 'topic':
'flexbe/state_logger',
56 'loggers': {
'flexbe': {
'level':
'INFO',
'handlers': [
'file']}}
57 }, **rospy.get_param(
'~log_config', {}))
58 if (
'handlers' in logger_config
and 'file' in logger_config[
'handlers']
and 59 'filename' in logger_config[
'handlers'][
'file']):
60 logger_config[
'handlers'][
'file'][
'filename'] %= {
61 'log_folder': log_folder,
63 'timestamp': time.strftime(
"%Y-%m-%d-%H_%M_%S")
65 if 'loggers' in logger_config
and 'flexbe' in logger_config[
'loggers']:
66 logger_config[
'loggers'][
'flexbe'][
'level'] = rospy.get_param(
'~log_level',
'INFO').upper()
67 logging.config.dictConfig(logger_config)
71 if not StateLogger.enabled:
74 StateLogger.enabled =
False 78 """ Obtain a reference to the named logger. """ 79 return logging.getLogger(name)
82 def log(name, state, **kwargs):
83 """ Log custom data as given by the keyword arguments. """ 84 if StateLogger.enabled:
85 StateLogger.get(name).
log(kwargs.get(
'loglevel', logging.INFO), dict(StateLogger._basic(state), **kwargs))
91 """ Log whenever any of the specified events of the state is activated. """ 95 def log_events_init(self, *args, **kwargs):
96 cls_init(self, *args, **kwargs)
97 for event, method
in events.items():
98 def wrap_event_method(event, method):
99 if hasattr(self, method):
100 event_method = getattr(self, method)
102 def event_wrapper(*args, **kwargs):
103 time_start = rospy.get_time()
105 event_method(*args, **kwargs)
107 if StateLogger.enabled:
108 StateLogger.get(name).info(dict(
109 StateLogger._basic(self),
111 duration=rospy.get_time() - time_start))
112 setattr(self, method, event_wrapper)
113 wrap_event_method(event, method)
120 """ Log all outcomes of the state. """ 124 def log_outcomes_init(self, *args, **kwargs):
125 cls_init(self, *args, **kwargs)
126 execute_method = getattr(self,
'execute')
127 @wraps(execute_method)
128 def execute_wrapper(*args, **kwargs):
131 outcome = execute_method(*args, **kwargs)
134 if StateLogger.enabled
and outcome
is not None:
135 StateLogger.get(name).info(dict(
136 StateLogger._basic(self),
138 setattr(self,
'execute', execute_wrapper)
145 """ Log all userdata that is passed to the state. """ 149 def log_userdata_init(self, *args, **kwargs):
150 cls_init(self, *args, **kwargs)
151 input_keys = kwargs.get(
'input_keys', [])
152 on_enter_method = getattr(self,
'on_enter')
153 @wraps(on_enter_method)
154 def on_enter_wrapper(userdata):
155 logger = StateLogger.get(name)
156 if StateLogger.enabled
and logger.isEnabledFor(logging.DEBUG)
and input_keys:
157 logdata = dict(StateLogger._basic(self), userdata=dict())
158 for key
in input_keys:
159 if keys
is not None and key
not in keys:
162 logdata[
'userdata'][key] = StateLogger._serialize(userdata[key])
163 except Exception
as e:
164 rospy.logwarn(
'State %s failed to log userdata for key %s: %s' %
165 (self.name, key, str(e)))
166 logger.debug(logdata)
167 on_enter_method(userdata)
168 setattr(self,
'on_enter', on_enter_wrapper)
178 'yaml': partial(yaml.dump, default_flow_style=
True),
181 'pickle': pickle.dumps,
182 }.
get(StateLogger._serialize_impl,
lambda o: eval(StateLogger._serialize_impl, locals={
'object': o}))(obj)
186 result = {
'time': rospy.get_time()}
187 if state
is not None:
190 'state': state.__class__.__name__,
199 record.msg.update(logger=record.name, loglevel=record.levelname)
200 return '- %s' % super(YamlFormatter, self).
format(record)
205 def __init__(self, level=logging.NOTSET, topic='flexbe/state_logger'):
206 super(PublishBehaviorLogMessage, self).
__init__(level)
211 message = self.format(record)
212 self._pub.publish(self.
_topic, String(message))
def log_userdata(name, keys=None)
def initialize(be_name=None)
def log(name, state, kwargs)
def __init__(self, level=logging.NOTSET, topic='flexbe/state_logger')
def log_events(name, events)