XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEWorkflow

【WinUI】XFE基于windows的工作流

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using XFEExtension.NetCore.XFEConsole;
using XFEWorkflow.Abstractions;
using XFEWorkflow.Core.Runtime;

namespace XFEWorkflow.Infrastructure.Storage;

public static class WorkflowLogExporter
{
    private static readonly SemaphoreSlim writeGate = new(1);

    public static string Format(IReadOnlyList<WorkflowLogEntry> entries, string format, SecretRedactor redactor)
    {
        if (format == "log") return entries.Count == 0 ? "" : string.Join(Environment.NewLine, entries.Select(entry =>
        {
            // Redact before XFELogEntry escapes line breaks, so multiline secrets
            // cannot survive as a different representation in a text export.
            var log = new XFELogEntry { Time = entry.Time, Level = entry.Level, LogText = redactor.Redact(entry.LogText) };
            var source = redactor.Redact("\t来源=" + entry.SourceLabel + "\t运行=" + entry.RunId + "\t节点=" + entry.NodeId).Replace("\r", "\\r").Replace("\n", "\\n");
            return log + source;
        })) + Environment.NewLine;
        if (format != "json") throw new ArgumentException("日志格式必须是 log 或 json");
        var json = JsonSerializer.SerializeToNode(entries, WorkflowJson.Options)!;
        void Redact(JsonNode? value)
        {
            if (value is JsonObject obj)
                foreach (var key in obj.Select(pair => pair.Key).ToArray())
                    if (obj[key] is JsonValue scalar && scalar.TryGetValue<string>(out var text)) obj[key] = redactor.Redact(text); else Redact(obj[key]);
            else if (value is JsonArray array)
                for (var i = 0; i < array.Count; i++)
                    if (array[i] is JsonValue scalar && scalar.TryGetValue<string>(out var text)) array[i] = redactor.Redact(text); else Redact(array[i]);
        }
        Redact(json);
        return json.Display(true);
    }

    public static async Task<JsonObject> ExportAsync(string path, IReadOnlyList<WorkflowLogEntry> entries, string format, bool append, SecretRedactor redactor, CancellationToken ct = default)
    {
        ct.ThrowIfCancellationRequested();
        if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("请选择或填写日志文件路径");
        if (format == "json" && append) throw new ArgumentException("JSON 日志不支持追加,请关闭追加或选择 log 格式");
        var text = Format(entries, format, redactor);
        path = Path.GetFullPath(path);
        await writeGate.WaitAsync(ct).ConfigureAwait(false);
        try
        {
            ct.ThrowIfCancellationRequested();
            Directory.CreateDirectory(Path.GetDirectoryName(path)!);
            if (append) await File.AppendAllTextAsync(path, text, new UTF8Encoding(false), ct).ConfigureAwait(false);
            else
            {
                var temporary = path + "." + Guid.NewGuid().ToString("N") + ".tmp";
                try
                {
                    await File.WriteAllTextAsync(temporary, text, new UTF8Encoding(false), ct).ConfigureAwait(false);
                    ct.ThrowIfCancellationRequested(); File.Move(temporary, path, true);
                }
                finally { if (File.Exists(temporary)) File.Delete(temporary); }
            }
        }
        finally { writeGate.Release(); }
        return new() { ["path"] = path, ["count"] = entries.Count };
    }
}