text: from __future__ import print_function
  from logging import getLogger
  from contextlib import contextmanager
  from infi.traceback import traceback_decorator
  from .util import LOGGING_FORMATTER_KWARGS, STRFTIME_SHORT, get_timestamp
  
  logger = getLogger(__name__)
  
  
  @contextmanager
  def create_logging_handler_for_collection(tempdir, prefix):
      from sys import maxsize
      from os import path
      from logging import FileHandler, DEBUG, Formatter
      from logging.handlers import MemoryHandler
      target = FileHandler(path.join(tempdir, "collection-logs", "{}.{}.debug.log".format(prefix, get_timestamp())))
      target.setFormatter(Formatter(**LOGGING_FORMATTER_KWARGS))
      handler = MemoryHandler(maxsize, target=target)
      handler.setLevel(DEBUG)
      try:
          yield handler
      finally:
          handler.close()
          target.close()
  
  @contextmanager
  def create_temporary_directory_for_log_collection(creation_dir, parent_dir_name, timestamp):
      from tempfile import mkdtemp
      from shutil import rmtree
      from os import path, makedirs
      from socket import gethostname
      tempdir = mkdtemp(dir=creation_dir)
      collect_dir = path.join(tempdir, parent_dir_name)
      specific_dir = path.join(collect_dir, gethostname(), timestamp.strftime(STRFTIME_SHORT))
      for dirname in ["commands", "files", "collection-logs"]:
          makedirs(path.join(specific_dir, dirname))
      def onerror(function, path, exc_info):
          logger.debug("Failed to delete {!r}".format(path))
      try:
          yield collect_dir, specific_dir
      finally:
          rmtree(tempdir, onerror=onerror)
  
  
  def get_tar_path(prefix, output_path, timestamp, creation_dir=None):
      import os
      from tempfile import mkstemp
      fd, archive_path = mkstemp(suffix=".tar.gz", prefix="{}-logs.{}-".format(
          prefix, timestamp.strftime(STRFTIME_SHORT)), dir=creation_dir)
      os.close(fd)
      os.remove(archive_path)
  
      if output_path is None:
          # if output_path was not given, set it to the archive_path we just calculated:
          output_path = archive_path
  
      # if a relative path is given an input:
      output_path = os.path.abspath(output_path)
  
      if os.path.isdir(output_path):
          # if output_path was set to a dir, set the output file dirname to this directory, and the output file path to a
          # concatenation of this dir and the archive name:
          dirname = output_path
          output_path = os.path.join(output_path, os.path.basename(archive_path))
      else:
          # if output_path was set to a filename, set the output file dirname to the dir name of this path:
          dirname = os.path.dirname(output_path)
  
      if not os.path.isdir(dirname):
          # if the dir the output file is supposed to be created in does not exist - abort:
          raise ValueError("Output path does not exist: {0}".format(dirname))
      if not os.access(dirname, os.W_OK):
          # if the dir the output file is supposed to be created in does not have write permissions - abort:
          raise ValueError("Output path has no write permissions: {0}".format(dirname))
  
      return output_path
  
  
  @contextmanager
  def log_collection_context(logging_memory_handler, tempdir, prefix, timestamp, output_path=None, creation_dir=None):
      from logging import root, DEBUG
      path = get_tar_path(prefix, output_path, timestamp, creation_dir)
      root.addHandler(logging_memory_handler)
      root.setLevel(DEBUG)
      try:
          yield path
      finally:
          with open_archive(path) as archive:
              root.removeHandler(logging_memory_handler)
              logging_memory_handler.flush()
              logging_memory_handler.close()
              add_directory(archive, tempdir)
              print("Logs collected successfully to {}".format(path))
  
  
  @contextmanager
  def open_archive(path):
      from tarfile import TarFile
      archive = TarFile.open(name=path, mode="w:gz", bufsize=16*1024)
      try:
          yield archive
      finally:
          archive.close()
  
  
  def workaround_issue_10760(srcdir):
      # WORKAROUND for http://bugs.python.org/issue10760
      # Python's TarFile has issues with files have less data than the reported size
      # The workaround we did back in 2010 was to wrap TarFile objects with methods that work around that case,
      # But due to the structure of tar files, the workaround was a bit cumbersome
      # This time around, since we're already copying aside what we want to put in the archive, we can fix the files
      # before adding them to the archive
      from os import path, walk, stat
      for dirpath, dirnames, filenames in walk(srcdir):
          for filename in filenames:
              filepath = path.join(dirpath, filename)
              expected = stat(filepath).st_size
              actual = 0
              with open(filepath, 'rb') as fd:
                  actual = bytes_read = len(fd.read(512))
                  while bytes_read == 512:
                      bytes_read = len(fd.read(512))
                      actual += bytes_read
              if actual < expected:
                  with open(filepath, 'ab') as fd:
                      fd.write(b'\x00' * (expected-actual))
  
  
  def add_directory(archive, srcdir):
      from os.path import basename
      try:
          workaround_issue_10760(srcdir)
      except OSError:
          logger.exception("OSError")
      archive.add(srcdir, basename(srcdir))
  
  
  def user_wants_to_collect(item):
      import six
      return six.moves.input('Do you want to collect {} [y/N]? '.format(item)).lower() in ('y', 'yes')
  
  
  def collect(item, tempdir, timestamp, delta, silent, interactive=False):
      from colorama import Fore
      from sys import stdout
      if interactive and not user_wants_to_collect(item):
          return
      logger.info("Collecting {!r}".format(item))
      if not silent:
          print("Collecting {} ... ".format(item), end='')
      try:
          stdout.flush()
      except:
          pass # ignore "IOError: [Errno 9] Bad file descriptor" when running through the GUI on Windows
      try:
          item.collect(tempdir, timestamp, delta)
          logger.info("Collected  {!r} successfully".format(item))
          if not silent:
              print(Fore.GREEN + "ok" + Fore.RESET)
          return True
      except:
          logger.exception("An error ocurred while collecting {!r}".format(item))
          if not silent:
              print(Fore.MAGENTA + "error" + Fore.RESET)
          return False
  
  
  @traceback_decorator
  def run(prefix, items, timestamp, delta, output_path=None, creation_dir=None, parent_dir_name="logs", silent=False, interactive=False):
      """ collects log items and creates an archive with all collected items.
      items is a list of instances of 'Item' subclasses (see the collectables submodule).
      timestamp and delta indicate the timeframe of logs that need to be collected.
      The timeframe starts at (timestamp - delta) and ends at (timestamp).
      creation_dir is a path to a directory where the logs will be collected in. The function will create a temporary
      directory and then remove it when done. It will also be used for the final archive path, unless specified
      otherwise by output_path.
      This parameter is optional and will default to the system temporary directory, as decided by the tempfile module.
      output_path indicates the path where the final archive will be created. This may be a directory path or a full
      filename path.
      By default, the system uses the creation_dir for the directory, and generates an archive name using 'prefix'
      and the current time.
      parent_dir_name is the name of the parent directory that will be created inside the output archive.
      silent specified whether or not to print the process to stdout. pass True to silence the prints. The process
      will still be logged to a file under 'collection-logs' in the creation directory. """
      end_result = True
      with create_temporary_directory_for_log_collection(creation_dir, parent_dir_name, timestamp) as (tempdir, runtime_dir):
          with create_logging_handler_for_collection(runtime_dir, prefix) as handler:
              with log_collection_context(handler, tempdir, prefix, timestamp, output_path, creation_dir) as archive_path:
                  kwargs = dict(prefix=prefix, timestamp=timestamp, delta=delta, output_path=output_path,
                                creation_dir=creation_dir, parent_dir_name=parent_dir_name)
                  logger.info("Starting log collection with kwargs {!r}".format(kwargs))
                  for item in items:
                      result = collect(item, runtime_dir, timestamp, delta, silent, interactive)
                      end_result = end_result and result
                  end_result = 0 if end_result else 1
                  return end_result, archive_path
  
  
  # TODO A web frontend that parser log collections
  # Get by ftp
  # Web frontend:
  #     additional metadata -- description, tags, resolved (t/f)
  #     sortable (customer, date, resolved/not)
  #     link to JIRA
  #     delete/bulk-delete
  #     authentication
  #     automatic analysis (e.g. not most recent version of power tools)
  # View:
  #     links to extracted files
  