mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-01-11 19:44:52 +00:00
1151 lines
42 KiB
C#
1151 lines
42 KiB
C#
using Crestron.SimplSharp;
|
|
using Crestron.SimplSharp.CrestronDataStore;
|
|
using Crestron.SimplSharp.CrestronIO;
|
|
using Crestron.SimplSharp.CrestronLogger;
|
|
using Newtonsoft.Json;
|
|
using PepperDash.Core.Logging;
|
|
using Serilog;
|
|
using Serilog.Context;
|
|
using Serilog.Core;
|
|
using Serilog.Events;
|
|
using Serilog.Formatting.Compact;
|
|
using Serilog.Formatting.Json;
|
|
using Serilog.Templates;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace PepperDash.Core
|
|
{
|
|
/// <summary>
|
|
/// Contains debug commands for use in various situations
|
|
/// </summary>
|
|
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 Dictionary<uint, LogEventLevel> _logLevels = new Dictionary<uint, LogEventLevel>()
|
|
{
|
|
{0, LogEventLevel.Information },
|
|
{3, LogEventLevel.Warning },
|
|
{4, LogEventLevel.Error },
|
|
{5, LogEventLevel.Fatal },
|
|
{1, LogEventLevel.Debug },
|
|
{2, LogEventLevel.Verbose },
|
|
};
|
|
|
|
private static ILogger _logger;
|
|
|
|
private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch;
|
|
|
|
private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch;
|
|
|
|
private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
|
|
|
|
private static readonly LoggingLevelSwitch _fileLevelSwitch;
|
|
|
|
public static LogEventLevel WebsocketMinimumLogLevel
|
|
{
|
|
get { return _websocketLoggingLevelSwitch.MinimumLevel; }
|
|
}
|
|
|
|
private static readonly DebugWebsocketSink _websocketSink;
|
|
|
|
public static DebugWebsocketSink WebsocketSink
|
|
{
|
|
get { return _websocketSink; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public static string OldFilePathPrefix = @"\nvram\debug\";
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public static string NewFilePathPrefix = @"\user\debug\";
|
|
|
|
/// <summary>
|
|
/// The name of the file containing the current debug settings.
|
|
/// </summary>
|
|
public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber);
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Level
|
|
/// </summary>
|
|
public static int Level { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the DoNotLoadConfigOnNextBoot
|
|
/// </summary>
|
|
public static bool DoNotLoadConfigOnNextBoot { get; private set; }
|
|
|
|
private static DebugContextCollection _contexts;
|
|
|
|
private const int SaveTimeoutMs = 30000;
|
|
|
|
public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the PepperDashCoreVersion
|
|
/// </summary>
|
|
public static string PepperDashCoreVersion { get; private set; }
|
|
|
|
private static CTimer _saveTimer;
|
|
|
|
/// <summary>
|
|
/// When true, the IncludedExcludedKeys dict will contain keys to include.
|
|
/// When false (default), IncludedExcludedKeys will contain keys to exclude.
|
|
/// </summary>
|
|
private static bool _excludeAllMode;
|
|
|
|
//static bool ExcludeNoKeyMessages;
|
|
|
|
private static readonly Dictionary<string, object> IncludedExcludedKeys;
|
|
|
|
private static readonly LoggerConfiguration _defaultLoggerConfiguration;
|
|
|
|
private static LoggerConfiguration _loggerConfiguration;
|
|
|
|
public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;
|
|
|
|
static Debug()
|
|
{
|
|
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<string, object>();
|
|
|
|
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);
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// UpdateLoggerConfiguration method
|
|
/// </summary>
|
|
public static void UpdateLoggerConfiguration(LoggerConfiguration config)
|
|
{
|
|
_loggerConfiguration = config;
|
|
|
|
_logger = config.CreateLogger();
|
|
}
|
|
|
|
/// <summary>
|
|
/// ResetLoggerConfiguration method
|
|
/// </summary>
|
|
public static void ResetLoggerConfiguration()
|
|
{
|
|
_loggerConfiguration = _defaultLoggerConfiguration;
|
|
|
|
_logger = _loggerConfiguration.CreateLogger();
|
|
}
|
|
|
|
private static LogEventLevel GetStoredLogEventLevel(string levelStoreKey)
|
|
{
|
|
try
|
|
{
|
|
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}");
|
|
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)
|
|
{
|
|
PepperDashCoreVersion = verAttribute.InformationalVersion;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var version = assembly.GetName().Version;
|
|
PepperDashCoreVersion = version.ToString();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to save memory when shutting down
|
|
/// </summary>
|
|
/// <param name="programEventType"></param>
|
|
static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
|
{
|
|
|
|
if (programEventType == eProgramStatusEventType.Stopping)
|
|
{
|
|
Log.CloseAndFlush();
|
|
|
|
if (_saveTimer != null)
|
|
{
|
|
_saveTimer.Stop();
|
|
_saveTimer = null;
|
|
}
|
|
LogMessage(LogEventLevel.Information, "Saving debug settings");
|
|
SaveMemory();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Callback for console command
|
|
/// </summary>
|
|
/// <param name="levelString"></param>
|
|
/// <summary>
|
|
/// SetDebugFromConsole method
|
|
/// </summary>
|
|
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<LogEventLevel>(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]");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the debug level
|
|
/// </summary>
|
|
/// <param name="level"> Valid values 0-5</param>
|
|
/// <summary>
|
|
/// SetDebugLevel method
|
|
/// </summary>
|
|
public static void SetDebugLevel(uint level)
|
|
{
|
|
if(!_logLevels.TryGetValue(level, out var logLevel))
|
|
{
|
|
logLevel = LogEventLevel.Information;
|
|
|
|
CrestronConsole.ConsoleCommandResponse($"{level} not valid. Setting level to {logLevel}");
|
|
|
|
SetDebugLevel(logLevel);
|
|
}
|
|
|
|
SetDebugLevel(logLevel);
|
|
}
|
|
|
|
/// <summary>
|
|
/// SetDebugLevel method
|
|
/// </summary>
|
|
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}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// SetWebSocketMinimumDebugLevel method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// SetErrorLogMinimumDebugLevel method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// SetFileMinimumDebugLevel method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Callback for console command
|
|
/// </summary>
|
|
/// <param name="stateString"></param>
|
|
/// <summary>
|
|
/// SetDoNotLoadOnNextBootFromConsole method
|
|
/// </summary>
|
|
public static void SetDoNotLoadOnNextBootFromConsole(string stateString)
|
|
{
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(stateString.Trim()))
|
|
{
|
|
CrestronConsole.ConsoleCommandResponse("DoNotLoadOnNextBoot = {0}", DoNotLoadConfigOnNextBoot);
|
|
return;
|
|
}
|
|
|
|
SetDoNotLoadConfigOnNextBoot(bool.Parse(stateString));
|
|
}
|
|
catch
|
|
{
|
|
CrestronConsole.ConsoleCommandResponse("Usage: donotloadonnextboot:P [true/false]");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Callback for console command
|
|
/// </summary>
|
|
/// <param name="items"></param>
|
|
/// <summary>
|
|
/// SetDebugFilterFromConsole method
|
|
/// </summary>
|
|
public static void SetDebugFilterFromConsole(string items)
|
|
{
|
|
var str = items.Trim();
|
|
if (str == "?")
|
|
{
|
|
CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
|
|
"+all: at beginning puts filter into 'default include' mode\r" +
|
|
" All keys that follow will be excluded from output.\r" +
|
|
"-all: at beginning puts filter into 'default exclude all' mode.\r" +
|
|
" All keys that follow will be the only keys that are shown\r" +
|
|
"+nokey: Enables messages with no key (default)\r" +
|
|
"-nokey: Disables messages with no key.\r" +
|
|
"(nokey settings are independent of all other settings)");
|
|
return;
|
|
}
|
|
var keys = Regex.Split(str, @"\s*");
|
|
foreach (var keyToken in keys)
|
|
{
|
|
var lkey = keyToken.ToLower();
|
|
if (lkey == "+all")
|
|
{
|
|
IncludedExcludedKeys.Clear();
|
|
_excludeAllMode = false;
|
|
}
|
|
else if (lkey == "-all")
|
|
{
|
|
IncludedExcludedKeys.Clear();
|
|
_excludeAllMode = true;
|
|
}
|
|
//else if (lkey == "+nokey")
|
|
//{
|
|
// ExcludeNoKeyMessages = false;
|
|
//}
|
|
//else if (lkey == "-nokey")
|
|
//{
|
|
// ExcludeNoKeyMessages = true;
|
|
//}
|
|
else
|
|
{
|
|
string key;
|
|
if (lkey.StartsWith("-"))
|
|
{
|
|
key = lkey.Substring(1);
|
|
// if in exclude all mode, we need to remove this from the inclusions
|
|
if (_excludeAllMode)
|
|
{
|
|
if (IncludedExcludedKeys.ContainsKey(key))
|
|
IncludedExcludedKeys.Remove(key);
|
|
}
|
|
// otherwise include all mode, add to the exclusions
|
|
else
|
|
{
|
|
IncludedExcludedKeys[key] = new object();
|
|
}
|
|
}
|
|
else if (lkey.StartsWith("+"))
|
|
{
|
|
key = lkey.Substring(1);
|
|
// if in exclude all mode, we need to add this as inclusion
|
|
if (_excludeAllMode)
|
|
{
|
|
|
|
IncludedExcludedKeys[key] = new object();
|
|
}
|
|
// otherwise include all mode, remove this from exclusions
|
|
else
|
|
{
|
|
if (IncludedExcludedKeys.ContainsKey(key))
|
|
IncludedExcludedKeys.Remove(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// sets the settings for a device or creates a new entry
|
|
/// </summary>
|
|
/// <param name="deviceKey"></param>
|
|
/// <param name="settings"></param>
|
|
/// <returns></returns>
|
|
public static void SetDeviceDebugSettings(string deviceKey, object settings)
|
|
{
|
|
_contexts.SetDebugSettingsForKey(deviceKey, settings);
|
|
SaveMemoryOnTimeout();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the device settings for a device by key or returns null
|
|
/// </summary>
|
|
/// <param name="deviceKey"></param>
|
|
/// <returns></returns>
|
|
/// <summary>
|
|
/// GetDeviceDebugSettingsForKey method
|
|
/// </summary>
|
|
public static object GetDeviceDebugSettingsForKey(string deviceKey)
|
|
{
|
|
return _contexts.GetDebugSettingsForKey(deviceKey);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the flag to prevent application starting on next boot
|
|
/// </summary>
|
|
/// <param name="state"></param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// ShowDebugLog method
|
|
/// </summary>
|
|
public static void ShowDebugLog(string s)
|
|
{
|
|
var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all");
|
|
foreach (var l in loglist)
|
|
CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Log an Exception as an Error
|
|
/// </summary>
|
|
/// <param name="ex">Exception to log</param>
|
|
/// <param name="message">Message template</param>
|
|
/// <param name="device">Optional IKeyed device. If provided, the Key of the device will be added to the log message</param>
|
|
/// <param name="args">Args to put into message template</param>
|
|
/// <summary>
|
|
/// LogMessage method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Log a message
|
|
/// </summary>
|
|
/// <param name="level">Level to log at</param>
|
|
/// <param name="message">Message template</param>
|
|
/// <param name="device">Optional IKeyed device. If provided, the Key of the device will be added to the log message</param>
|
|
/// <param name="args">Args to put into message template</param>
|
|
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, string message, params object[] args)
|
|
{
|
|
_logger.Write(level, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogMessage method
|
|
/// </summary>
|
|
public static void LogMessage(LogEventLevel level, Exception ex, string message, params object[] args)
|
|
{
|
|
_logger.Write(level, ex, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogMessage method
|
|
/// </summary>
|
|
public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args)
|
|
{
|
|
LogMessage(level, message, keyed, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogMessage method
|
|
/// </summary>
|
|
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
|
|
/// <summary>
|
|
/// LogVerbose method
|
|
/// </summary>
|
|
public static void LogVerbose(IKeyed keyed, string message, params object[] args)
|
|
{
|
|
using(LogContext.PushProperty("Key", keyed?.Key))
|
|
{
|
|
_logger.Write(LogEventLevel.Verbose, message, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogVerbose method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogVerbose method
|
|
/// </summary>
|
|
public static void LogVerbose(string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Verbose, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogVerbose method
|
|
/// </summary>
|
|
public static void LogVerbose(Exception ex, string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Verbose, ex, null, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogDebug method
|
|
/// </summary>
|
|
public static void LogDebug(IKeyed keyed, string message, params object[] args)
|
|
{
|
|
using (LogContext.PushProperty("Key", keyed?.Key))
|
|
{
|
|
_logger.Write(LogEventLevel.Debug, message, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogDebug method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogDebug method
|
|
/// </summary>
|
|
public static void LogDebug(string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Debug, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogDebug method
|
|
/// </summary>
|
|
public static void LogDebug(Exception ex, string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Debug, ex, null, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogInformation method
|
|
/// </summary>
|
|
public static void LogInformation(IKeyed keyed, string message, params object[] args)
|
|
{
|
|
using (LogContext.PushProperty("Key", keyed?.Key))
|
|
{
|
|
_logger.Write(LogEventLevel.Information, message, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogInformation method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogInformation method
|
|
/// </summary>
|
|
public static void LogInformation(string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Information, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogInformation method
|
|
/// </summary>
|
|
public static void LogInformation(Exception ex, string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Information, ex, null, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogWarning method
|
|
/// </summary>
|
|
public static void LogWarning(IKeyed keyed, string message, params object[] args)
|
|
{
|
|
using (LogContext.PushProperty("Key", keyed?.Key))
|
|
{
|
|
_logger.Write(LogEventLevel.Warning, message, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogWarning method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogWarning method
|
|
/// </summary>
|
|
public static void LogWarning(string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Warning, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogWarning method
|
|
/// </summary>
|
|
public static void LogWarning(Exception ex, string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Warning, ex, null, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogError method
|
|
/// </summary>
|
|
public static void LogError(IKeyed keyed, string message, params object[] args)
|
|
{
|
|
using (LogContext.PushProperty("Key", keyed?.Key))
|
|
{
|
|
_logger.Write(LogEventLevel.Error, message, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogError method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogError method
|
|
/// </summary>
|
|
public static void LogError(string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Error, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogError method
|
|
/// </summary>
|
|
public static void LogError(Exception ex, string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Error, ex, null, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogFatal method
|
|
/// </summary>
|
|
public static void LogFatal(IKeyed keyed, string message, params object[] args)
|
|
{
|
|
using (LogContext.PushProperty("Key", keyed?.Key))
|
|
{
|
|
_logger.Write(LogEventLevel.Fatal, message, args);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogFatal method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogFatal method
|
|
/// </summary>
|
|
public static void LogFatal(string message, params object[] args)
|
|
{
|
|
_logger.Write(LogEventLevel.Fatal, message, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogFatal method
|
|
/// </summary>
|
|
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;
|
|
|
|
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;
|
|
|
|
var logLevel = _logLevels[level];
|
|
|
|
LogMessage(logLevel, keyed, format, items);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Prints message to console if current debug level is equal to or higher than the level of this message.
|
|
/// Uses CrestronConsole.PrintLine.
|
|
/// </summary>
|
|
/// <param name="level"></param>
|
|
/// <param name="format">Console format string</param>
|
|
/// <param name="items">Object parameters</param>
|
|
[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);
|
|
|
|
//if (IsRunningOnAppliance)
|
|
//{
|
|
// CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"),
|
|
// InitialParametersClass.ApplicationNumber,
|
|
// level,
|
|
// string.Format(format, items));
|
|
//}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logs to Console when at-level, and all messages to error log, including device key
|
|
/// </summary>
|
|
[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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
[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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logs to Console when at-level, and all messages to error log
|
|
/// </summary>
|
|
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
|
/// <summary>
|
|
/// Console method
|
|
/// </summary>
|
|
public static void Console(uint level, ErrorLogLevel errorLogLevel,
|
|
string format, params object[] items)
|
|
{
|
|
LogMessage(level, format, items);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
|
/// <summary>
|
|
/// ConsoleWithLog method
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
[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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prints to log and error log
|
|
/// </summary>
|
|
/// <param name="errorLogLevel"></param>
|
|
/// <param name="str"></param>
|
|
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
|
public static void LogError(ErrorLogLevel errorLogLevel, string str)
|
|
{
|
|
switch (errorLogLevel)
|
|
{
|
|
case ErrorLogLevel.Error:
|
|
LogMessage(LogEventLevel.Error, str);
|
|
break;
|
|
case ErrorLogLevel.Warning:
|
|
LogMessage(LogEventLevel.Warning, str);
|
|
break;
|
|
case ErrorLogLevel.Notice:
|
|
LogMessage(LogEventLevel.Information, str);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the memory object after timeout
|
|
/// </summary>
|
|
static void SaveMemoryOnTimeout()
|
|
{
|
|
Console(0, "Saving debug settings");
|
|
if (_saveTimer == null)
|
|
_saveTimer = new CTimer(o =>
|
|
{
|
|
_saveTimer = null;
|
|
SaveMemory();
|
|
}, SaveTimeoutMs);
|
|
else
|
|
_saveTimer.Reset(SaveTimeoutMs);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the memory - use SaveMemoryOnTimeout
|
|
/// </summary>
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
static void LoadMemory()
|
|
{
|
|
var file = GetMemoryFileName();
|
|
if (File.Exists(file))
|
|
{
|
|
using (var sr = new StreamReader(file))
|
|
{
|
|
var json = sr.ReadToEnd();
|
|
_contexts = JsonConvert.DeserializeObject<DebugContextCollection>(json);
|
|
|
|
if (_contexts != null)
|
|
{
|
|
LogMessage(LogEventLevel.Debug, "Debug memory restored from file");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
_contexts = new DebugContextCollection();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to get the file path for this app's debug memory
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumeration of ErrorLogLevel values
|
|
/// </summary>
|
|
public enum ErrorLogLevel
|
|
{
|
|
/// <summary>
|
|
/// Error
|
|
/// </summary>
|
|
Error,
|
|
/// <summary>
|
|
/// Warning
|
|
/// </summary>
|
|
Warning,
|
|
/// <summary>
|
|
/// Notice
|
|
/// </summary>
|
|
Notice,
|
|
/// <summary>
|
|
/// None
|
|
/// </summary>
|
|
None,
|
|
}
|
|
}
|
|
} |