Ver código fonte

Merge pull request #3055 from MarchingCube/alloc-logging

Reduce allocations caused by logging.
Steven Kirk 6 anos atrás
pai
commit
eac67e891a

+ 10 - 13
src/Avalonia.Base/AvaloniaObject.cs

@@ -326,8 +326,6 @@ namespace Avalonia
 
             VerifyAccess();
 
-            var description = GetDescription(source);
-
             if (property.IsDirect)
             {
                 if (property.IsReadOnly)
@@ -335,12 +333,12 @@ namespace Avalonia
                     throw new ArgumentException($"The property {property.Name} is readonly.");
                 }
 
-                Logger.Verbose(
-                    LogArea.Property, 
+                Logger.TryGet(LogEventLevel.Verbose)?.Log(
+                    LogArea.Property,
                     this,
-                    "Bound {Property} to {Binding} with priority LocalValue", 
-                    property, 
-                    description);
+                    "Bound {Property} to {Binding} with priority LocalValue",
+                    property,
+                    GetDescription(source));
 
                 if (_directBindings == null)
                 {
@@ -351,12 +349,12 @@ namespace Avalonia
             }
             else
             {
-                Logger.Verbose(
+                Logger.TryGet(LogEventLevel.Verbose)?.Log(
                     LogArea.Property,
                     this,
                     "Bound {Property} to {Binding} with priority {Priority}",
                     property,
-                    description,
+                    GetDescription(source),
                     priority);
 
                 return Values.AddBinding(property, source, priority);
@@ -406,7 +404,7 @@ namespace Avalonia
             {
                 RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority);
 
-                Logger.Verbose(
+                Logger.TryGet(LogEventLevel.Verbose)?.Log(
                     LogArea.Property,
                     this,
                     "{Property} changed from {$Old} to {$Value} with priority {Priority}",
@@ -458,8 +456,7 @@ namespace Avalonia
         /// <param name="e">The binding error.</param>
         protected internal virtual void LogBindingError(AvaloniaProperty property, Exception e)
         {
-            Logger.Log(
-                LogEventLevel.Warning,
+            Logger.TryGet(LogEventLevel.Warning)?.Log(
                 LogArea.Binding,
                 this,
                 "Error in binding to {Target}.{Property}: {Message}",
@@ -812,7 +809,7 @@ namespace Avalonia
         /// <param name="priority">The priority.</param>
         private void LogPropertySet(AvaloniaProperty property, object value, BindingPriority priority)
         {
-            Logger.Verbose(
+            Logger.TryGet(LogEventLevel.Verbose)?.Log(
                 LogArea.Property,
                 this,
                 "Set {Property} to {$Value} with priority {Priority}",

+ 1 - 1
src/Avalonia.Base/Data/Core/BindingExpression.cs

@@ -165,7 +165,7 @@ namespace Avalonia.Data.Core
                             }
                             else
                             {
-                                Logger.Error(
+                                Logger.TryGet(LogEventLevel.Error)?.Log(
                                     LogArea.Binding,
                                     this,
                                     "Could not convert FallbackValue {FallbackValue} to {Type}",

+ 71 - 0
src/Avalonia.Base/Logging/ILogSink.cs

@@ -8,6 +8,77 @@ namespace Avalonia.Logging
     /// </summary>
     public interface ILogSink
     {
+        /// <summary>
+        /// Checks if given log level is enabled.
+        /// </summary>
+        /// <param name="level">The log event level.</param>
+        /// <returns><see langword="true"/> if given log level is enabled.</returns>
+        bool IsEnabled(LogEventLevel level);
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="level">The log event level.</param>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        void Log(
+            LogEventLevel level,
+            string area,
+            object source,
+            string messageTemplate);
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="level">The log event level.</param>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        void Log<T0>(
+            LogEventLevel level,
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0);
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="level">The log event level.</param>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        /// <param name="propertyValue1">Message property value.</param>
+        void Log<T0, T1>(
+            LogEventLevel level,
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0,
+            T1 propertyValue1);
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="level">The log event level.</param>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        /// <param name="propertyValue1">Message property value.</param>
+        /// <param name="propertyValue2">Message property value.</param>
+        void Log<T0, T1, T2>(
+            LogEventLevel level,
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0,
+            T1 propertyValue1,
+            T2 propertyValue2);
+
         /// <summary>
         /// Logs a new event.
         /// </summary>

+ 21 - 104
src/Avalonia.Base/Logging/Logger.cs

@@ -1,8 +1,6 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
-using System.Runtime.CompilerServices;
-
 namespace Avalonia.Logging
 {
     /// <summary>
@@ -16,124 +14,43 @@ namespace Avalonia.Logging
         public static ILogSink Sink { get; set; }
 
         /// <summary>
-        /// Logs an event.
+        /// Checks if given log level is enabled.
         /// </summary>
         /// <param name="level">The log event level.</param>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValues">The message property values.</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static void Log(
-            LogEventLevel level, 
-            string area,
-            object source,
-            string messageTemplate, 
-            params object[] propertyValues)
-        {
-            Sink?.Log(level, area, source, messageTemplate, propertyValues);
-        }
-
-        /// <summary>
-        /// Logs an event with the <see cref="LogEventLevel.Verbose"/> level.
-        /// </summary>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValues">The message property values.</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static void Verbose(
-            string area,
-            object source,
-            string messageTemplate, 
-            params object[] propertyValues)
+        /// <returns><see langword="true"/> if given log level is enabled.</returns>
+        public static bool IsEnabled(LogEventLevel level)
         {
-            Log(LogEventLevel.Verbose, area, source, messageTemplate, propertyValues);
+            return Sink?.IsEnabled(level) == true;
         }
 
         /// <summary>
-        /// Logs an event with the <see cref="LogEventLevel.Debug"/> level.
+        /// Returns parametrized logging sink if given log level is enabled.
         /// </summary>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValues">The message property values.</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static void Debug(
-            string area,
-            object source,
-            string messageTemplate,
-            params object[] propertyValues)
+        /// <param name="level">The log event level.</param>
+        /// <returns>Log sink or <see langword="null"/> if log level is not enabled.</returns>
+        public static ParametrizedLogger? TryGet(LogEventLevel level)
         {
-            Log(LogEventLevel.Debug, area, source, messageTemplate, propertyValues);
-        }
+            if (!IsEnabled(level))
+            {
+                return null;
+            }
 
-        /// <summary>
-        /// Logs an event with the <see cref="LogEventLevel.Information"/> level.
-        /// </summary>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValues">The message property values.</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static void Information(
-            string area,
-            object source,
-            string messageTemplate,
-            params object[] propertyValues)
-        {
-            Log(LogEventLevel.Information, area, source, messageTemplate, propertyValues);
+            return new ParametrizedLogger(Sink, level);
         }
 
         /// <summary>
-        /// Logs an event with the <see cref="LogEventLevel.Warning"/> level.
+        /// Returns parametrized logging sink if given log level is enabled.
         /// </summary>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValues">The message property values.</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static void Warning(
-            string area,
-            object source,
-            string messageTemplate,
-            params object[] propertyValues)
+        /// <param name="level">The log event level.</param>
+        /// <param name="outLogger">Log sink that is valid only if method returns <see langword="true"/>.</param>
+        /// <returns><see langword="true"/> if logger was obtained successfully.</returns>
+        public static bool TryGet(LogEventLevel level, out ParametrizedLogger outLogger)
         {
-            Log(LogEventLevel.Warning, area, source, messageTemplate, propertyValues);
-        }
+            ParametrizedLogger? logger = TryGet(level);
 
-        /// <summary>
-        /// Logs an event with the <see cref="LogEventLevel.Error"/> level.
-        /// </summary>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValues">The message property values.</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static void Error(
-            string area,
-            object source,
-            string messageTemplate, 
-            params object[] propertyValues)
-        {
-            Log(LogEventLevel.Error, area, source, messageTemplate, propertyValues);
-        }
+            outLogger = logger.GetValueOrDefault();
 
-        /// <summary>
-        /// Logs an event with the <see cref="LogEventLevel.Fatal"/> level.
-        /// </summary>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValues">The message property values.</param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static void Fatal(
-            string area,
-            object source,
-            string messageTemplate,
-            params object[] propertyValues)
-        {
-            Log(LogEventLevel.Fatal, area, source, messageTemplate, propertyValues);
+            return logger.HasValue;
         }
     }
 }

+ 174 - 0
src/Avalonia.Base/Logging/ParametrizedLogger.cs

@@ -0,0 +1,174 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System.Runtime.CompilerServices;
+
+namespace Avalonia.Logging
+{
+    /// <summary>
+    /// Logger sink parametrized for given logging level.
+    /// </summary>
+    public readonly struct ParametrizedLogger
+    {
+        private readonly ILogSink _sink;
+        private readonly LogEventLevel _level;
+
+        public ParametrizedLogger(ILogSink sink, LogEventLevel level)
+        {
+            _sink = sink;
+            _level = level;
+        }
+
+        /// <summary>
+        /// Checks if this logger can be used.
+        /// </summary>
+        public bool IsValid => _sink != null;
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Log(
+            string area,
+            object source,
+            string messageTemplate)
+        {
+            _sink.Log(_level, area, source, messageTemplate);
+        }
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Log<T0>(
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0)
+        {
+            _sink.Log(_level, area, source, messageTemplate, propertyValue0);
+        }
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        /// <param name="propertyValue1">Message property value.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Log<T0, T1>(
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0,
+            T1 propertyValue1)
+        {
+            _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1);
+        }
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        /// <param name="propertyValue1">Message property value.</param>
+        /// <param name="propertyValue2">Message property value.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Log<T0, T1, T2>(
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0,
+            T1 propertyValue1,
+            T2 propertyValue2)
+        {
+            _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
+        }
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        /// <param name="propertyValue1">Message property value.</param>
+        /// <param name="propertyValue2">Message property value.</param>
+        /// <param name="propertyValue3">Message property value.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Log<T0, T1, T2, T3>(
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0,
+            T1 propertyValue1,
+            T2 propertyValue2,
+            T3 propertyValue3)
+        {
+            _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3);
+        }
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        /// <param name="propertyValue1">Message property value.</param>
+        /// <param name="propertyValue2">Message property value.</param>
+        /// <param name="propertyValue3">Message property value.</param>
+        /// <param name="propertyValue4">Message property value.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Log<T0, T1, T2, T3, T4>(
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0,
+            T1 propertyValue1,
+            T2 propertyValue2,
+            T3 propertyValue3,
+            T4 propertyValue4)
+        {
+            _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4);
+        }
+
+        /// <summary>
+        /// Logs an event.
+        /// </summary>
+        /// <param name="area">The area that the event originates.</param>
+        /// <param name="source">The object from which the event originates.</param>
+        /// <param name="messageTemplate">The message template.</param>
+        /// <param name="propertyValue0">Message property value.</param>
+        /// <param name="propertyValue1">Message property value.</param>
+        /// <param name="propertyValue2">Message property value.</param>
+        /// <param name="propertyValue3">Message property value.</param>
+        /// <param name="propertyValue4">Message property value.</param>
+        /// <param name="propertyValue5">Message property value.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Log<T0, T1, T2, T3, T4, T5>(
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0,
+            T1 propertyValue1,
+            T2 propertyValue2,
+            T3 propertyValue3,
+            T4 propertyValue4,
+            T5 propertyValue5)
+        {
+            _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4, propertyValue5);
+        }
+    }
+}

+ 1 - 1
src/Avalonia.Base/PriorityValue.cs

@@ -301,7 +301,7 @@ namespace Avalonia
             }
             else
             {
-                Logger.Error(
+                Logger.TryGet(LogEventLevel.Error)?.Log(
                     LogArea.Binding,
                     Owner,
                     "Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})",

+ 2 - 2
src/Avalonia.Controls/DropDown.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Controls
     {
         public DropDown()
         {
-            Logger.Warning(LogArea.Control, this, "DropDown is deprecated: Use ComboBox");
+            Logger.TryGet(LogEventLevel.Warning)?.Log(LogArea.Control, this, "DropDown is deprecated: Use ComboBox");
         }
 
         Type IStyleable.StyleKey => typeof(ComboBox);
@@ -20,7 +20,7 @@ namespace Avalonia.Controls
     {
         public DropDownItem()
         {
-            Logger.Warning(LogArea.Control, this, "DropDownItem is deprecated: Use ComboBoxItem");
+            Logger.TryGet(LogEventLevel.Warning)?.Log(LogArea.Control, this, "DropDownItem is deprecated: Use ComboBoxItem");
         }
 
         Type IStyleable.StyleKey => typeof(ComboBoxItem);

+ 1 - 1
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -1062,7 +1062,7 @@ namespace Avalonia.Controls.Primitives
             }
             catch (Exception ex)
             {
-                Logger.Error(
+                Logger.TryGet(LogEventLevel.Error)?.Log(
                     LogArea.Property,
                     this,
                     "Error thrown updating SelectedItems: {Error}",

+ 1 - 1
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@@ -255,7 +255,7 @@ namespace Avalonia.Controls.Primitives
 
                 if (template != null)
                 {
-                    Logger.Verbose(LogArea.Control, this, "Creating control template");
+                    Logger.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Control, this, "Creating control template");
 
                     var (child, nameScope) = template.Build(this);
                     ApplyTemplatedParent(child);

+ 1 - 1
src/Avalonia.Controls/TopLevel.cs

@@ -330,7 +330,7 @@ namespace Avalonia.Controls
 
             if (result == null)
             {
-                Logger.Warning(
+                Logger.TryGet(LogEventLevel.Warning)?.Log(
                     LogArea.Control,
                     this,
                     "Could not create {Service} : maybe Application.RegisterServices() wasn't called?",

+ 23 - 10
src/Avalonia.Layout/LayoutManager.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Diagnostics;
 using Avalonia.Logging;
 using Avalonia.Threading;
 
@@ -69,15 +70,23 @@ namespace Avalonia.Layout
             {
                 _running = true;
 
-                Logger.Information(
-                    LogArea.Layout,
-                    this,
-                    "Started layout pass. To measure: {Measure} To arrange: {Arrange}",
-                    _toMeasure.Count,
-                    _toArrange.Count);
+                Stopwatch stopwatch = null;
 
-                var stopwatch = new System.Diagnostics.Stopwatch();
-                stopwatch.Start();
+                const LogEventLevel timingLogLevel = LogEventLevel.Information;
+                bool captureTiming = Logger.IsEnabled(timingLogLevel);
+
+                if (captureTiming)
+                {
+                    Logger.TryGet(timingLogLevel)?.Log(
+                        LogArea.Layout,
+                        this,
+                        "Started layout pass. To measure: {Measure} To arrange: {Arrange}",
+                        _toMeasure.Count,
+                        _toArrange.Count);
+
+                    stopwatch = new Stopwatch();
+                    stopwatch.Start();
+                }
 
                 _toMeasure.BeginLoop(MaxPasses);
                 _toArrange.BeginLoop(MaxPasses);
@@ -103,8 +112,12 @@ namespace Avalonia.Layout
                 _toMeasure.EndLoop();
                 _toArrange.EndLoop();
 
-                stopwatch.Stop();
-                Logger.Information(LogArea.Layout, this, "Layout pass finished in {Time}", stopwatch.Elapsed);
+                if (captureTiming)
+                {
+                    stopwatch.Stop();
+
+                    Logger.TryGet(timingLogLevel)?.Log(LogArea.Layout, this, "Layout pass finished in {Time}", stopwatch.Elapsed);
+                }
             }
 
             _queued = false;

+ 4 - 4
src/Avalonia.Layout/Layoutable.cs

@@ -329,7 +329,7 @@ namespace Avalonia.Layout
                 DesiredSize = desiredSize;
                 _previousMeasure = availableSize;
 
-                Logger.Verbose(LogArea.Layout, this, "Measure requested {DesiredSize}", DesiredSize);
+                Logger.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Layout, this, "Measure requested {DesiredSize}", DesiredSize);
 
                 if (DesiredSize != previousDesiredSize)
                 {
@@ -356,7 +356,7 @@ namespace Avalonia.Layout
 
             if (!IsArrangeValid || _previousArrange != rect)
             {
-                Logger.Verbose(LogArea.Layout, this, "Arrange to {Rect} ", rect);
+                Logger.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Layout, this, "Arrange to {Rect} ", rect);
 
                 IsArrangeValid = true;
                 ArrangeCore(rect);
@@ -381,7 +381,7 @@ namespace Avalonia.Layout
         {
             if (IsMeasureValid)
             {
-                Logger.Verbose(LogArea.Layout, this, "Invalidated measure");
+                Logger.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Layout, this, "Invalidated measure");
 
                 IsMeasureValid = false;
                 IsArrangeValid = false;
@@ -402,7 +402,7 @@ namespace Avalonia.Layout
         {
             if (IsArrangeValid)
             {
-                Logger.Verbose(LogArea.Layout, this, "Invalidated arrange");
+                Logger.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Layout, this, "Invalidated arrange");
 
                 IsArrangeValid = false;
                 (VisualRoot as ILayoutRoot)?.LayoutManager?.InvalidateArrange(this);

+ 101 - 3
src/Avalonia.Logging.Serilog/SerilogLogger.cs

@@ -34,6 +34,76 @@ namespace Avalonia.Logging.Serilog
             Logger.Sink = new SerilogLogger(output);
         }
 
+        public bool IsEnabled(LogEventLevel level)
+        {
+            return _output.IsEnabled((SerilogLogEventLevel)level);
+        }
+
+        public void Log(
+            LogEventLevel level,
+            string area,
+            object source,
+            string messageTemplate)
+        {
+            Contract.Requires<ArgumentNullException>(area != null);
+            Contract.Requires<ArgumentNullException>(messageTemplate != null);
+
+            using (PushLogContextProperties(area, source))
+            {
+                _output.Write((SerilogLogEventLevel)level, messageTemplate);
+            }
+        }
+
+        public void Log<T0>(
+            LogEventLevel level, 
+            string area, object source,
+            string messageTemplate, 
+            T0 propertyValue0)
+        {
+            Contract.Requires<ArgumentNullException>(area != null);
+            Contract.Requires<ArgumentNullException>(messageTemplate != null);
+
+            using (PushLogContextProperties(area, source))
+            {
+                _output.Write((SerilogLogEventLevel)level, messageTemplate, propertyValue0);
+            }
+        }
+
+        public void Log<T0, T1>(
+            LogEventLevel level, 
+            string area,
+            object source,
+            string messageTemplate,
+            T0 propertyValue0,
+            T1 propertyValue1)
+        {
+            Contract.Requires<ArgumentNullException>(area != null);
+            Contract.Requires<ArgumentNullException>(messageTemplate != null);
+
+            using (PushLogContextProperties(area, source))
+            {
+                _output.Write((SerilogLogEventLevel)level, messageTemplate, propertyValue0, propertyValue1);
+            }
+        }
+
+        public void Log<T0, T1, T2>(
+            LogEventLevel level, 
+            string area, 
+            object source, 
+            string messageTemplate, 
+            T0 propertyValue0,
+            T1 propertyValue1, 
+            T2 propertyValue2)
+        {
+            Contract.Requires<ArgumentNullException>(area != null);
+            Contract.Requires<ArgumentNullException>(messageTemplate != null);
+
+            using (PushLogContextProperties(area, source))
+            {
+                _output.Write((SerilogLogEventLevel)level, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
+            }
+        }
+
         /// <inheritdoc/>
         public void Log(
             AvaloniaLogEventLevel level,
@@ -45,12 +115,40 @@ namespace Avalonia.Logging.Serilog
             Contract.Requires<ArgumentNullException>(area != null);
             Contract.Requires<ArgumentNullException>(messageTemplate != null);
 
-            using (LogContext.PushProperty("Area", area))
-            using (LogContext.PushProperty("SourceType", source?.GetType()))
-            using (LogContext.PushProperty("SourceHash", source?.GetHashCode()))
+            using (PushLogContextProperties(area, source))
             {
                 _output.Write((SerilogLogEventLevel)level, messageTemplate, propertyValues);
             }
         }
+
+        private static LogContextDisposable PushLogContextProperties(string area, object source)
+        {
+            return new LogContextDisposable(
+                LogContext.PushProperty("Area", area),
+                LogContext.PushProperty("SourceType", source?.GetType()),
+                LogContext.PushProperty("SourceHash", source?.GetHashCode())
+                );
+        }
+        
+        private readonly struct LogContextDisposable : IDisposable
+        {
+            private readonly IDisposable _areaDisposable;
+            private readonly IDisposable _sourceTypeDisposable;
+            private readonly IDisposable _sourceHashDisposable;
+
+            public LogContextDisposable(IDisposable areaDisposable, IDisposable sourceTypeDisposable, IDisposable sourceHashDisposable)
+            {
+                _areaDisposable = areaDisposable;
+                _sourceTypeDisposable = sourceTypeDisposable;
+                _sourceHashDisposable = sourceHashDisposable;
+            }
+
+            public void Dispose()
+            {
+                _areaDisposable.Dispose();
+                _sourceTypeDisposable.Dispose();
+                _sourceHashDisposable.Dispose();
+            }
+        }
     }
 }

+ 1 - 1
src/Avalonia.OpenGL/EglGlPlatformFeature.cs

@@ -31,7 +31,7 @@ namespace Avalonia.OpenGL
             }
             catch(Exception e)
             {
-                Logger.Error("OpenGL", null, "Unable to initialize EGL-based rendering: {0}", e);
+                Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", null, "Unable to initialize EGL-based rendering: {0}", e);
                 return null;
             }
         }

+ 2 - 2
src/Avalonia.Styling/StyledElement.cs

@@ -743,11 +743,11 @@ namespace Avalonia
 #if DEBUG
                 if (((INotifyCollectionChangedDebug)_classes).GetCollectionChangedSubscribers()?.Length > 0)
                 {
-                    Logger.Warning(
+                    Logger.TryGet(LogEventLevel.Warning)?.Log(
                         LogArea.Control,
                         this,
                         "{Type} detached from logical tree but still has class listeners",
-                        this.GetType());
+                        GetType());
                 }
 #endif
             }

+ 2 - 2
src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs

@@ -65,14 +65,14 @@ namespace Avalonia.Animation.Animators
                     }
                 }
 
-                Logger.Warning(
+                Logger.TryGet(LogEventLevel.Warning)?.Log(
                     LogArea.Animations,
                     control,
                     $"Cannot find the appropriate transform: \"{Property.OwnerType}\" in {control}.");
             }
             else
             {
-                Logger.Error(
+                Logger.TryGet(LogEventLevel.Error)?.Log(
                     LogArea.Animations,
                     control,
                     $"Cannot apply animation: Target property owner {Property.OwnerType} is not a Transform object.");

+ 2 - 1
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using Avalonia.Logging;
 using Avalonia.Media;
 using Avalonia.Media.Immutable;
 using Avalonia.Platform;
@@ -269,7 +270,7 @@ namespace Avalonia.Rendering
                 }
                 catch (RenderTargetCorruptedException ex)
                 {
-                    Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
+                    Logger.TryGet(LogEventLevel.Information)?.Log("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
                     RenderTarget?.Dispose();
                     RenderTarget = null;
                 }

+ 2 - 1
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Avalonia.Logging;
 using Avalonia.Media;
 using Avalonia.Platform;
 using Avalonia.VisualTree;
@@ -80,7 +81,7 @@ namespace Avalonia.Rendering
             }
             catch (RenderTargetCorruptedException ex)
             {
-                Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
+                Logger.TryGet(LogEventLevel.Information)?.Log("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
                 _renderTarget.Dispose();
                 _renderTarget = null;
             }

+ 2 - 2
src/Avalonia.Visuals/Rendering/RenderLoop.cs

@@ -120,7 +120,7 @@ namespace Avalonia.Rendering
                                     }
                                     catch (Exception ex)
                                     {
-                                        Logger.Error(LogArea.Visual, this, "Exception in render update: {Error}", ex);
+                                        Logger.TryGet(LogEventLevel.Error)?.Log(LogArea.Visual, this, "Exception in render update: {Error}", ex);
                                     }
                                 }
                             }
@@ -136,7 +136,7 @@ namespace Avalonia.Rendering
                 }
                 catch (Exception ex)
                 {
-                    Logger.Error(LogArea.Visual, this, "Exception in render loop: {Error}", ex);
+                    Logger.TryGet(LogEventLevel.Error)?.Log(LogArea.Visual, this, "Exception in render loop: {Error}", ex);
                 }
                 finally
                 {

+ 3 - 4
src/Avalonia.Visuals/Visual.cs

@@ -359,7 +359,7 @@ namespace Avalonia
         /// <param name="e">The event args.</param>
         protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
         {
-            Logger.Verbose(LogArea.Visual, this, "Attached to visual tree");
+            Logger.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Visual, this, "Attached to visual tree");
 
             _visualRoot = e.Root;
 
@@ -388,7 +388,7 @@ namespace Avalonia
         /// <param name="e">The event args.</param>
         protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
         {
-            Logger.Verbose(LogArea.Visual, this, "Detached from visual tree");
+            Logger.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Visual, this, "Detached from visual tree");
 
             _visualRoot = null;
 
@@ -453,8 +453,7 @@ namespace Avalonia
                     return;
                 }
 
-                Logger.Log(
-                    LogEventLevel.Warning,
+                Logger.TryGet(LogEventLevel.Warning)?.Log(
                     LogArea.Binding,
                     this,
                     "Error in binding to {Target}.{Property}: {Message}",

+ 1 - 1
src/Avalonia.X11/Glx/GlxPlatformFeature.cs

@@ -36,7 +36,7 @@ namespace Avalonia.X11.Glx
             }
             catch(Exception e)
             {
-                Logger.Error("OpenGL", null, "Unable to initialize GLX-based rendering: {0}", e);
+                Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", null, "Unable to initialize GLX-based rendering: {0}", e);
                 return null;
             }
         }

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

@@ -42,7 +42,7 @@ namespace Avalonia.Markup.Xaml.Converters
                 !property.IsAttached &&
                 !registry.IsRegistered(targetType, property))
             {
-                Logger.Warning(
+                Logger.TryGet(LogEventLevel.Warning)?.Log(
                     LogArea.Property,
                     this,
                     "Property '{Owner}.{Name}' is not registered on '{Type}'.",

+ 1 - 1
src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs

@@ -150,7 +150,7 @@ namespace Avalonia.Markup.Data
                 }
                 catch (Exception e)
                 {
-                    Logger.Error(
+                    Logger.TryGet(LogEventLevel.Error)?.Log(
                         LogArea.Property,
                         control,
                         "Error setting {Property} on {Target}: {Exception}",

+ 1 - 1
src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs

@@ -85,7 +85,7 @@ namespace Avalonia.Direct2D1.Media
             }
             catch (Exception ex)
             {
-                Logger.Error(
+                Logger.TryGet(LogEventLevel.Error)?.Log(
                     LogArea.Visual,
                     this,
                     "GeometrySink.Close exception: {Exception}",

+ 30 - 2
tests/Avalonia.UnitTests/TestLogSink.cs

@@ -16,7 +16,7 @@ namespace Avalonia.UnitTests
 
     public class TestLogSink : ILogSink
     {
-        private LogCallback _callback;
+        private readonly LogCallback _callback;
 
         public TestLogSink(LogCallback callback)
         {
@@ -30,7 +30,35 @@ namespace Avalonia.UnitTests
             return Disposable.Create(() => Logger.Sink = null);
         }
 
-        public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
+        public bool IsEnabled(LogEventLevel level)
+        {
+            return true;
+        }
+
+        public void Log(LogEventLevel level, string area, object source, string messageTemplate)
+        {
+            _callback(level, area, source, messageTemplate);
+        }
+
+        public void Log<T0>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
+        {
+            _callback(level, area, source, messageTemplate, propertyValue0);
+        }
+
+        public void Log<T0, T1>(LogEventLevel level, string area, object source, string messageTemplate,
+            T0 propertyValue0, T1 propertyValue1)
+        {
+            _callback(level, area, source, messageTemplate, propertyValue0, propertyValue1);
+        }
+
+        public void Log<T0, T1, T2>(LogEventLevel level, string area, object source, string messageTemplate,
+            T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
+        {
+            _callback(level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
+        }
+
+        public void Log(LogEventLevel level, string area, object source, string messageTemplate,
+            params object[] propertyValues)
         {
             _callback(level, area, source, messageTemplate, propertyValues);
         }