Skip to content

Commit

Permalink
Removed dependency on LINQPad.Runtime - now reflecting necessary APIs…
Browse files Browse the repository at this point in the history
… instead upon initializing the sink
  • Loading branch information
lethek committed Nov 6, 2023
1 parent 66d0e50 commit aecedfe
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 36 deletions.
24 changes: 17 additions & 7 deletions src/Serilog.Sinks.LINQPad/LINQPadLoggerConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
using Serilog.Sinks.LINQPad.Output;
using Serilog.Sinks.LINQPad.Themes;
using System;

using LINQPad;
using System.Reflection;

namespace Serilog
{
Expand All @@ -45,7 +44,7 @@ public static class ConsoleLoggerConfigurationExtensions
/// to be changed at runtime.</param>
/// <param name="theme">The theme to apply to the styled output. If not specified,
/// uses <see cref="LINQPadTheme.LINQPadLiterate"/> or if dark-mode is enabled <see cref="LINQPadTheme.LINQPadDark"/>.</param>
/// <param name="dumpContainer">Optional write to a specified <see cref="DumpContainer"/> and not direct to the result panel.</param>
/// <param name="dumpContainer">Optionally write to a specified <see cref="DumpContainer"/> and not directly to the result panel.</param>
/// <returns>Configuration object allowing method chaining.</returns>
public static LoggerConfiguration LINQPad(
this LoggerSinkConfiguration sinkConfiguration,
Expand All @@ -54,7 +53,7 @@ public static LoggerConfiguration LINQPad(
IFormatProvider formatProvider = null,
LoggingLevelSwitch levelSwitch = null,
ConsoleTheme theme = null,
DumpContainer dumpContainer = null
object dumpContainer = null
)
{
if (sinkConfiguration == null) {
Expand All @@ -65,7 +64,7 @@ public static LoggerConfiguration LINQPad(
throw new ArgumentNullException(nameof(outputTemplate));
}

var appliedTheme = theme ?? (Util.IsDarkThemeEnabled ? DefaultThemes.LINQPadDark : DefaultThemes.LINQPadLiterate);
var appliedTheme = theme ?? (_isDarkThemeEnabled.Value ? DefaultThemes.LINQPadDark : DefaultThemes.LINQPadLiterate);

var formatter = new OutputTemplateRenderer(appliedTheme, outputTemplate, formatProvider);
return sinkConfiguration.Sink(new LINQPadSink(appliedTheme, formatter, dumpContainer), restrictedToMinimumLevel, levelSwitch);
Expand All @@ -81,14 +80,14 @@ public static LoggerConfiguration LINQPad(
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
/// to be changed at runtime.</param>
/// <param name="dumpContainer">Optional write to a specified <see cref="DumpContainer"/> and not direct to the result panel.</param>
/// <param name="dumpContainer">Optionally write to a specified <see cref="DumpContainer"/> and not directly to the result panel.</param>
/// <returns>Configuration object allowing method chaining.</returns>
public static LoggerConfiguration LINQPad(
this LoggerSinkConfiguration sinkConfiguration,
ITextFormatter formatter,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
LoggingLevelSwitch levelSwitch = null,
DumpContainer dumpContainer = null)
object dumpContainer = null)
{
if (sinkConfiguration == null) {
throw new ArgumentNullException(nameof(sinkConfiguration));
Expand All @@ -100,5 +99,16 @@ public static LoggerConfiguration LINQPad(

return sinkConfiguration.Sink(new LINQPadSink(DefaultThemes.None, formatter, dumpContainer), restrictedToMinimumLevel, levelSwitch);
}


private static Lazy<bool> _isDarkThemeEnabled = new(() => {
#if NETCOREAPP3_1_OR_GREATER
var utilType = Type.GetType("LINQPad.Util, LINQPad.Runtime");
#elif NET48_OR_GREATER
var utilType = Type.GetType("LINQPad.Util, LINQPad");
#endif
var isDarkThemeEnabledProperty = utilType!.GetProperty("IsDarkThemeEnabled", BindingFlags.Static | BindingFlags.Public);
return (bool)isDarkThemeEnabledProperty!.GetValue(null, null);
});
}
}
98 changes: 72 additions & 26 deletions src/Serilog.Sinks.LINQPad/LINQPadSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,29 @@
using Serilog.Events;
using Serilog.Formatting;
using Serilog.Sinks.LINQPad.Themes;

using System;

using LINQPad;
using System.Collections.Generic;
using System.Linq;

namespace Serilog.Sinks.LINQPad
{

internal class LINQPadSink : ILogEventSink, IDisposable
{
public LINQPadSink(ConsoleTheme theme, ITextFormatter formatter, DumpContainer dumpContainer = null)
public LINQPadSink(ConsoleTheme theme, ITextFormatter formatter, object dumpContainer = null)
{
_theme = theme ?? throw new ArgumentNullException(nameof(theme));
_formatter = formatter;
_writer = new ThemedHtmlWriter(_theme);
_writer = new ThemedHtmlWriter(theme ?? throw new ArgumentNullException(nameof(theme)));
_dumpContainer = dumpContainer;
ReflectLINQPadRuntime();
}


public void Emit(LogEvent logEvent)
{
lock (SyncRoot) {
_formatter.Format(logEvent, _writer);

var rawHtml = Util.RawHtml($"<span style='white-space:pre-wrap'>{_writer}</span>");

if (_dumpContainer != null) {
#if NETCOREAPP3_1_OR_GREATER
_dumpContainer.AppendContent(rawHtml);
#elif NET48_OR_GREATER
_content.Add(rawHtml);
_dumpContainer.Content = Util.VerticalRun(_content);
#endif
} else {
rawHtml.Dump();
}
_dumpContent($"<span style='white-space:pre-wrap'>{_writer}</span>");
_writer.Clear();
}
}
Expand All @@ -67,19 +53,79 @@ public void Dispose()
}


private bool _disposed;
private void ReflectLINQPadRuntime()
{
#if NETCOREAPP3_1_OR_GREATER
var utilType = Type.GetType("LINQPad.Util, LINQPad.Runtime");
var rawHtmlMethod = utilType!.GetMethod("RawHtml", new[] { typeof(string) });
var rawHtmlFunc = (Func<string, object>)rawHtmlMethod!.CreateDelegate(typeof(Func<string, object>));

if (_dumpContainer != null) {
var dumpContainerType = Type.GetType("LINQPad.DumpContainer, LINQPad.Runtime");
if (!_dumpContainer.GetType().IsAssignableFrom(dumpContainerType)) {
throw new ArgumentException($"The specified dumpContainer must be of type {dumpContainerType.FullName}", nameof(_dumpContainer));
}
var appendContentMethod = dumpContainerType
!.GetMethod("AppendContent", 1, new[] { Type.MakeGenericMethodParameter(0), typeof(bool) })
!.MakeGenericMethod(typeof(object));
var appendContentFunc = (Func<object, bool, object>)appendContentMethod.CreateDelegate(typeof(Func<object, bool, object>), _dumpContainer);
_dumpContent = o => appendContentFunc(rawHtmlFunc(o), false);

} else {
var extensionsType = Type.GetType("LINQPad.Extensions, LINQPad.Runtime");
var dumpMethod = extensionsType
!.GetMethod("Dump", 1, new[] { Type.MakeGenericMethodParameter(0) })
!.MakeGenericMethod(typeof(object));
var dumpFunc = (Func<object, object>)dumpMethod.CreateDelegate(typeof(Func<object, object>));
_dumpContent = o => dumpFunc(rawHtmlFunc(o));
}

#elif NET48_OR_GREATER
var utilType = Type.GetType("LINQPad.Util, LINQPad");
var rawHtmlMethod = utilType!.GetMethod("RawHtml", new[] { typeof(string) });
var rawHtmlFunc = (Func<string, object>)rawHtmlMethod!.CreateDelegate(typeof(Func<string, object>));

if (_dumpContainer != null) {
var dumpContainerType = Type.GetType("LINQPad.DumpContainer, LINQPad");
if (!_dumpContainer.GetType().IsAssignableFrom(dumpContainerType)) {
throw new ArgumentException($"The specified dumpContainer must be of type {dumpContainerType.FullName}", nameof(_dumpContainer));
}

var verticalRunMethod = utilType
!.GetMethod("VerticalRun", new[] { typeof(object[]) });
var verticalRunFunc = (Func<object[], object>)verticalRunMethod!.CreateDelegate(typeof(Func<object[], object>));

var contentProperty = dumpContainerType!.GetProperty("Content");
_dumpContent = o => {
_content.Add(o);
contentProperty!.SetValue(_dumpContainer, verticalRunFunc(new[] { rawHtmlFunc(o) }), null);
};

} else {
var extensionsType = Type.GetType("LINQPad.Extensions, LINQPad");
var dumpMethod = extensionsType
!.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)
.Where(x => x.Name == "Dump")
.Single(x => x.GetParameters().Length == 1)
.MakeGenericMethod(typeof(object));
var dumpFunc = (Func<object, object>)dumpMethod.CreateDelegate(typeof(Func<object, object>));
_dumpContent = o => dumpFunc(rawHtmlFunc(o));
}
#endif
}


private bool _disposed;
private Action<string> _dumpContent;

private readonly ConsoleTheme _theme;
private readonly ITextFormatter _formatter;
private readonly ThemedHtmlWriter _writer;
private readonly DumpContainer _dumpContainer;
private readonly object _dumpContainer;

#if NET48_OR_GREATER
private readonly List<object> _content = new List<object>();
private readonly List<object> _content = new();
#endif

private static readonly object SyncRoot = new object();
private static readonly object SyncRoot = new();
}

}
}
2 changes: 0 additions & 2 deletions src/Serilog.Sinks.LINQPad/Serilog.Sinks.LINQPad.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="LINQPad" Version="5.46.0" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="LINQPad.Runtime" Version="7.6.6" Condition="'$(TargetFramework)' == 'netcoreapp3.1'" />
<PackageReference Include="Serilog" Version="2.8.0" />
<PackageReference Include="System.Runtime.Extensions" Version="4.3.1" />
</ItemGroup>
Expand Down
1 change: 0 additions & 1 deletion tests/Serilog.Sinks.LINQPad.Tests/LINQPadSinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ private static LogEvent CreateLogEvent(string text)
return new LogEvent(DateTimeOffset.UtcNow, LogEventLevel.Debug, null, msg, Enumerable.Empty<LogEventProperty>());
}



private const string DefaultConsoleOutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}";
private static readonly LINQPadTheme DefaultTestTheme = DefaultThemes.LINQPadLiterate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="LINQPad" Version="5.46.0" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="LINQPad.Runtime" Version="7.7.15" Condition="'$(TargetFramework)' == 'net6.0'" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
Expand Down

0 comments on commit aecedfe

Please sign in to comment.