blob: b519f9d569c81858c438ee4adc9c5b18afc385e2 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The standard dart logger cannot be extended as the only way to create one is
// via factory methods. Those factory methods cannot return an instance of this
// class, which they know nothing about. To remedy that, this code provides a
// parallel implementation of Logger with extensions required to accommodate
// the extended functionality logger in fuchsia/zircon.
//
// This removes support for the hierarchical logger that is enabled in the dart
// version.
import 'dart:async';
import 'dart:io';
import 'package:logging/logging.dart';
import 'package:zircon/zircon.dart';
import 'fuchsia_log_record.dart';
/// Handler callback to process log entries as they are added to a [Logger].
typedef LoggerHandler = void Function(FuchsiaLogRecord logRecord);
const int _zxClockMonotonic = 0;
/// Use a [FuchsiaLogger] to log debug messages. This class adds fuchsia
/// specific extenstions for logging: time specified in zircon time and local
/// tags in log messages.
class FuchsiaLogger {
/// Constructor
FuchsiaLogger(this.level) {
Logger.root.clearListeners();
Logger.root.onRecord.listen((LogRecord rec) {
log(rec.level, rec.message, /* local tag */ null, rec.error,
rec.stackTrace);
});
}
/// Logging [Level] used for entries generated on this logger.
Level level;
/// Controller used to notify when log entries are added to this logger.
StreamController<FuchsiaLogRecord> _controller;
/// Returns a stream of messages added to this [FuchsiaLogger].
///
/// You can listen for messages using the standard stream APIs, for instance:
///
/// ```dart
/// logger.onRecord.listen((record) { ... });
/// ```
Stream<FuchsiaLogRecord> get onRecord => _getStream();
/// Remove the Listener attached to this [FuchsiaLogger].
void clearListener() {
if (_controller != null) {
_controller.close();
_controller = null;
}
}
/// Log message at level [Level.FINEST].
// ignore: type_annotate_public_apis, always_specify_types
void finest(message, [Object error, StackTrace stackTrace]) =>
log(Level.FINEST, message, null, error, stackTrace);
/// Log message at level [Level.FINER].
// ignore: type_annotate_public_apis, always_specify_types
void finer(message, [Object error, StackTrace stackTrace]) =>
log(Level.FINER, message, null, error, stackTrace);
/// Log message at level [Level.FINE].
// ignore: type_annotate_public_apis, always_specify_types
void fine(message, [Object error, StackTrace stackTrace]) =>
log(Level.FINE, message, null, error, stackTrace);
/// Log message at level [Level.CONFIG].
// ignore: type_annotate_public_apis, always_specify_types
void config(message, [Object error, StackTrace stackTrace]) =>
log(Level.CONFIG, message, null, error, stackTrace);
/// Log message at level [Level.INFO].
// ignore: type_annotate_public_apis, always_specify_types
void info(message, [Object error, StackTrace stackTrace]) =>
log(Level.INFO, message, null, error, stackTrace);
/// Log message at level [Level.WARNING].
// ignore: type_annotate_public_apis, always_specify_types
void warning(message, [Object error, StackTrace stackTrace]) =>
log(Level.WARNING, message, null, error, stackTrace);
/// Log message at level [Level.SEVERE].
// ignore: type_annotate_public_apis, always_specify_types
void severe(message, [Object error, StackTrace stackTrace]) =>
log(Level.SEVERE, message, null, error, stackTrace);
/// Log message at level [Level.SHOUT].
// ignore: type_annotate_public_apis, always_specify_types
void shout(message, [Object error, StackTrace stackTrace]) =>
log(Level.SHOUT, message, null, error, stackTrace);
/// Log message at level [Level.FINEST].
// ignore: type_annotate_public_apis, always_specify_types
void finestT(message, {String tag, Object error, StackTrace stackTrace}) =>
log(Level.FINEST, message, tag, error, stackTrace);
/// Log message at level [Level.FINER].
// ignore: type_annotate_public_apis, always_specify_types
void finerT(message, {String tag, Object error, StackTrace stackTrace}) =>
log(Level.FINER, message, tag, error, stackTrace);
/// Log message at level [Level.FINE].
// ignore: type_annotate_public_apis, always_specify_types
void fineT(message, {String tag, Object error, StackTrace stackTrace}) =>
log(Level.FINE, message, tag, error, stackTrace);
/// Log message at level [Level.CONFIG].
// ignore: type_annotate_public_apis, always_specify_types
void configT(message, {String tag, Object error, StackTrace stackTrace}) =>
log(Level.CONFIG, message, tag, error, stackTrace);
/// Log message at level [Level.INFO].
// ignore: type_annotate_public_apis, always_specify_types
void infoT(message, {String tag, Object error, StackTrace stackTrace}) =>
log(Level.INFO, message, tag, error, stackTrace);
/// Log message at level [Level.WARNING].
// ignore: type_annotate_public_apis, always_specify_types
void warningT(message, {String tag, Object error, StackTrace stackTrace}) =>
log(Level.WARNING, message, tag, error, stackTrace);
/// Log message at level [Level.SEVERE].
// ignore: type_annotate_public_apis, always_specify_types
void severeT(message, {String tag, Object error, StackTrace stackTrace}) =>
log(Level.SEVERE, message, tag, error, stackTrace);
/// Log message at level [Level.SHOUT].
// ignore: type_annotate_public_apis, always_specify_types
void shoutT(message, {String tag, Object error, StackTrace stackTrace}) =>
log(Level.SHOUT, message, tag, error, stackTrace);
/// 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, Object message, String localTag, Object error,
StackTrace stackTrace) {
if (!isLoggable(logLevel)) {
return;
}
int systemTime = Platform.isFuchsia
? System.clockGet(_zxClockMonotonic)
: DateTime.now().microsecondsSinceEpoch * 1000;
String logMsg;
if (message is Function) {
logMsg = message();
} else if (message is String) {
logMsg = message;
} else {
Object object = message;
logMsg = object.toString();
}
_publish(FuchsiaLogRecord(logLevel, logMsg, systemTime,
localTag: localTag, error: error, stackTrace: stackTrace));
}
Stream<FuchsiaLogRecord> _getStream() {
_controller ??=
StreamController<FuchsiaLogRecord>.broadcast(sync: true);
return _controller.stream;
}
void _publish(FuchsiaLogRecord record) {
if (_controller != null) {
_controller.add(record);
}
}
}