From 87020d35acf589d6b06fefcfadd92ee83004037e Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Sun, 28 Sep 2025 00:07:41 +0900 Subject: [PATCH] Refactor logger interface and add JsNative interop layer Updated ICompatibleLogger to accept params object[] for flexible logging. Refactored TraceLogger to support the new interface and improved formatting. Added JsNative.cs to encapsulate ChakraCore P/Invoke interop, and updated JsCore to use JsNative for all native calls. Modified all resource tools to accept and use ICompatibleLogger for consistent logging. Updated project file to include new and updated sources. --- .../WelsonJS.Launcher/ICompatibleLogger.cs | 6 +- WelsonJS.Toolkit/WelsonJS.Launcher/JsCore.cs | 152 +++--------------- .../WelsonJS.Launcher/JsNative.cs | 93 +++++++++++ .../WelsonJS.Launcher/ResourceServer.cs | 14 +- .../ResourceTools/ChromiumDevTools.cs | 5 +- .../ResourceTools/Completion.cs | 11 +- .../ResourceTools/DnsQuery.cs | 5 +- .../ResourceTools/IpQuery.cs | 5 +- .../ResourceTools/Settings.cs | 5 +- .../ResourceTools/TwoFactorAuth.cs | 5 +- .../WelsonJS.Launcher/ResourceTools/Whois.cs | 5 +- .../WelsonJS.Launcher/TraceLogger.cs | 29 +++- .../WelsonJS.Launcher.csproj | 2 + 13 files changed, 187 insertions(+), 150 deletions(-) create mode 100644 WelsonJS.Toolkit/WelsonJS.Launcher/JsNative.cs diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ICompatibleLogger.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ICompatibleLogger.cs index 74b323f..16d4edb 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ICompatibleLogger.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ICompatibleLogger.cs @@ -10,8 +10,8 @@ namespace WelsonJS.Launcher { public interface ICompatibleLogger { - void Info(string message); - void Warn(string message); - void Error(string message); + void Info(params object[] args); + void Warn(params object[] args); + void Error(params object[] args); } } diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/JsCore.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/JsCore.cs index 38db84a..53af2b5 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/JsCore.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/JsCore.cs @@ -4,53 +4,47 @@ // https://github.com/gnh1201/welsonjs // using System; +using System.Globalization; using System.Runtime.InteropServices; namespace WelsonJS.Launcher { public sealed class JsCore : IDisposable { - private IntPtr _runtime = IntPtr.Zero; - private IntPtr _context = IntPtr.Zero; + private JsNative.JsRuntime _rt; + private JsNative.JsContext _ctx; private bool _disposed; public JsCore() { - Check(JsCreateRuntime(0, IntPtr.Zero, out _runtime), nameof(JsCreateRuntime)); - Check(JsCreateContext(_runtime, out _context), nameof(JsCreateContext)); - Check(JsSetCurrentContext(_context), nameof(JsSetCurrentContext)); + Check(JsNative.JsCreateRuntime(JsNative.JsRuntimeAttributes.None, null, out _rt), "JsCreateRuntime"); + Check(JsNative.JsCreateContext(_rt, out _ctx), "JsCreateContext"); + Check(JsNative.JsSetCurrentContext(_ctx), "JsSetCurrentContext"); } - /// - /// Evaluates JavaScript and returns the result converted to string (via JsConvertValueToString). - /// public string EvaluateToString(string script, string sourceUrl = "repl") { if (_disposed) throw new ObjectDisposedException(nameof(JsCore)); - if (script is null) throw new ArgumentNullException(nameof(script)); + if (script == null) throw new ArgumentNullException(nameof(script)); - Check(JsRunScript(script, IntPtr.Zero, sourceUrl, out var result), nameof(JsRunScript)); + JsNative.JsValue result; + Check(JsNative.JsRunScript(script, UIntPtr.Zero, sourceUrl, out result), "JsRunScript"); - // Convert result -> JsString - Check(JsConvertValueToString(result, out var jsString), nameof(JsConvertValueToString)); + JsNative.JsValue jsStr; + Check(JsNative.JsConvertValueToString(result, out jsStr), "JsConvertValueToString"); - // Extract pointer/length (UTF-16) and marshal to managed string - Check(JsStringToPointer(jsString, out var p, out var len), nameof(JsStringToPointer)); - return Marshal.PtrToStringUni(p, checked((int)len)); + IntPtr p; + UIntPtr len; + Check(JsNative.JsStringToPointer(jsStr, out p, out len), "JsStringToPointer"); + + int chars = checked((int)len); + return Marshal.PtrToStringUni(p, chars); } - /// - /// Evaluates JavaScript for side effects; discards the result. - /// - public void Execute(string script, string sourceUrl = "repl") + private static void Check(JsNative.JsErrorCode code, string op) { - _ = EvaluateToString(script, sourceUrl); - } - - private static void Check(JsErrorCode code, string op) - { - if (code != JsErrorCode.JsNoError) - throw new InvalidOperationException($"{op} failed with {code} (0x{(int)code:X})."); + if (code != JsNative.JsErrorCode.JsNoError) + throw new InvalidOperationException(op + " failed: " + code + " (0x" + ((int)code).ToString("X", CultureInfo.InvariantCulture) + ")"); } public void Dispose() @@ -60,112 +54,18 @@ namespace WelsonJS.Launcher try { - // Unset the current context from the SAME physical thread that set it. - JsSetCurrentContext(IntPtr.Zero); - } - catch - { - // Swallow to ensure runtime is disposed. + // Unset current context + JsNative.JsSetCurrentContext(new JsNative.JsContext { Handle = IntPtr.Zero }); } + catch { /* ignore */ } finally { - if (_runtime != IntPtr.Zero) - { - JsDisposeRuntime(_runtime); - _runtime = IntPtr.Zero; - } - _context = IntPtr.Zero; + if (_rt.Handle != IntPtr.Zero) + JsNative.JsDisposeRuntime(_rt); } - GC.SuppressFinalize(this); } - ~JsCore() => Dispose(); - - // ========================= - // P/Invoke surface (as given) - // ========================= - - // Essential (expanded) JsErrorCode set matching ChakraCore’s headers layout. - // Values are grouped by category bases (0x10000, 0x20000, ...). - public enum JsErrorCode - { - // Success - JsNoError = 0, - - // Category bases (useful when inspecting ranges) - JsErrorCategoryUsage = 0x10000, - JsErrorCategoryEngine = 0x20000, - JsErrorCategoryScript = 0x30000, - JsErrorCategoryFatal = 0x40000, - - // Usage errors (0x10001+) - JsErrorInvalidArgument = 0x10001, - JsErrorNullArgument = 0x10002, - JsErrorNoCurrentContext = 0x10003, - JsErrorInExceptionState = 0x10004, - JsErrorNotImplemented = 0x10005, - JsErrorWrongThread = 0x10006, - JsErrorRuntimeInUse = 0x10007, - JsErrorBadSerializedScript = 0x10008, - JsErrorInDisabledState = 0x10009, - JsErrorCannotDisableExecution = 0x1000A, - JsErrorHeapEnumInProgress = 0x1000B, - JsErrorArgumentNotObject = 0x1000C, - JsErrorInProfileCallback = 0x1000D, - JsErrorInThreadServiceCallback = 0x1000E, - JsErrorCannotSerializeDebugScript = 0x1000F, - JsErrorAlreadyDebuggingContext = 0x10010, - JsErrorAlreadyProfilingContext = 0x10011, - JsErrorIdleNotEnabled = 0x10012, - - // Engine errors (0x20001+) - JsErrorOutOfMemory = 0x20001, - JsErrorBadFPUState = 0x20002, - - // Script errors (0x30001+) - JsErrorScriptException = 0x30001, - JsErrorScriptCompile = 0x30002, - JsErrorScriptTerminated = 0x30003, - JsErrorScriptEvalDisabled = 0x30004, - - // Fatal (0x40001) - JsErrorFatal = 0x40001, - - // Misc/diagnostic (0x50000+) - JsErrorWrongRuntime = 0x50000, - JsErrorDiagAlreadyInDebugMode = 0x50001, - JsErrorDiagNotInDebugMode = 0x50002, - JsErrorDiagNotAtBreak = 0x50003, - JsErrorDiagInvalidHandle = 0x50004, - JsErrorDiagObjectNotFound = 0x50005, - JsErrorDiagUnableToPerformAction = 0x50006, - } - - [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern JsErrorCode JsCreateRuntime(uint attributes, IntPtr callback, out IntPtr runtime); - - [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern JsErrorCode JsCreateContext(IntPtr runtime, out IntPtr context); - - [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern JsErrorCode JsSetCurrentContext(IntPtr context); - - [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] - public static extern JsErrorCode JsRunScript(string script, IntPtr sourceContext, string sourceUrl, out IntPtr result); - - [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern JsErrorCode JsConvertValueToString(IntPtr value, out IntPtr stringValue); - - [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern JsErrorCode JsStringToPointer(IntPtr value, out IntPtr buffer, out UIntPtr length); - - // Note: Unsetting is typically done via JsSetCurrentContext(IntPtr.Zero) - // Kept here only if your build exposes this symbol. - [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "JsSetCurrentContext")] - public static extern JsErrorCode JsUnSetCurrentContext(IntPtr zero); - - [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern JsErrorCode JsDisposeRuntime(IntPtr runtime); + ~JsCore() { Dispose(); } } } diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/JsNative.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/JsNative.cs new file mode 100644 index 0000000..3f44823 --- /dev/null +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/JsNative.cs @@ -0,0 +1,93 @@ +using System; +using System.Runtime.InteropServices; + +namespace WelsonJS.Launcher +{ + public static class JsNative + { + // === Enums / handles === + [Flags] + public enum JsRuntimeAttributes : uint + { + None = 0x00000000, + DisableBackgroundWork = 0x00000001, + AllowScriptInterrupt = 0x00000002, + EnableIdleProcessing = 0x00000004, + DisableNativeCodeGeneration = 0x00000008, + EnableExperimentalFeatures = 0x00000010, + } + + // ChakraCore typedefs are opaque pointers; represent as IntPtr + public struct JsRuntime { public IntPtr Handle; } + public struct JsContext { public IntPtr Handle; } + public struct JsValue { public IntPtr Handle; } + + // JsErrorCode (essential subset; expand as needed) + public enum JsErrorCode + { + JsNoError = 0, + + // Usage + JsErrorInvalidArgument = 0x10001, + JsErrorNullArgument = 0x10002, + JsErrorNoCurrentContext = 0x10003, + JsErrorInExceptionState = 0x10004, + JsErrorNotImplemented = 0x10005, + JsErrorWrongThread = 0x10006, + JsErrorRuntimeInUse = 0x10007, + + // Script + JsErrorScriptException = 0x30001, + JsErrorScriptCompile = 0x30002, + JsErrorScriptTerminated = 0x30003, + + // Engine + JsErrorOutOfMemory = 0x20001, + + // Fatal + JsErrorFatal = 0x40001, + } + + // Thread service callback: __stdcall + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate JsErrorCode JsThreadServiceCallback(IntPtr callback, IntPtr callbackState); + + // ======= FIXED SIGNATURES (StdCall + Unicode) ======= + + [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.StdCall)] + public static extern JsErrorCode JsCreateRuntime( + JsRuntimeAttributes attributes, + JsThreadServiceCallback threadService, // pass null if unused + out JsRuntime runtime); + + [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.StdCall)] + public static extern JsErrorCode JsCreateContext( + JsRuntime runtime, + out JsContext newContext); + + [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.StdCall)] + public static extern JsErrorCode JsSetCurrentContext(JsContext context); + + // JsSourceContext is size_t → UIntPtr; strings are wide-char + [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public static extern JsErrorCode JsRunScript( + string script, + UIntPtr sourceContext, + string sourceUrl, + out JsValue result); + + [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.StdCall)] + public static extern JsErrorCode JsConvertValueToString(JsValue value, out JsValue stringValue); + + // Returns pointer to UTF-16 buffer + length (size_t) for a JsString value + [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.StdCall)] + public static extern JsErrorCode JsStringToPointer( + JsValue value, + out IntPtr buffer, + out UIntPtr length); + + // Unset by passing "invalid" context (JS_INVALID_REFERENCE is typically null) + [DllImport("ChakraCore.dll", CallingConvention = CallingConvention.StdCall)] + public static extern JsErrorCode JsDisposeRuntime(JsRuntime runtime); + } +} diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs index 6d23016..be77132 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs @@ -60,13 +60,13 @@ namespace WelsonJS.Launcher }, TaskScheduler.Default); // Add resource tools - _tools.Add(new ResourceTools.Completion(this, _httpClient)); - _tools.Add(new ResourceTools.Settings(this, _httpClient)); - _tools.Add(new ResourceTools.ChromiumDevTools(this, _httpClient)); - _tools.Add(new ResourceTools.DnsQuery(this, _httpClient)); - _tools.Add(new ResourceTools.IpQuery(this, _httpClient)); - _tools.Add(new ResourceTools.TwoFactorAuth(this, _httpClient)); - _tools.Add(new ResourceTools.Whois(this, _httpClient)); + _tools.Add(new ResourceTools.Completion(this, _httpClient, _logger)); + _tools.Add(new ResourceTools.Settings(this, _httpClient, _logger)); + _tools.Add(new ResourceTools.ChromiumDevTools(this, _httpClient, _logger)); + _tools.Add(new ResourceTools.DnsQuery(this, _httpClient, _logger)); + _tools.Add(new ResourceTools.IpQuery(this, _httpClient, _logger)); + _tools.Add(new ResourceTools.TwoFactorAuth(this, _httpClient, _logger)); + _tools.Add(new ResourceTools.Whois(this, _httpClient, _logger)); // Register the prefix _listener.Prefixes.Add(prefix); diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/ChromiumDevTools.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/ChromiumDevTools.cs index 81feeb3..d56c5ec 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/ChromiumDevTools.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/ChromiumDevTools.cs @@ -19,13 +19,16 @@ namespace WelsonJS.Launcher.ResourceTools { private readonly ResourceServer Server; private readonly HttpClient _httpClient; + private readonly ICompatibleLogger _logger; private readonly WebSocketManager _wsManager = new WebSocketManager(); private const string Prefix = "devtools/"; - public ChromiumDevTools(ResourceServer server, HttpClient httpClient) + public ChromiumDevTools(ResourceServer server, HttpClient httpClient, ICompatibleLogger logger) { Server = server; + _httpClient = httpClient; + _logger = logger; } public bool CanHandle(string path) diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Completion.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Completion.cs index 57bb482..34abe58 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Completion.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Completion.cs @@ -22,13 +22,16 @@ namespace WelsonJS.Launcher.ResourceTools { private readonly ResourceServer Server; private readonly HttpClient _httpClient; + private readonly ICompatibleLogger _logger; private const string Prefix = "completion/"; private readonly ConcurrentBag DiscoveredExecutables = new ConcurrentBag(); - public Completion(ResourceServer server, HttpClient httpClient) + public Completion(ResourceServer server, HttpClient httpClient, ICompatibleLogger logger) { Server = server; + _httpClient = httpClient; + _logger = logger; Task.Run(async () => await SafeDiscoverAsync(DiscoverFromInstalledSoftware)); Task.Run(async () => await SafeDiscoverAsync(DiscoverFromPathVariable)); @@ -160,7 +163,7 @@ namespace WelsonJS.Launcher.ResourceTools { if (!Directory.Exists(path)) { - Trace.TraceInformation("Directory does not exist: {0}", path); + _logger.Info("Directory does not exist: {0}", path); return; } @@ -175,7 +178,7 @@ namespace WelsonJS.Launcher.ResourceTools } catch (Exception ex) { - Trace.TraceInformation("Error enumerating executables in '{0}': {1}", path, ex.Message); + _logger.Info("Error enumerating executables in '{0}': {1}", path, ex.Message); } } @@ -195,7 +198,7 @@ namespace WelsonJS.Launcher.ResourceTools } catch (Exception ex) { - Trace.TraceError($"Discovery failed: {ex.Message}"); + _logger.Error($"Discovery failed: {ex.Message}"); } } } diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/DnsQuery.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/DnsQuery.cs index 3b4c8b1..831294b 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/DnsQuery.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/DnsQuery.cs @@ -18,16 +18,19 @@ namespace WelsonJS.Launcher.ResourceTools { private readonly ResourceServer Server; private readonly HttpClient _httpClient; + private readonly ICompatibleLogger _logger; private const string Prefix = "dns-query/"; private string DnsServer; private const int DnsPort = 53; private const int Timeout = 5000; private static readonly Random _random = new Random(); - public DnsQuery(ResourceServer server, HttpClient httpClient) + public DnsQuery(ResourceServer server, HttpClient httpClient, ICompatibleLogger logger) { Server = server; + _httpClient = httpClient; + _logger = logger; DnsServer = Program.GetAppConfig("DnsServerAddress"); } diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/IpQuery.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/IpQuery.cs index 3a3aa6b..68d7400 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/IpQuery.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/IpQuery.cs @@ -14,12 +14,15 @@ namespace WelsonJS.Launcher.ResourceTools { private readonly ResourceServer Server; private readonly HttpClient _httpClient; + private readonly ICompatibleLogger _logger; private const string Prefix = "ip-query/"; - public IpQuery(ResourceServer server, HttpClient httpClient) + public IpQuery(ResourceServer server, HttpClient httpClient, ICompatibleLogger logger) { Server = server; + _httpClient = httpClient; + _logger = logger; } public bool CanHandle(string path) diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Settings.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Settings.cs index 2f74940..6d6fd94 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Settings.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Settings.cs @@ -20,12 +20,15 @@ namespace WelsonJS.Launcher.ResourceTools { private readonly ResourceServer Server; private readonly HttpClient _httpClient; + private readonly ICompatibleLogger _logger; private const string Prefix = "settings"; - public Settings(ResourceServer server, HttpClient httpClient) + public Settings(ResourceServer server, HttpClient httpClient, ICompatibleLogger logger) { Server = server; + _httpClient = httpClient; + _logger = logger; } public bool CanHandle(string path) diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/TwoFactorAuth.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/TwoFactorAuth.cs index e7094e2..aa44ba6 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/TwoFactorAuth.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/TwoFactorAuth.cs @@ -19,14 +19,17 @@ namespace WelsonJS.Launcher.ResourceTools { private readonly ResourceServer Server; private readonly HttpClient _httpClient; + private readonly ICompatibleLogger _logger; private const string Prefix = "tfa/"; private const string Base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; private static readonly int[] ValidKeyCharLengths = new[] { 16, 32 }; - public TwoFactorAuth(ResourceServer server, HttpClient httpClient) + public TwoFactorAuth(ResourceServer server, HttpClient httpClient, ICompatibleLogger logger) { Server = server; + _httpClient = httpClient; + _logger = logger; } public bool CanHandle(string path) diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Whois.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Whois.cs index 45ae8b9..fc82e85 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Whois.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Whois.cs @@ -15,12 +15,15 @@ namespace WelsonJS.Launcher.ResourceTools { private readonly ResourceServer Server; private readonly HttpClient _httpClient; + private readonly ICompatibleLogger _logger; private const string Prefix = "whois/"; - public Whois(ResourceServer server, HttpClient httpClient) + public Whois(ResourceServer server, HttpClient httpClient, ICompatibleLogger logger) { Server = server; + _httpClient = httpClient; + _logger = logger; } public bool CanHandle(string path) diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/TraceLogger.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/TraceLogger.cs index 5f86237..6efbaf6 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/TraceLogger.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/TraceLogger.cs @@ -6,7 +6,9 @@ // We use the ICompatibleLogger interface to maintain a BCL-first style. // This allows for later replacement with logging libraries such as ILogger or Log4Net. // +using System; using System.Diagnostics; +using System.Linq; namespace WelsonJS.Launcher { @@ -21,7 +23,7 @@ namespace WelsonJS.Launcher _logFileName = (typeof(TraceLogger).Namespace ?? "WelsonJS.Launcher") + ".log"; Trace.Listeners.Add(new TextWriterTraceListener(_logFileName)); } - catch (System.Exception ex) + catch (Exception ex) { // Fallback when the process cannot write to the working directory Trace.Listeners.Add(new ConsoleTraceListener()); @@ -30,8 +32,27 @@ namespace WelsonJS.Launcher Trace.AutoFlush = true; } - public void Info(string message) => Trace.TraceInformation(message); - public void Warn(string message) => Trace.TraceWarning(message); - public void Error(string message) => Trace.TraceError(message); + public void Info(params object[] args) => Trace.TraceInformation(Format(args)); + public void Warn(params object[] args) => Trace.TraceWarning(Format(args)); + public void Error(params object[] args) => Trace.TraceError(Format(args)); + + private static string Format(object[] args) + { + if (args == null || args.Length == 0) return string.Empty; + + if (args.Length == 1) + return args[0]?.ToString() ?? string.Empty; + + string format = args[0]?.ToString() ?? string.Empty; + try + { + return string.Format(format, args.Skip(1).ToArray()); + } + catch + { + // In case of mismatched format placeholders + return string.Join(" ", args); + } + } } } diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj b/WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj index c76f31e..a5b2e8e 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/WelsonJS.Launcher.csproj @@ -89,8 +89,10 @@ + +