762 lines
20 KiB
Dart
762 lines
20 KiB
Dart
|
|
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
||
|
|
// for details. 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:async';
|
||
|
|
|
||
|
|
import 'package:logging/logging.dart';
|
||
|
|
import 'package:test/test.dart';
|
||
|
|
|
||
|
|
void main() {
|
||
|
|
final hierarchicalLoggingEnabledDefault = hierarchicalLoggingEnabled;
|
||
|
|
|
||
|
|
test('level comparison is a valid comparator', () {
|
||
|
|
const level1 = Level('NOT_REAL1', 253);
|
||
|
|
expect(level1 == level1, isTrue);
|
||
|
|
expect(level1 <= level1, isTrue);
|
||
|
|
expect(level1 >= level1, isTrue);
|
||
|
|
expect(level1 < level1, isFalse);
|
||
|
|
expect(level1 > level1, isFalse);
|
||
|
|
|
||
|
|
const level2 = Level('NOT_REAL2', 455);
|
||
|
|
expect(level1 <= level2, isTrue);
|
||
|
|
expect(level1 < level2, isTrue);
|
||
|
|
expect(level2 >= level1, isTrue);
|
||
|
|
expect(level2 > level1, isTrue);
|
||
|
|
|
||
|
|
const level3 = Level('NOT_REAL3', 253);
|
||
|
|
expect(level1, isNot(same(level3))); // different instances
|
||
|
|
expect(level1, equals(level3)); // same value.
|
||
|
|
});
|
||
|
|
|
||
|
|
test('default levels are in order', () {
|
||
|
|
const levels = Level.LEVELS;
|
||
|
|
|
||
|
|
for (var i = 0; i < levels.length; i++) {
|
||
|
|
for (var j = i + 1; j < levels.length; j++) {
|
||
|
|
expect(levels[i] < levels[j], isTrue);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('levels are comparable', () {
|
||
|
|
final unsorted = [
|
||
|
|
Level.INFO,
|
||
|
|
Level.CONFIG,
|
||
|
|
Level.FINE,
|
||
|
|
Level.SHOUT,
|
||
|
|
Level.OFF,
|
||
|
|
Level.FINER,
|
||
|
|
Level.ALL,
|
||
|
|
Level.WARNING,
|
||
|
|
Level.FINEST,
|
||
|
|
Level.SEVERE,
|
||
|
|
];
|
||
|
|
|
||
|
|
const sorted = Level.LEVELS;
|
||
|
|
|
||
|
|
expect(unsorted, isNot(orderedEquals(sorted)));
|
||
|
|
|
||
|
|
unsorted.sort();
|
||
|
|
expect(unsorted, orderedEquals(sorted));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('levels are hashable', () {
|
||
|
|
final map = <Level, String>{};
|
||
|
|
map[Level.INFO] = 'info';
|
||
|
|
map[Level.SHOUT] = 'shout';
|
||
|
|
expect(map[Level.INFO], same('info'));
|
||
|
|
expect(map[Level.SHOUT], same('shout'));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('logger name cannot start with a "." ', () {
|
||
|
|
expect(() => Logger('.c'), throwsArgumentError);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('logger name cannot end with a "."', () {
|
||
|
|
expect(() => Logger('a.'), throwsArgumentError);
|
||
|
|
expect(() => Logger('a..d'), throwsArgumentError);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('root level has proper defaults', () {
|
||
|
|
expect(Logger.root, isNotNull);
|
||
|
|
expect(Logger.root.parent, null);
|
||
|
|
expect(Logger.root.level, defaultLevel);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('logger naming is hierarchical', () {
|
||
|
|
final c = Logger('a.b.c');
|
||
|
|
expect(c.name, equals('c'));
|
||
|
|
expect(c.parent!.name, equals('b'));
|
||
|
|
expect(c.parent!.parent!.name, equals('a'));
|
||
|
|
expect(c.parent!.parent!.parent!.name, equals(''));
|
||
|
|
expect(c.parent!.parent!.parent!.parent, isNull);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('logger full name', () {
|
||
|
|
final c = Logger('a.b.c');
|
||
|
|
expect(c.fullName, equals('a.b.c'));
|
||
|
|
expect(c.parent!.fullName, equals('a.b'));
|
||
|
|
expect(c.parent!.parent!.fullName, equals('a'));
|
||
|
|
expect(c.parent!.parent!.parent!.fullName, equals(''));
|
||
|
|
expect(c.parent!.parent!.parent!.parent, isNull);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('logger parent-child links are correct', () {
|
||
|
|
final a = Logger('a');
|
||
|
|
final b = Logger('a.b');
|
||
|
|
final c = Logger('a.c');
|
||
|
|
expect(a, same(b.parent));
|
||
|
|
expect(a, same(c.parent));
|
||
|
|
expect(a.children['b'], same(b));
|
||
|
|
expect(a.children['c'], same(c));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('loggers are singletons', () {
|
||
|
|
final a1 = Logger('a');
|
||
|
|
final a2 = Logger('a');
|
||
|
|
final b = Logger('a.b');
|
||
|
|
final root = Logger.root;
|
||
|
|
expect(a1, same(a2));
|
||
|
|
expect(a1, same(b.parent));
|
||
|
|
expect(root, same(a1.parent));
|
||
|
|
expect(root, same(Logger('')));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('cannot directly manipulate Logger.children', () {
|
||
|
|
final loggerAB = Logger('a.b');
|
||
|
|
final loggerA = loggerAB.parent!;
|
||
|
|
|
||
|
|
expect(loggerA.children['b'], same(loggerAB), reason: 'can read Children');
|
||
|
|
|
||
|
|
expect(() {
|
||
|
|
loggerAB.children['test'] = Logger('Fake1234');
|
||
|
|
}, throwsUnsupportedError, reason: 'Children is read-only');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('stackTrace gets throw to LogRecord', () {
|
||
|
|
Logger.root.level = Level.INFO;
|
||
|
|
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
|
||
|
|
final sub = Logger.root.onRecord.listen(records.add);
|
||
|
|
|
||
|
|
try {
|
||
|
|
throw UnsupportedError('test exception');
|
||
|
|
} catch (error, stack) {
|
||
|
|
Logger.root.log(Level.SEVERE, 'severe', error, stack);
|
||
|
|
Logger.root.warning('warning', error, stack);
|
||
|
|
}
|
||
|
|
|
||
|
|
Logger.root.log(Level.SHOUT, 'shout');
|
||
|
|
|
||
|
|
sub.cancel();
|
||
|
|
|
||
|
|
expect(records, hasLength(3));
|
||
|
|
|
||
|
|
final severe = records[0];
|
||
|
|
expect(severe.message, 'severe');
|
||
|
|
expect(severe.error is UnsupportedError, isTrue);
|
||
|
|
expect(severe.stackTrace is StackTrace, isTrue);
|
||
|
|
|
||
|
|
final warning = records[1];
|
||
|
|
expect(warning.message, 'warning');
|
||
|
|
expect(warning.error is UnsupportedError, isTrue);
|
||
|
|
expect(warning.stackTrace is StackTrace, isTrue);
|
||
|
|
|
||
|
|
final shout = records[2];
|
||
|
|
expect(shout.message, 'shout');
|
||
|
|
expect(shout.error, isNull);
|
||
|
|
expect(shout.stackTrace, isNull);
|
||
|
|
});
|
||
|
|
|
||
|
|
group('zone gets recorded to LogRecord', () {
|
||
|
|
test('root zone', () {
|
||
|
|
final root = Logger.root;
|
||
|
|
|
||
|
|
final recordingZone = Zone.current;
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
root.info('hello');
|
||
|
|
|
||
|
|
expect(records, hasLength(1));
|
||
|
|
expect(records.first.zone, equals(recordingZone));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('child zone', () {
|
||
|
|
final root = Logger.root;
|
||
|
|
|
||
|
|
late Zone recordingZone;
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
|
||
|
|
runZoned(() {
|
||
|
|
recordingZone = Zone.current;
|
||
|
|
root.info('hello');
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(records, hasLength(1));
|
||
|
|
expect(records.first.zone, equals(recordingZone));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('custom zone', () {
|
||
|
|
final root = Logger.root;
|
||
|
|
|
||
|
|
late Zone recordingZone;
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
|
||
|
|
runZoned(() {
|
||
|
|
recordingZone = Zone.current;
|
||
|
|
});
|
||
|
|
|
||
|
|
runZoned(() => root.log(Level.INFO, 'hello', null, null, recordingZone));
|
||
|
|
|
||
|
|
expect(records, hasLength(1));
|
||
|
|
expect(records.first.zone, equals(recordingZone));
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
group('detached loggers', () {
|
||
|
|
tearDown(() {
|
||
|
|
hierarchicalLoggingEnabled = hierarchicalLoggingEnabledDefault;
|
||
|
|
Logger.root.level = defaultLevel;
|
||
|
|
});
|
||
|
|
|
||
|
|
test('create new instances of Logger', () {
|
||
|
|
final a1 = Logger.detached('a');
|
||
|
|
final a2 = Logger.detached('a');
|
||
|
|
final a = Logger('a');
|
||
|
|
|
||
|
|
expect(a1, isNot(a2));
|
||
|
|
expect(a1, isNot(a));
|
||
|
|
expect(a2, isNot(a));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('parent is null', () {
|
||
|
|
final a = Logger.detached('a');
|
||
|
|
expect(a.parent, null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('children is empty', () {
|
||
|
|
final a = Logger.detached('a');
|
||
|
|
expect(a.children, <Object, Object>{});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('have levels independent of the root level', () {
|
||
|
|
void testDetachedLoggerLevel(bool withHierarchy) {
|
||
|
|
hierarchicalLoggingEnabled = withHierarchy;
|
||
|
|
|
||
|
|
const newRootLevel = Level.ALL;
|
||
|
|
const newDetachedLevel = Level.OFF;
|
||
|
|
|
||
|
|
Logger.root.level = newRootLevel;
|
||
|
|
|
||
|
|
final detached = Logger.detached('a');
|
||
|
|
expect(detached.level, defaultLevel);
|
||
|
|
expect(Logger.root.level, newRootLevel);
|
||
|
|
|
||
|
|
detached.level = newDetachedLevel;
|
||
|
|
expect(detached.level, newDetachedLevel);
|
||
|
|
expect(Logger.root.level, newRootLevel);
|
||
|
|
}
|
||
|
|
|
||
|
|
testDetachedLoggerLevel(false);
|
||
|
|
testDetachedLoggerLevel(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('log messages regardless of hierarchy', () {
|
||
|
|
void testDetachedLoggerOnRecord(bool withHierarchy) {
|
||
|
|
var calls = 0;
|
||
|
|
void handler(_) => calls += 1;
|
||
|
|
|
||
|
|
hierarchicalLoggingEnabled = withHierarchy;
|
||
|
|
|
||
|
|
final detached = Logger.detached('a');
|
||
|
|
detached.level = Level.ALL;
|
||
|
|
detached.onRecord.listen(handler);
|
||
|
|
|
||
|
|
Logger.root.info('foo');
|
||
|
|
expect(calls, 0);
|
||
|
|
|
||
|
|
detached.info('foo');
|
||
|
|
detached.info('foo');
|
||
|
|
expect(calls, 2);
|
||
|
|
}
|
||
|
|
|
||
|
|
testDetachedLoggerOnRecord(false);
|
||
|
|
testDetachedLoggerOnRecord(true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
group('mutating levels', () {
|
||
|
|
final root = Logger.root;
|
||
|
|
final a = Logger('a');
|
||
|
|
final b = Logger('a.b');
|
||
|
|
final c = Logger('a.b.c');
|
||
|
|
final d = Logger('a.b.c.d');
|
||
|
|
final e = Logger('a.b.c.d.e');
|
||
|
|
|
||
|
|
setUp(() {
|
||
|
|
hierarchicalLoggingEnabled = true;
|
||
|
|
root.level = Level.INFO;
|
||
|
|
a.level = null;
|
||
|
|
b.level = null;
|
||
|
|
c.level = null;
|
||
|
|
d.level = null;
|
||
|
|
e.level = null;
|
||
|
|
root.clearListeners();
|
||
|
|
a.clearListeners();
|
||
|
|
b.clearListeners();
|
||
|
|
c.clearListeners();
|
||
|
|
d.clearListeners();
|
||
|
|
e.clearListeners();
|
||
|
|
hierarchicalLoggingEnabled = false;
|
||
|
|
root.level = Level.INFO;
|
||
|
|
});
|
||
|
|
|
||
|
|
test('cannot set level if hierarchy is disabled', () {
|
||
|
|
expect(() => a.level = Level.FINE, throwsUnsupportedError);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('cannot set the level to null on the root logger', () {
|
||
|
|
expect(() => root.level = null, throwsUnsupportedError);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('cannot set the level to null on a detached logger', () {
|
||
|
|
expect(() => Logger.detached('l').level = null, throwsUnsupportedError);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('loggers effective level - no hierarchy', () {
|
||
|
|
expect(root.level, equals(Level.INFO));
|
||
|
|
expect(a.level, equals(Level.INFO));
|
||
|
|
expect(b.level, equals(Level.INFO));
|
||
|
|
|
||
|
|
root.level = Level.SHOUT;
|
||
|
|
|
||
|
|
expect(root.level, equals(Level.SHOUT));
|
||
|
|
expect(a.level, equals(Level.SHOUT));
|
||
|
|
expect(b.level, equals(Level.SHOUT));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('loggers effective level - with hierarchy', () {
|
||
|
|
hierarchicalLoggingEnabled = true;
|
||
|
|
expect(root.level, equals(Level.INFO));
|
||
|
|
expect(a.level, equals(Level.INFO));
|
||
|
|
expect(b.level, equals(Level.INFO));
|
||
|
|
expect(c.level, equals(Level.INFO));
|
||
|
|
|
||
|
|
root.level = Level.SHOUT;
|
||
|
|
b.level = Level.FINE;
|
||
|
|
|
||
|
|
expect(root.level, equals(Level.SHOUT));
|
||
|
|
expect(a.level, equals(Level.SHOUT));
|
||
|
|
expect(b.level, equals(Level.FINE));
|
||
|
|
expect(c.level, equals(Level.FINE));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('loggers effective level - with changing hierarchy', () {
|
||
|
|
hierarchicalLoggingEnabled = true;
|
||
|
|
d.level = Level.SHOUT;
|
||
|
|
hierarchicalLoggingEnabled = false;
|
||
|
|
|
||
|
|
expect(root.level, Level.INFO);
|
||
|
|
expect(d.level, root.level);
|
||
|
|
expect(e.level, root.level);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('isLoggable is appropriate', () {
|
||
|
|
hierarchicalLoggingEnabled = true;
|
||
|
|
root.level = Level.SEVERE;
|
||
|
|
c.level = Level.ALL;
|
||
|
|
e.level = Level.OFF;
|
||
|
|
|
||
|
|
expect(root.isLoggable(Level.SHOUT), isTrue);
|
||
|
|
expect(root.isLoggable(Level.SEVERE), isTrue);
|
||
|
|
expect(root.isLoggable(Level.WARNING), isFalse);
|
||
|
|
expect(c.isLoggable(Level.FINEST), isTrue);
|
||
|
|
expect(c.isLoggable(Level.FINE), isTrue);
|
||
|
|
expect(e.isLoggable(Level.SHOUT), isFalse);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('add/remove handlers - no hierarchy', () {
|
||
|
|
var calls = 0;
|
||
|
|
void handler(_) {
|
||
|
|
calls++;
|
||
|
|
}
|
||
|
|
|
||
|
|
final sub = c.onRecord.listen(handler);
|
||
|
|
root.info('foo');
|
||
|
|
root.info('foo');
|
||
|
|
expect(calls, equals(2));
|
||
|
|
sub.cancel();
|
||
|
|
root.info('foo');
|
||
|
|
expect(calls, equals(2));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('add/remove handlers - with hierarchy', () {
|
||
|
|
hierarchicalLoggingEnabled = true;
|
||
|
|
var calls = 0;
|
||
|
|
void handler(_) {
|
||
|
|
calls++;
|
||
|
|
}
|
||
|
|
|
||
|
|
c.onRecord.listen(handler);
|
||
|
|
root.info('foo');
|
||
|
|
root.info('foo');
|
||
|
|
expect(calls, equals(0));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('logging methods store appropriate level', () {
|
||
|
|
root.level = Level.ALL;
|
||
|
|
final rootMessages = <String>[];
|
||
|
|
root.onRecord.listen((record) {
|
||
|
|
rootMessages.add('${record.level}: ${record.message}');
|
||
|
|
});
|
||
|
|
|
||
|
|
root.finest('1');
|
||
|
|
root.finer('2');
|
||
|
|
root.fine('3');
|
||
|
|
root.config('4');
|
||
|
|
root.info('5');
|
||
|
|
root.warning('6');
|
||
|
|
root.severe('7');
|
||
|
|
root.shout('8');
|
||
|
|
|
||
|
|
expect(
|
||
|
|
rootMessages,
|
||
|
|
equals([
|
||
|
|
'FINEST: 1',
|
||
|
|
'FINER: 2',
|
||
|
|
'FINE: 3',
|
||
|
|
'CONFIG: 4',
|
||
|
|
'INFO: 5',
|
||
|
|
'WARNING: 6',
|
||
|
|
'SEVERE: 7',
|
||
|
|
'SHOUT: 8'
|
||
|
|
]));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('logging methods store exception', () {
|
||
|
|
root.level = Level.ALL;
|
||
|
|
final rootMessages = <String>[];
|
||
|
|
root.onRecord.listen((r) {
|
||
|
|
rootMessages.add('${r.level}: ${r.message} ${r.error}');
|
||
|
|
});
|
||
|
|
|
||
|
|
root.finest('1');
|
||
|
|
root.finer('2');
|
||
|
|
root.fine('3');
|
||
|
|
root.config('4');
|
||
|
|
root.info('5');
|
||
|
|
root.warning('6');
|
||
|
|
root.severe('7');
|
||
|
|
root.shout('8');
|
||
|
|
root.finest('1', 'a');
|
||
|
|
root.finer('2', 'b');
|
||
|
|
root.fine('3', ['c']);
|
||
|
|
root.config('4', 'd');
|
||
|
|
root.info('5', 'e');
|
||
|
|
root.warning('6', 'f');
|
||
|
|
root.severe('7', 'g');
|
||
|
|
root.shout('8', 'h');
|
||
|
|
|
||
|
|
expect(
|
||
|
|
rootMessages,
|
||
|
|
equals([
|
||
|
|
'FINEST: 1 null',
|
||
|
|
'FINER: 2 null',
|
||
|
|
'FINE: 3 null',
|
||
|
|
'CONFIG: 4 null',
|
||
|
|
'INFO: 5 null',
|
||
|
|
'WARNING: 6 null',
|
||
|
|
'SEVERE: 7 null',
|
||
|
|
'SHOUT: 8 null',
|
||
|
|
'FINEST: 1 a',
|
||
|
|
'FINER: 2 b',
|
||
|
|
'FINE: 3 [c]',
|
||
|
|
'CONFIG: 4 d',
|
||
|
|
'INFO: 5 e',
|
||
|
|
'WARNING: 6 f',
|
||
|
|
'SEVERE: 7 g',
|
||
|
|
'SHOUT: 8 h'
|
||
|
|
]));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('message logging - no hierarchy', () {
|
||
|
|
root.level = Level.WARNING;
|
||
|
|
final rootMessages = <String>[];
|
||
|
|
final aMessages = <String>[];
|
||
|
|
final cMessages = <String>[];
|
||
|
|
c.onRecord.listen((record) {
|
||
|
|
cMessages.add('${record.level}: ${record.message}');
|
||
|
|
});
|
||
|
|
a.onRecord.listen((record) {
|
||
|
|
aMessages.add('${record.level}: ${record.message}');
|
||
|
|
});
|
||
|
|
root.onRecord.listen((record) {
|
||
|
|
rootMessages.add('${record.level}: ${record.message}');
|
||
|
|
});
|
||
|
|
|
||
|
|
root.info('1');
|
||
|
|
root.fine('2');
|
||
|
|
root.shout('3');
|
||
|
|
|
||
|
|
b.info('4');
|
||
|
|
b.severe('5');
|
||
|
|
b.warning('6');
|
||
|
|
b.fine('7');
|
||
|
|
|
||
|
|
c.fine('8');
|
||
|
|
c.warning('9');
|
||
|
|
c.shout('10');
|
||
|
|
|
||
|
|
expect(
|
||
|
|
rootMessages,
|
||
|
|
equals([
|
||
|
|
// 'INFO: 1' is not loggable
|
||
|
|
// 'FINE: 2' is not loggable
|
||
|
|
'SHOUT: 3',
|
||
|
|
// 'INFO: 4' is not loggable
|
||
|
|
'SEVERE: 5',
|
||
|
|
'WARNING: 6',
|
||
|
|
// 'FINE: 7' is not loggable
|
||
|
|
// 'FINE: 8' is not loggable
|
||
|
|
'WARNING: 9',
|
||
|
|
'SHOUT: 10'
|
||
|
|
]));
|
||
|
|
|
||
|
|
// no hierarchy means we all hear the same thing.
|
||
|
|
expect(aMessages, equals(rootMessages));
|
||
|
|
expect(cMessages, equals(rootMessages));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('message logging - with hierarchy', () {
|
||
|
|
hierarchicalLoggingEnabled = true;
|
||
|
|
|
||
|
|
b.level = Level.WARNING;
|
||
|
|
|
||
|
|
final rootMessages = <String>[];
|
||
|
|
final aMessages = <String>[];
|
||
|
|
final cMessages = <String>[];
|
||
|
|
c.onRecord.listen((record) {
|
||
|
|
cMessages.add('${record.level}: ${record.message}');
|
||
|
|
});
|
||
|
|
a.onRecord.listen((record) {
|
||
|
|
aMessages.add('${record.level}: ${record.message}');
|
||
|
|
});
|
||
|
|
root.onRecord.listen((record) {
|
||
|
|
rootMessages.add('${record.level}: ${record.message}');
|
||
|
|
});
|
||
|
|
|
||
|
|
root.info('1');
|
||
|
|
root.fine('2');
|
||
|
|
root.shout('3');
|
||
|
|
|
||
|
|
b.info('4');
|
||
|
|
b.severe('5');
|
||
|
|
b.warning('6');
|
||
|
|
b.fine('7');
|
||
|
|
|
||
|
|
c.fine('8');
|
||
|
|
c.warning('9');
|
||
|
|
c.shout('10');
|
||
|
|
|
||
|
|
expect(
|
||
|
|
rootMessages,
|
||
|
|
equals([
|
||
|
|
'INFO: 1',
|
||
|
|
// 'FINE: 2' is not loggable
|
||
|
|
'SHOUT: 3',
|
||
|
|
// 'INFO: 4' is not loggable
|
||
|
|
'SEVERE: 5',
|
||
|
|
'WARNING: 6',
|
||
|
|
// 'FINE: 7' is not loggable
|
||
|
|
// 'FINE: 8' is not loggable
|
||
|
|
'WARNING: 9',
|
||
|
|
'SHOUT: 10'
|
||
|
|
]));
|
||
|
|
|
||
|
|
expect(
|
||
|
|
aMessages,
|
||
|
|
equals([
|
||
|
|
// 1,2 and 3 are lower in the hierarchy
|
||
|
|
// 'INFO: 4' is not loggable
|
||
|
|
'SEVERE: 5',
|
||
|
|
'WARNING: 6',
|
||
|
|
// 'FINE: 7' is not loggable
|
||
|
|
// 'FINE: 8' is not loggable
|
||
|
|
'WARNING: 9',
|
||
|
|
'SHOUT: 10'
|
||
|
|
]));
|
||
|
|
|
||
|
|
expect(
|
||
|
|
cMessages,
|
||
|
|
equals([
|
||
|
|
// 1 - 7 are lower in the hierarchy
|
||
|
|
// 'FINE: 8' is not loggable
|
||
|
|
'WARNING: 9',
|
||
|
|
'SHOUT: 10'
|
||
|
|
]));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('message logging - lazy functions', () {
|
||
|
|
root.level = Level.INFO;
|
||
|
|
final messages = <String>[];
|
||
|
|
root.onRecord.listen((record) {
|
||
|
|
messages.add('${record.level}: ${record.message}');
|
||
|
|
});
|
||
|
|
|
||
|
|
var callCount = 0;
|
||
|
|
String myClosure() => '${++callCount}';
|
||
|
|
|
||
|
|
root.info(myClosure);
|
||
|
|
root.finer(myClosure); // Should not get evaluated.
|
||
|
|
root.warning(myClosure);
|
||
|
|
|
||
|
|
expect(
|
||
|
|
messages,
|
||
|
|
equals([
|
||
|
|
'INFO: 1',
|
||
|
|
'WARNING: 2',
|
||
|
|
]));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('message logging - calls toString', () {
|
||
|
|
root.level = Level.INFO;
|
||
|
|
final messages = <String>[];
|
||
|
|
final objects = <Object?>[];
|
||
|
|
final object = Object();
|
||
|
|
root.onRecord.listen((record) {
|
||
|
|
messages.add('${record.level}: ${record.message}');
|
||
|
|
objects.add(record.object);
|
||
|
|
});
|
||
|
|
|
||
|
|
root.info(5);
|
||
|
|
root.info(false);
|
||
|
|
root.info([1, 2, 3]);
|
||
|
|
root.info(() => 10);
|
||
|
|
root.info(object);
|
||
|
|
|
||
|
|
expect(
|
||
|
|
messages,
|
||
|
|
equals([
|
||
|
|
'INFO: 5',
|
||
|
|
'INFO: false',
|
||
|
|
'INFO: [1, 2, 3]',
|
||
|
|
'INFO: 10',
|
||
|
|
"INFO: Instance of 'Object'"
|
||
|
|
]));
|
||
|
|
|
||
|
|
expect(objects, [
|
||
|
|
5,
|
||
|
|
false,
|
||
|
|
[1, 2, 3],
|
||
|
|
10,
|
||
|
|
object
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
group('recordStackTraceAtLevel', () {
|
||
|
|
final root = Logger.root;
|
||
|
|
tearDown(() {
|
||
|
|
recordStackTraceAtLevel = Level.OFF;
|
||
|
|
root.clearListeners();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('no stack trace by default', () {
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
root.severe('hello');
|
||
|
|
root.warning('hello');
|
||
|
|
root.info('hello');
|
||
|
|
expect(records, hasLength(3));
|
||
|
|
expect(records[0].stackTrace, isNull);
|
||
|
|
expect(records[1].stackTrace, isNull);
|
||
|
|
expect(records[2].stackTrace, isNull);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('trace recorded only on requested levels', () {
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
recordStackTraceAtLevel = Level.WARNING;
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
root.severe('hello');
|
||
|
|
root.warning('hello');
|
||
|
|
root.info('hello');
|
||
|
|
expect(records, hasLength(3));
|
||
|
|
expect(records[0].stackTrace, isNotNull);
|
||
|
|
expect(records[1].stackTrace, isNotNull);
|
||
|
|
expect(records[2].stackTrace, isNull);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('defaults a missing trace', () {
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
recordStackTraceAtLevel = Level.SEVERE;
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
root.severe('hello');
|
||
|
|
expect(records.single.stackTrace, isNotNull);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('defaults an empty trace', () {
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
recordStackTraceAtLevel = Level.SEVERE;
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
root.severe('hello', 'error', StackTrace.empty);
|
||
|
|
expect(records.single.stackTrace, isNot(StackTrace.empty));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('provided trace is used if given', () {
|
||
|
|
final trace = StackTrace.current;
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
recordStackTraceAtLevel = Level.WARNING;
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
root.severe('hello');
|
||
|
|
root.warning('hello', 'a', trace);
|
||
|
|
expect(records, hasLength(2));
|
||
|
|
expect(records[0].stackTrace, isNot(equals(trace)));
|
||
|
|
expect(records[1].stackTrace, trace);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('error also generated when generating a trace', () {
|
||
|
|
final records = <LogRecord>[];
|
||
|
|
recordStackTraceAtLevel = Level.WARNING;
|
||
|
|
root.onRecord.listen(records.add);
|
||
|
|
root.severe('hello');
|
||
|
|
root.warning('hello');
|
||
|
|
root.info('hello');
|
||
|
|
expect(records, hasLength(3));
|
||
|
|
expect(records[0].error, isNotNull);
|
||
|
|
expect(records[1].error, isNotNull);
|
||
|
|
expect(records[2].error, isNull);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('listen for level changed', () {
|
||
|
|
final levels = <Level?>[];
|
||
|
|
root.level = Level.ALL;
|
||
|
|
root.onLevelChanged.listen(levels.add);
|
||
|
|
root.level = Level.SEVERE;
|
||
|
|
root.level = Level.WARNING;
|
||
|
|
expect(levels, hasLength(2));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('onLevelChanged is not emited if set the level to the same value', () {
|
||
|
|
final levels = <Level?>[];
|
||
|
|
root.level = Level.ALL;
|
||
|
|
root.onLevelChanged.listen(levels.add);
|
||
|
|
root.level = Level.ALL;
|
||
|
|
expect(levels, hasLength(0));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('setting level in a loop throws state error', () {
|
||
|
|
root.level = Level.ALL;
|
||
|
|
root.onLevelChanged.listen((event) {
|
||
|
|
// Cannot fire new event. Controller is already firing an event
|
||
|
|
expect(() => root.level = Level.SEVERE, throwsStateError);
|
||
|
|
});
|
||
|
|
root.level = Level.WARNING;
|
||
|
|
expect(root.level, Level.SEVERE);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|