commit aecb3421c74ec7506222549f51cc1e5f9de6f524 Author: Yaakov Date: Thu May 21 10:35:58 2020 +1000 Initial version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ff6da7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vs/ +[Bb]in/ +[Oo]bj/ diff --git a/Notepad.Extensions.Logging.FunctionalTest/Notepad.Extensions.Logging.FunctionalTest.csproj b/Notepad.Extensions.Logging.FunctionalTest/Notepad.Extensions.Logging.FunctionalTest.csproj new file mode 100644 index 0000000..4e854f5 --- /dev/null +++ b/Notepad.Extensions.Logging.FunctionalTest/Notepad.Extensions.Logging.FunctionalTest.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/Notepad.Extensions.Logging.FunctionalTest/Program.cs b/Notepad.Extensions.Logging.FunctionalTest/Program.cs new file mode 100644 index 0000000..b8deb88 --- /dev/null +++ b/Notepad.Extensions.Logging.FunctionalTest/Program.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Notepad.Extensions.Logging.FunctionalTest +{ + class Program + { + static void Main(string[] args) + { + var sc = new ServiceCollection(); + sc.AddLogging(lb => + { + lb.AddConsole(); + lb.AddNotepad(); + }); + + var sp = sc.BuildServiceProvider(); + var logger = sp.GetRequiredService>(); + + logger.LogWarning("Here is a warning."); + logger.LogError(GetException(), "oh no!."); + logger.LogInformation("Here is some info."); + } + + static Exception GetException() + { + try + { + throw new InvalidOperationException("Wheeeeeeee"); + } + catch (Exception ex) + { + return ex; + } + } + } +} diff --git a/Notepad.Extensions.Logging.sln b/Notepad.Extensions.Logging.sln new file mode 100644 index 0000000..8b0ca14 --- /dev/null +++ b/Notepad.Extensions.Logging.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notepad.Extensions.Logging", "Notepad.Extensions.Logging\Notepad.Extensions.Logging.csproj", "{859E3EAB-04EB-42EE-86B2-A6FE90D71731}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notepad.Extensions.Logging.FunctionalTest", "Notepad.Extensions.Logging.FunctionalTest\Notepad.Extensions.Logging.FunctionalTest.csproj", "{82E1E9EF-9284-4E7B-B350-0318F9B7B411}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {859E3EAB-04EB-42EE-86B2-A6FE90D71731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {859E3EAB-04EB-42EE-86B2-A6FE90D71731}.Debug|Any CPU.Build.0 = Debug|Any CPU + {859E3EAB-04EB-42EE-86B2-A6FE90D71731}.Release|Any CPU.ActiveCfg = Release|Any CPU + {859E3EAB-04EB-42EE-86B2-A6FE90D71731}.Release|Any CPU.Build.0 = Release|Any CPU + {82E1E9EF-9284-4E7B-B350-0318F9B7B411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82E1E9EF-9284-4E7B-B350-0318F9B7B411}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82E1E9EF-9284-4E7B-B350-0318F9B7B411}.Release|Any CPU.ActiveCfg = Release|Any CPU + {82E1E9EF-9284-4E7B-B350-0318F9B7B411}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4F5721FB-D38E-487C-9851-8A268DE891CC} + EndGlobalSection +EndGlobal diff --git a/Notepad.Extensions.Logging/LoggingBuilderExtensions.cs b/Notepad.Extensions.Logging/LoggingBuilderExtensions.cs new file mode 100644 index 0000000..77a6920 --- /dev/null +++ b/Notepad.Extensions.Logging/LoggingBuilderExtensions.cs @@ -0,0 +1,11 @@ +namespace Microsoft.Extensions.Logging +{ + public static class LoggingBuilderExtensions + { + public static ILoggingBuilder AddNotepad(this ILoggingBuilder builder) + { + builder.AddProvider(NotepadLoggerProvider.Instance); + return builder; + } + } +} diff --git a/Notepad.Extensions.Logging/Notepad.Extensions.Logging.csproj b/Notepad.Extensions.Logging/Notepad.Extensions.Logging.csproj new file mode 100644 index 0000000..9035fad --- /dev/null +++ b/Notepad.Extensions.Logging/Notepad.Extensions.Logging.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + 8.0 + + + + + + + + \ No newline at end of file diff --git a/Notepad.Extensions.Logging/NotepadLogger.cs b/Notepad.Extensions.Logging/NotepadLogger.cs new file mode 100644 index 0000000..c7cb828 --- /dev/null +++ b/Notepad.Extensions.Logging/NotepadLogger.cs @@ -0,0 +1,107 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Extensions.ObjectPool; + +namespace Microsoft.Extensions.Logging +{ + class NotepadLogger : ILogger + { + public NotepadLogger(ObjectPool stringBuilderPool, string categoryName) + { + this.stringBuilderPool = stringBuilderPool; + this.categoryName = categoryName; + } + + readonly ObjectPool stringBuilderPool; + readonly string categoryName; + + public IDisposable BeginScope(TState state) => NullDisposable.Instance; + + public bool IsEnabled(LogLevel logLevel) => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!IsEnabled(logLevel)) + { + return; + } + + if (formatter is null) + { + throw new ArgumentNullException(nameof(formatter)); + } + + var message = formatter(state, exception); + + if (string.IsNullOrEmpty(message) && exception is null) + { + return; + } + + WriteMessage(logLevel, eventId.Id, message, exception); + } + + void WriteMessage(LogLevel logLevel, int eventId, string message, Exception ex) + { + var builder = stringBuilderPool.Get(); + try + { + builder + .Append('[') + .Append(GetLogLevelString(logLevel)) + .Append("] ") + .Append(categoryName) + .Append(" (") + .Append(eventId) + .Append(") ") + .AppendLine(message); + + if (ex is { }) + { + builder.Append(" "); + builder.AppendLine(ex.ToString()); + } + + WriteToNotepad(builder.ToString()); + } + finally + { + stringBuilderPool.Return(builder); + } + } + + static string GetLogLevelString(LogLevel logLevel) => logLevel switch + { + LogLevel.Trace => "trce", + LogLevel.Debug => "dbug", + LogLevel.Information => "info", + LogLevel.Warning => "warn", + LogLevel.Error => "fail", + LogLevel.Critical => "crit", + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), + }; + + static void WriteToNotepad(string message) + { + IntPtr hwnd = NativeMethods.FindWindow(null, "Untitled - Notepad"); + IntPtr edit = NativeMethods.FindWindowEx(hwnd, IntPtr.Zero, "EDIT", null); + NativeMethods.SendMessage(edit, NativeMethods.EM_REPLACESEL, (IntPtr)1, message); + } + } + + static class NativeMethods + { + [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpszClass, string lpszWindow); + + public const int EM_REPLACESEL = 0x00C2; + + [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam); + } +} diff --git a/Notepad.Extensions.Logging/NotepadLoggerProvider.cs b/Notepad.Extensions.Logging/NotepadLoggerProvider.cs new file mode 100644 index 0000000..e8c87b6 --- /dev/null +++ b/Notepad.Extensions.Logging/NotepadLoggerProvider.cs @@ -0,0 +1,24 @@ +using System.Text; +using Microsoft.Extensions.ObjectPool; + +namespace Microsoft.Extensions.Logging +{ + class NotepadLoggerProvider : ILoggerProvider + { + NotepadLoggerProvider() + { + var poolProvider = new DefaultObjectPoolProvider(); + stringBuilderPool = poolProvider.CreateStringBuilderPool(); + } + + public static ILoggerProvider Instance { get; } = new NotepadLoggerProvider(); + + readonly ObjectPool stringBuilderPool; + + public ILogger CreateLogger(string categoryName) => new NotepadLogger(stringBuilderPool, categoryName); + + public void Dispose() + { + } + } +} diff --git a/Notepad.Extensions.Logging/NullDisposable.cs b/Notepad.Extensions.Logging/NullDisposable.cs new file mode 100644 index 0000000..4e3e782 --- /dev/null +++ b/Notepad.Extensions.Logging/NullDisposable.cs @@ -0,0 +1,13 @@ +using System; + +namespace Microsoft.Extensions.Logging +{ + class NullDisposable : IDisposable + { + public static IDisposable Instance { get; } = new NullDisposable(); + + public void Dispose() + { + } + } +} \ No newline at end of file