Browse Source

use a module-specific logger instead of global one

that way we have one logger per module, and we can pick and choose
which module we want verbose, for example
Antoine Beaupré 10 years ago
parent
commit
c9b11316ab
7 changed files with 83 additions and 53 deletions
  1. 4 3
      borg/archive.py
  2. 33 30
      borg/archiver.py
  3. 11 9
      borg/cache.py
  4. 2 0
      borg/helpers.py
  5. 11 9
      borg/key.py
  6. 4 2
      borg/repository.py
  7. 18 0
      borg/testsuite/logger.py

+ 4 - 3
borg/archive.py

@@ -3,6 +3,7 @@ from getpass import getuser
 from itertools import groupby
 from itertools import groupby
 import errno
 import errno
 import logging
 import logging
+logger = logging.getLogger(__name__)
 
 
 from .key import key_factory
 from .key import key_factory
 from .remote import cache_if_remote
 from .remote import cache_if_remote
@@ -630,7 +631,7 @@ class ArchiveChecker:
         self.orphan_chunks_check()
         self.orphan_chunks_check()
         self.finish()
         self.finish()
         if not self.error_found:
         if not self.error_found:
-            logging.info('Archive consistency check complete, no problems found.')
+            logger.info('Archive consistency check complete, no problems found.')
         return self.repair or not self.error_found
         return self.repair or not self.error_found
 
 
     def init_chunks(self):
     def init_chunks(self):
@@ -652,7 +653,7 @@ class ArchiveChecker:
     def report_progress(self, msg, error=False):
     def report_progress(self, msg, error=False):
         if error:
         if error:
             self.error_found = True
             self.error_found = True
-        logging.log(logging.ERROR if error else logging.WARNING, msg)
+        logger.log(logging.ERROR if error else logging.WARNING, msg)
 
 
     def identify_key(self, repository):
     def identify_key(self, repository):
         cdata = repository.get(next(self.chunks.iteritems())[0])
         cdata = repository.get(next(self.chunks.iteritems())[0])
@@ -779,7 +780,7 @@ class ArchiveChecker:
             num_archives = 1
             num_archives = 1
             end = 1
             end = 1
         for i, (name, info) in enumerate(archive_items[:end]):
         for i, (name, info) in enumerate(archive_items[:end]):
-            logging.info('Analyzing archive {} ({}/{})'.format(name, num_archives - i, num_archives))
+            logger.info('Analyzing archive {} ({}/{})'.format(name, num_archives - i, num_archives))
             archive_id = info[b'id']
             archive_id = info[b'id']
             if archive_id not in self.chunks:
             if archive_id not in self.chunks:
                 self.report_progress('Archive metadata block is missing', error=True)
                 self.report_progress('Archive metadata block is missing', error=True)

+ 33 - 30
borg/archiver.py

@@ -8,6 +8,8 @@ import functools
 import inspect
 import inspect
 import io
 import io
 import logging
 import logging
+logger = logging.getLogger(__name__)
+
 import os
 import os
 import signal
 import signal
 import stat
 import stat
@@ -47,11 +49,11 @@ class Archiver:
     def print_error(self, msg, *args):
     def print_error(self, msg, *args):
         msg = args and msg % args or msg
         msg = args and msg % args or msg
         self.exit_code = 1
         self.exit_code = 1
-        logging.error('borg: ' + msg)
+        logger.error('borg: ' + msg)
 
 
     def print_verbose(self, msg, *args, **kw):
     def print_verbose(self, msg, *args, **kw):
         msg = args and msg % args or msg
         msg = args and msg % args or msg
-        logging.info(msg)
+        logger.info(msg)
 
 
     def do_serve(self, args):
     def do_serve(self, args):
         """Start in server mode. This command is usually not used manually.
         """Start in server mode. This command is usually not used manually.
@@ -60,7 +62,7 @@ class Archiver:
 
 
     def do_init(self, args):
     def do_init(self, args):
         """Initialize an empty repository"""
         """Initialize an empty repository"""
-        logging.info('Initializing repository at "%s"' % args.repository.orig)
+        logger.info('Initializing repository at "%s"' % args.repository.orig)
         repository = self.open_repository(args.repository, create=True, exclusive=True)
         repository = self.open_repository(args.repository, create=True, exclusive=True)
         key = key_creator(repository, args)
         key = key_creator(repository, args)
         manifest = Manifest(key, repository)
         manifest = Manifest(key, repository)
@@ -82,9 +84,9 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
                 if input('Do you want to continue? ') == 'Yes I am sure':
                 if input('Do you want to continue? ') == 'Yes I am sure':
                     break
                     break
         if not args.archives_only:
         if not args.archives_only:
-            logging.warning('Starting repository check...')
+            logger.warning('Starting repository check...')
             if repository.check(repair=args.repair):
             if repository.check(repair=args.repair):
-                logging.info('Repository check complete, no problems found.')
+                logger.info('Repository check complete, no problems found.')
             else:
             else:
                 return 1
                 return 1
         if not args.repo_only and not ArchiveChecker().check(
         if not args.repo_only and not ArchiveChecker().check(
@@ -160,15 +162,15 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
             if args.stats:
             if args.stats:
                 t = datetime.now()
                 t = datetime.now()
                 diff = t - t0
                 diff = t - t0
-                logging.warning('-' * 78)
-                logging.warning('Archive name: %s' % args.archive.archive)
-                logging.warning('Archive fingerprint: %s' % hexlify(archive.id).decode('ascii'))
-                logging.warning('Start time: %s' % t0.strftime('%c'))
-                logging.warning('End time: %s' % t.strftime('%c'))
-                logging.warning('Duration: %s' % format_timedelta(diff))
-                logging.warning('Number of files: %d' % archive.stats.nfiles)
-                logging.warning(archive.stats.print_('This archive:', cache))
-                logging.warning('-' * 78)
+                logger.warning('-' * 78)
+                logger.warning('Archive name: %s' % args.archive.archive)
+                logger.warning('Archive fingerprint: %s' % hexlify(archive.id).decode('ascii'))
+                logger.warning('Start time: %s' % t0.strftime('%c'))
+                logger.warning('End time: %s' % t.strftime('%c'))
+                logger.warning('Duration: %s' % format_timedelta(diff))
+                logger.warning('Number of files: %d' % archive.stats.nfiles)
+                logger.warning(archive.stats.print_('This archive:', cache))
+                logger.warning('-' * 78)
         return self.exit_code
         return self.exit_code
 
 
     def _process(self, archive, cache, excludes, exclude_caches, skip_inodes, path, restrict_dev,
     def _process(self, archive, cache, excludes, exclude_caches, skip_inodes, path, restrict_dev,
@@ -247,7 +249,7 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
         """Extract archive contents"""
         """Extract archive contents"""
         # be restrictive when restoring files, restore permissions later
         # be restrictive when restoring files, restore permissions later
         if sys.getfilesystemencoding() == 'ascii':
         if sys.getfilesystemencoding() == 'ascii':
-            logging.warning('Warning: File system encoding is "ascii", extracting non-ascii filenames will not be supported.')
+            logger.warning('Warning: File system encoding is "ascii", extracting non-ascii filenames will not be supported.')
         repository = self.open_repository(args.archive)
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         manifest, key = Manifest.load(repository)
         archive = Archive(repository, key, manifest, args.archive.archive,
         archive = Archive(repository, key, manifest, args.archive.archive,
@@ -313,11 +315,11 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
             repository.commit()
             repository.commit()
             cache.commit()
             cache.commit()
             if args.stats:
             if args.stats:
-                logging.warning(stats.print_('Deleted data:', cache))
+                logger.warning(stats.print_('Deleted data:', cache))
         else:
         else:
-            logging.warning("You requested to completely DELETE the repository *including* all archives it contains:")
+            logger.warning("You requested to completely DELETE the repository *including* all archives it contains:")
             for archive_info in manifest.list_archive_infos(sort_by='ts'):
             for archive_info in manifest.list_archive_infos(sort_by='ts'):
-                logging.warning(format_archive(archive_info))
+                logger.warning(format_archive(archive_info))
             if not os.environ.get('BORG_CHECK_I_KNOW_WHAT_I_AM_DOING'):
             if not os.environ.get('BORG_CHECK_I_KNOW_WHAT_I_AM_DOING'):
                 print("""Type "YES" if you understand this and want to continue.""")
                 print("""Type "YES" if you understand this and want to continue.""")
                 if input('Do you want to continue? ') != 'YES':
                 if input('Do you want to continue? ') != 'YES':
@@ -325,7 +327,7 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
                     return self.exit_code
                     return self.exit_code
             repository.destroy()
             repository.destroy()
             cache.destroy()
             cache.destroy()
-            logging.info("Repository and corresponding cache were deleted.")
+            logger.info("Repository and corresponding cache were deleted.")
         return self.exit_code
         return self.exit_code
 
 
     def do_mount(self, args):
     def do_mount(self, args):
@@ -405,14 +407,14 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
         cache = Cache(repository, key, manifest, do_files=args.cache_files)
         cache = Cache(repository, key, manifest, do_files=args.cache_files)
         archive = Archive(repository, key, manifest, args.archive.archive, cache=cache)
         archive = Archive(repository, key, manifest, args.archive.archive, cache=cache)
         stats = archive.calc_stats(cache)
         stats = archive.calc_stats(cache)
-        logging.warning('Name:', archive.name)
-        logging.warning('Fingerprint: %s' % hexlify(archive.id).decode('ascii'))
-        logging.warning('Hostname:', archive.metadata[b'hostname'])
-        logging.warning('Username:', archive.metadata[b'username'])
-        logging.warning('Time: %s' % to_localtime(archive.ts).strftime('%c'))
-        logging.warning('Command line:', remove_surrogates(' '.join(archive.metadata[b'cmdline'])))
-        logging.warning('Number of files: %d' % stats.nfiles)
-        logging.warning(stats.print_('This archive:', cache))
+        logger.warning('Name:', archive.name)
+        logger.warning('Fingerprint: %s' % hexlify(archive.id).decode('ascii'))
+        logger.warning('Hostname:', archive.metadata[b'hostname'])
+        logger.warning('Username:', archive.metadata[b'username'])
+        logger.warning('Time: %s' % to_localtime(archive.ts).strftime('%c'))
+        logger.warning('Command line:', remove_surrogates(' '.join(archive.metadata[b'cmdline'])))
+        logger.warning('Number of files: %d' % stats.nfiles)
+        logger.warning(stats.print_('This archive:', cache))
         return self.exit_code
         return self.exit_code
 
 
     def do_prune(self, args):
     def do_prune(self, args):
@@ -457,7 +459,7 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
             repository.commit()
             repository.commit()
             cache.commit()
             cache.commit()
         if args.stats:
         if args.stats:
-            logging.warning(stats.print_('Deleted data:', cache))
+            logger.warning(stats.print_('Deleted data:', cache))
         return self.exit_code
         return self.exit_code
 
 
     helptext = {}
     helptext = {}
@@ -511,6 +513,7 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
         logging.raiseExceptions = False
         logging.raiseExceptions = False
         l = logging.getLogger('')
         l = logging.getLogger('')
         sh = logging.StreamHandler(sys.stderr)
         sh = logging.StreamHandler(sys.stderr)
+        sh.setFormatter(logging.Formatter('%(name)s: %(message)s'))
         l.addHandler(sh)
         l.addHandler(sh)
         levels = { None: logging.WARNING,
         levels = { None: logging.WARNING,
            0: logging.WARNING,
            0: logging.WARNING,
@@ -936,7 +939,7 @@ def sig_info_handler(signum, stack):  # pragma: no cover
                 total = loc['st'].st_size
                 total = loc['st'].st_size
             except Exception:
             except Exception:
                 pos, total = 0, 0
                 pos, total = 0, 0
-            logging.warning("{0} {1}/{2}".format(path, format_file_size(pos), format_file_size(total)))
+            logger.warning("{0} {1}/{2}".format(path, format_file_size(pos), format_file_size(total)))
             break
             break
         if func in ('extract_item', ):  # extract op
         if func in ('extract_item', ):  # extract op
             path = loc['item'][b'path']
             path = loc['item'][b'path']
@@ -944,7 +947,7 @@ def sig_info_handler(signum, stack):  # pragma: no cover
                 pos = loc['fd'].tell()
                 pos = loc['fd'].tell()
             except Exception:
             except Exception:
                 pos = 0
                 pos = 0
-            logging.warning("{0} {1}/???".format(path, format_file_size(pos)))
+            logger.warning("{0} {1}/???".format(path, format_file_size(pos)))
             break
             break
 
 
 
 

+ 11 - 9
borg/cache.py

@@ -2,6 +2,8 @@ from configparser import RawConfigParser
 from .remote import cache_if_remote
 from .remote import cache_if_remote
 import errno
 import errno
 import logging
 import logging
+logger = logging.getLogger(__name__)
+
 import msgpack
 import msgpack
 import os
 import os
 import stat
 import stat
@@ -72,9 +74,9 @@ class Cache:
         self.close()
         self.close()
 
 
     def _confirm(self, message, env_var_override=None):
     def _confirm(self, message, env_var_override=None):
-        logging.warning(message)
+        logger.warning(message)
         if env_var_override and os.environ.get(env_var_override):
         if env_var_override and os.environ.get(env_var_override):
-            logging.warning("Yes (From {})".format(env_var_override))
+            logger.warning("Yes (From {})".format(env_var_override))
             return True
             return True
         if not sys.stdin.isatty():
         if not sys.stdin.isatty():
             return False
             return False
@@ -254,7 +256,7 @@ class Cache:
                 unpacker.feed(data)
                 unpacker.feed(data)
                 for item in unpacker:
                 for item in unpacker:
                     if not isinstance(item, dict):
                     if not isinstance(item, dict):
-                        logging.error('Error: Did not get expected metadata dict - archive corrupted!')
+                        logger.error('Error: Did not get expected metadata dict - archive corrupted!')
                         continue
                         continue
                     if b'chunks' in item:
                     if b'chunks' in item:
                         for chunk_id, size, csize in item[b'chunks']:
                         for chunk_id, size, csize in item[b'chunks']:
@@ -275,10 +277,10 @@ class Cache:
                     return name
                     return name
 
 
         def create_master_idx(chunk_idx):
         def create_master_idx(chunk_idx):
-            logging.info('Synchronizing chunks cache...')
+            logger.info('Synchronizing chunks cache...')
             cached_ids = cached_archives()
             cached_ids = cached_archives()
             archive_ids = repo_archives()
             archive_ids = repo_archives()
-            logging.info('Archives: %d, w/ cached Idx: %d, w/ outdated Idx: %d, w/o cached Idx: %d.' % (
+            logger.info('Archives: %d, w/ cached Idx: %d, w/ outdated Idx: %d, w/o cached Idx: %d.' % (
                 len(archive_ids), len(cached_ids),
                 len(archive_ids), len(cached_ids),
                 len(cached_ids - archive_ids), len(archive_ids - cached_ids), ))
                 len(cached_ids - archive_ids), len(archive_ids - cached_ids), ))
             # deallocates old hashindex, creates empty hashindex:
             # deallocates old hashindex, creates empty hashindex:
@@ -290,12 +292,12 @@ class Cache:
                     archive_name = lookup_name(archive_id)
                     archive_name = lookup_name(archive_id)
                     if archive_id in cached_ids:
                     if archive_id in cached_ids:
                         archive_chunk_idx_path = mkpath(archive_id)
                         archive_chunk_idx_path = mkpath(archive_id)
-                        logging.info("Reading cached archive chunk index for %s ..." % archive_name)
+                        logger.info("Reading cached archive chunk index for %s ..." % archive_name)
                         archive_chunk_idx = ChunkIndex.read(archive_chunk_idx_path)
                         archive_chunk_idx = ChunkIndex.read(archive_chunk_idx_path)
                     else:
                     else:
-                        logging.info('Fetching and building archive index for %s ...' % archive_name)
+                        logger.info('Fetching and building archive index for %s ...' % archive_name)
                         archive_chunk_idx = fetch_and_build_idx(archive_id, repository, self.key)
                         archive_chunk_idx = fetch_and_build_idx(archive_id, repository, self.key)
-                    logging.info("Merging into master chunks index ...")
+                    logger.info("Merging into master chunks index ...")
                     if chunk_idx is None:
                     if chunk_idx is None:
                         # we just use the first archive's idx as starting point,
                         # we just use the first archive's idx as starting point,
                         # to avoid growing the hash table from 0 size and also
                         # to avoid growing the hash table from 0 size and also
@@ -303,7 +305,7 @@ class Cache:
                         chunk_idx = archive_chunk_idx
                         chunk_idx = archive_chunk_idx
                     else:
                     else:
                         chunk_idx.merge(archive_chunk_idx)
                         chunk_idx.merge(archive_chunk_idx)
-            logging.info('Done.')
+            logger.info('Done.')
             return chunk_idx
             return chunk_idx
 
 
         def legacy_cleanup():
         def legacy_cleanup():

+ 2 - 0
borg/helpers.py

@@ -6,6 +6,8 @@ from collections import namedtuple
 from functools import wraps
 from functools import wraps
 import grp
 import grp
 import logging
 import logging
+logger = logging.getLogger(__name__)
+
 import os
 import os
 import pwd
 import pwd
 import re
 import re

+ 11 - 9
borg/key.py

@@ -2,6 +2,8 @@ from binascii import hexlify, a2b_base64, b2a_base64
 import configparser
 import configparser
 import getpass
 import getpass
 import logging
 import logging
+logger = logging.getLogger(__name__)
+
 import os
 import os
 import msgpack
 import msgpack
 import textwrap
 import textwrap
@@ -89,7 +91,7 @@ class PlaintextKey(KeyBase):
 
 
     @classmethod
     @classmethod
     def create(cls, repository, args):
     def create(cls, repository, args):
-        logging.info('Encryption NOT enabled.\nUse the "--encryption=repokey|keyfile|passphrase" to enable encryption.')
+        logger.info('Encryption NOT enabled.\nUse the "--encryption=repokey|keyfile|passphrase" to enable encryption.')
         return cls(repository)
         return cls(repository)
 
 
     @classmethod
     @classmethod
@@ -191,12 +193,12 @@ class Passphrase(str):
             if allow_empty or passphrase:
             if allow_empty or passphrase:
                 passphrase2 = cls.getpass('Enter same passphrase again: ')
                 passphrase2 = cls.getpass('Enter same passphrase again: ')
                 if passphrase == passphrase2:
                 if passphrase == passphrase2:
-                    logging.info('Remember your passphrase. Your data will be inaccessible without it.')
+                    logger.info('Remember your passphrase. Your data will be inaccessible without it.')
                     return passphrase
                     return passphrase
                 else:
                 else:
-                    logging.warning('Passphrases do not match')
+                    logger.warning('Passphrases do not match')
             else:
             else:
-                logging.warning('Passphrase must not be blank')
+                logger.warning('Passphrase must not be blank')
 
 
     def __repr__(self):
     def __repr__(self):
         return '<Passphrase "***hidden***">'
         return '<Passphrase "***hidden***">'
@@ -216,8 +218,8 @@ class PassphraseKey(AESKeyBase):
     @classmethod
     @classmethod
     def create(cls, repository, args):
     def create(cls, repository, args):
         key = cls(repository)
         key = cls(repository)
-        logging.warning('WARNING: "passphrase" mode is deprecated and will be removed in 1.0.')
-        logging.warning('If you want something similar (but with less issues), use "repokey" mode.')
+        logger.warning('WARNING: "passphrase" mode is deprecated and will be removed in 1.0.')
+        logger.warning('If you want something similar (but with less issues), use "repokey" mode.')
         passphrase = Passphrase.new(allow_empty=False)
         passphrase = Passphrase.new(allow_empty=False)
         key.init(repository, passphrase)
         key.init(repository, passphrase)
         return key
         return key
@@ -325,7 +327,7 @@ class KeyfileKeyBase(AESKeyBase):
     def change_passphrase(self):
     def change_passphrase(self):
         passphrase = Passphrase.new(allow_empty=True)
         passphrase = Passphrase.new(allow_empty=True)
         self.save(self.target, passphrase)
         self.save(self.target, passphrase)
-        logging.info('Key updated')
+        logger.info('Key updated')
 
 
     @classmethod
     @classmethod
     def create(cls, repository, args):
     def create(cls, repository, args):
@@ -336,8 +338,8 @@ class KeyfileKeyBase(AESKeyBase):
         key.init_ciphers()
         key.init_ciphers()
         target = key.get_new_target(args)
         target = key.get_new_target(args)
         key.save(target, passphrase)
         key.save(target, passphrase)
-        logging.info('Key in "%s" created.' % target)
-        logging.info('Keep this key safe. Your data will be inaccessible without it.')
+        logger.info('Key in "%s" created.' % target)
+        logger.info('Keep this key safe. Your data will be inaccessible without it.')
         return key
         return key
 
 
     def save(self, target, passphrase):
     def save(self, target, passphrase):

+ 4 - 2
borg/repository.py

@@ -3,6 +3,8 @@ from binascii import hexlify
 from itertools import islice
 from itertools import islice
 import errno
 import errno
 import logging
 import logging
+logger = logging.getLogger(__name__)
+
 import os
 import os
 import shutil
 import shutil
 import struct
 import struct
@@ -279,7 +281,7 @@ class Repository:
         def report_error(msg):
         def report_error(msg):
             nonlocal error_found
             nonlocal error_found
             error_found = True
             error_found = True
-            logging.error(msg)
+            logger.error(msg)
 
 
         assert not self._active_txn
         assert not self._active_txn
         try:
         try:
@@ -567,7 +569,7 @@ class LoggedIO:
         with open(filename, 'rb') as fd:
         with open(filename, 'rb') as fd:
             data = memoryview(fd.read())
             data = memoryview(fd.read())
         os.rename(filename, filename + '.beforerecover')
         os.rename(filename, filename + '.beforerecover')
-        logging.info('attempting to recover ' + filename)
+        logger.info('attempting to recover ' + filename)
         with open(filename, 'wb') as fd:
         with open(filename, 'wb') as fd:
             fd.write(MAGIC)
             fd.write(MAGIC)
             while len(data) >= self.header_fmt.size:
             while len(data) >= self.header_fmt.size:

+ 18 - 0
borg/testsuite/logger.py

@@ -0,0 +1,18 @@
+import logging
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
+
+import pytest
+
+def test_mod_logger():
+    logger = logging.getLogger(__name__)
+    io = StringIO()
+
+    ch = logging.StreamHandler(io)
+    ch.setFormatter(logging.Formatter('%(name)s: %(message)s'))
+    logger.addHandler(ch)
+    logger.setLevel(logging.DEBUG)
+    logger.info('hello world')
+    assert io.getvalue() == "borg.testsuite.logger: hello world\n"