diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 8cc512d132..22a85663c7 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/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,18 @@ namespace Avalonia
throw new ArgumentException($"The property {property.Name} is readonly.");
}
- Logger.Verbose(
- LogArea.Property,
- this,
- "Bound {Property} to {Binding} with priority LocalValue",
- property,
- description);
+ if (Logger.IsEnabled(LogEventLevel.Verbose))
+ {
+ var description = GetDescription(source);
+
+ Logger.Log(
+ LogEventLevel.Verbose,
+ LogArea.Property,
+ this,
+ "Bound {Property} to {Binding} with priority LocalValue",
+ property,
+ description);
+ }
if (_directBindings == null)
{
@@ -351,13 +355,19 @@ namespace Avalonia
}
else
{
- Logger.Verbose(
- LogArea.Property,
- this,
- "Bound {Property} to {Binding} with priority {Priority}",
- property,
- description,
- priority);
+ if (Logger.IsEnabled(LogEventLevel.Verbose))
+ {
+ var description = GetDescription(source);
+
+ Logger.Log(
+ LogEventLevel.Verbose,
+ LogArea.Property,
+ this,
+ "Bound {Property} to {Binding} with priority {Priority}",
+ property,
+ description,
+ priority);
+ }
return Values.AddBinding(property, source, priority);
}
@@ -406,14 +416,18 @@ namespace Avalonia
{
RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority);
- Logger.Verbose(
- LogArea.Property,
- this,
- "{Property} changed from {$Old} to {$Value} with priority {Priority}",
- property,
- oldValue,
- newValue,
- (BindingPriority)priority);
+ if (Logger.IsEnabled(LogEventLevel.Verbose))
+ {
+ Logger.Log(
+ LogEventLevel.Verbose,
+ LogArea.Property,
+ this,
+ "{Property} changed from {$Old} to {$Value} with priority {Priority}",
+ property,
+ oldValue,
+ newValue,
+ (BindingPriority)priority);
+ }
}
}
@@ -812,7 +826,13 @@ namespace Avalonia
/// The priority.
private void LogPropertySet(AvaloniaProperty property, object value, BindingPriority priority)
{
- Logger.Verbose(
+ if (!Logger.IsEnabled(LogEventLevel.Verbose))
+ {
+ return;
+ }
+
+ Logger.Log(
+ LogEventLevel.Verbose,
LogArea.Property,
this,
"Set {Property} to {$Value} with priority {Priority}",
diff --git a/src/Avalonia.Base/Logging/ILogSink.cs b/src/Avalonia.Base/Logging/ILogSink.cs
index 0ed4eede8f..8b5751b0af 100644
--- a/src/Avalonia.Base/Logging/ILogSink.cs
+++ b/src/Avalonia.Base/Logging/ILogSink.cs
@@ -8,6 +8,77 @@ namespace Avalonia.Logging
///
public interface ILogSink
{
+ ///
+ /// Checks if given log level is enabled.
+ ///
+ /// The log event level.
+ /// if given log level is enabled.
+ bool IsEnabled(LogEventLevel level);
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate);
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0);
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1);
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1,
+ T2 propertyValue2);
+
///
/// Logs a new event.
///
diff --git a/src/Avalonia.Base/Logging/Logger.cs b/src/Avalonia.Base/Logging/Logger.cs
index b1132ff4a9..c46edd4b4b 100644
--- a/src/Avalonia.Base/Logging/Logger.cs
+++ b/src/Avalonia.Base/Logging/Logger.cs
@@ -15,6 +15,96 @@ namespace Avalonia.Logging
///
public static ILogSink Sink { get; set; }
+ ///
+ /// Checks if given log level is enabled.
+ ///
+ /// The log event level.
+ /// if given log level is enabled.
+ public static bool IsEnabled(LogEventLevel level)
+ {
+ return Sink?.IsEnabled(level) == true;
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate)
+ {
+ Sink?.Log(level, area, source, messageTemplate);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0)
+ {
+ Sink?.Log(level, area, source, messageTemplate, propertyValue0);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1)
+ {
+ Sink?.Log(level, area, source, messageTemplate, propertyValue0, propertyValue1);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ Sink?.Log(level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
+ }
+
///
/// Logs an event.
///
diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs
index 47c3240374..daf79b3558 100644
--- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs
+++ b/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.Log(LogEventLevel.Verbose, LogArea.Control, this, "Creating control template");
var (child, nameScope) = template.Build(this);
ApplyTemplatedParent(child);
diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs
index 45efccc1fa..ef5464c4ae 100644
--- a/src/Avalonia.Layout/LayoutManager.cs
+++ b/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,22 @@ 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();
+ bool captureTiming = Logger.IsEnabled(LogEventLevel.Information);
+
+ if (captureTiming)
+ {
+ Logger.Log(LogEventLevel.Information,
+ 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 +111,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.Information(LogArea.Layout, this, "Layout pass finished in {Time}", stopwatch.Elapsed);
+ }
}
_queued = false;
diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs
index bd248d6d44..9b5f66b19d 100644
--- a/src/Avalonia.Layout/Layoutable.cs
+++ b/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.Log(LogEventLevel.Verbose, 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.Log(LogEventLevel.Verbose, 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.Log(LogEventLevel.Verbose, 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.Log(LogEventLevel.Verbose, LogArea.Layout, this, "Invalidated arrange");
IsArrangeValid = false;
(VisualRoot as ILayoutRoot)?.LayoutManager?.InvalidateArrange(this);
diff --git a/src/Avalonia.Logging.Serilog/SerilogLogger.cs b/src/Avalonia.Logging.Serilog/SerilogLogger.cs
index 0534fe3012..895ee268d2 100644
--- a/src/Avalonia.Logging.Serilog/SerilogLogger.cs
+++ b/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(area != null);
+ Contract.Requires(messageTemplate != null);
+
+ using (PushLogContextProperties(area, source))
+ {
+ _output.Write((SerilogLogEventLevel)level, messageTemplate);
+ }
+ }
+
+ public void Log(
+ LogEventLevel level,
+ string area, object source,
+ string messageTemplate,
+ T0 propertyValue0)
+ {
+ Contract.Requires(area != null);
+ Contract.Requires(messageTemplate != null);
+
+ using (PushLogContextProperties(area, source))
+ {
+ _output.Write((SerilogLogEventLevel)level, messageTemplate, propertyValue0);
+ }
+ }
+
+ public void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1)
+ {
+ Contract.Requires(area != null);
+ Contract.Requires(messageTemplate != null);
+
+ using (PushLogContextProperties(area, source))
+ {
+ _output.Write((SerilogLogEventLevel)level, messageTemplate, propertyValue0, propertyValue1);
+ }
+ }
+
+ public void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ Contract.Requires(area != null);
+ Contract.Requires(messageTemplate != null);
+
+ using (PushLogContextProperties(area, source))
+ {
+ _output.Write((SerilogLogEventLevel)level, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
+ }
+ }
+
///
public void Log(
AvaloniaLogEventLevel level,
@@ -45,12 +115,40 @@ namespace Avalonia.Logging.Serilog
Contract.Requires(area != null);
Contract.Requires(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();
+ }
+ }
}
}
diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs
index 1f2d67b69e..8d32371c6d 100644
--- a/src/Avalonia.Visuals/Visual.cs
+++ b/src/Avalonia.Visuals/Visual.cs
@@ -359,7 +359,7 @@ namespace Avalonia
/// The event args.
protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
- Logger.Verbose(LogArea.Visual, this, "Attached to visual tree");
+ Logger.Log(LogEventLevel.Verbose, LogArea.Visual, this, "Attached to visual tree");
_visualRoot = e.Root;
@@ -388,7 +388,7 @@ namespace Avalonia
/// The event args.
protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
- Logger.Verbose(LogArea.Visual, this, "Detached from visual tree");
+ Logger.Log(LogEventLevel.Verbose, LogArea.Visual, this, "Detached from visual tree");
_visualRoot = null;
diff --git a/tests/Avalonia.UnitTests/TestLogSink.cs b/tests/Avalonia.UnitTests/TestLogSink.cs
index 8e4dd7164f..a2b188c273 100644
--- a/tests/Avalonia.UnitTests/TestLogSink.cs
+++ b/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(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
+ {
+ _callback(level, area, source, messageTemplate, propertyValue0);
+ }
+
+ public void Log(LogEventLevel level, string area, object source, string messageTemplate,
+ T0 propertyValue0, T1 propertyValue1)
+ {
+ _callback(level, area, source, messageTemplate, propertyValue0, propertyValue1);
+ }
+
+ public void Log(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);
}