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, dummy=False): # Dummy second arg to match Python3 function declaration
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()[:3] 58 59 f = inspect.currentframe() 60 if f is not None: 61 f = f.f_back 62 while hasattr(f, "f_code"): 63 # Search for the right frame using the data already found by parent class. 64 co = f.f_code 65 filename = os.path.normcase(co.co_filename) 66 if filename == file_name and f.f_lineno == lineno and co.co_name == func_name: 67 break 68 f = f.f_back 69 70 # Jump up two more frames, as the logger methods have been double wrapped. 71 if f.f_back and f.f_code and f.f_code.co_name == '_base_logger': 72 f = f.f_back 73 if f.f_back: 74 f = f.f_back 75 co = f.f_code 76 func_name = co.co_name 77 78 # Now extend the function name with class name, if available. 79 try: 80 class_name = f.f_locals['self'].__class__.__name__ 81 func_name = '%s.%s' % (class_name, func_name) 82 except KeyError: # if the function is unbound, there is no self. 83 pass 84 85 if sys.version_info > (3, 2): 86 # Dummy last argument to match Python3 return type 87 return co.co_filename, f.f_lineno, func_name, None 88 else: 89 return co.co_filename, f.f_lineno, func_name
90 91 logging.setLoggerClass(RospyLogger) 92
93 -def renew_latest_logdir(logfile_dir):
94 log_dir = os.path.dirname(logfile_dir) 95 latest_dir = os.path.join(log_dir, 'latest') 96 if os.path.lexists(latest_dir): 97 if not os.path.islink(latest_dir): 98 return False 99 os.remove(latest_dir) 100 os.symlink(logfile_dir, latest_dir) 101 return True
102
103 -def configure_logging(logname, level=logging.INFO, filename=None, env=None):
104 """ 105 Configure Python logging package to send log files to ROS-specific log directory 106 :param logname str: name of logger, ``str`` 107 :param filename: filename to log to. If not set, a log filename 108 will be generated using logname, ``str`` 109 :param env: override os.environ dictionary, ``dict`` 110 :returns: log file name, ``str`` 111 :raises: :exc:`LoggingException` If logging cannot be configured as specified 112 """ 113 if env is None: 114 env = os.environ 115 116 logname = logname or 'unknown' 117 log_dir = rospkg.get_log_dir(env=env) 118 119 # if filename is not explicitly provided, generate one using logname 120 if not filename: 121 log_filename = os.path.join(log_dir, '%s-%s.log'%(logname, os.getpid())) 122 else: 123 log_filename = os.path.join(log_dir, filename) 124 125 logfile_dir = os.path.dirname(log_filename) 126 if not os.path.exists(logfile_dir): 127 try: 128 makedirs_with_parent_perms(logfile_dir) 129 except OSError: 130 # cannot print to screen because command-line tools with output use this 131 if os.path.exists(logfile_dir): 132 # We successfully created the logging folder, but could not change 133 # permissions of the new folder to the same as the parent folder 134 sys.stderr.write("WARNING: Could not change permissions for folder [%s], make sure that the parent folder has correct permissions.\n"%logfile_dir) 135 else: 136 # Could not create folder 137 sys.stderr.write("WARNING: cannot create log directory [%s]. Please set %s to a writable location.\n"%(logfile_dir, ROS_LOG_DIR)) 138 return None 139 elif os.path.isfile(logfile_dir): 140 raise LoggingException("Cannot save log files: file [%s] is in the way"%logfile_dir) 141 142 # the log dir itself should not be symlinked as latest 143 if logfile_dir != log_dir: 144 if sys.platform not in ['win32']: 145 try: 146 success = renew_latest_logdir(logfile_dir) 147 if not success: 148 sys.stderr.write("INFO: cannot create a symlink to latest log directory\n") 149 except OSError as e: 150 sys.stderr.write("INFO: cannot create a symlink to latest log directory: %s\n" % e) 151 152 config_file = os.environ.get('ROS_PYTHON_LOG_CONFIG_FILE') 153 if not config_file: 154 # search for logging config file in ROS_HOME, ROS_ETC_DIR or relative 155 # to the rosgraph package directory. 156 rosgraph_d = rospkg.RosPack().get_path('rosgraph') 157 for config_dir in [os.path.join(rospkg.get_ros_home(), 'config'), 158 rospkg.get_etc_ros_dir(), 159 os.path.join(rosgraph_d, 'conf')]: 160 for extension in ('conf', 'yaml'): 161 f = os.path.join(config_dir, 162 'python_logging{}{}'.format(os.path.extsep, 163 extension)) 164 if os.path.isfile(f): 165 config_file = f 166 break 167 if config_file is not None: 168 break 169 170 if config_file is None or not os.path.isfile(config_file): 171 # logging is considered soft-fail 172 sys.stderr.write("WARNING: cannot load logging configuration file, logging is disabled\n") 173 logging.getLogger(logname).setLevel(logging.CRITICAL) 174 return log_filename 175 176 # pass in log_filename as argument to pylogging.conf 177 os.environ['ROS_LOG_FILENAME'] = log_filename 178 if config_file.endswith(('.yaml', '.yml')): 179 with open(config_file) as f: 180 dict_conf = yaml.load(f) 181 dict_conf.setdefault('version', 1) 182 logging.config.dictConfig(dict_conf) 183 else: 184 # #3625: disabling_existing_loggers=False 185 logging.config.fileConfig(config_file, disable_existing_loggers=False) 186 187 return log_filename
188 189
190 -def makedirs_with_parent_perms(p):
191 """ 192 Create the directory using the permissions of the nearest 193 (existing) parent directory. This is useful for logging, where a 194 root process sometimes has to log in the user's space. 195 :param p: directory to create, ``str`` 196 """ 197 p = os.path.abspath(p) 198 parent = os.path.dirname(p) 199 # recurse upwards, checking to make sure we haven't reached the 200 # top 201 if not os.path.exists(p) and p and parent != p: 202 makedirs_with_parent_perms(parent) 203 s = os.stat(parent) 204 os.mkdir(p) 205 206 # if perms of new dir don't match, set anew 207 s2 = os.stat(p) 208 if s.st_uid != s2.st_uid or s.st_gid != s2.st_gid: 209 os.chown(p, s.st_uid, s.st_gid) 210 if s.st_mode != s2.st_mode: 211 os.chmod(p, s.st_mode)
212 213 _logging_to_rospy_names = { 214 'DEBUG': ('DEBUG', '\033[32m'), 215 'INFO': ('INFO', None), 216 'WARNING': ('WARN', '\033[33m'), 217 'ERROR': ('ERROR', '\033[31m'), 218 'CRITICAL': ('FATAL', '\033[31m') 219 } 220 _color_reset = '\033[0m' 221 _defaultFormatter = logging.Formatter() 222
223 -class RosStreamHandler(logging.Handler):
224 - def __init__(self, colorize=True, stdout=None, stderr=None):
225 super(RosStreamHandler, self).__init__() 226 self._stdout = stdout or sys.stdout 227 self._stderr = stderr or sys.stderr 228 self._colorize = colorize 229 try: 230 from rospy.rostime import get_time, is_wallclock 231 self._get_time = get_time 232 self._is_wallclock = is_wallclock 233 except ImportError: 234 self._get_time = None 235 self._is_wallclock = None
236
237 - def emit(self, record):
238 level, color = _logging_to_rospy_names[record.levelname] 239 record_message = _defaultFormatter.format(record) 240 msg = os.environ.get( 241 'ROSCONSOLE_FORMAT', '[${severity}] [${time}]: ${message}') 242 msg = msg.replace('${severity}', level) 243 msg = msg.replace('${message}', str(record_message)) 244 msg = msg.replace('${walltime}', '%f' % time.time()) 245 msg = msg.replace('${thread}', str(record.thread)) 246 msg = msg.replace('${logger}', str(record.name)) 247 msg = msg.replace('${file}', str(record.pathname)) 248 msg = msg.replace('${line}', str(record.lineno)) 249 msg = msg.replace('${function}', str(record.funcName)) 250 try: 251 from rospy import get_name 252 node_name = get_name() 253 except ImportError: 254 node_name = '<unknown_node_name>' 255 msg = msg.replace('${node}', node_name) 256 time_str = '%f' % time.time() 257 if self._get_time is not None and not self._is_wallclock(): 258 time_str += ', %f' % self._get_time() 259 msg = msg.replace('${time}', time_str) 260 msg += '\n' 261 if record.levelno < logging.WARNING: 262 self._write(self._stdout, msg, color) 263 else: 264 self._write(self._stderr, msg, color)
265
266 - def _write(self, fd, msg, color):
267 if self._colorize and color and hasattr(fd, 'isatty') and fd.isatty(): 268 msg = color + msg + _color_reset 269 fd.write(msg)
270