Automatically intercept print calls and append them to reponse.output
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d81018c..e42904a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,14 @@
+## 0.1.3
+
+* Add automatic intercepting of print calls and append them to
+  `response.output`. This makes more libraries work out of the box, as printing
+  would previously cause an error due to communication over stdin/stdout.
+  * Note that using stdin/stdout directly will still cause an error, but that is
+    less common.
+
 ## 0.1.2
 
-* Add better handling for the case where stdin gives an error instead of an EOF. 
+* Add better handling for the case where stdin gives an error instead of an EOF.
 
 ## 0.1.1
 
diff --git a/lib/src/async_worker_loop.dart b/lib/src/async_worker_loop.dart
index 4583ba2..bf6c893 100644
--- a/lib/src/async_worker_loop.dart
+++ b/lib/src/async_worker_loop.dart
@@ -41,7 +41,16 @@
       try {
         var request = await connection.readRequest();
         if (request == null) break;
-        response = await performRequest(request);
+        var printMessages = new StringBuffer();
+        response = await runZoned(() => performRequest(request),
+            zoneSpecification:
+                new ZoneSpecification(print: (self, parent, zone, message) {
+          printMessages.writeln();
+          printMessages.write(message);
+        }));
+        if (printMessages.isNotEmpty) {
+          response.output = '${response.output}$printMessages';
+        }
         // In case they forget to set this.
         response.exitCode ??= EXIT_CODE_OK;
       } catch (e, s) {
diff --git a/lib/src/sync_worker_loop.dart b/lib/src/sync_worker_loop.dart
index 3b2a05b..a3528a5 100644
--- a/lib/src/sync_worker_loop.dart
+++ b/lib/src/sync_worker_loop.dart
@@ -1,7 +1,7 @@
 // Copyright (c) 2016, 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 'dart:io';
 
 import 'constants.dart';
@@ -39,7 +39,15 @@
       try {
         var request = connection.readRequest();
         if (request == null) break;
-        response = performRequest(request);
+        var printMessages = new StringBuffer();
+        response = runZoned(() => performRequest(request), zoneSpecification:
+            new ZoneSpecification(print: (self, parent, zone, message) {
+          printMessages.writeln();
+          printMessages.write(message);
+        }));
+        if (printMessages.isNotEmpty) {
+          response.output = '${response.output}$printMessages';
+        }
         // In case they forget to set this.
         response.exitCode ??= EXIT_CODE_OK;
       } catch (e, s) {
diff --git a/lib/testing.dart b/lib/testing.dart
index 882097f..1afd231 100644
--- a/lib/testing.dart
+++ b/lib/testing.dart
@@ -101,6 +101,9 @@
 /// Interface for a [TestWorkerLoop] which allows you to enqueue responses.
 abstract class TestWorkerLoop implements WorkerLoop {
   void enqueueResponse(WorkResponse response);
+
+  /// If set, this message will be printed during the call to `performRequest`.
+  String get printMessage;
 }
 
 /// A [StdSyncWorkerConnection] which records its responses.
@@ -123,12 +126,16 @@
   final List<WorkRequest> requests = <WorkRequest>[];
   final Queue<WorkResponse> _responses = new Queue<WorkResponse>();
 
-  TestSyncWorkerLoop(SyncWorkerConnection connection)
+  @override
+  final String printMessage;
+
+  TestSyncWorkerLoop(SyncWorkerConnection connection, {this.printMessage})
       : super(connection: connection);
 
   @override
   WorkResponse performRequest(WorkRequest request) {
     requests.add(request);
+    if (printMessage != null) print(printMessage);
     return _responses.removeFirst();
   }
 
@@ -161,12 +168,16 @@
   final List<WorkRequest> requests = <WorkRequest>[];
   final Queue<WorkResponse> _responses = new Queue<WorkResponse>();
 
-  TestAsyncWorkerLoop(AsyncWorkerConnection connection)
+  @override
+  final String printMessage;
+
+  TestAsyncWorkerLoop(AsyncWorkerConnection connection, {this.printMessage})
       : super(connection: connection);
 
   @override
   Future<WorkResponse> performRequest(WorkRequest request) async {
     requests.add(request);
+    if (printMessage != null) print(printMessage);
     return _responses.removeFirst();
   }
 
diff --git a/pubspec.yaml b/pubspec.yaml
index e7e589e..e6ca01b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: bazel_worker
-version: 0.1.2
+version: 0.1.3
 description: Tools for creating a bazel persistent worker.
 author: Dart Team <misc@dartlang.org>
 homepage: https://github.com/dart-lang/bazel_worker
diff --git a/test/worker_loop_test.dart b/test/worker_loop_test.dart
index ac4dd11..1ef2845 100644
--- a/test/worker_loop_test.dart
+++ b/test/worker_loop_test.dart
@@ -28,6 +28,24 @@
         (TestAsyncWorkerConnection connection) =>
             new TestAsyncWorkerLoop(connection));
   });
+
+  group('SyncWorkerLoopWithPrint', () {
+    runTests(
+        () => new TestStdinSync(),
+        (Stdin stdinStream, Stdout stdoutStream) =>
+            new TestSyncWorkerConnection(stdinStream, stdoutStream),
+        (TestSyncWorkerConnection connection) =>
+            new TestSyncWorkerLoop(connection, printMessage: 'Goodbye!'));
+  });
+
+  group('AsyncWorkerLoopWithPrint', () {
+    runTests(
+        () => new TestStdinAsync(),
+        (Stdin stdinStream, Stdout stdoutStream) =>
+            new TestAsyncWorkerConnection(stdinStream, stdoutStream),
+        (TestAsyncWorkerConnection connection) =>
+            new TestAsyncWorkerLoop(connection, printMessage: 'Goodbye!'));
+  });
 }
 
 void runTests/*<T extends TestWorkerConnection>*/(
@@ -54,10 +72,23 @@
 
     var response = new WorkResponse()..output = 'Hello World';
     workerLoop.enqueueResponse(response);
-    await workerLoop.run();
+
+    // Make sure `print` never gets called in the parent zone.
+    var printMessages = <String>[];
+    await runZoned(() => workerLoop.run(), zoneSpecification:
+        new ZoneSpecification(print: (self, parent, zone, message) {
+      printMessages.add(message);
+    }));
+    expect(printMessages, isEmpty,
+        reason: 'The worker loop should hide all print calls from the parent '
+            'zone.');
 
     expect(connection.responses, hasLength(1));
     expect(connection.responses[0], response);
+    if (workerLoop.printMessage != null) {
+      expect(response.output, endsWith(workerLoop.printMessage),
+          reason: 'Print messages should get appended to the response output.');
+    }
 
     // Check that a serialized version was written to std out.
     expect(stdoutStream.writes, hasLength(1));