diff --git a/BasicNodes/Logging/LogVariables.cs b/BasicNodes/Logging/LogVariables.cs new file mode 100644 index 00000000..d9417384 --- /dev/null +++ b/BasicNodes/Logging/LogVariables.cs @@ -0,0 +1,138 @@ +using System.Collections; +using System.Text; +using FileFlows.Plugin; +using FileFlows.Plugin.Attributes; + +namespace FileFlows.BasicNodes.Logging; + +/// +/// Flow that logs all variables +/// +public class LogVariables: Node +{ + /// + public override int Inputs => 1; + /// + public override int Outputs => 1; + /// + public override FlowElementType Type => FlowElementType.Logic; + /// + public override string Icon => "fas fa-at"; + /// + public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/log-variables"; + /// + public override bool FailureNode => true; + + /// + public override string Group => "Logging"; + + /// + /// Gets or sets if the logging will be recursive + /// + [Boolean(1)] + public bool Recursive { get; set; } + + /// + public override int Execute(NodeParameters args) + { + var log = GetVariablesString(args.Variables, Recursive); + args.Logger?.ILog("Variables: \n" + log); + return 1; + } + + /// + /// Generates a string representation of variables to be logged. + /// + /// The variables to log. + /// Indicates whether to log variables recursively. + /// A formatted string of variables. + internal static string GetVariablesString(Dictionary variables, bool recursive) + { + StringBuilder log = new(); + + foreach (var variable in variables) + { + // Only log the variable's key and value for simple types (non-recursive) + string valueRepresentation = recursive + ? AppendComplexObject(variable.Key, variable.Value) + : FormatValue(variable.Value); + + if (recursive) + { + log.AppendLine(valueRepresentation); + } + else + { + // For non-recursive, log only the key and value + log.AppendLine($"{variable.Key}: {valueRepresentation}"); + } + } + + return log.ToString(); + } + + /// + /// Formats a value as a string. + /// + /// The value to format. + /// A formatted string representation of the value. + private static string FormatValue(object value) + { + return value switch + { + null => "null", + IEnumerable enumerable when value is not string => $"[{string.Join(", ", enumerable.Cast())}]", + _ => value.ToString() + }; + } + + /// + /// Recursively formats complex objects into a log-friendly string. + /// + /// The key or name of the variable. + /// The value of the variable. + /// A string representation of the complex object. + private static string AppendComplexObject(string key, object value) + { + if (value == null) + return "null"; + + StringBuilder log = new(); + + // Handle IDictionary (Dictionary) + if (value is IDictionary dictionary) + { + foreach (var dictKey in dictionary.Keys) + { + var dictValue = dictionary[dictKey]; + // Recursively call for dictionary items + log.AppendLine(AppendComplexObject($"{key}.{dictKey}", dictValue)); + } + } + // Handle IEnumerable (Collections like List or Array) + else if (value is IEnumerable enumerable && value is not string) + { + // Log list or collection elements + var values = string.Join(", ", enumerable.Cast()); + log.AppendLine($"{key}: [{values}]"); + } + // Handle Class types (Non-string objects) + else if (value.GetType().IsClass && value.GetType() != typeof(string)) + { + foreach (var property in value.GetType().GetProperties()) + { + var propertyValue = property.GetValue(value); + // Recursively call for class properties + log.AppendLine(AppendComplexObject($"{key}.{property.Name}", propertyValue)); + } + } + else + { + // Base case: simple value + log.AppendLine($"{key}: {value}"); + } + + return log.ToString().TrimEnd(); + } + +} \ No newline at end of file diff --git a/BasicNodes/Tests/LogVariablesTests.cs b/BasicNodes/Tests/LogVariablesTests.cs new file mode 100644 index 00000000..f1be8747 --- /dev/null +++ b/BasicNodes/Tests/LogVariablesTests.cs @@ -0,0 +1,121 @@ +#if(DEBUG) + +using System.Collections.Generic; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using FileFlows.BasicNodes.Logging; + +namespace BasicNodes.Tests; + +/// +/// Tests the LogVariables flow element +/// +[TestClass] +public class LogVariablesTests: TestBase +{ + [TestMethod] + public void Test_NonRecursive_VariablesLogging() + { + // Arrange + var variables = new Dictionary + { + { "Name", "John" }, + { "Age", 30 }, + { "Hobbies", new List { "Reading", "Traveling" } }, + { "NullValue", null } + }; + + // Act + string result = LogVariables.GetVariablesString(variables, recursive: false); + Logger.Raw(result); + + // Assert + string expected = "Name: John\nAge: 30\nHobbies: [Reading, Traveling]\nNullValue: null\n"; + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void Test_Recursive_VariablesLogging() + { + // Arrange + var variables = new Dictionary + { + { "User", new { + Name = "John", + Details = new { Age = 30, Address = "123 Street" } + } + }, + { "Hobbies", new List { "Reading", "Traveling" } } + }; + + // Act + string result = LogVariables.GetVariablesString(variables, recursive: true); + Logger.Raw(result); + + // Assert + string expected = "User.Name: John\nUser.Details.Age: 30\nUser.Details.Address: 123 Street\nHobbies: [Reading, Traveling]\n"; + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void Test_Recursive_WithDictionary() + { + // Arrange + var variables = new Dictionary + { + { "Config", new Dictionary + { + { "MaxRetries", 3 }, + { "Timeout", "30s" } + } + } + }; + + // Act + string result = LogVariables.GetVariablesString(variables, recursive: true); + Logger.Raw(result); + + // Assert + string expected = "Config.MaxRetries: 3\nConfig.Timeout: 30s\n"; + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void Test_NullValueHandling() + { + // Arrange + var variables = new Dictionary + { + { "NullEntry", null } + }; + + // Act + string result = LogVariables.GetVariablesString(variables, recursive: false); + Logger.Raw(result); + + // Assert + string expected = "NullEntry: null\n"; + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void Test_EnumerableHandling() + { + // Arrange + var variables = new Dictionary + { + { "Numbers", new[] { 1, 2, 3 } }, + { "EmptyList", new List() } + }; + + // Act + string result = LogVariables.GetVariablesString(variables, recursive: false); + Logger.Raw(result); + + // Assert + string expected = "Numbers: [1, 2, 3]\nEmptyList: []\n"; + Assert.AreEqual(expected, result); + } +} + +#endif diff --git a/BasicNodes/i18n/de.json b/BasicNodes/i18n/de.json index 677f3f4f..492862c5 100644 --- a/BasicNodes/i18n/de.json +++ b/BasicNodes/i18n/de.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "Protokolliert alle Variablen im Ablauf zur Fehlersuche in der Protokolldatei.", + "Label": "Variablen protokollieren", + "Fields": { + "Recursive": "Detailliertes Protokoll", + "Recursive-Help": "Wenn aktiviert, werden komplexe Variablen mit all ihren verschachtelten Eigenschaften protokolliert." + }, + "Outputs": { + "1": "Variablen wurden erfolgreich protokolliert." + } + }, + "BatchScript": { "Description": "Ermöglicht das Ausführen eines Batch (.bat) Skripts in einer Windows-Umgebung.", "Label": "Batch-Skript (.bat)", diff --git a/BasicNodes/i18n/en.json b/BasicNodes/i18n/en.json index 9f6fab91..83350988 100644 --- a/BasicNodes/i18n/en.json +++ b/BasicNodes/i18n/en.json @@ -9,6 +9,17 @@ }, "Flow": { "Parts": { + "LogVariables": { + "Description": "Logs all the variables in the flow to the file log for debugging purposes.", + "Label": "Log Variables", + "Fields": { + "Recursive": "Detailed Logging", + "Recursive-Help": "When enabled, complex variables will include all their nested properties in the log." + }, + "Outputs": { + "1": "Variables have been logged successfully." + } + }, "BatchScript": { "Description": "Allows you to execute a batch (.bat) script in a Windows environment.", "Label": "Batch Script (.bat)", diff --git a/BasicNodes/i18n/es.json b/BasicNodes/i18n/es.json index 9f805076..836cafb5 100644 --- a/BasicNodes/i18n/es.json +++ b/BasicNodes/i18n/es.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "Registra todas las variables del flujo en el archivo de registro para depuración.", + "Label": "Registrar Variables", + "Fields": { + "Recursive": "Registro Detallado", + "Recursive-Help": "Si está habilitado, las variables complejas incluirán todas sus propiedades anidadas en el registro." + }, + "Outputs": { + "1": "Las variables se han registrado con éxito." + } + }, + "BatchScript": { "Description": "Te permite ejecutar un script por lotes (.bat) en un entorno de Windows.", "Label": "Script por lotes (.bat)", diff --git a/BasicNodes/i18n/fr.json b/BasicNodes/i18n/fr.json index 90f27383..3ba6416b 100644 --- a/BasicNodes/i18n/fr.json +++ b/BasicNodes/i18n/fr.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "Enregistre toutes les variables du flux dans le fichier journal pour le débogage.", + "Label": "Enregistrer les Variables", + "Fields": { + "Recursive": "Journalisation Détaillée", + "Recursive-Help": "Lorsqu'elle est activée, les variables complexes incluront toutes leurs propriétés imbriquées dans le journal." + }, + "Outputs": { + "1": "Les variables ont été enregistrées avec succès." + } + }, + "BatchScript": { "Description": "Vous permet d'exécuter un script par lots (.bat) dans un environnement Windows.", "Label": "Script par lots (.bat)", diff --git a/BasicNodes/i18n/it.json b/BasicNodes/i18n/it.json index 1111f6c7..09248161 100644 --- a/BasicNodes/i18n/it.json +++ b/BasicNodes/i18n/it.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "Registra tutte le variabili del flusso nel file di log per il debug.", + "Label": "Registra Variabili", + "Fields": { + "Recursive": "Registrazione Dettagliata", + "Recursive-Help": "Se abilitato, le variabili complesse includeranno tutte le loro proprietà nidificate nel log." + }, + "Outputs": { + "1": "Le variabili sono state registrate con successo." + } + }, + "BatchScript": { "Description": "Ti consente di eseguire uno script batch (.bat) in un ambiente Windows.", "Label": "Script Batch (.bat)", diff --git a/BasicNodes/i18n/ja.json b/BasicNodes/i18n/ja.json index 6edf997c..dc5f3009 100644 --- a/BasicNodes/i18n/ja.json +++ b/BasicNodes/i18n/ja.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "デバッグのために、フロー内のすべての変数をログファイルに記録します。", + "Label": "変数をログに記録", + "Fields": { + "Recursive": "詳細ログ", + "Recursive-Help": "有効にすると、複雑な変数のすべてのネストされたプロパティがログに含まれます。" + }, + "Outputs": { + "1": "変数が正常に記録されました。" + } + }, + "BatchScript": { "Description": "Windows環境でバッチ(.bat)スクリプトを実行できます。", "Label": "バッチスクリプト(.bat)", diff --git a/BasicNodes/i18n/ko.json b/BasicNodes/i18n/ko.json index 5ea22729..e30a2ea8 100644 --- a/BasicNodes/i18n/ko.json +++ b/BasicNodes/i18n/ko.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "디버깅을 위해 흐름의 모든 변수를 로그 파일에 기록합니다.", + "Label": "변수 로그 기록", + "Fields": { + "Recursive": "상세 로그", + "Recursive-Help": "활성화되면 복잡한 변수의 모든 중첩된 속성이 로그에 포함됩니다." + }, + "Outputs": { + "1": "변수가 성공적으로 기록되었습니다." + } + }, + "BatchScript": { "Description": "Windows 환경에서 배치(.bat) 스크립트를 실행할 수 있습니다.", "Label": "배치 스크립트 (.bat)", diff --git a/BasicNodes/i18n/nl.json b/BasicNodes/i18n/nl.json index 6c984089..f50a9559 100644 --- a/BasicNodes/i18n/nl.json +++ b/BasicNodes/i18n/nl.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "Logt alle variabelen in de flow naar het logbestand voor foutopsporing.", + "Label": "Log Variabelen", + "Fields": { + "Recursive": "Gedetailleerd Loggen", + "Recursive-Help": "Als ingeschakeld, worden complexe variabelen met al hun geneste eigenschappen in het logbestand opgenomen." + }, + "Outputs": { + "1": "Variabelen zijn succesvol gelogd." + } + }, + "BatchScript": { "Description": "Staat je toe om een batch (.bat) script uit te voeren in een Windows omgeving.", "Label": "Batch Script (.bat)", diff --git a/BasicNodes/i18n/pt.json b/BasicNodes/i18n/pt.json index 9dd3f001..a188574f 100644 --- a/BasicNodes/i18n/pt.json +++ b/BasicNodes/i18n/pt.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "Registra todas as variáveis do fluxo no arquivo de log para depuração.", + "Label": "Registrar Variáveis", + "Fields": { + "Recursive": "Registro Detalhado", + "Recursive-Help": "Quando ativado, as variáveis complexas incluirão todas as suas propriedades aninhadas no log." + }, + "Outputs": { + "1": "As variáveis foram registradas com sucesso." + } + }, + "BatchScript": { "Description": "Permite que você execute um script em lote (.bat) em um ambiente Windows.", "Label": "Script em lote (.bat)", diff --git a/BasicNodes/i18n/ru.json b/BasicNodes/i18n/ru.json index dece141c..2b1b1603 100644 --- a/BasicNodes/i18n/ru.json +++ b/BasicNodes/i18n/ru.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "Логирует все переменные в потоке в файл журнала для отладки.", + "Label": "Логировать Переменные", + "Fields": { + "Recursive": "Детализированный Лог", + "Recursive-Help": "Если включено, сложные переменные будут включать все их вложенные свойства в журнал." + }, + "Outputs": { + "1": "Переменные успешно записаны в журнал." + } + }, + "BatchScript": { "Description": "Позволяет выполнять пакетный (.bat) скрипт в среде Windows.", "Label": "Пакетный скрипт (.bat)", diff --git a/BasicNodes/i18n/sv.json b/BasicNodes/i18n/sv.json index ba371183..5d326586 100644 --- a/BasicNodes/i18n/sv.json +++ b/BasicNodes/i18n/sv.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "Loggar alla variabler i flödet till loggfilen för felsökning.", + "Label": "Logga Variabler", + "Fields": { + "Recursive": "Detaljerad Loggning", + "Recursive-Help": "Om aktiverat kommer komplexa variabler att inkludera alla sina inbäddade egenskaper i loggen." + }, + "Outputs": { + "1": "Variabler har loggats framgångsrikt." + } + }, + "BatchScript": { "Description": "Tillåter dig att köra ett batch (.bat) skript i en Windows-miljö.", "Label": "Batch-skript (.bat)", diff --git a/BasicNodes/i18n/zh.json b/BasicNodes/i18n/zh.json index 00a97fa5..d1bb4896 100644 --- a/BasicNodes/i18n/zh.json +++ b/BasicNodes/i18n/zh.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "将流程中的所有变量记录到日志文件中以进行调试。", + "Label": "记录变量", + "Fields": { + "Recursive": "详细日志", + "Recursive-Help": "启用后,复杂变量的所有嵌套属性都将记录在日志中。" + }, + "Outputs": { + "1": "变量已成功记录。" + } + }, + "BatchScript": { "Description": "允许您在 Windows 环境中执行批处理 (.bat) 脚本。", "Label": "批处理脚本 (.bat)", diff --git a/BasicNodes/i18n/zht.json b/BasicNodes/i18n/zht.json index ea7ef9f0..6065d050 100644 --- a/BasicNodes/i18n/zht.json +++ b/BasicNodes/i18n/zht.json @@ -8,7 +8,18 @@ } }, "Flow": { - "Parts": { + "Parts": {"LogVariables": { + "Description": "將流程中的所有變量記錄到日誌文件中以進行調試。", + "Label": "記錄變量", + "Fields": { + "Recursive": "詳細日誌", + "Recursive-Help": "啟用後,複雜變量的所有嵌套屬性都將記錄在日誌中。" + }, + "Outputs": { + "1": "變量已成功記錄。" + } + }, + "BatchScript": { "Description": "允許您在 Windows 環境中執行批次 (.bat) 腳本。", "Label": "批次腳本 (.bat)", diff --git a/FileFlowsPlugins.sln.DotSettings.user b/FileFlowsPlugins.sln.DotSettings.user index 2e8c40d6..0f0730f1 100644 --- a/FileFlowsPlugins.sln.DotSettings.user +++ b/FileFlowsPlugins.sln.DotSettings.user @@ -9,5 +9,6 @@ <TestId>MSTest::CF96D3D1-1D8B-47F7-BEA7-BB238F7A566A::net8.0::FileFlows.VideoNodes.Tests.FfmpegBuilderTests.FfmpegBuilder_LanguageRemoverTests.KeepOnlyGerman</TestId> <TestId>MSTest::CF96D3D1-1D8B-47F7-BEA7-BB238F7A566A::net8.0::FileFlows.VideoNodes.Tests.FfmpegBuilderTests.FfmpegBuilder_LanguageRemoverTests.RemoveEnglishAudio</TestId> <TestId>MSTest::CF96D3D1-1D8B-47F7-BEA7-BB238F7A566A::net8.0::FileFlows.VideoNodes.Tests.FfmpegBuilderTests.FfmpegBuilder_LanguageRemoverTests</TestId> + <TestId>MSTest::7AE24315-9FE7-429F-83D9-C989CFF5420D::net8.0::BasicNodes.Tests.LogVariablesTests</TestId> </TestAncestor> </SessionState> \ No newline at end of file