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(); } }