Browse Source

Tweaked logging of binding errors.

1. Fixed some tests to expect `BindingNotification`s to be returned on a broken binding chain.
2. Changed logging of `BindingNotification`s - log at `Warning` level (instead of `Error`) except when the binding chain is broken at the root, in which case log at `Information` level. Do this to prevent flooding the output window when initializing.
pull/894/head
Steven Kirk 9 years ago
parent
commit
3eb5e0e200
  1. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  2. 11
      src/Avalonia.Base/AvaloniaObject.cs
  3. 1
      src/Avalonia.Base/Data/BindingNotification.cs
  4. 53
      src/Avalonia.Base/Logging/LoggerExtensions.cs
  5. 9
      src/Avalonia.Base/PriorityValue.cs
  6. 23
      src/Avalonia.Base/Utilities/ExceptionUtilities.cs
  7. 10
      src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs
  8. 2
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  9. 41
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs
  10. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>

11
src/Avalonia.Base/AvaloniaObject.cs

@ -664,16 +664,7 @@ namespace Avalonia
if (notification != null)
{
if (notification.ErrorType == BindingErrorType.Error)
{
Logger.Error(
LogArea.Binding,
this,
"Error in binding to {Target}.{Property}: {Message}",
this,
property,
ExceptionUtilities.GetMessage(notification.Error));
}
notification.LogIfError(this, property);
if (notification.HasValue)
{

1
src/Avalonia.Base/Data/BindingNotification.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 Avalonia.Logging;
namespace Avalonia.Data
{

53
src/Avalonia.Base/Logging/LoggerExtensions.cs

@ -0,0 +1,53 @@
using System;
using Avalonia.Data;
namespace Avalonia.Logging
{
internal static class LoggerExtensions
{
public static void LogIfError(
this BindingNotification notification,
object source,
AvaloniaProperty property)
{
if (notification.ErrorType == BindingErrorType.Error)
{
if (notification.Error is AggregateException aggregate)
{
foreach (var inner in aggregate.InnerExceptions)
{
LogError(source, property, inner);
}
}
else
{
LogError(source, property, notification.Error);
}
}
}
private static void LogError(object source, AvaloniaProperty property, Exception e)
{
var level = LogEventLevel.Warning;
if (e is BindingChainException b &&
!string.IsNullOrEmpty(b.Expression) &&
string.IsNullOrEmpty(b.ExpressionErrorPoint))
{
// The error occurred at the root of the binding chain: it's possible that the
// DataContext isn't set up yet, so log at Information level instead of Warning
// to prevent spewing hundreds of errors.
level = LogEventLevel.Information;
}
Logger.Log(
level,
LogArea.Binding,
source,
"Error in binding to {Target}.{Property}: {Message}",
source,
property,
e.Message);
}
}
}

9
src/Avalonia.Base/PriorityValue.cs

@ -189,14 +189,7 @@ namespace Avalonia
/// <param name="error">The binding error.</param>
public void LevelError(PriorityLevel level, BindingNotification error)
{
Logger.Log(
LogEventLevel.Error,
LogArea.Binding,
Owner,
"Error in binding to {Target}.{Property}: {Message}",
Owner,
Property,
error.Error.Message);
error.LogIfError(Owner, Property);
}
/// <summary>

23
src/Avalonia.Base/Utilities/ExceptionUtilities.cs

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Utilities
{
internal static class ExceptionUtilities
{
public static string GetMessage(Exception e)
{
var aggregate = e as AggregateException;
if (aggregate != null)
{
return string.Join(" | ", aggregate.InnerExceptions.Select(x => x.Message));
}
return e.Message;
}
}
}

10
src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs

@ -32,10 +32,12 @@ namespace Avalonia.Markup.Data
public void Commit(string expression)
{
Expression = expression;
ExpressionErrorPoint = string.Join(".", _nodes.Reverse())
.Replace(".!", "!")
.Replace(".[", "[")
.Replace(".^", "^");
ExpressionErrorPoint = _nodes != null ?
string.Join(".", _nodes.Reverse())
.Replace(".!", "!")
.Replace(".[", "[")
.Replace(".^", "^") :
string.Empty;
_nodes = null;
}
}

2
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@ -389,7 +389,7 @@ namespace Avalonia.Base.UnitTests
LogCallback checkLogMessage = (level, area, src, mt, pv) =>
{
if (level == LogEventLevel.Error &&
if (level == LogEventLevel.Warning &&
area == LogArea.Binding &&
mt == "Error in binding to {Target}.{Property}: {Message}" &&
pv.Length == 3 &&

41
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs

@ -58,43 +58,63 @@ namespace Avalonia.Markup.UnitTests.Data
}
[Fact]
public async void Should_Return_UnsetValue_For_Root_Null()
public async void Should_Return_BindingNotification_Error_For_Root_Null()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(default(object), "Foo");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
Assert.Equal(
new BindingNotification(
new MarkupBindingChainException("Null value", "Foo", string.Empty),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
public async void Should_Return_UnsetValue_For_Root_UnsetValue()
public async void Should_Return_BindingNotification_Error_For_Root_UnsetValue()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(AvaloniaProperty.UnsetValue, "Foo");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
Assert.Equal(
new BindingNotification(
new MarkupBindingChainException("Null value", "Foo", string.Empty),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
public async void Should_Return_UnsetValue_For_Observable_Root_Null()
public async void Should_Return_BindingNotification_Error_For_Observable_Root_Null()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(Observable.Return(default(object)), "Foo");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
Assert.Equal(
new BindingNotification(
new MarkupBindingChainException("Null value", "Foo", string.Empty),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
public async void Should_Return_UnsetValue_For_Observable_Root_UnsetValue()
public async void Should_Return_BindingNotification_Error_For_Observable_Root_UnsetValue()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(Observable.Return(AvaloniaProperty.UnsetValue), "Foo");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
Assert.Equal(
new BindingNotification(
new MarkupBindingChainException("Null value", "Foo", string.Empty),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
@ -492,7 +512,10 @@ namespace Avalonia.Markup.UnitTests.Data
{
"foo",
"bar",
AvaloniaProperty.UnsetValue,
new BindingNotification(
new MarkupBindingChainException("Null value", "Foo", string.Empty),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue)
},
result);

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs

@ -37,7 +37,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
LogCallback checkLogMessage = (level, area, src, mt, pv) =>
{
if (level == LogEventLevel.Error &&
if (level == LogEventLevel.Warning &&
area == LogArea.Binding &&
mt == "Error in binding to {Target}.{Property}: {Message}" &&
pv.Length == 3 &&

Loading…
Cancel
Save