diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj
index 02d578912..b631e0275 100644
--- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj
+++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj
@@ -124,6 +124,7 @@
+
diff --git a/LibGit2Sharp.Tests/LogFixture.cs b/LibGit2Sharp.Tests/LogFixture.cs
new file mode 100644
index 000000000..76654e3d3
--- /dev/null
+++ b/LibGit2Sharp.Tests/LogFixture.cs
@@ -0,0 +1,40 @@
+using System;
+using LibGit2Sharp;
+using LibGit2Sharp.Core;
+using Xunit;
+
+namespace LibGit2Sharp.Tests
+{
+ public class LogFixture
+ {
+ [Fact]
+ public void CanEnableAndDisableLogging()
+ {
+ // Setting logging produces a log message at level Info,
+ // ensure that we catch it.
+ LogLevel level = LogLevel.None;
+ string message = null;
+
+ GlobalSettings.LogConfiguration = new LogConfiguration(LogLevel.Trace, (l, m) => { level = l; message = m; });
+
+ Assert.Equal(LogLevel.Info, level);
+ Assert.Equal("Logging enabled at level Trace", message);
+
+ // Configuring at Warning and higher means that the
+ // message at level Info should not be produced.
+ level = LogLevel.None;
+ message = null;
+
+ GlobalSettings.LogConfiguration = new LogConfiguration(LogLevel.Warning, (l, m) => { level = l; message = m; });
+
+ Assert.Equal(LogLevel.None, level);
+ Assert.Equal(null, message);
+
+ // Similarly, turning logging off should produce no messages.
+ GlobalSettings.LogConfiguration = LogConfiguration.None;
+
+ Assert.Equal(LogLevel.None, level);
+ Assert.Equal(null, message);
+ }
+ }
+}
diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs
index 113e91f15..ab47bb327 100644
--- a/LibGit2Sharp/Core/NativeMethods.cs
+++ b/LibGit2Sharp/Core/NativeMethods.cs
@@ -1496,6 +1496,11 @@ internal static extern int git_tag_delete(
[DllImport(libgit2)]
internal static extern void git_threads_shutdown();
+ internal delegate void git_trace_cb(LogLevel level, IntPtr message);
+
+ [DllImport(libgit2)]
+ internal static extern int git_trace_set(LogLevel level, git_trace_cb trace_cb);
+
internal delegate int git_transfer_progress_callback(ref GitTransferProgress stats, IntPtr payload);
internal delegate int git_transport_cb(out IntPtr transport, IntPtr remote, IntPtr payload);
diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs
index a04bb0bef..e87ce58de 100644
--- a/LibGit2Sharp/Core/Proxy.cs
+++ b/LibGit2Sharp/Core/Proxy.cs
@@ -2951,6 +2951,19 @@ public static GitObjectType git_tag_target_type(GitObjectSafeHandle tag)
#endregion
+ #region git_trace_
+
+ public static void git_trace_set(LogLevel level, NativeMethods.git_trace_cb callback)
+ {
+ using (ThreadAffinity())
+ {
+ int res = NativeMethods.git_trace_set(level, callback);
+ Ensure.ZeroResult(res);
+ }
+ }
+
+ #endregion
+
#region git_transport_
public static void git_transport_register(String prefix, IntPtr transport_cb, IntPtr param)
diff --git a/LibGit2Sharp/GlobalSettings.cs b/LibGit2Sharp/GlobalSettings.cs
index 08f7ad53d..190cfadf4 100644
--- a/LibGit2Sharp/GlobalSettings.cs
+++ b/LibGit2Sharp/GlobalSettings.cs
@@ -1,5 +1,6 @@
using System;
using LibGit2Sharp.Core;
+using LibGit2Sharp.Handlers;
namespace LibGit2Sharp
{
@@ -10,6 +11,8 @@ public static class GlobalSettings
{
private static readonly Lazy version = new Lazy(Version.Build);
+ private static LogConfiguration logConfiguration = LogConfiguration.None;
+
///
/// Returns all the optional features that were compiled into
/// libgit2.
@@ -84,5 +87,38 @@ public static void UnregisterSmartSubtransport(SmartSubtransportRegistration<
Proxy.git_transport_unregister(registration.Scheme);
registration.Free();
}
+
+ ///
+ /// Registers a new to receive
+ /// information logging information from libgit2 and LibGit2Sharp.
+ ///
+ /// Note that this configuration is global to an entire process
+ /// and does not honor application domains.
+ ///
+ public static LogConfiguration LogConfiguration
+ {
+ set
+ {
+ Ensure.ArgumentNotNull(value, "value");
+
+ logConfiguration = value;
+
+ if (logConfiguration.Level == LogLevel.None)
+ {
+ Proxy.git_trace_set(0, null);
+ }
+ else
+ {
+ Proxy.git_trace_set(value.Level, value.GitTraceHandler);
+
+ Log.Write(LogLevel.Info, "Logging enabled at level {0}", value.Level);
+ }
+ }
+
+ get
+ {
+ return logConfiguration;
+ }
+ }
}
}
diff --git a/LibGit2Sharp/Handlers.cs b/LibGit2Sharp/Handlers.cs
index ef688c550..74c746f1a 100644
--- a/LibGit2Sharp/Handlers.cs
+++ b/LibGit2Sharp/Handlers.cs
@@ -111,4 +111,12 @@ public enum PackBuilderStage
///
Deltafying
}
+
+ ///
+ /// Delegate definition for logging. This callback will be used to
+ /// write logging messages in libgit2 and LibGit2Sharp.
+ ///
+ /// The level of the log message.
+ /// The log message.
+ public delegate void LogHandler(LogLevel level, string message);
}
diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj
index ba00f56df..4214e22bc 100644
--- a/LibGit2Sharp/LibGit2Sharp.csproj
+++ b/LibGit2Sharp/LibGit2Sharp.csproj
@@ -139,6 +139,9 @@
+
+
+
diff --git a/LibGit2Sharp/Log.cs b/LibGit2Sharp/Log.cs
new file mode 100644
index 000000000..23f79d93e
--- /dev/null
+++ b/LibGit2Sharp/Log.cs
@@ -0,0 +1,30 @@
+using System.Runtime.CompilerServices;
+using LibGit2Sharp.Core;
+
+namespace LibGit2Sharp
+{
+ internal class Log
+ {
+ private static bool IsEnabled(LogConfiguration configuration, LogLevel level)
+ {
+ return (configuration.Level != LogLevel.None && configuration.Level >= level);
+ }
+
+ internal static bool IsEnabled(LogLevel level)
+ {
+ return IsEnabled(GlobalSettings.LogConfiguration, level);
+ }
+
+ internal static void Write(LogLevel level, string message, params object[] args)
+ {
+ LogConfiguration configuration = GlobalSettings.LogConfiguration;
+
+ if (!IsEnabled(configuration, level))
+ {
+ return;
+ }
+
+ configuration.Handler(level, string.Format(message, args));
+ }
+ }
+}
diff --git a/LibGit2Sharp/LogConfiguration.cs b/LibGit2Sharp/LogConfiguration.cs
new file mode 100644
index 000000000..f31eeac18
--- /dev/null
+++ b/LibGit2Sharp/LogConfiguration.cs
@@ -0,0 +1,45 @@
+using System;
+using LibGit2Sharp.Core;
+using LibGit2Sharp.Handlers;
+
+namespace LibGit2Sharp
+{
+ ///
+ /// Logging and tracing configuration for libgit2 and LibGit2Sharp.
+ ///
+ public sealed class LogConfiguration
+ {
+ ///
+ /// The default logging configuration, which performs no logging at all.
+ ///
+ public static readonly LogConfiguration None = new LogConfiguration { Level = LogLevel.None };
+
+ ///
+ /// Creates a new logging configuration to call the given
+ /// delegate when logging occurs at the given level.
+ ///
+ /// Level to log at
+ /// Handler to call when logging occurs
+ public LogConfiguration(LogLevel level, LogHandler handler)
+ {
+ Ensure.ArgumentConformsTo(level, (t) => { return (level != LogLevel.None); }, "level");
+ Ensure.ArgumentNotNull(handler, "handler");
+
+ Level = level;
+ Handler = handler;
+ }
+
+ private LogConfiguration()
+ {
+ }
+
+ internal LogLevel Level { get; private set; }
+ internal LogHandler Handler { get; private set; }
+
+ internal void GitTraceHandler(LogLevel level, IntPtr msg)
+ {
+ string message = LaxUtf8Marshaler.FromNative(msg);
+ Handler(level, message);
+ }
+ }
+}
diff --git a/LibGit2Sharp/LogLevel.cs b/LibGit2Sharp/LogLevel.cs
new file mode 100644
index 000000000..472bd5aa5
--- /dev/null
+++ b/LibGit2Sharp/LogLevel.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace LibGit2Sharp
+{
+ ///
+ /// Available logging levels. When logging is enabled at a particular
+ /// level, callers will be provided logging at the given level and all
+ /// lower levels.
+ ///
+ public enum LogLevel
+ {
+ ///
+ /// No logging will be provided.
+ ///
+ None = 0,
+
+ ///
+ /// Severe errors that may impact the program's execution.
+ ///
+ Fatal = 1,
+
+ ///
+ /// Errors that do not impact the program's execution.
+ ///
+ Error = 2,
+
+ ///
+ /// Warnings that suggest abnormal data.
+ ///
+ Warning = 3,
+
+ ///
+ /// Informational messages about program execution.
+ ///
+ Info = 4,
+
+ ///
+ /// Detailed data that allows for debugging.
+ ///
+ Debug = 5,
+
+ ///
+ /// Tracing is exceptionally detailed debugging data.
+ ///
+ Trace = 6,
+ }
+}