logger.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. """
  2. Helpers for logging.
  3. This module needs much love to become useful.
  4. """
  5. # Author: Gael Varoquaux <gael dot varoquaux at normalesup dot org>
  6. # Copyright (c) 2008 Gael Varoquaux
  7. # License: BSD Style, 3 clauses.
  8. from __future__ import print_function
  9. import logging
  10. import os
  11. import pprint
  12. import shutil
  13. import sys
  14. import time
  15. from .disk import mkdirp
  16. def _squeeze_time(t):
  17. """Remove .1s to the time under Windows: this is the time it take to
  18. stat files. This is needed to make results similar to timings under
  19. Unix, for tests
  20. """
  21. if sys.platform.startswith("win"):
  22. return max(0, t - 0.1)
  23. else:
  24. return t
  25. def format_time(t):
  26. t = _squeeze_time(t)
  27. return "%.1fs, %.1fmin" % (t, t / 60.0)
  28. def short_format_time(t):
  29. t = _squeeze_time(t)
  30. if t > 60:
  31. return "%4.1fmin" % (t / 60.0)
  32. else:
  33. return " %5.1fs" % (t)
  34. def pformat(obj, indent=0, depth=3):
  35. if "numpy" in sys.modules:
  36. import numpy as np
  37. print_options = np.get_printoptions()
  38. np.set_printoptions(precision=6, threshold=64, edgeitems=1)
  39. else:
  40. print_options = None
  41. out = pprint.pformat(obj, depth=depth, indent=indent)
  42. if print_options:
  43. np.set_printoptions(**print_options)
  44. return out
  45. ###############################################################################
  46. # class `Logger`
  47. ###############################################################################
  48. class Logger(object):
  49. """Base class for logging messages."""
  50. def __init__(self, depth=3, name=None):
  51. """
  52. Parameters
  53. ----------
  54. depth: int, optional
  55. The depth of objects printed.
  56. name: str, optional
  57. The namespace to log to. If None, defaults to joblib.
  58. """
  59. self.depth = depth
  60. self._name = name if name else "joblib"
  61. def warn(self, msg):
  62. logging.getLogger(self._name).warning("[%s]: %s" % (self, msg))
  63. def info(self, msg):
  64. logging.info("[%s]: %s" % (self, msg))
  65. def debug(self, msg):
  66. # XXX: This conflicts with the debug flag used in children class
  67. logging.getLogger(self._name).debug("[%s]: %s" % (self, msg))
  68. def format(self, obj, indent=0):
  69. """Return the formatted representation of the object."""
  70. return pformat(obj, indent=indent, depth=self.depth)
  71. ###############################################################################
  72. # class `PrintTime`
  73. ###############################################################################
  74. class PrintTime(object):
  75. """Print and log messages while keeping track of time."""
  76. def __init__(self, logfile=None, logdir=None):
  77. if logfile is not None and logdir is not None:
  78. raise ValueError("Cannot specify both logfile and logdir")
  79. # XXX: Need argument docstring
  80. self.last_time = time.time()
  81. self.start_time = self.last_time
  82. if logdir is not None:
  83. logfile = os.path.join(logdir, "joblib.log")
  84. self.logfile = logfile
  85. if logfile is not None:
  86. mkdirp(os.path.dirname(logfile))
  87. if os.path.exists(logfile):
  88. # Rotate the logs
  89. for i in range(1, 9):
  90. try:
  91. shutil.move(logfile + ".%i" % i, logfile + ".%i" % (i + 1))
  92. except: # noqa: E722
  93. "No reason failing here"
  94. # Use a copy rather than a move, so that a process
  95. # monitoring this file does not get lost.
  96. try:
  97. shutil.copy(logfile, logfile + ".1")
  98. except: # noqa: E722
  99. "No reason failing here"
  100. try:
  101. with open(logfile, "w") as logfile:
  102. logfile.write("\nLogging joblib python script\n")
  103. logfile.write("\n---%s---\n" % time.ctime(self.last_time))
  104. except: # noqa: E722
  105. """ Multiprocessing writing to files can create race
  106. conditions. Rather fail silently than crash the
  107. computation.
  108. """
  109. # XXX: We actually need a debug flag to disable this
  110. # silent failure.
  111. def __call__(self, msg="", total=False):
  112. """Print the time elapsed between the last call and the current
  113. call, with an optional message.
  114. """
  115. if not total:
  116. time_lapse = time.time() - self.last_time
  117. full_msg = "%s: %s" % (msg, format_time(time_lapse))
  118. else:
  119. # FIXME: Too much logic duplicated
  120. time_lapse = time.time() - self.start_time
  121. full_msg = "%s: %.2fs, %.1f min" % (msg, time_lapse, time_lapse / 60)
  122. print(full_msg, file=sys.stderr)
  123. if self.logfile is not None:
  124. try:
  125. with open(self.logfile, "a") as f:
  126. print(full_msg, file=f)
  127. except: # noqa: E722
  128. """ Multiprocessing writing to files can create race
  129. conditions. Rather fail silently than crash the
  130. calculation.
  131. """
  132. # XXX: We actually need a debug flag to disable this
  133. # silent failure.
  134. self.last_time = time.time()