Adding a "zone" value to "class LogRecord"

R=sgjesse@google.com, wibling@google.com

Review URL: https://codereview.chromium.org//786813002
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..8b36d87
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,6 @@
+## 0.9.3
+
+* Added optional `LogRecord.zone` field.
+
+* Record current zone (or user specified zone) when creating new `LogRecord`s.
+
diff --git a/lib/logging.dart b/lib/logging.dart
index dc83d84..42344e8 100644
--- a/lib/logging.dart
+++ b/lib/logging.dart
@@ -138,16 +138,26 @@
    * 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 it and the result will be logged.
+   *
+   * The log record will contain a field for the zone in which this call was
+   * made.
+   * This can be advantagous if a log listener wants to handle 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]) {
+  void log(Level logLevel,
+           message,
+           [Object error, StackTrace stackTrace, Zone zone]) {
     if (isLoggable(logLevel)) {
       // If message is a Function, evaluate it.
       if (message is Function) message = message();
       // If message is still not a String, call toString().
       if (message is! String) message = message.toString();
+      // Only record the current zone if it was not given.
+      if (zone == null) zone = Zone.current;
 
       var record = new LogRecord(logLevel, message, fullName, error,
-          stackTrace);
+          stackTrace, zone);
 
       if (hierarchicalLoggingEnabled) {
         var target = this;
@@ -314,8 +324,12 @@
   /** 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.stackTrace,
+                                                        this.zone])
       : time = new DateTime.now(),
         sequenceNumber = LogRecord._nextNumber++;
 
diff --git a/pubspec.yaml b/pubspec.yaml
index 5a0b86f..3867be5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: logging
-version: 0.9.2
+version: 0.9.3
 author: Dart Team <misc@dartlang.org>
 description: >
   Provides APIs for debugging and error logging. This library introduces
diff --git a/test/logging_test.dart b/test/logging_test.dart
index 223498f..8eb116a 100644
--- a/test/logging_test.dart
+++ b/test/logging_test.dart
@@ -5,6 +5,8 @@
 
 library logging_test;
 
+import 'dart:async';
+
 import 'package:logging/logging.dart';
 import 'package:unittest/unittest.dart';
 
@@ -150,6 +152,53 @@
     expect(shout.stackTrace, isNull);
   });
 
+  group('zone gets recorded to LogRecord', () {
+    test('root zone', () {
+      var root = Logger.root;
+
+      var recordingZone = Zone.current;
+      var records = new List<LogRecord>();
+      root.onRecord.listen(records.add);
+      root.info('hello');
+
+      expect(records, hasLength(1));
+      expect(records.first.zone, equals(recordingZone));
+    });
+
+    test('child zone', () {
+      var root = Logger.root;
+
+      var recordingZone;
+      var records = new List<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', () {
+      var root = Logger.root;
+
+      var recordingZone;
+      var records = new List<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('mutating levels', () {
     Logger root = Logger.root;
     Logger a = new Logger('a');