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 28from logging import DEBUG 29from logging import ERROR 30from logging import FATAL 31from logging import INFO 32from logging import WARN 33import threading 34 35import six 36 37from tensorflow.python.util.all_util import remove_undocumented 38from tensorflow.python.util.tf_export import tf_export 39 40 41# Don't use this directly. Use _get_logger() instead. 42_logger = None 43_logger_lock = threading.Lock() 44 45 46def _get_logger(): 47 global _logger 48 49 # Use double-checked locking to avoid taking lock unnecessarily. 50 if _logger: 51 return _logger 52 53 _logger_lock.acquire() 54 55 try: 56 if _logger: 57 return _logger 58 59 # Scope the TensorFlow logger to not conflict with users' loggers. 60 logger = _logging.getLogger('tensorflow') 61 62 # Don't further configure the TensorFlow logger if the root logger is 63 # already configured. This prevents double logging in those cases. 64 if not _logging.getLogger().handlers: 65 # Determine whether we are in an interactive environment 66 _interactive = False 67 try: 68 # This is only defined in interactive shells. 69 if _sys.ps1: _interactive = True 70 except AttributeError: 71 # Even now, we may be in an interactive shell with `python -i`. 72 _interactive = _sys.flags.interactive 73 74 # If we are in an interactive environment (like Jupyter), set loglevel 75 # to INFO and pipe the output to stdout. 76 if _interactive: 77 logger.setLevel(INFO) 78 _logging_target = _sys.stdout 79 else: 80 _logging_target = _sys.stderr 81 82 # Add the output handler. 83 _handler = _logging.StreamHandler(_logging_target) 84 _handler.setFormatter(_logging.Formatter(_logging.BASIC_FORMAT, None)) 85 logger.addHandler(_handler) 86 87 _logger = logger 88 return _logger 89 90 finally: 91 _logger_lock.release() 92 93 94@tf_export('logging.log') 95def log(level, msg, *args, **kwargs): 96 _get_logger().log(level, msg, *args, **kwargs) 97 98 99@tf_export('logging.debug') 100def debug(msg, *args, **kwargs): 101 _get_logger().debug(msg, *args, **kwargs) 102 103 104@tf_export('logging.error') 105def error(msg, *args, **kwargs): 106 _get_logger().error(msg, *args, **kwargs) 107 108 109@tf_export('logging.fatal') 110def fatal(msg, *args, **kwargs): 111 _get_logger().fatal(msg, *args, **kwargs) 112 113 114@tf_export('logging.info') 115def info(msg, *args, **kwargs): 116 _get_logger().info(msg, *args, **kwargs) 117 118 119@tf_export('logging.warn') 120def warn(msg, *args, **kwargs): 121 _get_logger().warn(msg, *args, **kwargs) 122 123 124@tf_export('logging.warning') 125def warning(msg, *args, **kwargs): 126 _get_logger().warning(msg, *args, **kwargs) 127 128 129_level_names = { 130 FATAL: 'FATAL', 131 ERROR: 'ERROR', 132 WARN: 'WARN', 133 INFO: 'INFO', 134 DEBUG: 'DEBUG', 135} 136 137# Mask to convert integer thread ids to unsigned quantities for logging 138# purposes 139_THREAD_ID_MASK = 2 * _sys.maxsize + 1 140 141_log_prefix = None # later set to google2_log_prefix 142 143# Counter to keep track of number of log entries per token. 144_log_counter_per_token = {} 145 146 147@tf_export('logging.TaskLevelStatusMessage') 148def TaskLevelStatusMessage(msg): 149 error(msg) 150 151 152@tf_export('logging.flush') 153def flush(): 154 raise NotImplementedError() 155 156 157# Code below is taken from pyglib/logging 158@tf_export('logging.vlog') 159def vlog(level, msg, *args, **kwargs): 160 _get_logger().log(level, msg, *args, **kwargs) 161 162 163def _GetNextLogCountPerToken(token): 164 """Wrapper for _log_counter_per_token. 165 166 Args: 167 token: The token for which to look up the count. 168 169 Returns: 170 The number of times this function has been called with 171 *token* as an argument (starting at 0) 172 """ 173 global _log_counter_per_token # pylint: disable=global-variable-not-assigned 174 _log_counter_per_token[token] = 1 + _log_counter_per_token.get(token, -1) 175 return _log_counter_per_token[token] 176 177 178@tf_export('logging.log_every_n') 179def log_every_n(level, msg, n, *args): 180 """Log 'msg % args' at level 'level' once per 'n' times. 181 182 Logs the 1st call, (N+1)st call, (2N+1)st call, etc. 183 Not threadsafe. 184 185 Args: 186 level: The level at which to log. 187 msg: The message to be logged. 188 n: The number of times this should be called before it is logged. 189 *args: The args to be substituted into the msg. 190 """ 191 count = _GetNextLogCountPerToken(_GetFileAndLine()) 192 log_if(level, msg, not (count % n), *args) 193 194 195@tf_export('logging.log_first_n') 196def log_first_n(level, msg, n, *args): # pylint: disable=g-bad-name 197 """Log 'msg % args' at level 'level' only first 'n' times. 198 199 Not threadsafe. 200 201 Args: 202 level: The level at which to log. 203 msg: The message to be logged. 204 n: The number of times this should be called before it is logged. 205 *args: The args to be substituted into the msg. 206 """ 207 count = _GetNextLogCountPerToken(_GetFileAndLine()) 208 log_if(level, msg, count < n, *args) 209 210 211@tf_export('logging.log_if') 212def log_if(level, msg, condition, *args): 213 """Log 'msg % args' at level 'level' only if condition is fulfilled.""" 214 if condition: 215 vlog(level, msg, *args) 216 217 218def _GetFileAndLine(): 219 """Returns (filename, linenumber) for the stack frame.""" 220 # Use sys._getframe(). This avoids creating a traceback object. 221 # pylint: disable=protected-access 222 f = _sys._getframe() 223 # pylint: enable=protected-access 224 our_file = f.f_code.co_filename 225 f = f.f_back 226 while f: 227 code = f.f_code 228 if code.co_filename != our_file: 229 return (code.co_filename, f.f_lineno) 230 f = f.f_back 231 return ('<unknown>', 0) 232 233 234def google2_log_prefix(level, timestamp=None, file_and_line=None): 235 """Assemble a logline prefix using the google2 format.""" 236 # pylint: disable=global-variable-not-assigned 237 global _level_names 238 # pylint: enable=global-variable-not-assigned 239 240 # Record current time 241 now = timestamp or _time.time() 242 now_tuple = _time.localtime(now) 243 now_microsecond = int(1e6 * (now % 1.0)) 244 245 (filename, line) = file_and_line or _GetFileAndLine() 246 basename = _os.path.basename(filename) 247 248 # Severity string 249 severity = 'I' 250 if level in _level_names: 251 severity = _level_names[level][0] 252 253 s = '%c%02d%02d %02d:%02d:%02d.%06d %5d %s:%d] ' % ( 254 severity, 255 now_tuple[1], # month 256 now_tuple[2], # day 257 now_tuple[3], # hour 258 now_tuple[4], # min 259 now_tuple[5], # sec 260 now_microsecond, 261 _get_thread_id(), 262 basename, 263 line) 264 265 return s 266 267 268@tf_export('logging.get_verbosity') 269def get_verbosity(): 270 """Return how much logging output will be produced.""" 271 return _get_logger().getEffectiveLevel() 272 273 274@tf_export('logging.set_verbosity') 275def set_verbosity(v): 276 """Sets the threshold for what messages will be logged.""" 277 _get_logger().setLevel(v) 278 279 280def _get_thread_id(): 281 """Get id of current thread, suitable for logging as an unsigned quantity.""" 282 # pylint: disable=protected-access 283 thread_id = six.moves._thread.get_ident() 284 # pylint:enable=protected-access 285 return thread_id & _THREAD_ID_MASK 286 287 288_log_prefix = google2_log_prefix 289 290# Controls which methods from pyglib.logging are available within the project. 291# Do not add methods here without also adding to platform/tf_logging.py. 292_allowed_symbols = [ 293 'DEBUG', 294 'ERROR', 295 'FATAL', 296 'INFO', 297 'TaskLevelStatusMessage', 298 'WARN', 299 'debug', 300 'error', 301 'fatal', 302 'flush', 303 'get_verbosity', 304 'info', 305 'log', 306 'log_if', 307 'log_every_n', 308 'log_first_n', 309 'set_verbosity', 310 'vlog', 311 'warn', 312 'warning', 313] 314 315tf_export('logging.DEBUG').export_constant(__name__, 'DEBUG') 316tf_export('logging.ERROR').export_constant(__name__, 'ERROR') 317tf_export('logging.FATAL').export_constant(__name__, 'FATAL') 318tf_export('logging.INFO').export_constant(__name__, 'INFO') 319tf_export('logging.WARN').export_constant(__name__, 'WARN') 320 321remove_undocumented(__name__, _allowed_symbols) 322