diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 2705c95a26157998f2f2f31a7541e1ef60e0509f..0d879e9b1e0f3e5362efeab4cd8edf8eccd942b5 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -47,7 +47,6 @@
     <Compile Include="IServerStreamWriter.cs" />
     <Compile Include="IAsyncStreamWriter.cs" />
     <Compile Include="IAsyncStreamReader.cs" />
-    <Compile Include="Internal\GrpcLog.cs" />
     <Compile Include="Version.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="RpcException.cs" />
@@ -105,6 +104,9 @@
     <Compile Include="VersionInfo.cs" />
     <Compile Include="Internal\CStringSafeHandle.cs" />
     <Compile Include="KeyCertificatePair.cs" />
+    <Compile Include="Logging\ILogger.cs" />
+    <Compile Include="Logging\ConsoleLogger.cs" />
+    <Compile Include="Internal\NativeLogRedirector.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="Grpc.Core.nuspec" />
@@ -135,4 +137,7 @@
   </Target>
   <Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" />
   <Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" />
+  <ItemGroup>
+    <Folder Include="Logging\" />
+  </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs
index 47d1651aab86c9d41e4175ea2dd59d64047dca26..034a66be3c5ced2553622017e016d80ec7e33abc 100644
--- a/src/csharp/Grpc.Core/GrpcEnvironment.cs
+++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs
@@ -35,6 +35,7 @@ using System;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using Grpc.Core.Internal;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core
@@ -55,6 +56,8 @@ namespace Grpc.Core
         static object staticLock = new object();
         static GrpcEnvironment instance;
 
+        static ILogger logger = new ConsoleLogger();
+
         readonly GrpcThreadPool threadPool;
         readonly CompletionRegistry completionRegistry;
         readonly DebugStats debugStats = new DebugStats();
@@ -92,18 +95,39 @@ namespace Grpc.Core
             }
         }
 
+        /// <summary>
+        /// Gets application-wide logger used by gRPC.
+        /// </summary>
+        /// <value>The logger.</value>
+        public static ILogger Logger
+        {
+            get
+            {
+                return logger;
+            }
+        }
+
+        /// <summary>
+        /// Sets the application-wide logger that should be used by gRPC.
+        /// </summary>
+        public static void SetLogger(ILogger customLogger)
+        {
+            Preconditions.CheckNotNull(customLogger);
+            logger = customLogger;
+        }
+
         /// <summary>
         /// Creates gRPC environment.
         /// </summary>
         private GrpcEnvironment()
         {
-            GrpcLog.RedirectNativeLogs(Console.Error);
+            NativeLogRedirector.Redirect();
             grpcsharp_init();
             completionRegistry = new CompletionRegistry(this);
             threadPool = new GrpcThreadPool(this, THREAD_POOL_SIZE);
             threadPool.Start();
             // TODO: use proper logging here
-            Console.WriteLine("GRPC initialized.");
+            Logger.Info("gRPC initialized.");
         }
 
         /// <summary>
@@ -154,8 +178,7 @@ namespace Grpc.Core
 
             debugStats.CheckOK();
 
-            // TODO: use proper logging here
-            Console.WriteLine("GRPC shutdown.");
+            Logger.Info("gRPC shutdown.");
         }
 
         /// <summary>
@@ -171,7 +194,7 @@ namespace Grpc.Core
                 }
                 catch (Exception e)
                 {
-                    Console.WriteLine("Error occured while shutting down GrpcEnvironment: " + e);
+                    Logger.Error(e, "Error occured while shutting down GrpcEnvironment.");
                 }
             });
         }
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index 51022ac34fc1646fb2a607c542725c8e6c287164..bfcb9366a12331ec1276b867aba5c90e9ba2b36f 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -38,6 +38,7 @@ using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
 using Grpc.Core.Internal;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core.Internal
@@ -47,6 +48,8 @@ namespace Grpc.Core.Internal
     /// </summary>
     internal class AsyncCall<TRequest, TResponse> : AsyncCallBase<TRequest, TResponse>
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCall<TRequest, TResponse>>();
+
         Channel channel;
 
         // Completion of a pending unary response if not null.
@@ -106,7 +109,7 @@ namespace Grpc.Core.Internal
                         }
                         catch (Exception e)
                         {
-                            Console.WriteLine("Exception occured while invoking completion delegate: " + e);
+                            Logger.Error(e, "Exception occured while invoking completion delegate.");
                         }
                     }
                 }
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
index 64713c8c52c4f982070f8b10e057f70e6e363e66..38f2a5baebd0cbac4b523ada8c9c5abb94ff2293 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
@@ -38,6 +38,7 @@ using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
 using Grpc.Core.Internal;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core.Internal
@@ -48,6 +49,8 @@ namespace Grpc.Core.Internal
     /// </summary>
     internal abstract class AsyncCallBase<TWrite, TRead>
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCallBase<TWrite, TRead>>();
+
         readonly Func<TWrite, byte[]> serializer;
         readonly Func<byte[], TRead> deserializer;
 
@@ -233,9 +236,9 @@ namespace Grpc.Core.Internal
                 payload = serializer(msg);
                 return true;
             }
-            catch (Exception)
+            catch (Exception e)
             {
-                Console.WriteLine("Exception occured while trying to serialize message");
+                Logger.Error(e, "Exception occured while trying to serialize message");
                 payload = null;
                 return false;
             }
@@ -248,9 +251,9 @@ namespace Grpc.Core.Internal
                 msg = deserializer(payload);
                 return true;
             } 
-            catch (Exception)
+            catch (Exception e)
             {
-                Console.WriteLine("Exception occured while trying to deserialize message");
+                Logger.Error(e, "Exception occured while trying to deserialize message.");
                 msg = default(TRead);
                 return false;
             }
@@ -264,7 +267,7 @@ namespace Grpc.Core.Internal
             }
             catch (Exception e)
             {
-                Console.WriteLine("Exception occured while invoking completion delegate: " + e);
+                Logger.Error(e, "Exception occured while invoking completion delegate.");
             }
         }
 
diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
index f6d8aa0600f57073b0724001bbe295dcd4af4993..2796c959a389c52485b3db8fd566b9ee15ccb394 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
@@ -35,6 +35,7 @@ using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core.Internal
@@ -45,6 +46,8 @@ namespace Grpc.Core.Internal
 
     internal class CompletionRegistry
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<CompletionRegistry>();
+
         readonly GrpcEnvironment environment;
         readonly ConcurrentDictionary<IntPtr, OpCompletionDelegate> dict = new ConcurrentDictionary<IntPtr, OpCompletionDelegate>();
 
@@ -81,7 +84,7 @@ namespace Grpc.Core.Internal
             }
             catch (Exception e)
             {
-                Console.WriteLine("Exception occured while invoking completion delegate: " + e);
+                Logger.Error(e, "Exception occured while invoking completion delegate.");
             }
             finally
             {
diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
index b77e89304472189665930d6819b7caeb4a13c14c..cb4c7c821e728036a7e6c47730de588e1113b68c 100644
--- a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
+++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
@@ -36,7 +36,7 @@ using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
-using Grpc.Core.Internal;
+using Grpc.Core.Logging;
 
 namespace Grpc.Core.Internal
 {
@@ -45,6 +45,8 @@ namespace Grpc.Core.Internal
     /// </summary>
     internal class GrpcThreadPool
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<GrpcThreadPool>();
+
         readonly GrpcEnvironment environment;
         readonly object myLock = new object();
         readonly List<Thread> threads = new List<Thread>();
@@ -82,7 +84,7 @@ namespace Grpc.Core.Internal
             {
                 cq.Shutdown();
 
-                Console.WriteLine("Waiting for GRPC threads to finish.");
+                Logger.Info("Waiting for GRPC threads to finish.");
                 foreach (var thread in threads)
                 {
                     thread.Join();
@@ -129,12 +131,12 @@ namespace Grpc.Core.Internal
                     }
                     catch (Exception e)
                     {
-                        Console.WriteLine("Exception occured while invoking completion delegate: " + e);
+                        Logger.Error(e, "Exception occured while invoking completion delegate");
                     }
                 }
             }
             while (ev.type != GRPCCompletionType.Shutdown);
-            Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting.");
+            Logger.Info("Completion queue has shutdown successfully, thread {0} exiting.", Thread.CurrentThread.Name);
         }
     }
 }
diff --git a/src/csharp/Grpc.Core/Internal/GrpcLog.cs b/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs
similarity index 74%
rename from src/csharp/Grpc.Core/Internal/GrpcLog.cs
rename to src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs
index 2f3c8ad71c1f850c24f1b2b9ec416dda408378d7..b8a55c5fe85bcd759fc26c8ec25f6dcfa5563a0b 100644
--- a/src/csharp/Grpc.Core/Internal/GrpcLog.cs
+++ b/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs
@@ -44,30 +44,26 @@ namespace Grpc.Core.Internal
 
     /// <summary>
     /// Logs from gRPC C core library can get lost if your application is not a console app.
-    /// This class allows redirection of logs to arbitrary destination.
+    /// This class allows redirection of logs to gRPC logger.
     /// </summary>
-    internal static class GrpcLog
+    internal static class NativeLogRedirector
     {
         static object staticLock = new object();
         static GprLogDelegate writeCallback;
-        static TextWriter dest;
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern void grpcsharp_redirect_log(GprLogDelegate callback);
 
         /// <summary>
-        /// Sets text writer as destination for logs from native gRPC C core library.
-        /// Only first invocation has effect.
+        /// Redirects logs from native gRPC C core library to a general logger.
         /// </summary>
-        /// <param name="textWriter"></param>
-        public static void RedirectNativeLogs(TextWriter textWriter)
+        public static void Redirect()
         {
             lock (staticLock)
             {
                 if (writeCallback == null)
                 {
                     writeCallback = new GprLogDelegate(HandleWrite);
-                    dest = textWriter;
                     grpcsharp_redirect_log(writeCallback);    
                 }
             }
@@ -77,13 +73,30 @@ namespace Grpc.Core.Internal
         {
             try
             {
-                // TODO: DateTime format used here is different than in C core.
-                dest.WriteLine(string.Format("{0}{1} {2} {3}:{4}: {5}", 
-                    Marshal.PtrToStringAnsi(severityStringPtr), DateTime.Now,  
+                var logger = GrpcEnvironment.Logger;
+                string severityString = Marshal.PtrToStringAnsi(severityStringPtr);
+                string message = string.Format("{0} {1}:{2}: {3}",
                     threadId,
                     Marshal.PtrToStringAnsi(fileStringPtr), 
                     line, 
-                    Marshal.PtrToStringAnsi(msgPtr)));
+                    Marshal.PtrToStringAnsi(msgPtr));
+                
+                switch (severityString)
+                {
+                    case "D":
+                        logger.Debug(message);
+                        break;
+                    case "I":
+                        logger.Info(message);
+                        break;
+                    case "E":
+                        logger.Error(message);
+                        break;
+                    default:
+                        // severity not recognized, default to error.
+                        logger.Error(message);
+                        break;
+                }
             }
             catch (Exception e)
             {
diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
index 84ea346ebc00239c0e573aaf0f68917df9c36fa7..19f0e3c57f6c4043f8c94de7261ee8b2529d553a 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
@@ -37,6 +37,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using Grpc.Core.Internal;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core.Internal
@@ -50,6 +51,8 @@ namespace Grpc.Core.Internal
         where TRequest : class
         where TResponse : class
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<UnaryServerCallHandler<TRequest, TResponse>>();
+
         readonly Method<TRequest, TResponse> method;
         readonly UnaryServerMethod<TRequest, TResponse> handler;
 
@@ -85,7 +88,7 @@ namespace Grpc.Core.Internal
             } 
             catch (Exception e)
             {
-                Console.WriteLine("Exception occured in handler: " + e);
+                Logger.Error(e, "Exception occured in handler.");
                 status = HandlerUtils.StatusFromException(e);
             }
             try
@@ -104,6 +107,8 @@ namespace Grpc.Core.Internal
         where TRequest : class
         where TResponse : class
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<ServerStreamingServerCallHandler<TRequest, TResponse>>();
+
         readonly Method<TRequest, TResponse> method;
         readonly ServerStreamingServerMethod<TRequest, TResponse> handler;
 
@@ -138,7 +143,7 @@ namespace Grpc.Core.Internal
             }
             catch (Exception e)
             {
-                Console.WriteLine("Exception occured in handler: " + e);
+                Logger.Error(e, "Exception occured in handler.");
                 status = HandlerUtils.StatusFromException(e);
             }
 
@@ -158,6 +163,8 @@ namespace Grpc.Core.Internal
         where TRequest : class
         where TResponse : class
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<ClientStreamingServerCallHandler<TRequest, TResponse>>();
+
         readonly Method<TRequest, TResponse> method;
         readonly ClientStreamingServerMethod<TRequest, TResponse> handler;
 
@@ -196,7 +203,7 @@ namespace Grpc.Core.Internal
             }
             catch (Exception e)
             {
-                Console.WriteLine("Exception occured in handler: " + e);
+                Logger.Error(e, "Exception occured in handler.");
                 status = HandlerUtils.StatusFromException(e);
             }
 
@@ -216,6 +223,8 @@ namespace Grpc.Core.Internal
         where TRequest : class
         where TResponse : class
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<DuplexStreamingServerCallHandler<TRequest, TResponse>>();
+
         readonly Method<TRequest, TResponse> method;
         readonly DuplexStreamingServerMethod<TRequest, TResponse> handler;
 
@@ -246,7 +255,7 @@ namespace Grpc.Core.Internal
             }
             catch (Exception e)
             {
-                Console.WriteLine("Exception occured in handler: " + e);
+                Logger.Error(e, "Exception occured in handler.");
                 status = HandlerUtils.StatusFromException(e);
             }
             try
diff --git a/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c67765c78d5164ac77fdc69deb7762ea023c930d
--- /dev/null
+++ b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs
@@ -0,0 +1,103 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace Grpc.Core.Logging
+{
+    /// <summary>Logger that logs to System.Console.</summary>
+    public class ConsoleLogger : ILogger
+    {
+        readonly Type forType;
+        readonly string forTypeString;
+
+        public ConsoleLogger() : this(null)
+        {
+        }
+
+        private ConsoleLogger(Type forType)
+        {
+            this.forType = forType;
+            this.forTypeString = forType != null ? forType.FullName + " " : "";
+        }
+
+        public ILogger ForType<T>()
+        {
+            if (typeof(T) == forType)
+            {
+                return this;
+            }
+            return new ConsoleLogger(typeof(T));
+        }
+
+        public void Debug(string message, params object[] formatArgs)
+        {
+            Log("D", message, formatArgs);
+        }
+
+        public void Info(string message, params object[] formatArgs)
+        {
+            Log("I", message, formatArgs);
+        }
+
+        public void Warning(string message, params object[] formatArgs)
+        {
+            Log("W", message, formatArgs);
+        }
+
+        public void Warning(Exception exception, string message, params object[] formatArgs)
+        {
+            Log("W", message + " " + exception, formatArgs);
+        }
+
+        public void Error(string message, params object[] formatArgs)
+        {
+            Log("E", message, formatArgs);
+        }
+
+        public void Error(Exception exception, string message, params object[] formatArgs)
+        {
+            Log("E", message + " " + exception, formatArgs);
+        }
+
+        private void Log(string severityString, string message, object[] formatArgs)
+        {
+            Console.Error.WriteLine("{0}{1} {2}{3}",
+                severityString,
+                DateTime.Now,
+                forTypeString,
+                string.Format(message, formatArgs));
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Logging/ILogger.cs b/src/csharp/Grpc.Core/Logging/ILogger.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0d58f133e3a53fc1e17417fcf7207c74c6ef19dc
--- /dev/null
+++ b/src/csharp/Grpc.Core/Logging/ILogger.cs
@@ -0,0 +1,57 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace Grpc.Core.Logging
+{
+    /// <summary>For logging messages.</summary>
+    public interface ILogger
+    {
+        /// <summary>Returns a logger associated with the specified type.</summary>
+        ILogger ForType<T>();
+
+        void Debug(string message, params object[] formatArgs);
+
+        void Info(string message, params object[] formatArgs);
+
+        void Warning(string message, params object[] formatArgs);
+
+        void Warning(Exception exception, string message, params object[] formatArgs);
+
+        void Error(string message, params object[] formatArgs);
+
+        void Error(Exception exception, string message, params object[] formatArgs);
+    }
+}
diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs
index fd30735359fe74736d6c51a23020309dcf02f039..d80e3d624fb2b5f622e1eb76b5445d9b1ff0afd2 100644
--- a/src/csharp/Grpc.Core/Server.cs
+++ b/src/csharp/Grpc.Core/Server.cs
@@ -38,6 +38,7 @@ using System.Diagnostics;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using Grpc.Core.Internal;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core
@@ -52,6 +53,8 @@ namespace Grpc.Core
         /// </summary>
         public const int PickUnusedPort = 0;
 
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Server>();
+
         readonly GrpcEnvironment environment;
         readonly List<ChannelOption> options;
         readonly ServerSafeHandle handle;
@@ -233,7 +236,7 @@ namespace Grpc.Core
             }
             catch (Exception e)
             {
-                Console.WriteLine("Exception while handling RPC: " + e);
+                Logger.Warning(e, "Exception while handling RPC.");
             }
         }
 
diff --git a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs
index 4180d989385ca2069d4cb72ed0d7acadcb54de01..82653c3a1f587d12547104180cbd39072ffd3a89 100644
--- a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs
+++ b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs
@@ -46,13 +46,15 @@ namespace Grpc.Core.Utils
         /// </summary>
         public static void RunBenchmark(int warmupIterations, int benchmarkIterations, Action action)
         {
-            Console.WriteLine("Warmup iterations: " + warmupIterations);
+            var logger = GrpcEnvironment.Logger;
+            
+            logger.Info("Warmup iterations: {0}", warmupIterations);
             for (int i = 0; i < warmupIterations; i++)
             {
                 action();
             }
 
-            Console.WriteLine("Benchmark iterations: " + benchmarkIterations);
+            logger.Info("Benchmark iterations: {0}", benchmarkIterations);
             var stopwatch = new Stopwatch();
             stopwatch.Start();
             for (int i = 0; i < benchmarkIterations; i++)
@@ -60,8 +62,8 @@ namespace Grpc.Core.Utils
                 action();
             }
             stopwatch.Stop();
-            Console.WriteLine("Elapsed time: " + stopwatch.ElapsedMilliseconds + "ms");
-            Console.WriteLine("Ops per second: " + (int)((double)benchmarkIterations  * 1000 / stopwatch.ElapsedMilliseconds));
+            logger.Info("Elapsed time: {0}ms", stopwatch.ElapsedMilliseconds);
+            logger.Info("Ops per second: {0}", (int)((double)benchmarkIterations  * 1000 / stopwatch.ElapsedMilliseconds));
         }
     }
 }
diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
index b2397d4e23634b57575634779df4e737a28b5fe9..2d841a9c1190ce82b31b8a0fdeda56dd008d0380 100644
--- a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
@@ -62,7 +62,7 @@ namespace Grpc.IntegrationTesting
                 File.ReadAllText(TestCredentials.ServerCertChainPath),
                 File.ReadAllText(TestCredentials.ServerPrivateKeyPath));
 
-            var serverCredentials = new SslServerCredentials(new [] { keyCertPair }, rootCert);
+            var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert);
             var clientCredentials = new SslCredentials(rootCert, keyCertPair);
 
             server = new Server();
@@ -93,6 +93,5 @@ namespace Grpc.IntegrationTesting
             var response = client.UnaryCall(SimpleRequest.CreateBuilder().SetResponseSize(10).Build());
             Assert.AreEqual(10, response.Payload.Body.Length);
         }
-
     }
 }