From 1cbc8194ec63a37d8c1b7cb7906a3454e1db469b Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 4 Jul 2025 16:03:00 -0500 Subject: [PATCH] feat: remove context file for storing values --- src/PepperDash.Core/Logging/Debug.cs | 1528 +++++++++-------- .../Logging/DebugWebsocketSink.cs | 578 +++---- 2 files changed, 1056 insertions(+), 1050 deletions(-) diff --git a/src/PepperDash.Core/Logging/Debug.cs b/src/PepperDash.Core/Logging/Debug.cs index 219af2d5..9a405b28 100644 --- a/src/PepperDash.Core/Logging/Debug.cs +++ b/src/PepperDash.Core/Logging/Debug.cs @@ -18,87 +18,88 @@ using System.Text.RegularExpressions; namespace PepperDash.Core; - /// - /// Contains debug commands for use in various situations - /// - public static class Debug +/// +/// Contains debug commands for use in various situations +/// +public static class Debug +{ + private static readonly string LevelStoreKey = "ConsoleDebugLevel"; + private static readonly string WebSocketLevelStoreKey = "WebsocketDebugLevel"; + private static readonly string ErrorLogLevelStoreKey = "ErrorLogDebugLevel"; + private static readonly string FileLevelStoreKey = "FileDebugLevel"; + private static readonly string DoNotLoadOnNextBootKey = "DoNotLoadOnNextBoot"; + + private static readonly Dictionary _logLevels = new Dictionary() { - private static readonly string LevelStoreKey = "ConsoleDebugLevel"; - private static readonly string WebSocketLevelStoreKey = "WebsocketDebugLevel"; - private static readonly string ErrorLogLevelStoreKey = "ErrorLogDebugLevel"; - private static readonly string FileLevelStoreKey = "FileDebugLevel"; + {0, LogEventLevel.Information }, + {3, LogEventLevel.Warning }, + {4, LogEventLevel.Error }, + {5, LogEventLevel.Fatal }, + {1, LogEventLevel.Debug }, + {2, LogEventLevel.Verbose }, + }; - private static readonly Dictionary _logLevels = new Dictionary() - { - {0, LogEventLevel.Information }, - {3, LogEventLevel.Warning }, - {4, LogEventLevel.Error }, - {5, LogEventLevel.Fatal }, - {1, LogEventLevel.Debug }, - {2, LogEventLevel.Verbose }, - }; + private static ILogger _logger; - private static ILogger _logger; + private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch; - private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch; + private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch; - private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch; + private static readonly LoggingLevelSwitch _errorLogLevelSwitch; - private static readonly LoggingLevelSwitch _errorLogLevelSwitch; + private static readonly LoggingLevelSwitch _fileLevelSwitch; - private static readonly LoggingLevelSwitch _fileLevelSwitch; + public static LogEventLevel WebsocketMinimumLogLevel + { + get { return _websocketLoggingLevelSwitch.MinimumLevel; } + } - public static LogEventLevel WebsocketMinimumLogLevel - { - get { return _websocketLoggingLevelSwitch.MinimumLevel; } - } + private static readonly DebugWebsocketSink _websocketSink; - private static readonly DebugWebsocketSink _websocketSink; + public static DebugWebsocketSink WebsocketSink + { + get { return _websocketSink; } + } - public static DebugWebsocketSink WebsocketSink - { - get { return _websocketSink; } - } + /// + /// Describes the folder location where a given program stores it's debug level memory. By default, the + /// file written will be named appNdebug where N is 1-10. + /// + public static string OldFilePathPrefix = @"\nvram\debug\"; - /// - /// Describes the folder location where a given program stores it's debug level memory. By default, the - /// file written will be named appNdebug where N is 1-10. - /// - public static string OldFilePathPrefix = @"\nvram\debug\"; + /// + /// Describes the new folder location where a given program stores it's debug level memory. By default, the + /// file written will be named appNdebug where N is 1-10. + /// + public static string NewFilePathPrefix = @"\user\debug\"; - /// - /// Describes the new folder location where a given program stores it's debug level memory. By default, the - /// file written will be named appNdebug where N is 1-10. - /// - public static string NewFilePathPrefix = @"\user\debug\"; + /// + /// The name of the file containing the current debug settings. + /// + public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber); - /// - /// The name of the file containing the current debug settings. - /// - public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber); + /// + /// Debug level to set for a given program. + /// + public static int Level { get; private set; } - /// - /// Debug level to set for a given program. - /// - public static int Level { get; private set; } + /// + /// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal + /// + public static bool DoNotLoadConfigOnNextBoot { get; private set; } - /// - /// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal - /// - public static bool DoNotLoadConfigOnNextBoot { get; private set; } + private static DebugContextCollection _contexts; - private static DebugContextCollection _contexts; + private const int SaveTimeoutMs = 30000; - private const int SaveTimeoutMs = 30000; + public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance; - public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance; + /// + /// Version for the currently loaded PepperDashCore dll + /// + public static string PepperDashCoreVersion { get; private set; } - /// - /// Version for the currently loaded PepperDashCore dll - /// - public static string PepperDashCoreVersion { get; private set; } - - private static CTimer _saveTimer; + private static CTimer _saveTimer; /// /// When true, the IncludedExcludedKeys dict will contain keys to include. @@ -110,360 +111,361 @@ namespace PepperDash.Core; private static readonly Dictionary IncludedExcludedKeys; - private static readonly LoggerConfiguration _defaultLoggerConfiguration; + private static readonly LoggerConfiguration _defaultLoggerConfiguration; - private static LoggerConfiguration _loggerConfiguration; + private static LoggerConfiguration _loggerConfiguration; - public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration; + public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration; - static Debug() + static Debug() + { + try + { + CrestronDataStoreStatic.InitCrestronDataStore(); + + var defaultConsoleLevel = GetStoredLogEventLevel(LevelStoreKey); + + var defaultWebsocketLevel = GetStoredLogEventLevel(WebSocketLevelStoreKey); + + var defaultErrorLogLevel = GetStoredLogEventLevel(ErrorLogLevelStoreKey); + + var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey); + + _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel); + + _websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel); + + _errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel); + + _fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel); + + _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true)); + + var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? + $@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" : + $@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log"; + + CrestronConsole.PrintLine($"Saving log files to {logFilePath}"); + + var errorLogTemplate = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance + ? "{@t:fff}ms [{@l:u4}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}" + : "[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}"; + + _defaultLoggerConfiguration = new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.FromLogContext() + .Enrich.With(new CrestronEnricher()) + .WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch) + .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch) + .WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch) + .WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath, + rollingInterval: RollingInterval.Day, + restrictedToMinimumLevel: LogEventLevel.Debug, + retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60, + levelSwitch: _fileLevelSwitch + ); + + // Instantiate the root logger + _loggerConfiguration = _defaultLoggerConfiguration; + + _logger = _loggerConfiguration.CreateLogger(); + // Get the assembly version and print it to console and the log + GetVersion(); + + string msg = $"[App {InitialParametersClass.ApplicationNumber}] Using PepperDash_Core v{PepperDashCoreVersion}"; + + if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) + { + msg = $"[Room {InitialParametersClass.RoomId}] Using PepperDash_Core v{PepperDashCoreVersion}"; + } + + CrestronConsole.PrintLine(msg); + + LogMessage(LogEventLevel.Information, msg); + + IncludedExcludedKeys = new Dictionary(); + + if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro) + { + // Add command to console + CrestronConsole.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, "donotloadonnextboot", + "donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator); + + CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug", + "appdebug:P [0-5]: Sets the application's console debug message level", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog", + "appdebuglog:P [all] Use \"all\" for full log.", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear", + "appdebugclear:P Clears the current custom log", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter", + "appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator); + } + + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; + + DoNotLoadConfigOnNextBoot = GetDoNotLoadOnNextBoot(); + + if (DoNotLoadConfigOnNextBoot) + CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber)); + + _consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) => + { + LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", _consoleLoggingLevelSwitch.MinimumLevel); + }; + } + catch (Exception ex) { - CrestronDataStoreStatic.InitCrestronDataStore(); - - var defaultConsoleLevel = GetStoredLogEventLevel(LevelStoreKey); - - var defaultWebsocketLevel = GetStoredLogEventLevel(WebSocketLevelStoreKey); - - var defaultErrorLogLevel = GetStoredLogEventLevel(ErrorLogLevelStoreKey); - - var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey); - - _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel); - - _websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel); - - _errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel); - - _fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel); - - _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true)); - - var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? - $@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" : - $@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log"; - - CrestronConsole.PrintLine($"Saving log files to {logFilePath}"); - - var errorLogTemplate = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance - ? "{@t:fff}ms [{@l:u4}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}" - : "[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}"; - - _defaultLoggerConfiguration = new LoggerConfiguration() - .MinimumLevel.Verbose() - .Enrich.FromLogContext() - .Enrich.With(new CrestronEnricher()) - .WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch) - .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch) - .WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch) - .WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath, - rollingInterval: RollingInterval.Day, - restrictedToMinimumLevel: LogEventLevel.Debug, - retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60, - levelSwitch: _fileLevelSwitch - ); - - try - { - if (InitialParametersClass.NumberOfRemovableDrives > 0) - { - CrestronConsole.PrintLine("{0} RM Drive(s) Present. Initializing CrestronLogger", InitialParametersClass.NumberOfRemovableDrives); - _defaultLoggerConfiguration.WriteTo.Sink(new DebugCrestronLoggerSink()); - } - else - CrestronConsole.PrintLine("No RM Drive(s) Present. Not using Crestron Logger"); - } - catch (Exception e) - { - CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e); - } - - // Instantiate the root logger - _loggerConfiguration = _defaultLoggerConfiguration; - - _logger = _loggerConfiguration.CreateLogger(); - // Get the assembly version and print it to console and the log - GetVersion(); - - string msg = $"[App {InitialParametersClass.ApplicationNumber}] Using PepperDash_Core v{PepperDashCoreVersion}"; - - if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) - { - msg = $"[Room {InitialParametersClass.RoomId}] Using PepperDash_Core v{PepperDashCoreVersion}"; - } - - CrestronConsole.PrintLine(msg); - - LogMessage(LogEventLevel.Information,msg); - - IncludedExcludedKeys = new Dictionary(); - - if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro) - { - // Add command to console - CrestronConsole.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, "donotloadonnextboot", - "donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator); - - CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug", - "appdebug:P [0-5]: Sets the application's console debug message level", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog", - "appdebuglog:P [all] Use \"all\" for full log.", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear", - "appdebugclear:P Clears the current custom log", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter", - "appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator); - } - - CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; - - LoadMemory(); - - var context = _contexts.GetOrCreateItem("DEFAULT"); - Level = context.Level; - DoNotLoadConfigOnNextBoot = context.DoNotLoadOnNextBoot; - - if(DoNotLoadConfigOnNextBoot) - CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber)); - - _consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) => - { - LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", _consoleLoggingLevelSwitch.MinimumLevel); - }; + LogError(ex, "Exception in Debug static constructor: {message}", ex.Message); } + } - public static void UpdateLoggerConfiguration(LoggerConfiguration config) + private static bool GetDoNotLoadOnNextBoot() + { + var err = CrestronDataStoreStatic.GetLocalBoolValue(DoNotLoadOnNextBootKey, out var doNotLoad); + + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + { + LogError("Error retrieving DoNotLoadOnNextBoot value: {err}", err); + doNotLoad = false; + } + + return doNotLoad; + } + + public static void UpdateLoggerConfiguration(LoggerConfiguration config) + { + _loggerConfiguration = config; + + _logger = config.CreateLogger(); + } + + public static void ResetLoggerConfiguration() + { + _loggerConfiguration = _defaultLoggerConfiguration; + + _logger = _loggerConfiguration.CreateLogger(); + } + + private static LogEventLevel GetStoredLogEventLevel(string levelStoreKey) + { + try { - _loggerConfiguration = config; + var result = CrestronDataStoreStatic.GetLocalIntValue(levelStoreKey, out int logLevel); - _logger = config.CreateLogger(); - } - - public static void ResetLoggerConfiguration() - { - _loggerConfiguration = _defaultLoggerConfiguration; - - _logger = _loggerConfiguration.CreateLogger(); - } - - private static LogEventLevel GetStoredLogEventLevel(string levelStoreKey) - { - try + if (result != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) { - var result = CrestronDataStoreStatic.GetLocalIntValue(levelStoreKey, out int logLevel); - - if (result != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - { - CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n"); - return LogEventLevel.Information; - } - - if(logLevel < 0 || logLevel > 5) - { - CrestronConsole.PrintLine($"Stored Log level not valid for {levelStoreKey}: {logLevel}. Setting level to {LogEventLevel.Information}"); - return LogEventLevel.Information; - } - - return (LogEventLevel)logLevel; - } catch (Exception ex) - { - CrestronConsole.PrintLine($"Exception retrieving log level for {levelStoreKey}: {ex.Message}"); + CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n"); return LogEventLevel.Information; } - } - private static void GetVersion() - { - var assembly = Assembly.GetExecutingAssembly(); - var ver = - assembly - .GetCustomAttributes(typeof (AssemblyInformationalVersionAttribute), false); - - if (ver != null && ver.Length > 0) + if(logLevel < 0 || logLevel > 5) { - if (ver[0] is AssemblyInformationalVersionAttribute verAttribute) - { - PepperDashCoreVersion = verAttribute.InformationalVersion; - } + CrestronConsole.PrintLine($"Stored Log level not valid for {levelStoreKey}: {logLevel}. Setting level to {LogEventLevel.Information}"); + return LogEventLevel.Information; } - else + + return (LogEventLevel)logLevel; + } catch (Exception ex) + { + CrestronConsole.PrintLine($"Exception retrieving log level for {levelStoreKey}: {ex.Message}"); + return LogEventLevel.Information; + } + } + + private static void GetVersion() + { + var assembly = Assembly.GetExecutingAssembly(); + var ver = + assembly + .GetCustomAttributes(typeof (AssemblyInformationalVersionAttribute), false); + + if (ver != null && ver.Length > 0) + { + if (ver[0] is AssemblyInformationalVersionAttribute verAttribute) { - var version = assembly.GetName().Version; - PepperDashCoreVersion = version.ToString(); + PepperDashCoreVersion = verAttribute.InformationalVersion; } } - - /// - /// Used to save memory when shutting down - /// - /// - static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + else { - - if (programEventType == eProgramStatusEventType.Stopping) - { - Log.CloseAndFlush(); - - if (_saveTimer != null) - { - _saveTimer.Stop(); - _saveTimer = null; - } - LogMessage(LogEventLevel.Information, "Saving debug settings"); - SaveMemory(); - } + var version = assembly.GetName().Version; + PepperDashCoreVersion = version.ToString(); } + } - /// - /// Callback for console command - /// - /// - public static void SetDebugFromConsole(string levelString) + /// + /// Used to save memory when shutting down + /// + /// + static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + + if (programEventType == eProgramStatusEventType.Stopping) { - try + Log.CloseAndFlush(); + + if (_saveTimer != null) { - if (levelString.Trim() == "?") - { - CrestronConsole.ConsoleCommandResponse( - $@"Used to set the minimum level of debug messages to be printed to the console: + _saveTimer.Stop(); + _saveTimer = null; + } + LogMessage(LogEventLevel.Information, "Saving debug settings"); + SaveMemory(); + } + } + + /// + /// Callback for console command + /// + /// + public static void SetDebugFromConsole(string levelString) + { + try + { + if (levelString.Trim() == "?") + { + CrestronConsole.ConsoleCommandResponse( + $@"Used to set the minimum level of debug messages to be printed to the console: {_logLevels[0]} = 0 {_logLevels[1]} = 1 {_logLevels[2]} = 2 {_logLevels[3]} = 3 {_logLevels[4]} = 4 {_logLevels[5]} = 5"); - return; - } - - if (string.IsNullOrEmpty(levelString.Trim())) - { - CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel); - return; - } - - if(int.TryParse(levelString, out var levelInt)) - { - if(levelInt < 0 || levelInt > 5) - { - CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level. If using a number, value must be between 0-5"); - return; - } - SetDebugLevel((uint) levelInt); - return; - } - - if(Enum.TryParse(levelString, out var levelEnum)) - { - SetDebugLevel(levelEnum); - return; - } - - CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level"); - } - catch - { - CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]"); + return; } - } - /// - /// Sets the debug level - /// - /// Valid values 0-5 - public static void SetDebugLevel(uint level) + if (string.IsNullOrEmpty(levelString.Trim())) + { + CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel); + return; + } + + if(int.TryParse(levelString, out var levelInt)) + { + if(levelInt < 0 || levelInt > 5) + { + CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level. If using a number, value must be between 0-5"); + return; + } + SetDebugLevel((uint) levelInt); + return; + } + + if(Enum.TryParse(levelString, out var levelEnum)) + { + SetDebugLevel(levelEnum); + return; + } + + CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level"); + } + catch { - if(!_logLevels.TryGetValue(level, out var logLevel)) - { - logLevel = LogEventLevel.Information; + CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]"); + } + } - CrestronConsole.ConsoleCommandResponse($"{level} not valid. Setting level to {logLevel}"); + /// + /// Sets the debug level + /// + /// Valid values 0-5 + public static void SetDebugLevel(uint level) + { + if(!_logLevels.TryGetValue(level, out var logLevel)) + { + logLevel = LogEventLevel.Information; - SetDebugLevel(logLevel); - } + CrestronConsole.ConsoleCommandResponse($"{level} not valid. Setting level to {logLevel}"); SetDebugLevel(logLevel); } - public static void SetDebugLevel(LogEventLevel level) + SetDebugLevel(logLevel); + } + + public static void SetDebugLevel(LogEventLevel level) + { + _consoleLoggingLevelSwitch.MinimumLevel = level; + + CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}\r\n", + InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel); + + CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int) level}"); + + var err = CrestronDataStoreStatic.SetLocalIntValue(LevelStoreKey, (int) level); + + CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}"); + + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + CrestronConsole.PrintLine($"Error saving console debug level setting: {err}"); + } + + public static void SetWebSocketMinimumDebugLevel(LogEventLevel level) + { + _websocketLoggingLevelSwitch.MinimumLevel = level; + + var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint) level); + + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err); + + LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); + } + + public static void SetErrorLogMinimumDebugLevel(LogEventLevel level) + { + _errorLogLevelSwitch.MinimumLevel = level; + + var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level); + + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting: {error}", err); + + LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); + } + + public static void SetFileMinimumDebugLevel(LogEventLevel level) + { + _errorLogLevelSwitch.MinimumLevel = level; + + var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level); + + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err); + + LogMessage(LogEventLevel.Information, "File debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); + } + + /// + /// Callback for console command + /// + /// + public static void SetDoNotLoadOnNextBootFromConsole(string stateString) + { + try { - _consoleLoggingLevelSwitch.MinimumLevel = level; - - CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}\r\n", - InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel); - - CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int) level}"); - - var err = CrestronDataStoreStatic.SetLocalIntValue(LevelStoreKey, (int) level); - - CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}"); - - if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - CrestronConsole.PrintLine($"Error saving console debug level setting: {err}"); - } - - public static void SetWebSocketMinimumDebugLevel(LogEventLevel level) - { - _websocketLoggingLevelSwitch.MinimumLevel = level; - - var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint) level); - - if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err); - - LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); - } - - public static void SetErrorLogMinimumDebugLevel(LogEventLevel level) - { - _errorLogLevelSwitch.MinimumLevel = level; - - var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level); - - if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting: {error}", err); - - LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); - } - - public static void SetFileMinimumDebugLevel(LogEventLevel level) - { - _errorLogLevelSwitch.MinimumLevel = level; - - var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level); - - if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err); - - LogMessage(LogEventLevel.Information, "File debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); - } - - /// - /// Callback for console command - /// - /// - public static void SetDoNotLoadOnNextBootFromConsole(string stateString) - { - try + if (string.IsNullOrEmpty(stateString.Trim())) { - if (string.IsNullOrEmpty(stateString.Trim())) - { - CrestronConsole.ConsoleCommandResponse("DoNotLoadOnNextBoot = {0}", DoNotLoadConfigOnNextBoot); - return; - } + CrestronConsole.ConsoleCommandResponse("DoNotLoadOnNextBoot = {0}", DoNotLoadConfigOnNextBoot); + return; + } - SetDoNotLoadConfigOnNextBoot(bool.Parse(stateString)); - } - catch - { - CrestronConsole.ConsoleCommandResponse("Usage: donotloadonnextboot:P [true/false]"); - } + SetDoNotLoadConfigOnNextBoot(bool.Parse(stateString)); } + catch + { + CrestronConsole.ConsoleCommandResponse("Usage: donotloadonnextboot:P [true/false]"); + } + } - /// - /// Callback for console command - /// - /// + /// + /// Callback for console command + /// + /// public static void SetDebugFilterFromConsole(string items) { var str = items.Trim(); @@ -542,486 +544,488 @@ namespace PepperDash.Core; - /// - /// sets the settings for a device or creates a new entry - /// - /// - /// - /// - public static void SetDeviceDebugSettings(string deviceKey, object settings) + /// + /// sets the settings for a device or creates a new entry + /// + /// + /// + /// + public static void SetDeviceDebugSettings(string deviceKey, object settings) + { + _contexts.SetDebugSettingsForKey(deviceKey, settings); + SaveMemoryOnTimeout(); + } + + /// + /// Gets the device settings for a device by key or returns null + /// + /// + /// + public static object GetDeviceDebugSettingsForKey(string deviceKey) + { + return _contexts.GetDebugSettingsForKey(deviceKey); + } + + /// + /// Sets the flag to prevent application starting on next boot + /// + /// + public static void SetDoNotLoadConfigOnNextBoot(bool state) + { + DoNotLoadConfigOnNextBoot = state; + + var err = CrestronDataStoreStatic.SetLocalBoolValue(DoNotLoadOnNextBootKey, state); + + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + LogError("Error saving console debug level setting: {err}", err); + + LogInformation("Do Not Load Config on Next Boot set to {state}", DoNotLoadConfigOnNextBoot); + } + + /// + /// + /// + public static void ShowDebugLog(string s) + { + var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all"); + foreach (var l in loglist) + CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine); + } + + /// + /// Log an Exception as an Error + /// + /// Exception to log + /// Message template + /// Optional IKeyed device. If provided, the Key of the device will be added to the log message + /// Args to put into message template + public static void LogMessage(Exception ex, string message, IKeyed device = null, params object[] args) + { + using (LogContext.PushProperty("Key", device?.Key)) { - _contexts.SetDebugSettingsForKey(deviceKey, settings); - SaveMemoryOnTimeout(); + _logger.Error(ex, message, args); } + } - /// - /// Gets the device settings for a device by key or returns null - /// - /// - /// - public static object GetDeviceDebugSettingsForKey(string deviceKey) + /// + /// Log a message + /// + /// Level to log at + /// Message template + /// Optional IKeyed device. If provided, the Key of the device will be added to the log message + /// Args to put into message template + public static void LogMessage(LogEventLevel level, string message, IKeyed device = null, params object[] args) + { + using (LogContext.PushProperty("Key", device?.Key)) { - return _contexts.GetDebugSettingsForKey(deviceKey); - } - - /// - /// Sets the flag to prevent application starting on next boot - /// - /// - public static void SetDoNotLoadConfigOnNextBoot(bool state) - { - DoNotLoadConfigOnNextBoot = state; - _contexts.GetOrCreateItem("DEFAULT").DoNotLoadOnNextBoot = state; - SaveMemoryOnTimeout(); - - CrestronConsole.ConsoleCommandResponse("[Application {0}], Do Not Load Config on Next Boot set to {1}", - InitialParametersClass.ApplicationNumber, DoNotLoadConfigOnNextBoot); + _logger.Write(level, message, args); } + } - /// - /// - /// - public static void ShowDebugLog(string s) - { - var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all"); - foreach (var l in loglist) - CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine); - } + public static void LogMessage(LogEventLevel level, string message, params object[] args) + { + _logger.Write(level, message, args); + } - /// - /// Log an Exception as an Error - /// - /// Exception to log - /// Message template - /// Optional IKeyed device. If provided, the Key of the device will be added to the log message - /// Args to put into message template - public static void LogMessage(Exception ex, string message, IKeyed device = null, params object[] args) - { - using (LogContext.PushProperty("Key", device?.Key)) - { - _logger.Error(ex, message, args); - } - } + public static void LogMessage(LogEventLevel level, Exception ex, string message, params object[] args) + { + _logger.Write(level, ex, message, args); + } - /// - /// Log a message - /// - /// Level to log at - /// Message template - /// Optional IKeyed device. If provided, the Key of the device will be added to the log message - /// Args to put into message template - public static void LogMessage(LogEventLevel level, string message, IKeyed device = null, params object[] args) - { - using (LogContext.PushProperty("Key", device?.Key)) - { - _logger.Write(level, message, args); - } - } + public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args) + { + LogMessage(level, message, keyed, args); + } - public static void LogMessage(LogEventLevel level, string message, params object[] args) - { - _logger.Write(level, message, args); - } - - public static void LogMessage(LogEventLevel level, Exception ex, string message, params object[] args) + public static void LogMessage(LogEventLevel level, Exception ex, IKeyed device, string message, params object[] args) + { + using (LogContext.PushProperty("Key", device?.Key)) { _logger.Write(level, ex, message, args); } + } - public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args) - { - LogMessage(level, message, keyed, args); - } - - public static void LogMessage(LogEventLevel level, Exception ex, IKeyed device, string message, params object[] args) - { - using (LogContext.PushProperty("Key", device?.Key)) - { - _logger.Write(level, ex, message, args); - } - } - - #region Explicit methods for logging levels - public static void LogVerbose(IKeyed keyed, string message, params object[] args) - { - using(LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Verbose, message, args); - } - } - - public static void LogVerbose(Exception ex, IKeyed keyed, string message, params object[] args) - { - using(LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Verbose, ex, message, args); - } - } - - public static void LogVerbose(string message, params object[] args) + #region Explicit methods for logging levels + public static void LogVerbose(IKeyed keyed, string message, params object[] args) + { + using(LogContext.PushProperty("Key", keyed?.Key)) { _logger.Write(LogEventLevel.Verbose, message, args); } + } - public static void LogVerbose(Exception ex, string message, params object[] args) + public static void LogVerbose(Exception ex, IKeyed keyed, string message, params object[] args) + { + using(LogContext.PushProperty("Key", keyed?.Key)) { - _logger.Write(LogEventLevel.Verbose, ex, null, message, args); + _logger.Write(LogEventLevel.Verbose, ex, message, args); } + } - public static void LogDebug(IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Debug, message, args); - } - } + public static void LogVerbose(string message, params object[] args) + { + _logger.Write(LogEventLevel.Verbose, message, args); + } - public static void LogDebug(Exception ex, IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Debug, ex, message, args); - } - } + public static void LogVerbose(Exception ex, string message, params object[] args) + { + _logger.Write(LogEventLevel.Verbose, ex, null, message, args); + } - public static void LogDebug(string message, params object[] args) + public static void LogDebug(IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { _logger.Write(LogEventLevel.Debug, message, args); } + } - public static void LogDebug(Exception ex, string message, params object[] args) + public static void LogDebug(Exception ex, IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { - _logger.Write(LogEventLevel.Debug, ex, null, message, args); + _logger.Write(LogEventLevel.Debug, ex, message, args); } + } - public static void LogInformation(IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Information, message, args); - } - } + public static void LogDebug(string message, params object[] args) + { + _logger.Write(LogEventLevel.Debug, message, args); + } - public static void LogInformation(Exception ex, IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Information, ex, message, args); - } - } + public static void LogDebug(Exception ex, string message, params object[] args) + { + _logger.Write(LogEventLevel.Debug, ex, null, message, args); + } - public static void LogInformation(string message, params object[] args) + public static void LogInformation(IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { _logger.Write(LogEventLevel.Information, message, args); } + } - public static void LogInformation(Exception ex, string message, params object[] args) + public static void LogInformation(Exception ex, IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { - _logger.Write(LogEventLevel.Information, ex, null, message, args); + _logger.Write(LogEventLevel.Information, ex, message, args); } + } - public static void LogWarning(IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Warning, message, args); - } - } + public static void LogInformation(string message, params object[] args) + { + _logger.Write(LogEventLevel.Information, message, args); + } - public static void LogWarning(Exception ex, IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Warning, ex, message, args); - } - } + public static void LogInformation(Exception ex, string message, params object[] args) + { + _logger.Write(LogEventLevel.Information, ex, null, message, args); + } - public static void LogWarning(string message, params object[] args) + public static void LogWarning(IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { _logger.Write(LogEventLevel.Warning, message, args); } + } - public static void LogWarning(Exception ex, string message, params object[] args) + public static void LogWarning(Exception ex, IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { - _logger.Write(LogEventLevel.Warning, ex, null, message, args); + _logger.Write(LogEventLevel.Warning, ex, message, args); } + } - public static void LogError(IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Error, message, args); - } - } + public static void LogWarning(string message, params object[] args) + { + _logger.Write(LogEventLevel.Warning, message, args); + } - public static void LogError(Exception ex, IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Error, ex, message, args); - } - } + public static void LogWarning(Exception ex, string message, params object[] args) + { + _logger.Write(LogEventLevel.Warning, ex, null, message, args); + } - public static void LogError(string message, params object[] args) + public static void LogError(IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { _logger.Write(LogEventLevel.Error, message, args); } + } - public static void LogError(Exception ex, string message, params object[] args) + public static void LogError(Exception ex, IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { - _logger.Write(LogEventLevel.Error, ex, null, message, args); + _logger.Write(LogEventLevel.Error, ex, message, args); } + } - public static void LogFatal(IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Fatal, message, args); - } - } + public static void LogError(string message, params object[] args) + { + _logger.Write(LogEventLevel.Error, message, args); + } - public static void LogFatal(Exception ex, IKeyed keyed, string message, params object[] args) - { - using (LogContext.PushProperty("Key", keyed?.Key)) - { - _logger.Write(LogEventLevel.Fatal, ex, message, args); - } - } + public static void LogError(Exception ex, string message, params object[] args) + { + _logger.Write(LogEventLevel.Error, ex, null, message, args); + } - public static void LogFatal(string message, params object[] args) + public static void LogFatal(IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { _logger.Write(LogEventLevel.Fatal, message, args); } + } - public static void LogFatal(Exception ex, string message, params object[] args) + public static void LogFatal(Exception ex, IKeyed keyed, string message, params object[] args) + { + using (LogContext.PushProperty("Key", keyed?.Key)) { - _logger.Write(LogEventLevel.Fatal, ex, null, message, args); + _logger.Write(LogEventLevel.Fatal, ex, message, args); } + } - #endregion + public static void LogFatal(string message, params object[] args) + { + _logger.Write(LogEventLevel.Fatal, message, args); + } + + public static void LogFatal(Exception ex, string message, params object[] args) + { + _logger.Write(LogEventLevel.Fatal, ex, null, message, args); + } + + #endregion - private static void LogMessage(uint level, string format, params object[] items) - { - if (!_logLevels.ContainsKey(level)) return; + private static void LogMessage(uint level, string format, params object[] items) + { + if (!_logLevels.ContainsKey(level)) return; - var logLevel = _logLevels[level]; - - LogMessage(logLevel, format, items); - } + var logLevel = _logLevels[level]; + + LogMessage(logLevel, format, items); + } - private static void LogMessage(uint level, IKeyed keyed, string format, params object[] items) - { - if (!_logLevels.ContainsKey(level)) return; + private static void LogMessage(uint level, IKeyed keyed, string format, params object[] items) + { + if (!_logLevels.ContainsKey(level)) return; - var logLevel = _logLevels[level]; + var logLevel = _logLevels[level]; - LogMessage(logLevel, keyed, format, items); - } + LogMessage(logLevel, keyed, format, items); + } - /// - /// Prints message to console if current debug level is equal to or higher than the level of this message. - /// Uses CrestronConsole.PrintLine. - /// - /// - /// Console format string - /// Object parameters - [Obsolete("Use LogMessage methods. Will be removed in 2.2.0 and later versions")] - public static void Console(uint level, string format, params object[] items) - { + /// + /// Prints message to console if current debug level is equal to or higher than the level of this message. + /// Uses CrestronConsole.PrintLine. + /// + /// + /// Console format string + /// Object parameters + [Obsolete("Use LogMessage methods. Will be removed in 2.2.0 and later versions")] + public static void Console(uint level, string format, params object[] items) + { - LogMessage(level, format, items); + LogMessage(level, format, items); - //if (IsRunningOnAppliance) - //{ - // CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"), - // InitialParametersClass.ApplicationNumber, - // level, - // string.Format(format, items)); - //} - } + //if (IsRunningOnAppliance) + //{ + // CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"), + // InitialParametersClass.ApplicationNumber, + // level, + // string.Format(format, items)); + //} + } - /// + /// /// Logs to Console when at-level, and all messages to error log, including device key - /// - [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] - public static void Console(uint level, IKeyed dev, string format, params object[] items) + /// + [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] + public static void Console(uint level, IKeyed dev, string format, params object[] items) + { + LogMessage(level, dev, format, items); + + //if (Level >= level) + // Console(level, "[{0}] {1}", dev.Key, message); + } + + /// + /// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log. + /// Uses CrestronConsole.PrintLine. + /// + [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] + public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, + string format, params object[] items) + { + LogMessage(level, dev, format, items); + } + + /// + /// Logs to Console when at-level, and all messages to error log + /// + [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] + public static void Console(uint level, ErrorLogLevel errorLogLevel, + string format, params object[] items) + { + LogMessage(level, format, items); + } + + /// + /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at + /// or above the level provided, then the output will be written to both console and the log. Otherwise + /// it will only be written to the log. + /// + [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] + public static void ConsoleWithLog(uint level, string format, params object[] items) + { + LogMessage(level, format, items); + + // var str = string.Format(format, items); + //if (Level >= level) + // CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str); + // CrestronLogger.WriteToLog(str, level); + } + + /// + /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at + /// or above the level provided, then the output will be written to both console and the log. Otherwise + /// it will only be written to the log. + /// + [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] + public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items) + { + LogMessage(level, dev, format, items); + + // var str = string.Format(format, items); + // CrestronLogger.WriteToLog(string.Format("[{0}] {1}", dev.Key, str), level); + } + + /// + /// Prints to log and error log + /// + /// + /// + [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] + public static void LogError(ErrorLogLevel errorLogLevel, string str) + { + switch (errorLogLevel) { - LogMessage(level, dev, format, items); - - //if (Level >= level) - // Console(level, "[{0}] {1}", dev.Key, message); + case ErrorLogLevel.Error: + LogMessage(LogEventLevel.Error, str); + break; + case ErrorLogLevel.Warning: + LogMessage(LogEventLevel.Warning, str); + break; + case ErrorLogLevel.Notice: + LogMessage(LogEventLevel.Information, str); + break; } + } - /// - /// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log. - /// Uses CrestronConsole.PrintLine. - /// - [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] - public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, - string format, params object[] items) - { - LogMessage(level, dev, format, items); - } - - /// - /// Logs to Console when at-level, and all messages to error log - /// - [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] - public static void Console(uint level, ErrorLogLevel errorLogLevel, - string format, params object[] items) - { - LogMessage(level, format, items); - } - - /// - /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at - /// or above the level provided, then the output will be written to both console and the log. Otherwise - /// it will only be written to the log. - /// - [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] - public static void ConsoleWithLog(uint level, string format, params object[] items) - { - LogMessage(level, format, items); - - // var str = string.Format(format, items); - //if (Level >= level) - // CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str); - // CrestronLogger.WriteToLog(str, level); - } - - /// - /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at - /// or above the level provided, then the output will be written to both console and the log. Otherwise - /// it will only be written to the log. - /// - [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] - public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items) - { - LogMessage(level, dev, format, items); - - // var str = string.Format(format, items); - // CrestronLogger.WriteToLog(string.Format("[{0}] {1}", dev.Key, str), level); - } - - /// - /// Prints to log and error log - /// - /// - /// - [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] - public static void LogError(ErrorLogLevel errorLogLevel, string str) - { - switch (errorLogLevel) + /// + /// Writes the memory object after timeout + /// + static void SaveMemoryOnTimeout() + { + Console(0, "Saving debug settings"); + if (_saveTimer == null) + _saveTimer = new CTimer(o => { - case ErrorLogLevel.Error: - LogMessage(LogEventLevel.Error, str); - break; - case ErrorLogLevel.Warning: - LogMessage(LogEventLevel.Warning, str); - break; - case ErrorLogLevel.Notice: - LogMessage(LogEventLevel.Information, str); - break; - } - } + _saveTimer = null; + SaveMemory(); + }, SaveTimeoutMs); + else + _saveTimer.Reset(SaveTimeoutMs); + } - /// - /// Writes the memory object after timeout - /// - static void SaveMemoryOnTimeout() + /// + /// Writes the memory - use SaveMemoryOnTimeout + /// + static void SaveMemory() + { + //var dir = @"\NVRAM\debug"; + //if (!Directory.Exists(dir)) + // Directory.Create(dir); + + var fileName = GetMemoryFileName(); + + LogMessage(LogEventLevel.Information, "Loading debug settings file from {fileName}", fileName); + + using (var sw = new StreamWriter(fileName)) { - Console(0, "Saving debug settings"); - if (_saveTimer == null) - _saveTimer = new CTimer(o => + var json = JsonConvert.SerializeObject(_contexts); + sw.Write(json); + sw.Flush(); + } + } + + /// + /// + /// + static void LoadMemory() + { + var file = GetMemoryFileName(); + if (File.Exists(file)) + { + using (var sr = new StreamReader(file)) + { + var json = sr.ReadToEnd(); + _contexts = JsonConvert.DeserializeObject(json); + + if (_contexts != null) { - _saveTimer = null; - SaveMemory(); - }, SaveTimeoutMs); - else - _saveTimer.Reset(SaveTimeoutMs); - } - - /// - /// Writes the memory - use SaveMemoryOnTimeout - /// - static void SaveMemory() - { - //var dir = @"\NVRAM\debug"; - //if (!Directory.Exists(dir)) - // Directory.Create(dir); - - var fileName = GetMemoryFileName(); - - LogMessage(LogEventLevel.Information, "Loading debug settings file from {fileName}", fileName); - - using (var sw = new StreamWriter(fileName)) - { - var json = JsonConvert.SerializeObject(_contexts); - sw.Write(json); - sw.Flush(); - } - } - - /// - /// - /// - static void LoadMemory() - { - var file = GetMemoryFileName(); - if (File.Exists(file)) - { - using (var sr = new StreamReader(file)) - { - var json = sr.ReadToEnd(); - _contexts = JsonConvert.DeserializeObject(json); - - if (_contexts != null) - { - LogMessage(LogEventLevel.Debug, "Debug memory restored from file"); - return; - } + LogMessage(LogEventLevel.Debug, "Debug memory restored from file"); + return; } } - - _contexts = new DebugContextCollection(); } - /// - /// Helper to get the file path for this app's debug memory - /// - static string GetMemoryFileName() - { - if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance) - { - // CheckForMigration(); - return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber); - } + _contexts = new DebugContextCollection(); + } - return string.Format("{0}{1}user{1}debugSettings{1}{2}.json",Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId); + /// + /// Helper to get the file path for this app's debug memory + /// + static string GetMemoryFileName() + { + if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance) + { + // CheckForMigration(); + return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber); } + return string.Format("{0}{1}user{1}debugSettings{1}{2}.json",Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId); + } + + /// + /// Error level to for message to be logged at + /// + public enum ErrorLogLevel + { /// - /// Error level to for message to be logged at + /// Error /// - public enum ErrorLogLevel - { - /// - /// Error - /// - Error, - /// - /// Warning - /// - Warning, - /// - /// Notice - /// - Notice, - /// - /// None - /// - None, + Error, + /// + /// Warning + /// + Warning, + /// + /// Notice + /// + Notice, + /// + /// None + /// + None, } } \ No newline at end of file diff --git a/src/PepperDash.Core/Logging/DebugWebsocketSink.cs b/src/PepperDash.Core/Logging/DebugWebsocketSink.cs index af479cfa..d80d1c7b 100644 --- a/src/PepperDash.Core/Logging/DebugWebsocketSink.cs +++ b/src/PepperDash.Core/Logging/DebugWebsocketSink.cs @@ -15,316 +15,318 @@ using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certi namespace PepperDash.Core; - /// - /// Provides a WebSocket-based logging sink for debugging purposes, allowing log events to be broadcast to connected - /// WebSocket clients. +/// +/// Provides a WebSocket-based logging sink for debugging purposes, allowing log events to be broadcast to connected +/// WebSocket clients. +/// +/// This class implements the interface and is designed to send +/// formatted log events to WebSocket clients connected to a secure WebSocket server. The server is hosted locally +/// and uses a self-signed certificate for SSL/TLS encryption. +public class DebugWebsocketSink : ILogEventSink, IKeyed +{ + private HttpServer _httpsServer; + + private readonly string _path = "/debug/join/"; + private const string _certificateName = "selfCres"; + private const string _certificatePassword = "cres12345"; + + /// + /// Gets the port number on which the HTTPS server is currently running. /// - /// This class implements the interface and is designed to send - /// formatted log events to WebSocket clients connected to a secure WebSocket server. The server is hosted locally - /// and uses a self-signed certificate for SSL/TLS encryption. - public class DebugWebsocketSink : ILogEventSink, IKeyed + public int Port + { get + { + + if(_httpsServer == null) return 0; + return _httpsServer.Port; + } + } + + /// + /// Gets the WebSocket URL for the current server instance. + /// + /// The URL is dynamically constructed based on the server's current IP address, port, + /// and WebSocket path. + public string Url { - private HttpServer _httpsServer; - - private readonly string _path = "/debug/join/"; - private const string _certificateName = "selfCres"; - private const string _certificatePassword = "cres12345"; - - /// - /// Gets the port number on which the HTTPS server is currently running. - /// - public int Port - { get - { - - if(_httpsServer == null) return 0; - return _httpsServer.Port; - } - } - - /// - /// Gets the WebSocket URL for the current server instance. - /// - /// The URL is dynamically constructed based on the server's current IP address, port, - /// and WebSocket path. - public string Url + get { - get + if (_httpsServer == null) return ""; + return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{_httpsServer.WebSocketServices[_path].Path}"; + } + } + + /// + /// Gets a value indicating whether the HTTPS server is currently listening for incoming connections. + /// + public bool IsRunning { get => _httpsServer?.IsListening ?? false; } + + /// + public string Key => "DebugWebsocketSink"; + + private readonly ITextFormatter _textFormatter; + + /// + /// Initializes a new instance of the class with the specified text formatter. + /// + /// This constructor initializes the WebSocket sink and ensures that a certificate is + /// available for secure communication. If the required certificate does not exist, it will be created + /// automatically. Additionally, the sink is configured to stop the server when the program is + /// stopping. + /// The text formatter used to format log messages. If null, a default JSON formatter is used. + public DebugWebsocketSink(ITextFormatter formatProvider) + { + + _textFormatter = formatProvider ?? new JsonFormatter(); + + if (!File.Exists($"\\user\\{_certificateName}.pfx")) + CreateCert(); + + CrestronEnvironment.ProgramStatusEventHandler += type => + { + if (type == eProgramStatusEventType.Stopping) { - if (_httpsServer == null) return ""; - return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{_httpsServer.WebSocketServices[_path].Path}"; + StopServer(); } + }; + } + + private static void CreateCert() + { + try + { + var utility = new BouncyCertificate(); + + var ipAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); + var hostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); + var domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); + + CrestronConsole.PrintLine(string.Format("DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress)); + + var certificate = utility.CreateSelfSignedCertificate(string.Format("CN={0}.{1}", hostName, domainName), [string.Format("{0}.{1}", hostName, domainName), ipAddress], [KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth]); + + //Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested + + var separator = Path.DirectorySeparatorChar; + + utility.CertificatePassword = _certificatePassword; + utility.WriteCertificate(certificate, @$"{separator}user{separator}", _certificateName); } - - /// - /// Gets a value indicating whether the HTTPS server is currently listening for incoming connections. - /// - public bool IsRunning { get => _httpsServer?.IsListening ?? false; } - - /// - public string Key => "DebugWebsocketSink"; - - private readonly ITextFormatter _textFormatter; - - /// - /// Initializes a new instance of the class with the specified text formatter. - /// - /// This constructor initializes the WebSocket sink and ensures that a certificate is - /// available for secure communication. If the required certificate does not exist, it will be created - /// automatically. Additionally, the sink is configured to stop the server when the program is - /// stopping. - /// The text formatter used to format log messages. If null, a default JSON formatter is used. - public DebugWebsocketSink(ITextFormatter formatProvider) + catch (Exception ex) { + //Debug.Console(0, "WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace); + CrestronConsole.PrintLine("WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace); + } + } - _textFormatter = formatProvider ?? new JsonFormatter(); + /// + /// Sends a log event to all connected WebSocket clients. + /// + /// The log event is formatted using the configured text formatter and then broadcasted + /// to all clients connected to the WebSocket server. If the WebSocket server is not initialized or not + /// listening, the method exits without performing any action. + /// The log event to be formatted and broadcasted. Cannot be null. + public void Emit(LogEvent logEvent) + { + if (_httpsServer == null || !_httpsServer.IsListening) return; - if (!File.Exists($"\\user\\{_certificateName}.pfx")) - CreateCert(); + var sw = new StringWriter(); + _textFormatter.Format(logEvent, sw); - CrestronEnvironment.ProgramStatusEventHandler += type => + _httpsServer.WebSocketServices[_path].Sessions.Broadcast(sw.ToString()); + } + + /// + /// Starts the WebSocket server on the specified port and configures it with the appropriate certificate. + /// + /// This method initializes the WebSocket server and binds it to the specified port. It + /// also applies the server's certificate for secure communication. Ensure that the port is not already in use + /// and that the certificate file is accessible. + /// The port number on which the WebSocket server will listen. Must be a valid, non-negative port number. + public void StartServerAndSetPort(int port) + { + Debug.Console(0, "Starting Websocket Server on port: {0}", port); + + + Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword); + } + + private void Start(int port, string certPath = "", string certPassword = "") + { + try + { + _httpsServer = new HttpServer(port, true); + + if (!string.IsNullOrWhiteSpace(certPath)) { - if (type == eProgramStatusEventType.Stopping) - { - StopServer(); - } - }; - } + Debug.Console(0, "Assigning SSL Configuration"); - private static void CreateCert() - { - try - { - var utility = new BouncyCertificate(); - - var ipAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); - var hostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); - var domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); - - CrestronConsole.PrintLine(string.Format("DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress)); - - var certificate = utility.CreateSelfSignedCertificate(string.Format("CN={0}.{1}", hostName, domainName), [string.Format("{0}.{1}", hostName, domainName), ipAddress], [KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth]); - - //Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested - - utility.CertificatePassword = _certificatePassword; - utility.WriteCertificate(certificate, @"\user\", _certificateName); - } - catch (Exception ex) - { - //Debug.Console(0, "WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace); - CrestronConsole.PrintLine("WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace); - } - } - - /// - /// Sends a log event to all connected WebSocket clients. - /// - /// The log event is formatted using the configured text formatter and then broadcasted - /// to all clients connected to the WebSocket server. If the WebSocket server is not initialized or not - /// listening, the method exits without performing any action. - /// The log event to be formatted and broadcasted. Cannot be null. - public void Emit(LogEvent logEvent) - { - if (_httpsServer == null || !_httpsServer.IsListening) return; - - var sw = new StringWriter(); - _textFormatter.Format(logEvent, sw); - - _httpsServer.WebSocketServices[_path].Sessions.Broadcast(sw.ToString()); - } - - /// - /// Starts the WebSocket server on the specified port and configures it with the appropriate certificate. - /// - /// This method initializes the WebSocket server and binds it to the specified port. It - /// also applies the server's certificate for secure communication. Ensure that the port is not already in use - /// and that the certificate file is accessible. - /// The port number on which the WebSocket server will listen. Must be a valid, non-negative port number. - public void StartServerAndSetPort(int port) - { - Debug.Console(0, "Starting Websocket Server on port: {0}", port); - - - Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword); - } - - private void Start(int port, string certPath = "", string certPassword = "") - { - try - { - _httpsServer = new HttpServer(port, true); - - if (!string.IsNullOrWhiteSpace(certPath)) - { - Debug.Console(0, "Assigning SSL Configuration"); - - _httpsServer.SslConfiguration.ServerCertificate = new X509Certificate2(certPath, certPassword); - _httpsServer.SslConfiguration.ClientCertificateRequired = false; - _httpsServer.SslConfiguration.CheckCertificateRevocation = false; - _httpsServer.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12; - //this is just to test, you might want to actually validate - _httpsServer.SslConfiguration.ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => - { - Debug.Console(0, "HTTPS ClientCerticateValidation Callback triggered"); - return true; - }; - } - Debug.Console(0, "Adding Debug Client Service"); - _httpsServer.AddWebSocketService(_path); - Debug.Console(0, "Assigning Log Info"); - _httpsServer.Log.Level = LogLevel.Trace; - _httpsServer.Log.Output = (d, s) => - { - uint level; - - switch(d.Level) + _httpsServer.SslConfiguration.ServerCertificate = new X509Certificate2(certPath, certPassword); + _httpsServer.SslConfiguration.ClientCertificateRequired = false; + _httpsServer.SslConfiguration.CheckCertificateRevocation = false; + _httpsServer.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12; + //this is just to test, you might want to actually validate + _httpsServer.SslConfiguration.ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { - case WebSocketSharp.LogLevel.Fatal: - level = 3; - break; - case WebSocketSharp.LogLevel.Error: - level = 2; - break; - case WebSocketSharp.LogLevel.Warn: - level = 1; - break; - case WebSocketSharp.LogLevel.Info: - level = 0; - break; - case WebSocketSharp.LogLevel.Debug: - level = 4; - break; - case WebSocketSharp.LogLevel.Trace: - level = 5; - break; - default: - level = 4; - break; - } - - Debug.Console(level, "{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s); - }; - Debug.Console(0, "Starting"); - - _httpsServer.Start(); - Debug.Console(0, "Ready"); + Debug.Console(0, "HTTPS ClientCerticateValidation Callback triggered"); + return true; + }; } - catch (Exception ex) + Debug.Console(0, "Adding Debug Client Service"); + _httpsServer.AddWebSocketService(_path); + Debug.Console(0, "Assigning Log Info"); + _httpsServer.Log.Level = LogLevel.Trace; + _httpsServer.Log.Output = (d, s) => { - Debug.Console(0, "WebSocket Failed to start {0}", ex.Message); - } + uint level; + + switch(d.Level) + { + case WebSocketSharp.LogLevel.Fatal: + level = 3; + break; + case WebSocketSharp.LogLevel.Error: + level = 2; + break; + case WebSocketSharp.LogLevel.Warn: + level = 1; + break; + case WebSocketSharp.LogLevel.Info: + level = 0; + break; + case WebSocketSharp.LogLevel.Debug: + level = 4; + break; + case WebSocketSharp.LogLevel.Trace: + level = 5; + break; + default: + level = 4; + break; + } + + Debug.Console(level, "{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s); + }; + Debug.Console(0, "Starting"); + + _httpsServer.Start(); + Debug.Console(0, "Ready"); } - - /// - /// Stops the WebSocket server if it is currently running. - /// - /// This method halts the WebSocket server and releases any associated resources. After - /// calling this method, the server will no longer accept or process incoming connections. - public void StopServer() + catch (Exception ex) { - Debug.Console(0, "Stopping Websocket Server"); - _httpsServer?.Stop(); - - _httpsServer = null; + Debug.Console(0, "WebSocket Failed to start {0}", ex.Message); } } - /// - /// Configures the logger to write log events to a debug WebSocket sink. + /// + /// Stops the WebSocket server if it is currently running. /// - /// This extension method allows you to direct log events to a WebSocket sink for debugging - /// purposes. - public static class DebugWebsocketSinkExtensions + /// This method halts the WebSocket server and releases any associated resources. After + /// calling this method, the server will no longer accept or process incoming connections. + public void StopServer() { - /// - /// Configures a logger to write log events to a debug WebSocket sink. - /// - /// This method adds a sink that writes log events to a WebSocket for debugging purposes. - /// It is typically used during development to stream log events in real-time. - /// The logger sink configuration to apply the WebSocket sink to. - /// An optional text formatter to format the log events. If not provided, a default formatter will be used. - /// A object that can be used to further configure the logger. - public static LoggerConfiguration DebugWebsocketSink( - this LoggerSinkConfiguration loggerConfiguration, - ITextFormatter formatProvider = null) - { - return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider)); - } - } + Debug.Console(0, "Stopping Websocket Server"); + _httpsServer?.Stop(); - /// - /// Represents a WebSocket client for debugging purposes, providing connection lifecycle management and message - /// handling functionality. - /// - /// The class extends to handle - /// WebSocket connections, including events for opening, closing, receiving messages, and errors. It tracks the - /// duration of the connection and logs relevant events for debugging. - public class DebugClient : WebSocketBehavior - { - private DateTime _connectionTime; - - /// - /// Gets the duration of time the WebSocket connection has been active. - /// - public TimeSpan ConnectedDuration - { - get - { - if (Context.WebSocket.IsAlive) - { - return DateTime.Now - _connectionTime; - } - else - { - return new TimeSpan(0); - } - } - } - - /// - /// Initializes a new instance of the class. - /// - /// This constructor creates a new instance and logs its - /// creation using the method with a debug level of 0. - public DebugClient() - { - Debug.Console(0, "DebugClient Created"); - } - - /// - protected override void OnOpen() - { - base.OnOpen(); - - var url = Context.WebSocket.Url; - Debug.Console(0, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url); - - _connectionTime = DateTime.Now; - } - - /// - protected override void OnMessage(MessageEventArgs e) - { - base.OnMessage(e); - - Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data); - } - - /// - protected override void OnClose(CloseEventArgs e) - { - base.OnClose(e); - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason); - - } - - /// - protected override void OnError(WebSocketSharp.ErrorEventArgs e) - { - base.OnError(e); - - Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message); + _httpsServer = null; + } +} + +/// +/// Configures the logger to write log events to a debug WebSocket sink. +/// +/// This extension method allows you to direct log events to a WebSocket sink for debugging +/// purposes. +public static class DebugWebsocketSinkExtensions +{ + /// + /// Configures a logger to write log events to a debug WebSocket sink. + /// + /// This method adds a sink that writes log events to a WebSocket for debugging purposes. + /// It is typically used during development to stream log events in real-time. + /// The logger sink configuration to apply the WebSocket sink to. + /// An optional text formatter to format the log events. If not provided, a default formatter will be used. + /// A object that can be used to further configure the logger. + public static LoggerConfiguration DebugWebsocketSink( + this LoggerSinkConfiguration loggerConfiguration, + ITextFormatter formatProvider = null) + { + return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider)); + } +} + +/// +/// Represents a WebSocket client for debugging purposes, providing connection lifecycle management and message +/// handling functionality. +/// +/// The class extends to handle +/// WebSocket connections, including events for opening, closing, receiving messages, and errors. It tracks the +/// duration of the connection and logs relevant events for debugging. +public class DebugClient : WebSocketBehavior +{ + private DateTime _connectionTime; + + /// + /// Gets the duration of time the WebSocket connection has been active. + /// + public TimeSpan ConnectedDuration + { + get + { + if (Context.WebSocket.IsAlive) + { + return DateTime.Now - _connectionTime; + } + else + { + return new TimeSpan(0); + } + } + } + + /// + /// Initializes a new instance of the class. + /// + /// This constructor creates a new instance and logs its + /// creation using the method with a debug level of 0. + public DebugClient() + { + Debug.Console(0, "DebugClient Created"); + } + + /// + protected override void OnOpen() + { + base.OnOpen(); + + var url = Context.WebSocket.Url; + Debug.Console(0, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url); + + _connectionTime = DateTime.Now; + } + + /// + protected override void OnMessage(MessageEventArgs e) + { + base.OnMessage(e); + + Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data); + } + + /// + protected override void OnClose(CloseEventArgs e) + { + base.OnClose(e); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason); + + } + + /// + protected override void OnError(WebSocketSharp.ErrorEventArgs e) + { + base.OnError(e); + + Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message); } }