Package rosgraph :: Module roslogging
[frames] | no frames]

Source Code for Module rosgraph.roslogging

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2008, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  """ 
 34  Library for configuring python logging to standard ROS locations (e.g. ROS_LOG_DIR). 
 35  """ 
 36   
 37  import os 
 38  import sys 
 39  import time 
 40  import logging 
 41  import logging.config 
 42  import inspect 
 43   
 44  import yaml 
 45   
 46  import rospkg 
 47  from rospkg.environment import ROS_LOG_DIR 
 48   
49 -class LoggingException(Exception): pass
50
51 -class RospyLogger(logging.getLoggerClass()):
52 - def findCaller(self, *args, **kwargs):
53 """ 54 Find the stack frame of the caller so that we can note the source 55 file name, line number, and function name with class name if possible. 56 """ 57 file_name, lineno, func_name = super(RospyLogger, self).findCaller(*args, **kwargs)[:3] 58 file_name = os.path.normcase(file_name) 59 60 f = inspect.currentframe() 61 if f is not None: 62 f = f.f_back 63 while hasattr(f, "f_code"): 64 # Search for the right frame using the data already found by parent class. 65 co = f.f_code 66 filename = os.path.normcase(co.co_filename) 67 if filename == file_name and f.f_lineno == lineno and co.co_name == func_name: 68 break 69 if f.f_back: 70 f = f.f_back 71 72 # Jump up two more frames, as the logger methods have been double wrapped. 73 if f is not None and f.f_back and f.f_code and f.f_code.co_name == '_base_logger': 74 f = f.f_back 75 if f.f_back: 76 f = f.f_back 77 co = f.f_code 78 func_name = co.co_name 79 80 # Now extend the function name with class name, if available. 81 try: 82 class_name = f.f_locals['self'].__class__.__name__ 83 func_name = '%s.%s' % (class_name, func_name) 84 except KeyError: # if the function is unbound, there is no self. 85 pass 86 87 if sys.version_info > (3, 2): 88 # Dummy last argument to match Python3 return type 89 return co.co_filename, f.f_lineno, func_name, None 90 else: 91 return co.co_filename, f.f_lineno, func_name
92 93 logging.setLoggerClass(RospyLogger) 94
95 -def renew_latest_logdir(logfile_dir):
96 log_dir = os.path.dirname(logfile_dir) 97 latest_dir = os.path.join(log_dir, 'latest') 98 if os.path.lexists(latest_dir): 99 if not os.path.islink(latest_dir): 100 return False 101 os.remove(latest_dir) 102 os.symlink(logfile_dir, latest_dir) 103 return True
104
105 -def configure_logging(logname, level=logging.INFO, filename=None, env=None):
106 """ 107 Configure Python logging package to send log files to ROS-specific log directory 108 :param logname str: name of logger, ``str`` 109 :param filename: filename to log to. If not set, a log filename 110 will be generated using logname, ``str`` 111 :param env: override os.environ dictionary, ``dict`` 112 :returns: log file name, ``str`` 113 :raises: :exc:`LoggingException` If logging cannot be configured as specified 114 """ 115 if env is None: 116 env = os.environ 117 118 logname = logname or 'unknown' 119 log_dir = rospkg.get_log_dir(env=env) 120 121 # if filename is not explicitly provided, generate one using logname 122 if not filename: 123 log_filename = os.path.join(log_dir, '%s-%s.log'%(logname, os.getpid())) 124 else: 125 log_filename = os.path.join(log_dir, filename) 126 127 logfile_dir = os.path.dirname(log_filename) 128 if not os.path.exists(logfile_dir): 129 try: 130 makedirs_with_parent_perms(logfile_dir) 131 except OSError: 132 # cannot print to screen because command-line tools with output use this 133 if os.path.exists(logfile_dir): 134 # We successfully created the logging folder, but could not change 135 # permissions of the new folder to the same as the parent folder 136 sys.stderr.write("WARNING: Could not change permissions for folder [%s], make sure that the parent folder has correct permissions.\n"%logfile_dir) 137 else: 138 # Could not create folder 139 sys.stderr.write("WARNING: cannot create log directory [%s]. Please set %s to a writable location.\n"%(logfile_dir, ROS_LOG_DIR)) 140 return None 141 elif os.path.isfile(logfile_dir): 142 raise LoggingException("Cannot save log files: file [%s] is in the way"%logfile_dir) 143 144 # the log dir itself should not be symlinked as latest 145 if logfile_dir != log_dir: 146 if sys.platform not in ['win32']: 147 try: 148 success = renew_latest_logdir(logfile_dir) 149 if not success: 150 sys.stderr.write("INFO: cannot create a symlink to latest log directory\n") 151 except OSError as e: 152 sys.stderr.write("INFO: cannot create a symlink to latest log directory: %s\n" % e) 153 154 config_file = os.environ.get('ROS_PYTHON_LOG_CONFIG_FILE') 155 if not config_file: 156 # search for logging config file in ROS_HOME, ROS_ETC_DIR or relative 157 # to the rosgraph package directory. 158 config_dirs = [os.path.join(rospkg.get_ros_home(), 'config'), 159 rospkg.get_etc_ros_dir()] 160 try: 161 config_dirs.append(os.path.join( 162 rospkg.RosPack().get_path('rosgraph'), 'conf')) 163 except rospkg.common.ResourceNotFound: 164 pass 165 for config_dir in config_dirs: 166 for extension in ('conf', 'yaml'): 167 f = os.path.join(config_dir, 168 'python_logging{}{}'.format(os.path.extsep, 169 extension)) 170 if os.path.isfile(f): 171 config_file = f 172 break 173 if config_file is not None: 174 break 175 176 if config_file is None or not os.path.isfile(config_file): 177 # logging is considered soft-fail 178 sys.stderr.write("WARNING: cannot load logging configuration file, logging is disabled\n") 179 logging.getLogger(logname).setLevel(logging.CRITICAL) 180 return log_filename 181 182 # pass in log_filename as argument to pylogging.conf 183 os.environ['ROS_LOG_FILENAME'] = log_filename 184 if config_file.endswith(('.yaml', '.yml')): 185 with open(config_file) as f: 186 dict_conf = yaml.safe_load(f) 187 dict_conf.setdefault('version', 1) 188 logging.config.dictConfig(dict_conf) 189 else: 190 # #3625: disabling_existing_loggers=False 191 logging.config.fileConfig(config_file, disable_existing_loggers=False) 192 193 return log_filename
194 195
196 -def makedirs_with_parent_perms(p):
197 """ 198 Create the directory using the permissions of the nearest 199 (existing) parent directory. This is useful for logging, where a 200 root process sometimes has to log in the user's space. 201 :param p: directory to create, ``str`` 202 """ 203 p = os.path.abspath(p) 204 parent = os.path.dirname(p) 205 # recurse upwards, checking to make sure we haven't reached the 206 # top 207 if not os.path.exists(p) and p and parent != p: 208 makedirs_with_parent_perms(parent) 209 s = os.stat(parent) 210 os.mkdir(p) 211 212 # if perms of new dir don't match, set anew 213 s2 = os.stat(p) 214 if s.st_uid != s2.st_uid or s.st_gid != s2.st_gid: 215 os.chown(p, s.st_uid, s.st_gid) 216 if s.st_mode != s2.st_mode: 217 os.chmod(p, s.st_mode)
218 219 _logging_to_rospy_names = { 220 'DEBUG': ('DEBUG', '\033[32m'), 221 'INFO': ('INFO', None), 222 'WARNING': ('WARN', '\033[33m'), 223 'ERROR': ('ERROR', '\033[31m'), 224 'CRITICAL': ('FATAL', '\033[31m') 225 } 226 _color_reset = '\033[0m' 227 _defaultFormatter = logging.Formatter() 228
229 -class RosStreamHandler(logging.Handler):
230 - def __init__(self, colorize=True, stdout=None, stderr=None):
231 super(RosStreamHandler, self).__init__() 232 self._stdout = stdout or sys.stdout 233 self._stderr = stderr or sys.stderr 234 self._colorize = colorize 235 try: 236 from rospy.rostime import get_time, is_wallclock 237 self._get_time = get_time 238 self._is_wallclock = is_wallclock 239 except ImportError: 240 self._get_time = None 241 self._is_wallclock = None
242
243 - def emit(self, record):
244 level, color = _logging_to_rospy_names[record.levelname] 245 record_message = _defaultFormatter.format(record) 246 msg = os.environ.get( 247 'ROSCONSOLE_FORMAT', '[${severity}] [${time}]: ${message}') 248 msg = msg.replace('${severity}', level) 249 msg = msg.replace('${message}', str(record_message)) 250 msg = msg.replace('${walltime}', '%f' % time.time()) 251 msg = msg.replace('${thread}', str(record.thread)) 252 msg = msg.replace('${logger}', str(record.name)) 253 msg = msg.replace('${file}', str(record.pathname)) 254 msg = msg.replace('${line}', str(record.lineno)) 255 msg = msg.replace('${function}', str(record.funcName)) 256 try: 257 from rospy import get_name 258 node_name = get_name() 259 except ImportError: 260 node_name = '<unknown_node_name>' 261 msg = msg.replace('${node}', node_name) 262 time_str = '%f' % time.time() 263 if self._get_time is not None and not self._is_wallclock(): 264 time_str += ', %f' % self._get_time() 265 msg = msg.replace('${time}', time_str) 266 msg += '\n' 267 if record.levelno < logging.WARNING: 268 self._write(self._stdout, msg, color) 269 else: 270 self._write(self._stderr, msg, color)
271
272 - def _write(self, fd, msg, color):
273 if self._colorize and color and hasattr(fd, 'isatty') and fd.isatty(): 274 msg = color + msg + _color_reset 275 fd.write(msg)
276