Browse Source

Merge branch 'master' into alloc-equals

pull/3074/head
Steven Kirk 7 years ago
committed by GitHub
parent
commit
423b675f0b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      src/Avalonia.Base/AvaloniaObject.cs
  2. 2
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  3. 71
      src/Avalonia.Base/Logging/ILogSink.cs
  4. 125
      src/Avalonia.Base/Logging/Logger.cs
  5. 174
      src/Avalonia.Base/Logging/ParametrizedLogger.cs
  6. 2
      src/Avalonia.Base/PriorityValue.cs
  7. 4
      src/Avalonia.Controls/DropDown.cs
  8. 2
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  9. 2
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  10. 2
      src/Avalonia.Controls/TopLevel.cs
  11. 12
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  12. 33
      src/Avalonia.Layout/LayoutManager.cs
  13. 8
      src/Avalonia.Layout/Layoutable.cs
  14. 104
      src/Avalonia.Logging.Serilog/SerilogLogger.cs
  15. 2
      src/Avalonia.OpenGL/EglGlPlatformFeature.cs
  16. 4
      src/Avalonia.Styling/StyledElement.cs
  17. 90
      src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs
  18. 4
      src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs
  19. 3
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  20. 3
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  21. 4
      src/Avalonia.Visuals/Rendering/RenderLoop.cs
  22. 7
      src/Avalonia.Visuals/Visual.cs
  23. 2
      src/Avalonia.X11/Glx/GlxPlatformFeature.cs
  24. 2
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  25. 2
      src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs
  26. 2
      src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs
  27. 32
      tests/Avalonia.UnitTests/TestLogSink.cs

23
src/Avalonia.Base/AvaloniaObject.cs

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

2
src/Avalonia.Base/Data/Core/BindingExpression.cs

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

71
src/Avalonia.Base/Logging/ILogSink.cs

@ -8,6 +8,77 @@ namespace Avalonia.Logging
/// </summary> /// </summary>
public interface ILogSink 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> /// <summary>
/// Logs a new event. /// Logs a new event.
/// </summary> /// </summary>

125
src/Avalonia.Base/Logging/Logger.cs

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

174
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);
}
}
}

2
src/Avalonia.Base/PriorityValue.cs

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

4
src/Avalonia.Controls/DropDown.cs

@ -9,7 +9,7 @@ namespace Avalonia.Controls
{ {
public DropDown() 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); Type IStyleable.StyleKey => typeof(ComboBox);
@ -20,7 +20,7 @@ namespace Avalonia.Controls
{ {
public DropDownItem() 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); Type IStyleable.StyleKey => typeof(ComboBoxItem);

2
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

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

2
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -255,7 +255,7 @@ namespace Avalonia.Controls.Primitives
if (template != null) 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); var (child, nameScope) = template.Build(this);
ApplyTemplatedParent(child); ApplyTemplatedParent(child);

2
src/Avalonia.Controls/TopLevel.cs

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

12
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -69,7 +69,17 @@ namespace Avalonia.DesignerSupport
} }
if (!window.IsSet(Window.SizeToContentProperty)) 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(); window.Show();
Design.ApplyDesignModeProperties(window, control); Design.ApplyDesignModeProperties(window, control);

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

8
src/Avalonia.Layout/Layoutable.cs

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

104
src/Avalonia.Logging.Serilog/SerilogLogger.cs

@ -34,6 +34,76 @@ namespace Avalonia.Logging.Serilog
Logger.Sink = new SerilogLogger(output); 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/> /// <inheritdoc/>
public void Log( public void Log(
AvaloniaLogEventLevel level, AvaloniaLogEventLevel level,
@ -45,12 +115,40 @@ namespace Avalonia.Logging.Serilog
Contract.Requires<ArgumentNullException>(area != null); Contract.Requires<ArgumentNullException>(area != null);
Contract.Requires<ArgumentNullException>(messageTemplate != null); Contract.Requires<ArgumentNullException>(messageTemplate != null);
using (LogContext.PushProperty("Area", area)) using (PushLogContextProperties(area, source))
using (LogContext.PushProperty("SourceType", source?.GetType()))
using (LogContext.PushProperty("SourceHash", source?.GetHashCode()))
{ {
_output.Write((SerilogLogEventLevel)level, messageTemplate, propertyValues); _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();
}
}
} }
} }

2
src/Avalonia.OpenGL/EglGlPlatformFeature.cs

@ -31,7 +31,7 @@ namespace Avalonia.OpenGL
} }
catch(Exception e) 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; return null;
} }
} }

4
src/Avalonia.Styling/StyledElement.cs

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

90
src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs

@ -18,8 +18,9 @@ namespace Avalonia.Styling
internal class TypeNameAndClassSelector : Selector internal class TypeNameAndClassSelector : Selector
{ {
private readonly Selector _previous; private readonly Selector _previous;
private readonly Lazy<List<string>> _classes = new Lazy<List<string>>(() => new List<string>());
private Type _targetType; private Type _targetType;
private Lazy<List<string>> _classes = new Lazy<List<string>>(() => new List<string>());
private string _selectorString; private string _selectorString;
public static TypeNameAndClassSelector OfType(Selector previous, Type targetType) public static TypeNameAndClassSelector OfType(Selector previous, Type targetType)
@ -27,6 +28,7 @@ namespace Avalonia.Styling
var result = new TypeNameAndClassSelector(previous); var result = new TypeNameAndClassSelector(previous);
result._targetType = targetType; result._targetType = targetType;
result.IsConcreteType = true; result.IsConcreteType = true;
return result; return result;
} }
@ -35,6 +37,7 @@ namespace Avalonia.Styling
var result = new TypeNameAndClassSelector(previous); var result = new TypeNameAndClassSelector(previous);
result._targetType = targetType; result._targetType = targetType;
result.IsConcreteType = false; result.IsConcreteType = false;
return result; return result;
} }
@ -42,6 +45,7 @@ namespace Avalonia.Styling
{ {
var result = new TypeNameAndClassSelector(previous); var result = new TypeNameAndClassSelector(previous);
result.Name = name; result.Name = name;
return result; return result;
} }
@ -49,6 +53,7 @@ namespace Avalonia.Styling
{ {
var result = new TypeNameAndClassSelector(previous); var result = new TypeNameAndClassSelector(previous);
result.Classes.Add(className); result.Classes.Add(className);
return result; return result;
} }
@ -126,9 +131,11 @@ namespace Avalonia.Styling
if (subscribe) if (subscribe)
{ {
var observable = new ClassObserver(control.Classes, _classes.Value); var observable = new ClassObserver(control.Classes, _classes.Value);
return new SelectorMatch(observable); return new SelectorMatch(observable);
} }
else if (!Matches(control.Classes))
if (!AreClassesMatching(control.Classes, Classes))
{ {
return SelectorMatch.NeverThisInstance; return SelectorMatch.NeverThisInstance;
} }
@ -139,21 +146,6 @@ namespace Avalonia.Styling
protected override Selector MovePrevious() => _previous; protected override Selector MovePrevious() => _previous;
private bool Matches(IEnumerable<string> classes)
{
int remaining = Classes.Count;
foreach (var c in classes)
{
if (Classes.Contains(c))
{
--remaining;
}
}
return remaining == 0;
}
private string BuildSelectorString() private string BuildSelectorString()
{ {
var builder = new StringBuilder(); var builder = new StringBuilder();
@ -199,11 +191,41 @@ namespace Avalonia.Styling
return builder.ToString(); return builder.ToString();
} }
private class ClassObserver : LightweightObservableBase<bool> private static bool AreClassesMatching(IReadOnlyList<string> classes, IList<string> toMatch)
{ {
readonly IList<string> _match; int remainingMatches = toMatch.Count;
IAvaloniaReadOnlyList<string> _classes; int classesCount = classes.Count;
bool _value;
// 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<bool>
{
private readonly IList<string> _match;
private readonly IAvaloniaReadOnlyList<string> _classes;
private bool _hasMatch;
public ClassObserver(IAvaloniaReadOnlyList<string> classes, IList<string> match) public ClassObserver(IAvaloniaReadOnlyList<string> classes, IList<string> match)
{ {
@ -215,42 +237,32 @@ namespace Avalonia.Styling
protected override void Initialize() protected override void Initialize()
{ {
_value = GetResult(); _hasMatch = IsMatching();
_classes.CollectionChanged += ClassesChanged; _classes.CollectionChanged += ClassesChanged;
} }
protected override void Subscribed(IObserver<bool> observer, bool first) protected override void Subscribed(IObserver<bool> observer, bool first)
{ {
observer.OnNext(_value); observer.OnNext(_hasMatch);
} }
private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e) private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
if (e.Action != NotifyCollectionChangedAction.Move) if (e.Action != NotifyCollectionChangedAction.Move)
{ {
var value = GetResult(); var hasMatch = IsMatching();
if (value != _value) if (hasMatch != _hasMatch)
{ {
PublishNext(GetResult()); PublishNext(hasMatch);
_value = value; _hasMatch = hasMatch;
} }
} }
} }
private bool GetResult() private bool IsMatching()
{ {
int remaining = _match.Count; return AreClassesMatching(_classes, _match);
foreach (var c in _classes)
{
if (_match.Contains(c))
{
--remaining;
}
}
return remaining == 0;
} }
} }
} }

4
src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs

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

3
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Avalonia.Logging;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
using Avalonia.Platform; using Avalonia.Platform;
@ -269,7 +270,7 @@ namespace Avalonia.Rendering
} }
catch (RenderTargetCorruptedException ex) 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?.Dispose();
RenderTarget = null; RenderTarget = null;
} }

3
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Logging;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -80,7 +81,7 @@ namespace Avalonia.Rendering
} }
catch (RenderTargetCorruptedException ex) 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.Dispose();
_renderTarget = null; _renderTarget = null;
} }

4
src/Avalonia.Visuals/Rendering/RenderLoop.cs

@ -120,7 +120,7 @@ namespace Avalonia.Rendering
} }
catch (Exception ex) 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) 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 finally
{ {

7
src/Avalonia.Visuals/Visual.cs

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

2
src/Avalonia.X11/Glx/GlxPlatformFeature.cs

@ -36,7 +36,7 @@ namespace Avalonia.X11.Glx
} }
catch(Exception e) 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; return null;
} }
} }

2
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

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

2
src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs

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

2
src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs

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

32
tests/Avalonia.UnitTests/TestLogSink.cs

@ -16,7 +16,7 @@ namespace Avalonia.UnitTests
public class TestLogSink : ILogSink public class TestLogSink : ILogSink
{ {
private LogCallback _callback; private readonly LogCallback _callback;
public TestLogSink(LogCallback callback) public TestLogSink(LogCallback callback)
{ {
@ -30,7 +30,35 @@ namespace Avalonia.UnitTests
return Disposable.Create(() => Logger.Sink = null); 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); _callback(level, area, source, messageTemplate, propertyValues);
} }

Loading…
Cancel
Save