/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/trace.py

  • Committer: Jelmer Vernooij
  • Date: 2018-05-06 11:48:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180506114854-h4qd9ojaqy8wxjsd
Move .mailmap to root.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
Messages are classified by severity levels: critical, error, warning, info,
24
24
and debug.
25
25
 
26
 
They can be sent to two places: stderr, and `$XDG_CACHE_HOME/breezy/brz.log`.
27
 
For purposes such as running the test suite, they can also be redirected away
28
 
from both of those two places to another location.
 
26
They can be sent to two places: to stderr, and to ~/.brz.log.  For purposes
 
27
such as running the test suite, they can also be redirected away from both of
 
28
those two places to another location.
29
29
 
30
 
`brz.log` gets all messages, and full tracebacks for uncaught exceptions.
 
30
~/.brz.log gets all messages, and full tracebacks for uncaught exceptions.
31
31
This trace file is always in UTF-8, regardless of the user's default encoding,
32
32
so that we can always rely on writing any message.
33
33
 
64
64
 
65
65
from .lazy_import import lazy_import
66
66
lazy_import(globals(), """
 
67
import locale
67
68
import tempfile
68
69
import traceback
69
70
""")
72
73
 
73
74
lazy_import(globals(), """
74
75
from breezy import (
75
 
    bedding,
76
76
    debug,
 
77
    errors,
77
78
    osutils,
78
79
    ui,
79
80
    )
80
81
""")
81
 
from . import (
82
 
    errors,
83
 
    )
84
82
 
85
83
from .sixish import (
 
84
    BytesIO,
86
85
    PY3,
87
86
    StringIO,
88
87
    text_type,
99
98
# than push/pop_log_file.
100
99
_trace_file = None
101
100
 
102
 
# Absolute path for brz.log.  Not changed even if the log/trace output is
 
101
# Absolute path for ~/.brz.log.  Not changed even if the log/trace output is
103
102
# redirected elsewhere.  Used to show the location in --version.
104
103
_brz_log_filename = None
105
104
 
139
138
    _brz_logger.error(*args, **kwargs)
140
139
 
141
140
 
142
 
class _Bytes(str):
143
 
    """Compat class for displaying bytes on Python 2."""
144
 
 
145
 
    def __repr__(self):
146
 
        return 'b' + str.__repr__(self)
147
 
 
148
 
    def __unicode__(self):
149
 
        return self.decode('ascii', 'replace')
150
 
 
151
 
 
152
141
def mutter(fmt, *args):
153
142
    if _trace_file is None:
154
143
        return
157
146
    if (getattr(_trace_file, 'closed', None) is not None) and _trace_file.closed:
158
147
        return
159
148
 
160
 
    # Let format strings be specified as ascii bytes to help Python 2
161
 
    if isinstance(fmt, bytes):
162
 
        fmt = fmt.decode('ascii', 'replace')
 
149
    if isinstance(fmt, text_type):
 
150
        fmt = fmt.encode('utf8')
163
151
 
164
 
    if args:
165
 
        if not PY3:
166
 
            args = tuple(
167
 
                _Bytes(arg) if isinstance(arg, bytes) else arg for arg in args)
168
 
        out = fmt % args
 
152
    if len(args) > 0:
 
153
        # It seems that if we do ascii % (unicode, ascii) we can
 
154
        # get a unicode cannot encode ascii error, so make sure that "fmt"
 
155
        # is a unicode string
 
156
        real_args = []
 
157
        for arg in args:
 
158
            if isinstance(arg, text_type):
 
159
                arg = arg.encode('utf8')
 
160
            real_args.append(arg)
 
161
        out = fmt % tuple(real_args)
169
162
    else:
170
163
        out = fmt
171
164
    now = time.time()
172
 
    out = '%0.3f  %s\n' % (now - _brz_log_start_time, out)
173
 
    _trace_file.write(out.encode('utf-8'))
 
165
    out = b'%0.3f  %s\n' % (now - _brz_log_start_time, out)
 
166
    _trace_file.write(out)
174
167
    # there's no explicit flushing; the file is typically line buffered.
175
168
 
176
169
 
206
199
 
207
200
 
208
201
def _get_brz_log_filename():
209
 
    """Return the brz log filename.
210
 
 
211
 
    :return: A path to the log file
212
 
    :raise EnvironmentError: If the cache directory could not be created
213
 
    """
214
202
    brz_log = osutils.path_from_environ('BRZ_LOG')
215
203
    if brz_log:
216
204
        return brz_log
217
 
    return os.path.join(bedding.cache_dir(), 'brz.log')
 
205
    home = osutils.path_from_environ('BRZ_HOME')
 
206
    if home is None:
 
207
        # GZ 2012-02-01: Logging to the home dir is bad, but XDG is unclear
 
208
        #                over what would be better. On windows, bug 240550
 
209
        #                suggests LOCALAPPDATA be used instead.
 
210
        home = osutils._get_home_dir()
 
211
    return os.path.join(home, '.brz.log')
218
212
 
219
213
 
220
214
def _open_brz_log():
221
 
    """Open the brz.log trace file.
 
215
    """Open the .brz.log trace file.
222
216
 
223
217
    If the log is more than a particular length, the old file is renamed to
224
 
    brz.log.old and a new file is started.  Otherwise, we append to the
 
218
    .brz.log.old and a new file is started.  Otherwise, we append to the
225
219
    existing file.
226
220
 
227
221
    This sets the global _brz_log_filename.
251
245
            else:
252
246
                osutils.copy_ownership_from_path(filename)
253
247
                break
254
 
        return os.fdopen(fd, 'ab', 0)  # unbuffered
255
 
 
 
248
        return os.fdopen(fd, 'ab', 0) # unbuffered
 
249
 
 
250
 
 
251
    _brz_log_filename = _get_brz_log_filename()
 
252
    _rollover_trace_maybe(_brz_log_filename)
256
253
    try:
257
 
        _brz_log_filename = _get_brz_log_filename()
258
 
        _rollover_trace_maybe(_brz_log_filename)
259
 
 
260
254
        brz_log_file = _open_or_create_log_file(_brz_log_filename)
261
255
        brz_log_file.write(b'\n')
262
256
        if brz_log_file.tell() <= 2:
263
 
            brz_log_file.write(
264
 
                b"this is a debug log for diagnosing/reporting problems in brz\n")
265
 
            brz_log_file.write(
266
 
                b"you can delete or truncate this file, or include sections in\n")
267
 
            brz_log_file.write(
268
 
                b"bug reports to https://bugs.launchpad.net/brz/+filebug\n\n")
 
257
            brz_log_file.write(b"this is a debug log for diagnosing/reporting problems in brz\n")
 
258
            brz_log_file.write(b"you can delete or truncate this file, or include sections in\n")
 
259
            brz_log_file.write(b"bug reports to https://bugs.launchpad.net/brz/+filebug\n\n")
269
260
 
270
261
        return brz_log_file
271
262
 
283
274
 
284
275
 
285
276
def enable_default_logging():
286
 
    """Configure default logging: messages to stderr and debug to brz.log
 
277
    """Configure default logging: messages to stderr and debug to .brz.log
287
278
 
288
279
    This should only be called once per process.
289
280
 
300
291
    brz_log_file = _open_brz_log()
301
292
    if brz_log_file is not None:
302
293
        brz_log_file.write(start_time.encode('utf-8') + b'\n')
303
 
    memento = push_log_file(
304
 
        brz_log_file,
 
294
    memento = push_log_file(brz_log_file,
305
295
        r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s',
306
296
        r'%Y-%m-%d %H:%M:%S')
307
297
    # after hooking output into brz_log, we also need to attach a stderr
308
298
    # handler, writing only at level info and with encoding
309
 
    if sys.version_info[0] == 2:
310
 
        stderr_handler = EncodedStreamHandler(
311
 
            sys.stderr, osutils.get_terminal_encoding(), 'replace',
312
 
            level=logging.INFO)
313
 
    else:
314
 
        stderr_handler = logging.StreamHandler(stream=sys.stderr)
 
299
    stderr_handler = EncodedStreamHandler(sys.stderr,
 
300
        osutils.get_terminal_encoding(), 'replace', level=logging.INFO)
315
301
    logging.getLogger('brz').addHandler(stderr_handler)
316
302
    return memento
317
303
 
347
333
    old_trace_file = _trace_file
348
334
    # send traces to the new one
349
335
    _trace_file = to_file
 
336
    result = new_handler, _trace_file
350
337
    return ('log_memento', old_handlers, new_handler, old_trace_file, to_file)
351
338
 
352
339
 
435
422
 
436
423
_short_fields = ('VmPeak', 'VmSize', 'VmRSS')
437
424
 
438
 
 
439
425
def _debug_memory_proc(message='', short=True):
440
426
    try:
441
 
        status_file = open('/proc/%s/status' % os.getpid(), 'rb')
 
427
        status_file = file('/proc/%s/status' % os.getpid(), 'rb')
442
428
    except IOError:
443
429
        return
444
430
    try:
456
442
                    note(line)
457
443
                    break
458
444
 
459
 
 
460
445
def _dump_memory_usage(err_file):
461
446
    try:
462
447
        try:
468
453
        except ImportError:
469
454
            err_file.write("Dumping memory requires meliae module.\n")
470
455
            log_exception_quietly()
471
 
        except BaseException:
 
456
        except:
472
457
            err_file.write("Exception while dumping memory.\n")
473
458
            log_exception_quietly()
474
459
    finally:
493
478
 
494
479
 
495
480
def report_exception(exc_info, err_file):
496
 
    """Report an exception to err_file (typically stderr) and to brz.log.
 
481
    """Report an exception to err_file (typically stderr) and to .brz.log.
497
482
 
498
483
    This will show either a full traceback or a short message as appropriate.
499
484
 
500
485
    :return: The appropriate exit code for this error.
501
486
    """
502
 
    # Log the full traceback to brz.log
 
487
    # Log the full traceback to ~/.brz.log
503
488
    log_exception_quietly()
504
489
    if 'error' in debug.debug_flags:
505
490
        print_exception(exc_info, err_file)
516
501
            err_file.write("Use -Dmem_dump to dump memory to a file.\n")
517
502
        return errors.EXIT_ERROR
518
503
    elif isinstance(exc_object, ImportError) \
519
 
            and str(exc_object).startswith("No module named "):
520
 
        report_user_error(
521
 
            exc_info, err_file,
 
504
        and str(exc_object).startswith("No module named "):
 
505
        report_user_error(exc_info, err_file,
522
506
            'You may need to install this Python library separately.')
523
507
        return errors.EXIT_ERROR
524
508
    elif not getattr(exc_object, 'internal_error', True):
555
539
    :param advice: Extra advice to the user to be printed following the
556
540
        exception.
557
541
    """
558
 
    err_file.write(("brz: ERROR: %s\n" % (str(exc_info[1]),)))
 
542
    err_file.write("brz: ERROR: %s\n" % (exc_info[1],))
559
543
    if advice:
560
 
        err_file.write(("%s\n" % advice))
 
544
        err_file.write("%s\n" % advice)
561
545
 
562
546
 
563
547
def report_bug(exc_info, err_file):
571
555
    try:
572
556
        sys.stdout.flush()
573
557
        sys.stderr.flush()
574
 
    except ValueError:
 
558
    except ValueError as e:
575
559
        # On Windows, I get ValueError calling stdout.flush() on a closed
576
560
        # handle
577
561
        pass
628
612
            # Try saving the details that would have been logged in some form
629
613
            msg = args = "<Unformattable>"
630
614
            try:
631
 
                msg = repr(record.msg)
632
 
                args = repr(record.args)
 
615
                msg = repr(record.msg).encode("ascii", "backslashescape")
 
616
                args = repr(record.args).encode("ascii", "backslashescape")
633
617
            except Exception:
634
618
                pass
635
619
            # Using mutter() bypasses the logging module and writes directly
646
630
    """
647
631
 
648
632
    def __enter__(self):
649
 
        return self  # This is bound to the 'as' clause in a with statement.
 
633
        return self # This is bound to the 'as' clause in a with statement.
650
634
 
651
635
    def __exit__(self, exc_type, exc_val, exc_tb):
652
 
        return False  # propogate exceptions.
 
636
        return False # propogate exceptions.
653
637
 
654
638
 
655
639
class DefaultConfig(Config):
661
645
    def __enter__(self):
662
646
        self._original_filename = _brz_log_filename
663
647
        self._original_state = enable_default_logging()
664
 
        return self  # This is bound to the 'as' clause in a with statement.
 
648
        return self # This is bound to the 'as' clause in a with statement.
665
649
 
666
650
    def __exit__(self, exc_type, exc_val, exc_tb):
667
651
        pop_log_file(self._original_state)
668
652
        global _brz_log_filename
669
653
        _brz_log_filename = self._original_filename
670
 
        return False  # propogate exceptions.
 
654
        return False # propogate exceptions.