From 4cbaa7fa731a515343b4f4ea6fc4db63f7191377 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Wed, 29 Mar 2023 23:05:50 -0400 Subject: [PATCH] more logging pls --- src/Ionide.ProjInfo/FsLibLog.fs | 10 ++- src/Ionide.ProjInfo/Library.fs | 47 ++++++++-- .../Ionide.ProjInfo.Tests/FsLibLog.Expecto.fs | 86 +++++++++++++++++++ .../Ionide.ProjInfo.Tests.fsproj | 10 +-- test/Ionide.ProjInfo.Tests/Program.fs | 3 + test/Ionide.ProjInfo.Tests/Tests.fs | 6 +- 6 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 test/Ionide.ProjInfo.Tests/FsLibLog.Expecto.fs diff --git a/src/Ionide.ProjInfo/FsLibLog.fs b/src/Ionide.ProjInfo/FsLibLog.fs index 8980011e..4dd1a699 100644 --- a/src/Ionide.ProjInfo/FsLibLog.fs +++ b/src/Ionide.ProjInfo/FsLibLog.fs @@ -1,4 +1,4 @@ -// As of commit 599e765db64ba5bcde23a589f427453c52fdb132 +// As of commit 1fe15c95c008567db63e00e823ca8768155e85c6 namespace Ionide.ProjInfo.Logging open System.Text.RegularExpressions @@ -772,10 +772,13 @@ module Providers = static member Create() = let createLogger = let factoryType = getLogFactoryType.Value + let createLoggerMethodInfo = factoryType.GetMethod("CreateLogger", [| typedefof |]) + let instanceParam = Expression.Parameter(typedefof) let nameParam = Expression.Parameter(typedefof) let instanceCast = Expression.Convert(instanceParam, factoryType) + let createLoggerMethodExp = Expression.Call(instanceCast, createLoggerMethodInfo, nameParam) let createLogger = @@ -801,7 +804,9 @@ module Providers = Type.GetType("Microsoft.Extensions.Logging.LoggerExtensions, Microsoft.Extensions.Logging.Abstractions") let loggerType = Type.GetType("Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.Abstractions") + let logEventLevelType = Type.GetType("Microsoft.Extensions.Logging.LogLevel, Microsoft.Extensions.Logging.Abstractions") + let instanceParam = Expression.Parameter(typedefof) let instanceCast = Expression.Convert(instanceParam, loggerType) @@ -811,6 +816,7 @@ module Providers = let isEnabled = let isEnabledMethodInfo = loggerType.GetMethod("IsEnabled", [| logEventLevelType |]) + let isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast) @@ -907,6 +913,7 @@ module Providers = let beginScopeMethodInfo = loggerType.GetMethod("BeginScope").MakeGenericMethod(typedefof) let stateParam = Expression.Parameter(typedefof) + let beginScopeMethodCall = Expression.Call(instanceCast, beginScopeMethodInfo, stateParam) Expression @@ -973,6 +980,7 @@ module Providers = | Some factory -> // Create bogus logger that will propagate to a real logger later let logger = factoryGateway.Value.CreateLogger factory (Guid.NewGuid().ToString()) + loggerGateway.Value.BeginScope logger (box message) let create () = MicrosoftProvider() :> ILogProvider diff --git a/src/Ionide.ProjInfo/Library.fs b/src/Ionide.ProjInfo/Library.fs index 211ae29b..73b7a951 100644 --- a/src/Ionide.ProjInfo/Library.fs +++ b/src/Ionide.ProjInfo/Library.fs @@ -329,6 +329,7 @@ type BinaryLogGeneration = /// module ProjectLoader = + type LoadedProject = internal LoadedProject of ProjectInstance [] @@ -337,11 +338,14 @@ module ProjectLoader = | Success of LoadedProject | Error of string - let internal logger (writer: StringWriter) = + let internal stringWriterLogger (writer: StringWriter) = { new ILogger with member this.Initialize(eventSource: IEventSource) : unit = // eventSource.ErrorRaised.Add(fun t -> writer.WriteLine t.Message) //Only log errors - eventSource.AnyEventRaised.Add(fun t -> writer.WriteLine t.Message) + eventSource.AnyEventRaised.Add(fun t -> + let message = t.Message + writer.WriteLine message + ) member this.Parameters: string = "" @@ -355,6 +359,28 @@ module ProjectLoader = with set (v: LoggerVerbosity): unit = () } + + let msBuildToLogProvider () = + let msBuildLogger = LogProvider.getLoggerByName "MsBuild" + + { new ILogger with + member this.Initialize(eventSource: IEventSource) : unit = + eventSource.ErrorRaised.Add(fun t -> msBuildLogger.error (Log.setMessage t.Message)) + eventSource.WarningRaised.Add(fun t -> msBuildLogger.warn (Log.setMessage t.Message)) + + eventSource.AnyEventRaised.Add(fun t -> msBuildLogger.info (Log.setMessage t.Message)) + + member this.Parameters + with get (): string = "" + and set (v: string): unit = () + + member this.Shutdown() : unit = () + + member this.Verbosity + with get (): LoggerVerbosity = LoggerVerbosity.Diagnostic + and set (v: LoggerVerbosity): unit = () + } + let getTfm (path: string) readingProps isLegacyFrameworkProj = let pi = ProjectInstance(path, globalProperties = readingProps, toolsVersion = null) @@ -379,7 +405,8 @@ module ProjectLoader = Some tfm let createLoggers (paths: string seq) (binaryLogs: BinaryLogGeneration) (sw: StringWriter) = - let logger = logger (sw) + let swLogger = stringWriterLogger (sw) + let msbuildLogger = msBuildToLogProvider () let logFilePath (dir: DirectoryInfo, projectPath: string) = let projectFileName = Path.GetFileName projectPath @@ -387,7 +414,10 @@ module ProjectLoader = Path.Combine(dir.FullName, logFileName) match binaryLogs with - | BinaryLogGeneration.Off -> [ logger ] + | BinaryLogGeneration.Off -> [ + swLogger + msbuildLogger + ] | BinaryLogGeneration.Within dir -> let loggers = paths @@ -397,7 +427,8 @@ module ProjectLoader = ) [ - logger + msbuildLogger + swLogger yield! loggers ] @@ -851,6 +882,7 @@ type WorkspaceLoaderViaProjectGraph private (toolsPath, ?globalProperties: (stri let (ToolsPath toolsPath) = toolsPath let globalProperties = defaultArg globalProperties [] let logger = LogProvider.getLoggerFor () + let loadingNotification = new Event() let handleProjectGraphFailures f = @@ -1041,10 +1073,7 @@ type WorkspaceLoaderViaProjectGraph private (toolsPath, ?globalProperties: (stri allProjectOptions |> Seq.iter (fun po -> - logger.info ( - Log.setMessage "Project loaded {project}" - >> Log.addContextDestructured "project" po.ProjectFileName - ) + logger.info (Log.setMessageI $"Project loaded {po.ProjectFileName:project}") loadingNotification.Trigger( WorkspaceProjectState.Loaded( diff --git a/test/Ionide.ProjInfo.Tests/FsLibLog.Expecto.fs b/test/Ionide.ProjInfo.Tests/FsLibLog.Expecto.fs new file mode 100644 index 00000000..25a7ca88 --- /dev/null +++ b/test/Ionide.ProjInfo.Tests/FsLibLog.Expecto.fs @@ -0,0 +1,86 @@ +namespace FsLibLog.Providers.Expecto + +open System +open Ionide.ProjInfo.Logging + +module EMsg = Expecto.Logging.Message +type ELL = Expecto.Logging.LogLevel + +module internal Helpers = + let addExnOpt exOpt msg = + match exOpt with + | None -> msg + | Some ex -> + msg + |> EMsg.addExn ex + + let addValues (items: obj[]) msg = + (msg, + items + |> Seq.mapi (fun i item -> i, item)) + ||> Seq.fold (fun msg (i, item) -> + msg + |> EMsg.setField (string i) item + ) + + let getLogLevel: LogLevel -> Expecto.Logging.LogLevel = + function + | LogLevel.Debug -> ELL.Debug + | LogLevel.Error -> ELL.Error + | LogLevel.Fatal -> ELL.Fatal + | LogLevel.Info -> ELL.Info + | LogLevel.Trace -> ELL.Verbose + | LogLevel.Warn -> ELL.Warn + | _ -> ELL.Warn + +open Helpers + +// Naive implementation, not that important, just need logging to actually work +type ExpectoLogProvider() = + let propertyStack = System.Collections.Generic.Stack() + + + let addProp key value = + propertyStack.Push(key, value) + + { new IDisposable with + member __.Dispose() = + propertyStack.Pop() + |> ignore + } + + interface ILogProvider with + override __.GetLogger(name: string) : Logger = + let logger = Expecto.Logging.Log.create name + + fun ll mt exnOpt values -> + match mt with + | Some f -> + let ll = getLogLevel ll + + logger.log + ll + (fun ll -> + let message = f () + let mutable msg = Expecto.Logging.Message.eventX message ll + + for (propertyName, propertyValue) in (Seq.rev propertyStack) do + msg <- Expecto.Logging.Message.setField propertyName propertyValue msg + + match exnOpt with + | None -> msg + | Some ex -> + msg + |> Expecto.Logging.Message.addExn ex + |> addValues values + ) + |> Async.RunSynchronously + + true + | None -> false + + override __.OpenMappedContext (key: string) (value: obj) (b: bool) = addProp key value + override __.OpenNestedContext name = addProp "NDC" name + +module ExpectoLogProvider = + let create () = ExpectoLogProvider() :> ILogProvider diff --git a/test/Ionide.ProjInfo.Tests/Ionide.ProjInfo.Tests.fsproj b/test/Ionide.ProjInfo.Tests/Ionide.ProjInfo.Tests.fsproj index 5a0d1e30..d45b0acf 100644 --- a/test/Ionide.ProjInfo.Tests/Ionide.ProjInfo.Tests.fsproj +++ b/test/Ionide.ProjInfo.Tests/Ionide.ProjInfo.Tests.fsproj @@ -9,6 +9,7 @@ false + @@ -20,17 +21,12 @@ - - + + DestinationFolder="$(OutputPath)" ContinueOnError="false" /> - \ No newline at end of file diff --git a/test/Ionide.ProjInfo.Tests/Program.fs b/test/Ionide.ProjInfo.Tests/Program.fs index 094d47b0..15302257 100644 --- a/test/Ionide.ProjInfo.Tests/Program.fs +++ b/test/Ionide.ProjInfo.Tests/Program.fs @@ -7,12 +7,15 @@ open Ionide.ProjInfo.ProjectSystem open Expecto open Expecto.Impl open Expecto.Logging +open FsLibLog.Providers.Expecto let toolsPath = Init.init (IO.DirectoryInfo Environment.CurrentDirectory) None [] let tests = Tests.tests toolsPath +Ionide.ProjInfo.Logging.LogProvider.setLoggerProvider (ExpectoLogProvider()) + [] let main argv = diff --git a/test/Ionide.ProjInfo.Tests/Tests.fs b/test/Ionide.ProjInfo.Tests/Tests.fs index eadb10a8..f537a070 100644 --- a/test/Ionide.ProjInfo.Tests/Tests.fs +++ b/test/Ionide.ProjInfo.Tests/Tests.fs @@ -821,11 +821,7 @@ let testParseSln toolsPath = let p = InspectSln.tryParseSln (slnPath) () - // Expect.isTrue - // (match p with - // | Ok _ -> true - // | Result.Error _ -> false) - // "expected successful parse" + Expect.isOk p "expected successful parse" // let actualProjects = // InspectSln.loadingBuildOrder (