exelogging.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. """lvc.windows.exelogging -- handle logging inside an exe file
  2. Most of this is copied from the Miro code.
  3. """
  4. import logging
  5. import os
  6. import sys
  7. import tempfile
  8. from StringIO import StringIO
  9. from logging.handlers import RotatingFileHandler
  10. class ApatheticRotatingFileHandler(RotatingFileHandler):
  11. """The whole purpose of this class is to prevent rotation errors
  12. from percolating up into stdout/stderr and popping up a dialog
  13. that's not particularly useful to users or us.
  14. """
  15. def doRollover(self):
  16. # If you shut down LibreVideoConverter then
  17. # start it up again immediately afterwards,
  18. # then we get in this squirrely situation where
  19. # the log is opened by another process. We ignore the
  20. # exception, but make sure we have an open file. (bug #11228)
  21. try:
  22. RotatingFileHandler.doRollover(self)
  23. except WindowsError:
  24. if not self.stream or self.stream.closed:
  25. self.stream = open(self.baseFilename, "a")
  26. try:
  27. RotatingFileHandler.doRollover(self)
  28. except WindowsError:
  29. pass
  30. def shouldRollover(self, record):
  31. # if doRollover doesn't work, then we don't want to find
  32. # ourselves in a situation where we're trying to do things on
  33. # a closed stream.
  34. if self.stream.closed:
  35. self.stream = open(self.baseFilename, "a")
  36. return RotatingFileHandler.shouldRollover(self, record)
  37. def handleError(self, record):
  38. # ignore logging errors that occur rather than printing them to
  39. # stdout/stderr which isn't helpful to us
  40. pass
  41. class AutoLoggingStream(StringIO):
  42. """Create a stream that intercepts write calls and sends them to
  43. the log.
  44. """
  45. def __init__(self, logging_callback, prefix):
  46. StringIO.__init__(self)
  47. # We init from StringIO to give us a bunch of stream-related
  48. # methods, like closed() and read() automatically.
  49. self.logging_callback = logging_callback
  50. self.prefix = prefix
  51. def write(self, data):
  52. if isinstance(data, unicode):
  53. data = data.encode('ascii', 'backslashreplace')
  54. if data.endswith("\n"):
  55. data = data[:-1]
  56. if data:
  57. self.logging_callback(self.prefix + data)
  58. FORMAT = "%(asctime)s %(levelname)-8s %(name)s: %(message)s"
  59. def setup_logging():
  60. """Setup logging for when we're running inside a windows exe.
  61. The object here is to avoid logging anything to stderr since
  62. windows will consider that an error.
  63. We also catch things written to sys.stdout and forward that to the logging
  64. system.
  65. Finally we also copy the log output to stdout so that when MVC is run in
  66. console mode we see the logs
  67. """
  68. log_path = os.path.join(tempfile.gettempdir(), "MVC.log")
  69. rotater = ApatheticRotatingFileHandler(
  70. log_path, mode="a", maxBytes=100000, backupCount=5)
  71. formatter = logging.Formatter(FORMAT)
  72. rotater.setFormatter(formatter)
  73. logger = logging.getLogger('')
  74. logger.addHandler(rotater)
  75. logger.addHandler(logging.StreamHandler(sys.stdout))
  76. logger.setLevel(logging.INFO)
  77. rotater.doRollover()
  78. sys.stdout = AutoLoggingStream(logging.warn, '(from stdout) ')
  79. sys.stderr = AutoLoggingStream(logging.error, '(from stderr) ')