Refactor into mini-libraries (#64)
diff --git a/lib/logging.dart b/lib/logging.dart
index ba8c529..f84bbb1 100644
--- a/lib/logging.dart
+++ b/lib/logging.dart
@@ -2,365 +2,13 @@
// 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.
-library logging;
+import 'src/log_record.dart';
+import 'src/logger.dart';
-import 'dart:async';
-import 'dart:collection';
-
-/// Whether to allow fine-grain logging and configuration of loggers in a
-/// hierarchy.
-///
-/// When false, all logging is merged in the root logger.
-bool hierarchicalLoggingEnabled = false;
-
-/// Automatically record stack traces for any message of this level or above.
-///
-/// Because this is expensive, this is off by default.
-Level recordStackTraceAtLevel = Level.OFF;
-
-/// Level for the root-logger.
-///
-/// This will be the level of all loggers if [hierarchicalLoggingEnabled] is
-/// false.
-Level _rootLevel = Level.INFO;
-
-/// Use a [Logger] to log debug messages.
-///
-/// [Logger]s are named using a hierarchical dot-separated name convention.
-class Logger {
- /// Simple name of this logger.
- final String name;
-
- /// The full name of this logger, which includes the parent's full name.
- String get fullName =>
- (parent == null || parent.name == '') ? name : '${parent.fullName}.$name';
-
- /// Parent of this logger in the hierarchy of loggers.
- final Logger parent;
-
- /// Logging [Level] used for entries generated on this logger.
- Level _level;
-
- final Map<String, Logger> _children;
-
- /// Children in the hierarchy of loggers, indexed by their simple names.
- final Map<String, Logger> children;
-
- /// Controller used to notify when log entries are added to this logger.
- StreamController<LogRecord> _controller;
-
- /// Singleton constructor. Calling `new Logger(name)` will return the same
- /// actual instance whenever it is called with the same string name.
- factory Logger(String name) =>
- _loggers.putIfAbsent(name, () => Logger._named(name));
-
- /// Creates a new detached [Logger].
- ///
- /// Returns a new [Logger] instance (unlike `new Logger`, which returns a
- /// [Logger] singleton), which doesn't have any parent or children,
- /// and is not a part of the global hierarchical loggers structure.
- ///
- /// It can be useful when you just need a local short-living logger,
- /// which you'd like to be garbage-collected later.
- factory Logger.detached(String name) =>
- Logger._internal(name, null, <String, Logger>{});
-
- factory Logger._named(String name) {
- if (name.startsWith('.')) {
- throw ArgumentError("name shouldn't start with a '.'");
- }
- // Split hierarchical names (separated with '.').
- var dot = name.lastIndexOf('.');
- Logger parent;
- String thisName;
- if (dot == -1) {
- if (name != '') parent = Logger('');
- thisName = name;
- } else {
- parent = Logger(name.substring(0, dot));
- thisName = name.substring(dot + 1);
- }
- return Logger._internal(thisName, parent, <String, Logger>{});
- }
-
- Logger._internal(this.name, this.parent, Map<String, Logger> children)
- : _children = children,
- children = UnmodifiableMapView(children) {
- if (parent != null) parent._children[name] = this;
- }
-
- /// 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;
- }
- return _rootLevel;
- }
-
- /// 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.');
- }
- _rootLevel = value;
- }
- }
-
- /// Returns a stream of messages added to this [Logger].
- ///
- /// You can listen for messages using the standard stream APIs, for instance:
- ///
- /// ```dart
- /// logger.onRecord.listen((record) { ... });
- /// ```
- Stream<LogRecord> get onRecord => _getStream();
-
- void clearListeners() {
- if (hierarchicalLoggingEnabled || parent == null) {
- if (_controller != null) {
- _controller.close();
- _controller = null;
- }
- } else {
- root.clearListeners();
- }
- }
-
- /// Whether a message for [value]'s level is loggable in this logger.
- bool isLoggable(Level value) => (value >= level);
-
- /// Adds a log record for a [message] at a particular [logLevel] if
- /// `isLoggable(logLevel)` is true.
- ///
- /// Use this method to create log entries for user-defined levels. To record a
- /// message at a predefined level (e.g. [Level.INFO], [Level.WARNING], etc)
- /// you can use their specialized methods instead (e.g. [info], [warning],
- /// etc).
- ///
- /// If [message] is a [Function], it will be lazy evaluated. Additionally, if
- /// [message] or its evaluated value is not a [String], then 'toString()' will
- /// be called on the object and the result will be logged. The log record will
- /// contain a field holding the original object.
- ///
- /// The log record will also contain a field for the zone in which this call
- /// was made. This can be advantageous if a log listener wants to handler
- /// records of different zones differently (e.g. group log records by HTTP
- /// request if each HTTP request handler runs in it's own zone).
- void log(Level logLevel, message,
- [Object error, StackTrace stackTrace, Zone zone]) {
- Object object;
- if (isLoggable(logLevel)) {
- if (message is Function) {
- message = message();
- }
-
- String msg;
- if (message is String) {
- msg = message;
- } else {
- msg = message.toString();
- object = message;
- }
-
- if (stackTrace == null && logLevel >= recordStackTraceAtLevel) {
- stackTrace = StackTrace.current;
- error ??= 'autogenerated stack trace for $logLevel $msg';
- }
- zone ??= Zone.current;
-
- var record =
- LogRecord(logLevel, msg, fullName, error, stackTrace, zone, object);
-
- if (hierarchicalLoggingEnabled) {
- var target = this;
- while (target != null) {
- target._publish(record);
- target = target.parent;
- }
- } else {
- root._publish(record);
- }
- }
- }
-
- /// Log message at level [Level.FINEST].
- void finest(message, [Object error, StackTrace stackTrace]) =>
- log(Level.FINEST, message, error, stackTrace);
-
- /// Log message at level [Level.FINER].
- void finer(message, [Object error, StackTrace stackTrace]) =>
- log(Level.FINER, message, error, stackTrace);
-
- /// Log message at level [Level.FINE].
- void fine(message, [Object error, StackTrace stackTrace]) =>
- log(Level.FINE, message, error, stackTrace);
-
- /// Log message at level [Level.CONFIG].
- void config(message, [Object error, StackTrace stackTrace]) =>
- log(Level.CONFIG, message, error, stackTrace);
-
- /// Log message at level [Level.INFO].
- void info(message, [Object error, StackTrace stackTrace]) =>
- log(Level.INFO, message, error, stackTrace);
-
- /// Log message at level [Level.WARNING].
- void warning(message, [Object error, StackTrace stackTrace]) =>
- log(Level.WARNING, message, error, stackTrace);
-
- /// Log message at level [Level.SEVERE].
- void severe(message, [Object error, StackTrace stackTrace]) =>
- log(Level.SEVERE, message, error, stackTrace);
-
- /// Log message at level [Level.SHOUT].
- void shout(message, [Object error, StackTrace stackTrace]) =>
- log(Level.SHOUT, message, error, stackTrace);
-
- Stream<LogRecord> _getStream() {
- if (hierarchicalLoggingEnabled || parent == null) {
- _controller ??= StreamController<LogRecord>.broadcast(sync: true);
- return _controller.stream;
- } else {
- return root._getStream();
- }
- }
-
- void _publish(LogRecord record) {
- if (_controller != null) {
- _controller.add(record);
- }
- }
-
- /// Top-level root [Logger].
- static final Logger root = Logger('');
-
- /// All [Logger]s in the system.
- static final Map<String, Logger> _loggers = <String, Logger>{};
-}
+export 'src/level.dart';
+export 'src/log_record.dart';
+export 'src/logger.dart';
/// Handler callback to process log entries as they are added to a [Logger].
-@deprecated
+@Deprecated('Will be removed in 1.0.0')
typedef LoggerHandler = void Function(LogRecord record);
-
-/// [Level]s to control logging output. Logging can be enabled to include all
-/// levels above certain [Level]. [Level]s are ordered using an integer
-/// value [Level.value]. The predefined [Level] constants below are sorted as
-/// follows (in descending order): [Level.SHOUT], [Level.SEVERE],
-/// [Level.WARNING], [Level.INFO], [Level.CONFIG], [Level.FINE], [Level.FINER],
-/// [Level.FINEST], and [Level.ALL].
-///
-/// We recommend using one of the predefined logging levels. If you define your
-/// own level, make sure you use a value between those used in [Level.ALL] and
-/// [Level.OFF].
-class Level implements Comparable<Level> {
- final String name;
-
- /// Unique value for this level. Used to order levels, so filtering can
- /// exclude messages whose level is under certain value.
- final int value;
-
- const Level(this.name, this.value);
-
- /// Special key to turn on logging for all levels ([value] = 0).
- static const Level ALL = Level('ALL', 0);
-
- /// Special key to turn off all logging ([value] = 2000).
- static const Level OFF = Level('OFF', 2000);
-
- /// Key for highly detailed tracing ([value] = 300).
- static const Level FINEST = Level('FINEST', 300);
-
- /// Key for fairly detailed tracing ([value] = 400).
- static const Level FINER = Level('FINER', 400);
-
- /// Key for tracing information ([value] = 500).
- static const Level FINE = Level('FINE', 500);
-
- /// Key for static configuration messages ([value] = 700).
- static const Level CONFIG = Level('CONFIG', 700);
-
- /// Key for informational messages ([value] = 800).
- static const Level INFO = Level('INFO', 800);
-
- /// Key for potential problems ([value] = 900).
- static const Level WARNING = Level('WARNING', 900);
-
- /// Key for serious failures ([value] = 1000).
- static const Level SEVERE = Level('SEVERE', 1000);
-
- /// Key for extra debugging loudness ([value] = 1200).
- static const Level SHOUT = Level('SHOUT', 1200);
-
- static const List<Level> LEVELS = [
- ALL,
- FINEST,
- FINER,
- FINE,
- CONFIG,
- INFO,
- WARNING,
- SEVERE,
- SHOUT,
- OFF
- ];
-
- @override
- bool operator ==(Object other) => other is Level && value == other.value;
- bool operator <(Level other) => value < other.value;
- bool operator <=(Level other) => value <= other.value;
- bool operator >(Level other) => value > other.value;
- bool operator >=(Level other) => value >= other.value;
-
- @override
- int compareTo(Level other) => value - other.value;
-
- @override
- int get hashCode => value;
-
- @override
- String toString() => name;
-}
-
-/// A log entry representation used to propagate information from [Logger] to
-/// individual handlers.
-class LogRecord {
- final Level level;
- final String message;
-
- /// Non-string message passed to Logger.
- final Object object;
-
- /// Logger where this record is stored.
- final String loggerName;
-
- /// Time when this record was created.
- final DateTime time;
-
- /// Unique sequence number greater than all log records created before it.
- final int sequenceNumber;
-
- static int _nextNumber = 0;
-
- /// Associated error (if any) when recording errors messages.
- final Object error;
-
- /// Associated stackTrace (if any) when recording errors messages.
- final StackTrace stackTrace;
-
- /// Zone of the calling code which resulted in this LogRecord.
- final Zone zone;
-
- LogRecord(this.level, this.message, this.loggerName,
- [this.error, this.stackTrace, this.zone, this.object])
- : time = DateTime.now(),
- sequenceNumber = LogRecord._nextNumber++;
-
- @override
- String toString() => '[${level.name}] $loggerName: $message';
-}
diff --git a/lib/src/level.dart b/lib/src/level.dart
new file mode 100644
index 0000000..b2f7c88
--- /dev/null
+++ b/lib/src/level.dart
@@ -0,0 +1,86 @@
+// Copyright (c) 2019, 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.
+
+/// [Level]s to control logging output. Logging can be enabled to include all
+/// levels above certain [Level]. [Level]s are ordered using an integer
+/// value [Level.value]. The predefined [Level] constants below are sorted as
+/// follows (in descending order): [Level.SHOUT], [Level.SEVERE],
+/// [Level.WARNING], [Level.INFO], [Level.CONFIG], [Level.FINE], [Level.FINER],
+/// [Level.FINEST], and [Level.ALL].
+///
+/// We recommend using one of the predefined logging levels. If you define your
+/// own level, make sure you use a value between those used in [Level.ALL] and
+/// [Level.OFF].
+class Level implements Comparable<Level> {
+ final String name;
+
+ /// Unique value for this level. Used to order levels, so filtering can
+ /// exclude messages whose level is under certain value.
+ final int value;
+
+ const Level(this.name, this.value);
+
+ /// Special key to turn on logging for all levels ([value] = 0).
+ static const Level ALL = Level('ALL', 0);
+
+ /// Special key to turn off all logging ([value] = 2000).
+ static const Level OFF = Level('OFF', 2000);
+
+ /// Key for highly detailed tracing ([value] = 300).
+ static const Level FINEST = Level('FINEST', 300);
+
+ /// Key for fairly detailed tracing ([value] = 400).
+ static const Level FINER = Level('FINER', 400);
+
+ /// Key for tracing information ([value] = 500).
+ static const Level FINE = Level('FINE', 500);
+
+ /// Key for static configuration messages ([value] = 700).
+ static const Level CONFIG = Level('CONFIG', 700);
+
+ /// Key for informational messages ([value] = 800).
+ static const Level INFO = Level('INFO', 800);
+
+ /// Key for potential problems ([value] = 900).
+ static const Level WARNING = Level('WARNING', 900);
+
+ /// Key for serious failures ([value] = 1000).
+ static const Level SEVERE = Level('SEVERE', 1000);
+
+ /// Key for extra debugging loudness ([value] = 1200).
+ static const Level SHOUT = Level('SHOUT', 1200);
+
+ static const List<Level> LEVELS = [
+ ALL,
+ FINEST,
+ FINER,
+ FINE,
+ CONFIG,
+ INFO,
+ WARNING,
+ SEVERE,
+ SHOUT,
+ OFF
+ ];
+
+ @override
+ bool operator ==(Object other) => other is Level && value == other.value;
+
+ bool operator <(Level other) => value < other.value;
+
+ bool operator <=(Level other) => value <= other.value;
+
+ bool operator >(Level other) => value > other.value;
+
+ bool operator >=(Level other) => value >= other.value;
+
+ @override
+ int compareTo(Level other) => value - other.value;
+
+ @override
+ int get hashCode => value;
+
+ @override
+ String toString() => name;
+}
diff --git a/lib/src/log_record.dart b/lib/src/log_record.dart
new file mode 100644
index 0000000..40d934e
--- /dev/null
+++ b/lib/src/log_record.dart
@@ -0,0 +1,42 @@
+import 'dart:async';
+
+import 'level.dart';
+import 'logger.dart';
+
+/// A log entry representation used to propagate information from [Logger] to
+/// individual handlers.
+class LogRecord {
+ final Level level;
+ final String message;
+
+ /// Non-string message passed to Logger.
+ final Object object;
+
+ /// Logger where this record is stored.
+ final String loggerName;
+
+ /// Time when this record was created.
+ final DateTime time;
+
+ /// Unique sequence number greater than all log records created before it.
+ final int sequenceNumber;
+
+ static int _nextNumber = 0;
+
+ /// Associated error (if any) when recording errors messages.
+ final Object error;
+
+ /// Associated stackTrace (if any) when recording errors messages.
+ final StackTrace stackTrace;
+
+ /// Zone of the calling code which resulted in this LogRecord.
+ final Zone zone;
+
+ LogRecord(this.level, this.message, this.loggerName,
+ [this.error, this.stackTrace, this.zone, this.object])
+ : time = DateTime.now(),
+ sequenceNumber = LogRecord._nextNumber++;
+
+ @override
+ String toString() => '[${level.name}] $loggerName: $message';
+}
diff --git a/lib/src/logger.dart b/lib/src/logger.dart
new file mode 100644
index 0000000..bb71838
--- /dev/null
+++ b/lib/src/logger.dart
@@ -0,0 +1,242 @@
+import 'dart:async';
+import 'dart:collection';
+
+import 'level.dart';
+import 'log_record.dart';
+
+/// Whether to allow fine-grain logging and configuration of loggers in a
+/// hierarchy.
+///
+/// When false, all logging is merged in the root logger.
+bool hierarchicalLoggingEnabled = false;
+
+/// Automatically record stack traces for any message of this level or above.
+///
+/// Because this is expensive, this is off by default.
+Level recordStackTraceAtLevel = Level.OFF;
+
+/// Level for the root-logger.
+///
+/// This will be the level of all loggers if [hierarchicalLoggingEnabled] is
+/// false.
+Level _rootLevel = Level.INFO;
+
+/// Use a [Logger] to log debug messages.
+///
+/// [Logger]s are named using a hierarchical dot-separated name convention.
+class Logger {
+ /// Simple name of this logger.
+ final String name;
+
+ /// The full name of this logger, which includes the parent's full name.
+ String get fullName =>
+ (parent == null || parent.name == '') ? name : '${parent.fullName}.$name';
+
+ /// Parent of this logger in the hierarchy of loggers.
+ final Logger parent;
+
+ /// Logging [Level] used for entries generated on this logger.
+ Level _level;
+
+ final Map<String, Logger> _children;
+
+ /// Children in the hierarchy of loggers, indexed by their simple names.
+ final Map<String, Logger> children;
+
+ /// Controller used to notify when log entries are added to this logger.
+ StreamController<LogRecord> _controller;
+
+ /// Singleton constructor. Calling `new Logger(name)` will return the same
+ /// actual instance whenever it is called with the same string name.
+ factory Logger(String name) =>
+ _loggers.putIfAbsent(name, () => Logger._named(name));
+
+ /// Creates a new detached [Logger].
+ ///
+ /// Returns a new [Logger] instance (unlike `new Logger`, which returns a
+ /// [Logger] singleton), which doesn't have any parent or children,
+ /// and is not a part of the global hierarchical loggers structure.
+ ///
+ /// It can be useful when you just need a local short-living logger,
+ /// which you'd like to be garbage-collected later.
+ factory Logger.detached(String name) =>
+ Logger._internal(name, null, <String, Logger>{});
+
+ factory Logger._named(String name) {
+ if (name.startsWith('.')) {
+ throw ArgumentError("name shouldn't start with a '.'");
+ }
+ // Split hierarchical names (separated with '.').
+ var dot = name.lastIndexOf('.');
+ Logger parent;
+ String thisName;
+ if (dot == -1) {
+ if (name != '') parent = Logger('');
+ thisName = name;
+ } else {
+ parent = Logger(name.substring(0, dot));
+ thisName = name.substring(dot + 1);
+ }
+ return Logger._internal(thisName, parent, <String, Logger>{});
+ }
+
+ Logger._internal(this.name, this.parent, Map<String, Logger> children)
+ : _children = children,
+ children = UnmodifiableMapView(children) {
+ if (parent != null) parent._children[name] = this;
+ }
+
+ /// 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;
+ }
+ return _rootLevel;
+ }
+
+ /// 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.');
+ }
+ _rootLevel = value;
+ }
+ }
+
+ /// Returns a stream of messages added to this [Logger].
+ ///
+ /// You can listen for messages using the standard stream APIs, for instance:
+ ///
+ /// ```dart
+ /// logger.onRecord.listen((record) { ... });
+ /// ```
+ Stream<LogRecord> get onRecord => _getStream();
+
+ void clearListeners() {
+ if (hierarchicalLoggingEnabled || parent == null) {
+ if (_controller != null) {
+ _controller.close();
+ _controller = null;
+ }
+ } else {
+ root.clearListeners();
+ }
+ }
+
+ /// Whether a message for [value]'s level is loggable in this logger.
+ bool isLoggable(Level value) => (value >= level);
+
+ /// Adds a log record for a [message] at a particular [logLevel] if
+ /// `isLoggable(logLevel)` is true.
+ ///
+ /// Use this method to create log entries for user-defined levels. To record a
+ /// message at a predefined level (e.g. [Level.INFO], [Level.WARNING], etc)
+ /// you can use their specialized methods instead (e.g. [info], [warning],
+ /// etc).
+ ///
+ /// If [message] is a [Function], it will be lazy evaluated. Additionally, if
+ /// [message] or its evaluated value is not a [String], then 'toString()' will
+ /// be called on the object and the result will be logged. The log record will
+ /// contain a field holding the original object.
+ ///
+ /// The log record will also contain a field for the zone in which this call
+ /// was made. This can be advantageous if a log listener wants to handler
+ /// records of different zones differently (e.g. group log records by HTTP
+ /// request if each HTTP request handler runs in it's own zone).
+ void log(Level logLevel, message,
+ [Object error, StackTrace stackTrace, Zone zone]) {
+ Object object;
+ if (isLoggable(logLevel)) {
+ if (message is Function) {
+ message = message();
+ }
+
+ String msg;
+ if (message is String) {
+ msg = message;
+ } else {
+ msg = message.toString();
+ object = message;
+ }
+
+ if (stackTrace == null && logLevel >= recordStackTraceAtLevel) {
+ stackTrace = StackTrace.current;
+ error ??= 'autogenerated stack trace for $logLevel $msg';
+ }
+ zone ??= Zone.current;
+
+ var record =
+ LogRecord(logLevel, msg, fullName, error, stackTrace, zone, object);
+
+ if (hierarchicalLoggingEnabled) {
+ var target = this;
+ while (target != null) {
+ target._publish(record);
+ target = target.parent;
+ }
+ } else {
+ root._publish(record);
+ }
+ }
+ }
+
+ /// Log message at level [Level.FINEST].
+ void finest(message, [Object error, StackTrace stackTrace]) =>
+ log(Level.FINEST, message, error, stackTrace);
+
+ /// Log message at level [Level.FINER].
+ void finer(message, [Object error, StackTrace stackTrace]) =>
+ log(Level.FINER, message, error, stackTrace);
+
+ /// Log message at level [Level.FINE].
+ void fine(message, [Object error, StackTrace stackTrace]) =>
+ log(Level.FINE, message, error, stackTrace);
+
+ /// Log message at level [Level.CONFIG].
+ void config(message, [Object error, StackTrace stackTrace]) =>
+ log(Level.CONFIG, message, error, stackTrace);
+
+ /// Log message at level [Level.INFO].
+ void info(message, [Object error, StackTrace stackTrace]) =>
+ log(Level.INFO, message, error, stackTrace);
+
+ /// Log message at level [Level.WARNING].
+ void warning(message, [Object error, StackTrace stackTrace]) =>
+ log(Level.WARNING, message, error, stackTrace);
+
+ /// Log message at level [Level.SEVERE].
+ void severe(message, [Object error, StackTrace stackTrace]) =>
+ log(Level.SEVERE, message, error, stackTrace);
+
+ /// Log message at level [Level.SHOUT].
+ void shout(message, [Object error, StackTrace stackTrace]) =>
+ log(Level.SHOUT, message, error, stackTrace);
+
+ Stream<LogRecord> _getStream() {
+ if (hierarchicalLoggingEnabled || parent == null) {
+ _controller ??= StreamController<LogRecord>.broadcast(sync: true);
+ return _controller.stream;
+ } else {
+ return root._getStream();
+ }
+ }
+
+ void _publish(LogRecord record) {
+ if (_controller != null) {
+ _controller.add(record);
+ }
+ }
+
+ /// Top-level root [Logger].
+ static final Logger root = Logger('');
+
+ /// All [Logger]s in the system.
+ static final Map<String, Logger> _loggers = <String, Logger>{};
+}