Make detached Loggers work regardless of hierarchicalLoggingEnabled (#71)
Previously detached Loggers could log messages only if
hierarchicalLoggingEnabled was true. This makes no sense to me since
detached Loggers aren't part of a Logger hierarchy.
Fixes https://github.com/dart-lang/logging/issues/34.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b9bc10..adca9ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
* Add top level `defaultLevel`.
* Require Dart `>=2.0.0`.
+* Make detached loggers work regardless of `hierarchicalLoggingEnabled`.
## 0.11.3+2
@@ -21,7 +22,7 @@
## 0.11.2
-* Added Logger.detached - a convenience factory to obtain a logger that is not
+* Added `Logger.detached` - a convenience factory to obtain a logger that is not
attached to this library's logger hierarchy.
## 0.11.1+1
diff --git a/lib/src/logger.dart b/lib/src/logger.dart
index a34df50..b94f034 100644
--- a/lib/src/logger.dart
+++ b/lib/src/logger.dart
@@ -80,7 +80,9 @@
Logger._internal(this.name, this.parent, Map<String, Logger> children)
: _children = children,
children = UnmodifiableMapView(children) {
- if (parent != null) {
+ if (parent == null) {
+ _level = defaultLevel;
+ } else {
parent._children[name] = this;
}
}
@@ -88,25 +90,30 @@
/// Effective level considering the levels established in this logger's
/// parents (when [hierarchicalLoggingEnabled] is true).
Level get level {
- if (hierarchicalLoggingEnabled) {
- if (_level != null) return _level;
- if (parent != null) return parent.level;
+ Level effectiveLevel;
+
+ if (parent == null) {
+ // We're either the root logger or a detached logger. Return our own
+ // level.
+ effectiveLevel = _level;
+ } else if (!hierarchicalLoggingEnabled) {
+ effectiveLevel = root._level;
+ } else {
+ effectiveLevel = _level ?? parent.level;
}
- return root._level;
+
+ assert(effectiveLevel != null);
+ return effectiveLevel;
}
/// Override the level for this particular [Logger] and its children.
set level(Level value) {
- if (hierarchicalLoggingEnabled && parent != null) {
- _level = value;
- } else {
- if (parent != null) {
- throw UnsupportedError(
- 'Please set "hierarchicalLoggingEnabled" to true if you want to '
- 'change the level on a non-root logger.');
- }
- root._level = value;
+ if (!hierarchicalLoggingEnabled && parent != null) {
+ throw UnsupportedError(
+ 'Please set "hierarchicalLoggingEnabled" to true if you want to '
+ 'change the level on a non-root logger.');
}
+ _level = value;
}
/// Returns a stream of messages added to this [Logger].
@@ -174,14 +181,16 @@
var record =
LogRecord(logLevel, msg, fullName, error, stackTrace, zone, object);
- if (hierarchicalLoggingEnabled) {
+ if (parent == null) {
+ _publish(record);
+ } else if (!hierarchicalLoggingEnabled) {
+ root._publish(record);
+ } else {
var target = this;
while (target != null) {
target._publish(record);
target = target.parent;
}
- } else {
- root._publish(record);
}
}
}
@@ -234,7 +243,7 @@
}
/// Top-level root [Logger].
- static final Logger root = Logger('').._level = defaultLevel;
+ static final Logger root = Logger('');
/// All [Logger]s in the system.
static final Map<String, Logger> _loggers = <String, Logger>{};
diff --git a/test/logging_test.dart b/test/logging_test.dart
index 53fbf4e..d0af49c 100644
--- a/test/logging_test.dart
+++ b/test/logging_test.dart
@@ -10,6 +10,8 @@
import 'package:test/test.dart';
void main() {
+ final hierarchicalLoggingEnabledDefault = hierarchicalLoggingEnabled;
+
test('level comparison is a valid comparator', () {
var level1 = const Level('NOT_REAL1', 253);
expect(level1 == level1, isTrue);
@@ -213,6 +215,11 @@
});
group('detached loggers', () {
+ tearDown(() {
+ hierarchicalLoggingEnabled = hierarchicalLoggingEnabledDefault;
+ Logger.root.level = defaultLevel;
+ });
+
test('create new instances of Logger', () {
var a1 = Logger.detached('a');
var a2 = Logger.detached('a');
@@ -232,6 +239,51 @@
var a = Logger.detached('a');
expect(a.children, {});
});
+
+ 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', () {
@@ -294,6 +346,16 @@
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;