FF-1960: New flow element Log Variables

This commit is contained in:
John Andrews
2024-12-06 08:24:02 +13:00
parent f94fafec2c
commit adecc963f4
16 changed files with 415 additions and 12 deletions
+138
View File
@@ -0,0 +1,138 @@
using System.Collections;
using System.Text;
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
namespace FileFlows.BasicNodes.Logging;
/// <summary>
/// Flow that logs all variables
/// </summary>
public class LogVariables: Node
{
/// <inheritdoc />
public override int Inputs => 1;
/// <inheritdoc />
public override int Outputs => 1;
/// <inheritdoc />
public override FlowElementType Type => FlowElementType.Logic;
/// <inheritdoc />
public override string Icon => "fas fa-at";
/// <inheritdoc />
public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/log-variables";
/// <inheritdoc />
public override bool FailureNode => true;
/// <inheritdoc />
public override string Group => "Logging";
/// <summary>
/// Gets or sets if the logging will be recursive
/// </summary>
[Boolean(1)]
public bool Recursive { get; set; }
/// <inheritdoc />
public override int Execute(NodeParameters args)
{
var log = GetVariablesString(args.Variables, Recursive);
args.Logger?.ILog("Variables: \n" + log);
return 1;
}
/// <summary>
/// Generates a string representation of variables to be logged.
/// </summary>
/// <param name="variables">The variables to log.</param>
/// <param name="recursive">Indicates whether to log variables recursively.</param>
/// <returns>A formatted string of variables.</returns>
internal static string GetVariablesString(Dictionary<string, object> 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();
}
/// <summary>
/// Formats a value as a string.
/// </summary>
/// <param name="value">The value to format.</param>
/// <returns>A formatted string representation of the value.</returns>
private static string FormatValue(object value)
{
return value switch
{
null => "null",
IEnumerable enumerable when value is not string => $"[{string.Join(", ", enumerable.Cast<object>())}]",
_ => value.ToString()
};
}
/// <summary>
/// Recursively formats complex objects into a log-friendly string.
/// </summary>
/// <param name="key">The key or name of the variable.</param>
/// <param name="value">The value of the variable.</param>
/// <returns>A string representation of the complex object.</returns>
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<object>());
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();
}
}
+121
View File
@@ -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;
/// <summary>
/// Tests the LogVariables flow element
/// </summary>
[TestClass]
public class LogVariablesTests: TestBase
{
[TestMethod]
public void Test_NonRecursive_VariablesLogging()
{
// Arrange
var variables = new Dictionary<string, object>
{
{ "Name", "John" },
{ "Age", 30 },
{ "Hobbies", new List<string> { "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<string, object>
{
{ "User", new {
Name = "John",
Details = new { Age = 30, Address = "123 Street" }
}
},
{ "Hobbies", new List<string> { "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<string, object>
{
{ "Config", new Dictionary<string, object>
{
{ "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<string, object>
{
{ "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<string, object>
{
{ "Numbers", new[] { 1, 2, 3 } },
{ "EmptyList", new List<string>() }
};
// 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
+12 -1
View File
@@ -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)",
+11
View File
@@ -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)",
+12 -1
View File
@@ -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)",
+12 -1
View File
@@ -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)",
+12 -1
View File
@@ -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)",
+12 -1
View File
@@ -8,7 +8,18 @@
}
},
"Flow": {
"Parts": {
"Parts": {"LogVariables": {
"Description": "デバッグのために、フロー内のすべての変数をログファイルに記録します。",
"Label": "変数をログに記録",
"Fields": {
"Recursive": "詳細ログ",
"Recursive-Help": "有効にすると、複雑な変数のすべてのネストされたプロパティがログに含まれます。"
},
"Outputs": {
"1": "変数が正常に記録されました。"
}
},
"BatchScript": {
"Description": "Windows環境でバッチ(.bat)スクリプトを実行できます。",
"Label": "バッチスクリプト(.bat)",
+12 -1
View File
@@ -8,7 +8,18 @@
}
},
"Flow": {
"Parts": {
"Parts": {"LogVariables": {
"Description": "디버깅을 위해 흐름의 모든 변수를 로그 파일에 기록합니다.",
"Label": "변수 로그 기록",
"Fields": {
"Recursive": "상세 로그",
"Recursive-Help": "활성화되면 복잡한 변수의 모든 중첩된 속성이 로그에 포함됩니다."
},
"Outputs": {
"1": "변수가 성공적으로 기록되었습니다."
}
},
"BatchScript": {
"Description": "Windows 환경에서 배치(.bat) 스크립트를 실행할 수 있습니다.",
"Label": "배치 스크립트 (.bat)",
+12 -1
View File
@@ -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)",
+12 -1
View File
@@ -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)",
+12 -1
View File
@@ -8,7 +8,18 @@
}
},
"Flow": {
"Parts": {
"Parts": {"LogVariables": {
"Description": "Логирует все переменные в потоке в файл журнала для отладки.",
"Label": "Логировать Переменные",
"Fields": {
"Recursive": "Детализированный Лог",
"Recursive-Help": "Если включено, сложные переменные будут включать все их вложенные свойства в журнал."
},
"Outputs": {
"1": "Переменные успешно записаны в журнал."
}
},
"BatchScript": {
"Description": "Позволяет выполнять пакетный (.bat) скрипт в среде Windows.",
"Label": "Пакетный скрипт (.bat)",
+12 -1
View File
@@ -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)",
+12 -1
View File
@@ -8,7 +8,18 @@
}
},
"Flow": {
"Parts": {
"Parts": {"LogVariables": {
"Description": "将流程中的所有变量记录到日志文件中以进行调试。",
"Label": "记录变量",
"Fields": {
"Recursive": "详细日志",
"Recursive-Help": "启用后,复杂变量的所有嵌套属性都将记录在日志中。"
},
"Outputs": {
"1": "变量已成功记录。"
}
},
"BatchScript": {
"Description": "允许您在 Windows 环境中执行批处理 (.bat) 脚本。",
"Label": "批处理脚本 (.bat)",
+12 -1
View File
@@ -8,7 +8,18 @@
}
},
"Flow": {
"Parts": {
"Parts": {"LogVariables": {
"Description": "將流程中的所有變量記錄到日誌文件中以進行調試。",
"Label": "記錄變量",
"Fields": {
"Recursive": "詳細日誌",
"Recursive-Help": "啟用後,複雜變量的所有嵌套屬性都將記錄在日誌中。"
},
"Outputs": {
"1": "變量已成功記錄。"
}
},
"BatchScript": {
"Description": "允許您在 Windows 環境中執行批次 (.bat) 腳本。",
"Label": "批次腳本 (.bat)",
+1
View File
@@ -9,5 +9,6 @@
&lt;TestId&gt;MSTest::CF96D3D1-1D8B-47F7-BEA7-BB238F7A566A::net8.0::FileFlows.VideoNodes.Tests.FfmpegBuilderTests.FfmpegBuilder_LanguageRemoverTests.KeepOnlyGerman&lt;/TestId&gt;
&lt;TestId&gt;MSTest::CF96D3D1-1D8B-47F7-BEA7-BB238F7A566A::net8.0::FileFlows.VideoNodes.Tests.FfmpegBuilderTests.FfmpegBuilder_LanguageRemoverTests.RemoveEnglishAudio&lt;/TestId&gt;
&lt;TestId&gt;MSTest::CF96D3D1-1D8B-47F7-BEA7-BB238F7A566A::net8.0::FileFlows.VideoNodes.Tests.FfmpegBuilderTests.FfmpegBuilder_LanguageRemoverTests&lt;/TestId&gt;
&lt;TestId&gt;MSTest::7AE24315-9FE7-429F-83D9-C989CFF5420D::net8.0::BasicNodes.Tests.LogVariablesTests&lt;/TestId&gt;
&lt;/TestAncestor&gt;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>