• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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