logger.dart 3.9 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

Ian Hickson's avatar
Ian Hickson committed
7
final _AnsiTerminal _terminal = new _AnsiTerminal();
Devon Carew's avatar
Devon Carew committed
8

9
abstract class Logger {
Devon Carew's avatar
Devon Carew committed
10
  bool get isVerbose => false;
11 12 13 14 15 16 17 18 19 20 21 22

  /// Display an error level message to the user. Commands should use this if they
  /// fail in some way.
  void printError(String message, [StackTrace stackTrace]);

  /// Display normal output of the command. This should be used for things like
  /// progress messages, success messages, or just normal command output.
  void printStatus(String message);

  /// Use this for verbose tracing output. Users can turn this output on in order
  /// to help diagnose issues with the toolchain or with their setup.
  void printTrace(String message);
Devon Carew's avatar
Devon Carew committed
23 24 25

  /// Flush any buffered output.
  void flush() { }
26 27 28
}

class StdoutLogger implements Logger {
29
  @override
Devon Carew's avatar
Devon Carew committed
30
  bool get isVerbose => false;
31

32
  @override
33
  void printError(String message, [StackTrace stackTrace]) {
Devon Carew's avatar
Devon Carew committed
34
    stderr.writeln(message);
35 36 37 38
    if (stackTrace != null)
      stderr.writeln(stackTrace);
  }

39
  @override
Devon Carew's avatar
Devon Carew committed
40
  void printStatus(String message) => print(message);
41

42
  @override
Devon Carew's avatar
Devon Carew committed
43
  void printTrace(String message) { }
44

45
  @override
Devon Carew's avatar
Devon Carew committed
46
  void flush() { }
47 48 49
}

class BufferLogger implements Logger {
50
  @override
Devon Carew's avatar
Devon Carew committed
51 52
  bool get isVerbose => false;

53 54 55 56 57 58 59 60
  StringBuffer _error = new StringBuffer();
  StringBuffer _status = new StringBuffer();
  StringBuffer _trace = new StringBuffer();

  String get errorText => _error.toString();
  String get statusText => _status.toString();
  String get traceText => _trace.toString();

61
  @override
62
  void printError(String message, [StackTrace stackTrace]) => _error.writeln(message);
63 64

  @override
65
  void printStatus(String message) => _status.writeln(message);
66 67

  @override
68
  void printTrace(String message) => _trace.writeln(message);
Devon Carew's avatar
Devon Carew committed
69

70
  @override
Devon Carew's avatar
Devon Carew committed
71 72 73 74 75 76
  void flush() { }
}

class VerboseLogger implements Logger {
  _LogMessage lastMessage;

77
  @override
Devon Carew's avatar
Devon Carew committed
78 79
  bool get isVerbose => true;

80
  @override
Devon Carew's avatar
Devon Carew committed
81 82 83 84 85
  void printError(String message, [StackTrace stackTrace]) {
    _emit();
    lastMessage = new _LogMessage(_LogType.error, message, stackTrace);
  }

86
  @override
Devon Carew's avatar
Devon Carew committed
87 88 89 90 91
  void printStatus(String message) {
    _emit();
    lastMessage = new _LogMessage(_LogType.status, message);
  }

92
  @override
Devon Carew's avatar
Devon Carew committed
93 94 95 96 97
  void printTrace(String message) {
    _emit();
    lastMessage = new _LogMessage(_LogType.trace, message);
  }

98
  @override
Devon Carew's avatar
Devon Carew committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
  void flush() => _emit();

  void _emit() {
    lastMessage?.emit();
    lastMessage = null;
  }
}

enum _LogType {
  error,
  status,
  trace
}

class _LogMessage {
  _LogMessage(this.type, this.message, [this.stackTrace]) {
    stopwatch.start();
  }

  final _LogType type;
  final String message;
  final StackTrace stackTrace;

  Stopwatch stopwatch = new Stopwatch();

  void emit() {
    stopwatch.stop();

    int millis = stopwatch.elapsedMilliseconds;
    String prefix = '${millis.toString().padLeft(4)} ms • ';
    String indent = ''.padLeft(prefix.length);
    if (millis >= 100)
      prefix = _terminal.writeBold(prefix.substring(0, prefix.length - 3)) + ' • ';
    String indentMessage = message.replaceAll('\n', '\n$indent');

    if (type == _LogType.error) {
      stderr.writeln(prefix + _terminal.writeBold(indentMessage));
      if (stackTrace != null)
        stderr.writeln(indent + stackTrace.toString().replaceAll('\n', '\n$indent'));
    } else if (type == _LogType.status) {
      print(prefix + _terminal.writeBold(indentMessage));
    } else {
      print(prefix + indentMessage);
    }
  }
}

class _AnsiTerminal {
  _AnsiTerminal() {
    String term = Platform.environment['TERM'];
    _supportsColor = term != null && term != 'dumb';
  }

  static const String _bold = '\u001B[1m';
  static const String _reset = '\u001B[0m';

  bool _supportsColor;
  bool get supportsColor => _supportsColor;

  String writeBold(String str) => supportsColor ? '$_bold$str$_reset' : str;
159
}