You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用函数式F#处理含多行条目的debug/warning/info日志解析

日志解析的函数式F#实现需求

我正在逐行解析以debug、warning、info开头的日志文件,遇到了个别日志条目跨多行的情况,示例如下:

debug: Preparing movie /movies/menu_background.sfd: 272204
info: /init
debug: Playing movie c:\program files (x86)\steam\steamapps\common\supreme commander forged alliance\movies\menu_background.sfd: 272204
warning: Error running OnDestroy script in Entity bsa0001 at 2f8b6908: ...gramdata\faforever\gamedata\lua.nx2\lua\sim\unit.lua(5494): attempt to call method `Destroy' (a nil value)
         stack traceback:
            ...gramdata\faforever\gamedata\lua.nx2\lua\sim\unit.lua(5494): in function <...gramdata\faforever\gamedata\lua.nx2\lua\sim\unit.lua:5489>
            ...orever\gamedata\lua.nx2\lua\sim\units\mobileunit.lua(65): in function `DestroyAllTrashBags'

当前我的F#代码如下,它会将这些额外行归入misclist,不符合需求。我希望修改代码,将额外行合并到对应的前序debug/warning/info条目中。我能用C#实现过程式方案,但希望以函数式风格完成,以此学习F#:

let rec sortLines (lines: string list) (warninglist: string list) (debuglist: string list) (infolist: string list) (misclist: string list) : string list list =
    match lines with
    | (h::t) when h.StartsWith("debug") ->
        sortLines t warninglist (h :: debuglist) infolist misclist
    | (h::t) when h.StartsWith("info") ->
        sortLines t warninglist debuglist (h :: infolist) misclist
    | (h::t) when h.StartsWith("warning") ->
        sortLines t (h :: warninglist) debuglist infolist misclist
    | (h::t) ->
        sortLines t warninglist debuglist infolist (h :: misclist)
    | [] ->
        [warninglist; debuglist; infolist; misclist]

函数式解决方案

步骤1:合并跨多行的日志条目

先把所有行合并为完整的日志条目:新行以debug:/info:/warning:开头时作为新条目,否则追加到上一个条目末尾。

type LogEntry = { Type: string; Content: string }

let mergeMultilineLogs (lines: string list) =
    lines
    |> List.fold (fun (currentEntryOpt, completedEntries) line ->
        match line.TrimStart() with
        // 识别新日志条目的起始行
        | l when l.StartsWith("debug:") ->
            // 先将未完成的条目加入已完成列表
            let newCompleted = 
                match currentEntryOpt with
                | Some entry -> entry :: completedEntries
                | None -> completedEntries
            // 开启新的debug条目
            (Some { Type = "debug"; Content = l }, newCompleted)
        | l when l.StartsWith("info:") ->
            let newCompleted = 
                match currentEntryOpt with
                | Some entry -> entry :: completedEntries
                | None -> completedEntries
            (Some { Type = "info"; Content = l }, newCompleted)
        | l when l.StartsWith("warning:") ->
            let newCompleted = 
                match currentEntryOpt with
                | Some entry -> entry :: completedEntries
                | None -> completedEntries
            (Some { Type = "warning"; Content = l }, newCompleted)
        // 非起始行,追加到当前条目末尾
        | l ->
            match currentEntryOpt with
            | Some current ->
                let updatedContent = current.Content + "\n" + line
                (Some { current with Content = updatedContent }, completedEntries)
            // 处理日志开头就是非起始行的情况,作为misc条目
            | None ->
                (Some { Type = "misc"; Content = l }, completedEntries)
    ) (None, [])
    // 处理最后一个未完成的条目
    |> fun (lastEntryOpt, completed) ->
        match lastEntryOpt with
        | Some entry -> entry :: completed
        | None -> completed
    // 反转列表恢复原始日志顺序
    |> List.rev

步骤2:按类型分类条目

把合并后的完整条目分类到对应列表中:

let sortLogEntries (entries: LogEntry list) =
    entries
    |> List.fold (fun (warnings, debugs, infos, miscs) entry ->
        match entry.Type with
        | "warning" -> (entry.Content :: warnings, debugs, infos, miscs)
        | "debug" -> (warnings, entry.Content :: debugs, infos, miscs)
        | "info" -> (warnings, debugs, entry.Content :: infos, miscs)
        | _ -> (warnings, debugs, infos, entry.Content :: miscs)
    ) ([], [], [], [])
    // 反转每个列表恢复原始顺序
    |> fun (w, d, i, m) -> [List.rev w; List.rev d; List.rev i; List.rev m]

组合使用

将两个函数结合,即可得到符合需求的处理结果:

let processLogs lines =
    lines
    |> mergeMultilineLogs
    |> sortLogEntries

代码说明

  1. mergeMultilineLogs:通过List.fold维护状态,跟踪当前正在构建的日志条目和已完成的条目列表,全程无可变状态,纯函数式处理跨多行合并逻辑。
  2. sortLogEntries:再次使用List.fold完成分类,最后反转列表保证输出顺序与原始日志一致。
  3. 整个流程完全遵循函数式编程范式,避免了过程式代码中的可变变量,贴合F#的语言特性。

内容的提问来源于stack exchange,提问作者user426445

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 06:05:02