3 changed files with 207 additions and 4 deletions
@ -0,0 +1,178 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Text; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Logging |
|||
{ |
|||
public class DebugLogSink : ILogSink |
|||
{ |
|||
private readonly LogEventLevel _level; |
|||
private readonly IList<string> _areas; |
|||
|
|||
public DebugLogSink( |
|||
LogEventLevel minimumLevel, |
|||
IList<string> areas = null) |
|||
{ |
|||
_level = minimumLevel; |
|||
_areas = areas?.Count > 0 ? areas : null; |
|||
} |
|||
|
|||
public bool IsEnabled(LogEventLevel level) => level >= _level; |
|||
|
|||
public void Log(LogEventLevel level, string area, object source, string messageTemplate) |
|||
{ |
|||
if (IsEnabled(level, area)) |
|||
{ |
|||
Debug.WriteLine(Format<object, object, object>(area, messageTemplate, source)); |
|||
} |
|||
} |
|||
|
|||
public void Log<T0>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0) |
|||
{ |
|||
if (IsEnabled(level, area)) |
|||
{ |
|||
Debug.WriteLine(Format<T0, object, object>(area, messageTemplate, source, propertyValue0)); |
|||
} |
|||
} |
|||
|
|||
public void Log<T0, T1>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) |
|||
{ |
|||
if (IsEnabled(level, area)) |
|||
{ |
|||
Debug.WriteLine(Format<T0, T1, object>(area, messageTemplate, source, propertyValue0, propertyValue1)); |
|||
} |
|||
} |
|||
|
|||
public void Log<T0, T1, T2>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) |
|||
{ |
|||
if (IsEnabled(level, area)) |
|||
{ |
|||
Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); |
|||
} |
|||
} |
|||
|
|||
public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues) |
|||
{ |
|||
if (IsEnabled(level, area)) |
|||
{ |
|||
Debug.WriteLine(Format(area, messageTemplate, source, propertyValues)); |
|||
} |
|||
} |
|||
|
|||
private bool IsEnabled(LogEventLevel level, string area) => IsEnabled(level) && (_areas?.Contains(area) ?? true); |
|||
|
|||
private static string Format<T0, T1, T2>( |
|||
string area, |
|||
string template, |
|||
object source, |
|||
T0 v0 = default, |
|||
T1 v1 = default, |
|||
T2 v2 = default) |
|||
{ |
|||
var result = new StringBuilder(template.Length); |
|||
var r = new CharacterReader(template.AsSpan()); |
|||
var i = 0; |
|||
|
|||
result.Append('['); |
|||
result.Append(area); |
|||
result.Append("] "); |
|||
|
|||
while (!r.End) |
|||
{ |
|||
var c = r.Take(); |
|||
|
|||
if (c != '{') |
|||
{ |
|||
result.Append(c); |
|||
} |
|||
else |
|||
{ |
|||
if (r.Peek != '{') |
|||
{ |
|||
result.Append('\''); |
|||
result.Append(i++ switch |
|||
{ |
|||
0 => v0, |
|||
1 => v1, |
|||
2 => v2, |
|||
_ => null |
|||
}); |
|||
result.Append('\''); |
|||
r.TakeUntil('}'); |
|||
r.Take(); |
|||
} |
|||
else |
|||
{ |
|||
result.Append('{'); |
|||
r.Take(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (source is object) |
|||
{ |
|||
result.Append(" ("); |
|||
result.Append(source.GetType().Name); |
|||
result.Append(" #"); |
|||
result.Append(source.GetHashCode()); |
|||
result.Append(')'); |
|||
} |
|||
|
|||
return result.ToString(); |
|||
} |
|||
|
|||
private static string Format( |
|||
string area, |
|||
string template, |
|||
object source, |
|||
object[] v) |
|||
{ |
|||
var result = new StringBuilder(template.Length); |
|||
var r = new CharacterReader(template.AsSpan()); |
|||
var i = 0; |
|||
|
|||
result.Append('['); |
|||
result.Append(area); |
|||
result.Append(']'); |
|||
|
|||
while (!r.End) |
|||
{ |
|||
var c = r.Take(); |
|||
|
|||
if (c != '{') |
|||
{ |
|||
result.Append(c); |
|||
} |
|||
else |
|||
{ |
|||
if (r.Peek != '{') |
|||
{ |
|||
result.Append('\''); |
|||
result.Append(i < v.Length ? v[i++] : null); |
|||
result.Append('\''); |
|||
r.TakeUntil('}'); |
|||
r.Take(); |
|||
} |
|||
else |
|||
{ |
|||
result.Append('{'); |
|||
r.Take(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (source is object) |
|||
{ |
|||
result.Append('('); |
|||
result.Append(source.GetType().Name); |
|||
result.Append(" #"); |
|||
result.Append(source.GetHashCode()); |
|||
result.Append(')'); |
|||
} |
|||
|
|||
return result.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.Logging; |
|||
|
|||
namespace Avalonia |
|||
{ |
|||
public static class LoggingExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Logs Avalonia events to the <see cref="System.Diagnostics.Debug"/> sink.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The application class type.</typeparam>
|
|||
/// <param name="builder">The app builder instance.</param>
|
|||
/// <param name="level">The minimum level to log.</param>
|
|||
/// <param name="areas">The areas to log. Valid values are listed in <see cref="LogArea"/>.</param>
|
|||
/// <returns>The app builder instance.</returns>
|
|||
public static T LogToDebug<T>( |
|||
this T builder, |
|||
LogEventLevel level = LogEventLevel.Warning, |
|||
params string[] areas) |
|||
where T : AppBuilderBase<T>, new() |
|||
{ |
|||
Logger.Sink = new DebugLogSink(level, areas); |
|||
return builder; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue