1# Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================== 15 16"""Logging utilities.""" 17# pylint: disable=unused-import 18# pylint: disable=g-bad-import-order 19# pylint: disable=invalid-name 20from __future__ import absolute_import 21from __future__ import division 22from __future__ import print_function 23 24import logging as _logging 25import os as _os 26import sys as _sys 27import time as _time 28import traceback as _traceback 29from logging import DEBUG 30from logging import ERROR 31from logging import FATAL 32from logging import INFO 33from logging import WARN 34import threading 35 36import six 37 38from tensorflow.python.util.tf_export import tf_export 39 40# Don't use this directly. Use get_logger() instead. 41_logger = None 42_logger_lock = threading.Lock() 43 44 45def _get_caller(offset=3): 46 """Returns a code and frame object for the lowest non-logging stack frame.""" 47 # Use sys._getframe(). This avoids creating a traceback object. 48 # pylint: disable=protected-access 49 f = _sys._getframe(offset) 50 # pylint: enable=protected-access 51 our_file = f.f_code.co_filename 52 f = f.f_back 53 while f: 54 code = f.f_code 55 if code.co_filename != our_file: 56 return code, f 57 f = f.f_back 58 return None, None 59 60# The definition of `findCaller` changed in Python 3.2, 61# and further changed in Python 3.8 62if _sys.version_info.major >= 3 and _sys.version_info.minor >= 8: 63 64 def _logger_find_caller(stack_info=False, stacklevel=1): # pylint: disable=g-wrong-blank-lines 65 code, frame = _get_caller(4) 66 sinfo = None 67 if stack_info: 68 sinfo = '\n'.join(_traceback.format_stack()) 69 if code: 70 return (code.co_filename, frame.f_lineno, code.co_name, sinfo) 71 else: 72 return '(unknown file)', 0, '(unknown function)', sinfo 73elif _sys.version_info.major >= 3 and _sys.version_info.minor >= 2: 74 75 def _logger_find_caller(stack_info=False): # pylint: disable=g-wrong-blank-lines 76 code, frame = _get_caller(4) 77 sinfo = None 78 if stack_info: 79 sinfo = '\n'.join(_traceback.format_stack()) 80 if code: 81 return (code.co_filename, frame.f_lineno, code.co_name, sinfo) 82 else: 83 return '(unknown file)', 0, '(unknown function)', sinfo 84else: 85 def _logger_find_caller(): # pylint: disable=g-wrong-blank-lines 86 code, frame = _get_caller(4) 87 if code: 88 return (code.co_filename, frame.f_lineno, code.co_name) 89 else: 90 return '(unknown file)', 0, '(unknown function)' 91 92 93@tf_export('get_logger') 94def get_logger(): 95 """Return TF logger instance.""" 96 global _logger 97 98 # Use double-checked locking to avoid taking lock unnecessarily. 99 if _logger: 100 return _logger 101 102 _logger_lock.acquire() 103 104 try: 105 if _logger: 106 return _logger 107 108 # Scope the TensorFlow logger to not conflict with users' loggers. 109 logger = _logging.getLogger('tensorflow') 110 111 # Override findCaller on the logger to skip internal helper functions 112 logger.findCaller = _logger_find_caller 113 114 # Don't further configure the TensorFlow logger if the root logger is 115 # already configured. This prevents double logging in those cases. 116 if not _logging.getLogger().handlers: 117 # Determine whether we are in an interactive environment 118 _interactive = False 119 try: 120 # This is only defined in interactive shells. 121 if _sys.ps1: _interactive = True 122 except AttributeError: 123 # Even now, we may be in an interactive shell with `python -i`. 124 _interactive = _sys.flags.interactive 125 126 # If we are in an interactive environment (like Jupyter), set loglevel 127 # to INFO and pipe the output to stdout. 128 if _interactive: 129 logger.setLevel(INFO) 130 _logging_target = _sys.stdout 131 else: 132 _logging_target = _sys.stderr 133 134 # Add the output handler. 135 _handler = _logging.StreamHandler(_logging_target) 136 _handler.setFormatter(_logging.Formatter(_logging.BASIC_FORMAT, None)) 137 logger.addHandler(_handler) 138 139 _logger = logger 140 return _logger 141 142 finally: 143 _logger_lock.release() 144 145 146@tf_export(v1=['logging.log']) 147def log(level, msg, *args, **kwargs): 148 get_logger().log(level, msg, *args, **kwargs) 149 150 151@tf_export(v1=['logging.debug']) 152def debug(msg, *args, **kwargs): 153 get_logger().debug(msg, *args, **kwargs) 154 155 156@tf_export(v1=['logging.error']) 157def error(msg, *args, **kwargs): 158 get_logger().error(msg, *args, **kwargs) 159 160 161@tf_export(v1=['logging.fatal']) 162def fatal(msg, *args, **kwargs): 163 get_logger().fatal(msg, *args, **kwargs) 164 165 166@tf_export(v1=['logging.info']) 167def info(msg, *args, **kwargs): 168 get_logger().info(msg, *args, **kwargs) 169 170 171@tf_export(v1=['logging.warn']) 172def warn(msg, *args, **kwargs): 173 get_logger().warning(msg, *args, **kwargs) 174 175 176@tf_export(v1=['logging.warning']) 177def warning(msg, *args, **kwargs): 178 get_logger().warning(msg, *args, **kwargs) 179 180 181_level_names = { 182 FATAL: 'FATAL', 183 ERROR: 'ERROR', 184 WARN: 'WARN', 185 INFO: 'INFO', 186 DEBUG: 'DEBUG', 187} 188 189# Mask to convert integer thread ids to unsigned quantities for logging 190# purposes 191_THREAD_ID_MASK = 2 * _sys.maxsize + 1 192 193_log_prefix = None # later set to google2_log_prefix 194 195# Counter to keep track of number of log entries per token. 196_log_counter_per_token = {} 197 198 199@tf_export(v1=['logging.TaskLevelStatusMessage']) 200def TaskLevelStatusMessage(msg): 201 error(msg) 202 203 204@tf_export(v1=['logging.flush']) 205def flush(): 206 raise NotImplementedError() 207 208 209# Code below is taken from pyglib/logging 210@tf_export(v1=['logging.vlog']) 211def vlog(level, msg, *args, **kwargs): 212 get_logger().log(level, msg, *args, **kwargs) 213 214 215def _GetNextLogCountPerToken(token): 216 """Wrapper for _log_counter_per_token. 217 218 Args: 219 token: The token for which to look up the count. 220 221 Returns: 222 The number of times this function has been called with 223 *token* as an argument (starting at 0) 224 """ 225 global _log_counter_per_token # pylint: disable=global-variable-not-assigned 226 _log_counter_per_token[token] = 1 + _log_counter_per_token.get(token, -1) 227 return _log_counter_per_token[token] 228 229 230@tf_export(v1=['logging.log_every_n']) 231def log_every_n(level, msg, n, *args): 232 """Log 'msg % args' at level 'level' once per 'n' times. 233 234 Logs the 1st call, (N+1)st call, (2N+1)st call, etc. 235 Not threadsafe. 236 237 Args: 238 level: The level at which to log. 239 msg: The message to be logged. 240 n: The number of times this should be called before it is logged. 241 *args: The args to be substituted into the msg. 242 """ 243 count = _GetNextLogCountPerToken(_GetFileAndLine()) 244 log_if(level, msg, not (count % n), *args) 245 246 247@tf_export(v1=['logging.log_first_n']) 248def log_first_n(level, msg, n, *args): # pylint: disable=g-bad-name 249 """Log 'msg % args' at level 'level' only first 'n' times. 250 251 Not threadsafe. 252 253 Args: 254 level: The level at which to log. 255 msg: The message to be logged. 256 n: The number of times this should be called before it is logged. 257 *args: The args to be substituted into the msg. 258 """ 259 count = _GetNextLogCountPerToken(_GetFileAndLine()) 260 log_if(level, msg, count < n, *args) 261 262 263@tf_export(v1=['logging.log_if']) 264def log_if(level, msg, condition, *args): 265 """Log 'msg % args' at level 'level' only if condition is fulfilled.""" 266 if condition: 267 vlog(level, msg, *args) 268 269 270def _GetFileAndLine(): 271 """Returns (filename, linenumber) for the stack frame.""" 272 code, f = _get_caller() 273 if not code: 274 return ('<unknown>', 0) 275 return (code.co_filename, f.f_lineno) 276 277 278def google2_log_prefix(level, timestamp=None, file_and_line=None): 279 """Assemble a logline prefix using the google2 format.""" 280 # pylint: disable=global-variable-not-assigned 281 global _level_names 282 # pylint: enable=global-variable-not-assigned 283 284 # Record current time 285 now = timestamp or _time.time() 286 now_tuple = _time.localtime(now) 287 now_microsecond = int(1e6 * (now % 1.0)) 288 289 (filename, line) = file_and_line or _GetFileAndLine() 290 basename = _os.path.basename(filename) 291 292 # Severity string 293 severity = 'I' 294 if level in _level_names: 295 severity = _level_names[level][0] 296 297 s = '%c%02d%02d %02d:%02d:%02d.%06d %5d %s:%d] ' % ( 298 severity, 299 now_tuple[1], # month 300 now_tuple[2], # day 301 now_tuple[3], # hour 302 now_tuple[4], # min 303 now_tuple[5], # sec 304 now_microsecond, 305 _get_thread_id(), 306 basename, 307 line) 308 309 return s 310 311 312@tf_export(v1=['logging.get_verbosity']) 313def get_verbosity(): 314 """Return how much logging output will be produced.""" 315 return get_logger().getEffectiveLevel() 316 317 318@tf_export(v1=['logging.set_verbosity']) 319def set_verbosity(v): 320 """Sets the threshold for what messages will be logged.""" 321 get_logger().setLevel(v) 322 323 324def _get_thread_id(): 325 """Get id of current thread, suitable for logging as an unsigned quantity.""" 326 # pylint: disable=protected-access 327 thread_id = six.moves._thread.get_ident() 328 # pylint:enable=protected-access 329 return thread_id & _THREAD_ID_MASK 330 331 332_log_prefix = google2_log_prefix 333 334tf_export(v1=['logging.DEBUG']).export_constant(__name__, 'DEBUG') 335tf_export(v1=['logging.ERROR']).export_constant(__name__, 'ERROR') 336tf_export(v1=['logging.FATAL']).export_constant(__name__, 'FATAL') 337tf_export(v1=['logging.INFO']).export_constant(__name__, 'INFO') 338tf_export(v1=['logging.WARN']).export_constant(__name__, 'WARN') 339