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