Browse Source

Add a simple non-serilog debug log sink.

Steven Kirk 5 years ago
parent
commit
ccd6d54b38

+ 3 - 4
samples/ControlCatalog.NetCore/Program.cs

@@ -1,13 +1,11 @@
 using System;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Globalization;
 using System.Linq;
 using System.Threading;
 using Avalonia;
-using Avalonia.ReactiveUI;
 using Avalonia.Dialogs;
-using Avalonia.OpenGL;
+using Avalonia.ReactiveUI;
 
 namespace ControlCatalog.NetCore
 {
@@ -69,7 +67,8 @@ namespace ControlCatalog.NetCore
                 })
                 .UseSkia()
                 .UseReactiveUI()
-                .UseManagedSystemDialogs();
+                .UseManagedSystemDialogs()
+                .LogToDebug();
 
         static void SilenceConsole()
         {

+ 178 - 0
src/Avalonia.Base/Logging/DebugLogSink.cs

@@ -0,0 +1,178 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using Avalonia.Utilities;
+
+namespace Avalonia.Logging
+{
+    public class DebugLogSink : ILogSink
+    {
+        private readonly LogEventLevel _level;
+        private readonly IList<string> _areas;
+
+        public DebugLogSink(
+            LogEventLevel minimumLevel,
+            IList<string> areas = null)
+        {
+            _level = minimumLevel;
+            _areas = areas?.Count > 0 ? areas : null;
+        }
+
+        public bool IsEnabled(LogEventLevel level) => level >= _level;
+
+        public void Log(LogEventLevel level, string area, object source, string messageTemplate)
+        {
+            if (IsEnabled(level, area))
+            {
+                Debug.WriteLine(Format<object, object, object>(area, messageTemplate, source));
+            }
+        }
+
+        public void Log<T0>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
+        {
+            if (IsEnabled(level, area))
+            {
+                Debug.WriteLine(Format<T0, object, object>(area, messageTemplate, source, propertyValue0));
+            }
+        }
+
+        public void Log<T0, T1>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
+        {
+            if (IsEnabled(level, area))
+            {
+                Debug.WriteLine(Format<T0, T1, object>(area, messageTemplate, source, propertyValue0, propertyValue1));
+            }
+        }
+
+        public void Log<T0, T1, T2>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
+        {
+            if (IsEnabled(level, area))
+            {
+                Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2));
+            }
+        }
+
+        public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
+        {
+            if (IsEnabled(level, area))
+            {
+                Debug.WriteLine(Format(area, messageTemplate, source, propertyValues));
+            }
+        }
+
+        private bool IsEnabled(LogEventLevel level, string area) => IsEnabled(level) && (_areas?.Contains(area) ?? true);
+
+        private static string Format<T0, T1, T2>(
+            string area,
+            string template,
+            object source,
+            T0 v0 = default,
+            T1 v1 = default,
+            T2 v2 = default)
+        {
+            var result = new StringBuilder(template.Length);
+            var r = new CharacterReader(template.AsSpan());
+            var i = 0;
+
+            result.Append('[');
+            result.Append(area);
+            result.Append("] ");
+
+            while (!r.End)
+            {
+                var c = r.Take();
+
+                if (c != '{')
+                {
+                    result.Append(c);
+                }
+                else
+                {
+                    if (r.Peek != '{')
+                    {
+                        result.Append('\'');
+                        result.Append(i++ switch
+                        {
+                            0 => v0,
+                            1 => v1,
+                            2 => v2,
+                            _ => null
+                        });
+                        result.Append('\'');
+                        r.TakeUntil('}');
+                        r.Take();
+                    }
+                    else
+                    {
+                        result.Append('{');
+                        r.Take();
+                    }
+                }
+            }
+
+            if (source is object)
+            {
+                result.Append(" (");
+                result.Append(source.GetType().Name);
+                result.Append(" #");
+                result.Append(source.GetHashCode());
+                result.Append(')');
+            }
+
+            return result.ToString();
+        }
+
+        private static string Format(
+            string area,
+            string template,
+            object source,
+            object[] v)
+        {
+            var result = new StringBuilder(template.Length);
+            var r = new CharacterReader(template.AsSpan());
+            var i = 0;
+
+            result.Append('[');
+            result.Append(area);
+            result.Append(']');
+
+            while (!r.End)
+            {
+                var c = r.Take();
+
+                if (c != '{')
+                {
+                    result.Append(c);
+                }
+                else
+                {
+                    if (r.Peek != '{')
+                    {
+                        result.Append('\'');
+                        result.Append(i < v.Length ? v[i++] : null);
+                        result.Append('\'');
+                        r.TakeUntil('}');
+                        r.Take();
+                    }
+                    else
+                    {
+                        result.Append('{');
+                        r.Take();
+                    }
+                }
+            }
+
+            if (source is object)
+            {
+                result.Append('(');
+                result.Append(source.GetType().Name);
+                result.Append(" #");
+                result.Append(source.GetHashCode());
+                result.Append(')');
+            }
+
+            return result.ToString();
+        }
+    }
+}

+ 26 - 0
src/Avalonia.Controls/LoggingExtensions.cs

@@ -0,0 +1,26 @@
+using Avalonia.Controls;
+using Avalonia.Logging;
+
+namespace Avalonia
+{
+    public static class LoggingExtensions
+    {
+        /// <summary>
+        /// Logs Avalonia events to the <see cref="System.Diagnostics.Debug"/> sink.
+        /// </summary>
+        /// <typeparam name="T">The application class type.</typeparam>
+        /// <param name="builder">The app builder instance.</param>
+        /// <param name="level">The minimum level to log.</param>
+        /// <param name="areas">The areas to log. Valid values are listed in <see cref="LogArea"/>.</param>
+        /// <returns>The app builder instance.</returns>
+        public static T LogToDebug<T>(
+            this T builder,
+            LogEventLevel level = LogEventLevel.Warning,
+            params string[] areas)
+                where T : AppBuilderBase<T>, new()
+        {
+            Logger.Sink = new DebugLogSink(level, areas);
+            return builder;
+        }
+    }
+}