diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 8cc512d132..2450f1a3a1 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,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
/// The binding error.
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
/// The priority.
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}",
diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs
index 7f8396cdfa..986e2cf012 100644
--- a/src/Avalonia.Base/Data/Core/BindingExpression.cs
+++ b/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}",
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..c895c70094 100644
--- a/src/Avalonia.Base/Logging/Logger.cs
+++ b/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
{
///
@@ -16,124 +14,43 @@ namespace Avalonia.Logging
public static ILogSink Sink { get; set; }
///
- /// Logs an event.
+ /// Checks if given log level is enabled.
///
/// The log event level.
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [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);
- }
-
- ///
- /// Logs an event with the level.
- ///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Verbose(
- string area,
- object source,
- string messageTemplate,
- params object[] propertyValues)
+ /// if given log level is enabled.
+ public static bool IsEnabled(LogEventLevel level)
{
- Log(LogEventLevel.Verbose, area, source, messageTemplate, propertyValues);
+ return Sink?.IsEnabled(level) == true;
}
///
- /// Logs an event with the level.
+ /// Returns parametrized logging sink if given log level is enabled.
///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Debug(
- string area,
- object source,
- string messageTemplate,
- params object[] propertyValues)
+ /// The log event level.
+ /// Log sink or if log level is not enabled.
+ public static ParametrizedLogger? TryGet(LogEventLevel level)
{
- Log(LogEventLevel.Debug, area, source, messageTemplate, propertyValues);
- }
+ if (!IsEnabled(level))
+ {
+ return null;
+ }
- ///
- /// Logs an event with the level.
- ///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [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);
}
///
- /// Logs an event with the level.
+ /// Returns parametrized logging sink if given log level is enabled.
///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Warning(
- string area,
- object source,
- string messageTemplate,
- params object[] propertyValues)
+ /// The log event level.
+ /// Log sink that is valid only if method returns .
+ /// if logger was obtained successfully.
+ public static bool TryGet(LogEventLevel level, out ParametrizedLogger outLogger)
{
- Log(LogEventLevel.Warning, area, source, messageTemplate, propertyValues);
- }
+ ParametrizedLogger? logger = TryGet(level);
- ///
- /// Logs an event with the level.
- ///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [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();
- ///
- /// Logs an event with the level.
- ///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [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;
}
}
}
diff --git a/src/Avalonia.Base/Logging/ParametrizedLogger.cs b/src/Avalonia.Base/Logging/ParametrizedLogger.cs
new file mode 100644
index 0000000000..1550cc1b40
--- /dev/null
+++ b/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
+{
+ ///
+ /// Logger sink parametrized for given logging level.
+ ///
+ public readonly struct ParametrizedLogger
+ {
+ private readonly ILogSink _sink;
+ private readonly LogEventLevel _level;
+
+ public ParametrizedLogger(ILogSink sink, LogEventLevel level)
+ {
+ _sink = sink;
+ _level = level;
+ }
+
+ ///
+ /// Checks if this logger can be used.
+ ///
+ public bool IsValid => _sink != null;
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ string area,
+ object source,
+ string messageTemplate)
+ {
+ _sink.Log(_level, area, source, messageTemplate);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0)
+ {
+ _sink.Log(_level, area, source, messageTemplate, propertyValue0);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// 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 void Log(
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1)
+ {
+ _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// 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 void Log(
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// 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.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1,
+ T2 propertyValue2,
+ T3 propertyValue3)
+ {
+ _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// 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.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ 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);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// 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.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ 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);
+ }
+ }
+}
diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs
index 2871271062..61184ef7b1 100644
--- a/src/Avalonia.Base/PriorityValue.cs
+++ b/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})",
diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs
index b67d9ef89a..9da803d16d 100644
--- a/src/Avalonia.Controls/DropDown.cs
+++ b/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);
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index 6869ea0822..c6172c0f36 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/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}",
diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs
index 47c3240374..7d0f306db8 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.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Control, this, "Creating control template");
var (child, nameScope) = template.Build(this);
ApplyTemplatedParent(child);
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index e0acab1133..c54ebd5360 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/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?",
diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs
index a7d4b96974..f3bb0edce5 100644
--- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs
+++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs
@@ -69,7 +69,17 @@ namespace Avalonia.DesignerSupport
}
if (!window.IsSet(Window.SizeToContentProperty))
- window.SizeToContent = SizeToContent.WidthAndHeight;
+ {
+ if (double.IsNaN(window.Width))
+ {
+ window.SizeToContent |= SizeToContent.Width;
+ }
+
+ if (double.IsNaN(window.Height))
+ {
+ window.SizeToContent |= SizeToContent.Height;
+ }
+ }
}
window.Show();
Design.ApplyDesignModeProperties(window, control);
diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs
index 45efccc1fa..855f123748 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,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;
diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs
index bd248d6d44..b0757a823d 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.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);
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.OpenGL/EglGlPlatformFeature.cs b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs
index 86411b89da..5f5064fba5 100644
--- a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs
+++ b/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;
}
}
diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs
index 38c29289b6..1465b9eb85 100644
--- a/src/Avalonia.Styling/StyledElement.cs
+++ b/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
}
diff --git a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs
index 362ac86e50..f1fd2f6c7f 100644
--- a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs
+++ b/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs
@@ -18,8 +18,9 @@ namespace Avalonia.Styling
internal class TypeNameAndClassSelector : Selector
{
private readonly Selector _previous;
+ private readonly Lazy> _classes = new Lazy>(() => new List());
private Type _targetType;
- private Lazy> _classes = new Lazy>(() => new List());
+
private string _selectorString;
public static TypeNameAndClassSelector OfType(Selector previous, Type targetType)
@@ -27,6 +28,7 @@ namespace Avalonia.Styling
var result = new TypeNameAndClassSelector(previous);
result._targetType = targetType;
result.IsConcreteType = true;
+
return result;
}
@@ -35,6 +37,7 @@ namespace Avalonia.Styling
var result = new TypeNameAndClassSelector(previous);
result._targetType = targetType;
result.IsConcreteType = false;
+
return result;
}
@@ -42,6 +45,7 @@ namespace Avalonia.Styling
{
var result = new TypeNameAndClassSelector(previous);
result.Name = name;
+
return result;
}
@@ -49,6 +53,7 @@ namespace Avalonia.Styling
{
var result = new TypeNameAndClassSelector(previous);
result.Classes.Add(className);
+
return result;
}
@@ -126,9 +131,11 @@ namespace Avalonia.Styling
if (subscribe)
{
var observable = new ClassObserver(control.Classes, _classes.Value);
+
return new SelectorMatch(observable);
}
- else if (!Matches(control.Classes))
+
+ if (!AreClassesMatching(control.Classes, Classes))
{
return SelectorMatch.NeverThisInstance;
}
@@ -139,21 +146,6 @@ namespace Avalonia.Styling
protected override Selector MovePrevious() => _previous;
- private bool Matches(IEnumerable classes)
- {
- int remaining = Classes.Count;
-
- foreach (var c in classes)
- {
- if (Classes.Contains(c))
- {
- --remaining;
- }
- }
-
- return remaining == 0;
- }
-
private string BuildSelectorString()
{
var builder = new StringBuilder();
@@ -199,11 +191,41 @@ namespace Avalonia.Styling
return builder.ToString();
}
- private class ClassObserver : LightweightObservableBase
+ private static bool AreClassesMatching(IReadOnlyList classes, IList toMatch)
{
- readonly IList _match;
- IAvaloniaReadOnlyList _classes;
- bool _value;
+ int remainingMatches = toMatch.Count;
+ int classesCount = classes.Count;
+
+ // Early bail out - we can't match if control does not have enough classes.
+ if (classesCount < remainingMatches)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < classesCount; i++)
+ {
+ var c = classes[i];
+
+ if (toMatch.Contains(c))
+ {
+ --remainingMatches;
+
+ // Already matched so we can skip checking other classes.
+ if (remainingMatches == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ return remainingMatches == 0;
+ }
+
+ private sealed class ClassObserver : LightweightObservableBase
+ {
+ private readonly IList _match;
+ private readonly IAvaloniaReadOnlyList _classes;
+ private bool _hasMatch;
public ClassObserver(IAvaloniaReadOnlyList classes, IList match)
{
@@ -215,42 +237,32 @@ namespace Avalonia.Styling
protected override void Initialize()
{
- _value = GetResult();
+ _hasMatch = IsMatching();
_classes.CollectionChanged += ClassesChanged;
}
protected override void Subscribed(IObserver observer, bool first)
{
- observer.OnNext(_value);
+ observer.OnNext(_hasMatch);
}
private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action != NotifyCollectionChangedAction.Move)
{
- var value = GetResult();
+ var hasMatch = IsMatching();
- if (value != _value)
+ if (hasMatch != _hasMatch)
{
- PublishNext(GetResult());
- _value = value;
+ PublishNext(hasMatch);
+ _hasMatch = hasMatch;
}
}
}
- private bool GetResult()
+ private bool IsMatching()
{
- int remaining = _match.Count;
-
- foreach (var c in _classes)
- {
- if (_match.Contains(c))
- {
- --remaining;
- }
- }
-
- return remaining == 0;
+ return AreClassesMatching(_classes, _match);
}
}
}
diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs
index e7be272f13..1f1590bdcd 100644
--- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs
+++ b/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.");
diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
index efcc555159..d9a68b236a 100644
--- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
+++ b/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;
}
diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
index b2d242d4af..68d56eeedd 100644
--- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
+++ b/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;
}
diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Visuals/Rendering/RenderLoop.cs
index 140688f8bc..c2594658b9 100644
--- a/src/Avalonia.Visuals/Rendering/RenderLoop.cs
+++ b/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
{
diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs
index 1f2d67b69e..f4306d3929 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.TryGet(LogEventLevel.Verbose)?.Log(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.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}",
diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
index d15b5fe4b8..3dc2e8e41f 100644
--- a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
+++ b/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;
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
index b42bd53619..6000b71f9d 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
+++ b/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}'.",
diff --git a/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs b/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs
index e03427c161..f7d228609d 100644
--- a/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs
+++ b/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}",
diff --git a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs
index 10b89d79b8..fd2f14fc58 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs
+++ b/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}",
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);
}