重构多线程日志文件的pyparsing解析结果

Restructuring pyparsing parse results of multithreaded log file

我有一个多线程进程的日志文件,如下所示:

<timestamp_in> <first_function_call_input> <thread:1>
    input_parameter_1:     value
    input_parameter_2:     value

<timestamp_in> <another_function_call_input> <thread:2>
    input_parameters:      values
<timestamp_out> <another_function_call_output> <thread:2>
    output_parameters:     values

<timestamp_out> <first_function_call_output> <thread:1>
    output_parameters:     values

在我的解析结果变量中,我希望将一个函数调用的输入和输出信息配对在一起,例如:

>>> print(parse_results.dump())
  -[0]:
       -function: first_function
       -thread: 1
       -timestamp_in: ...
       -timestamp_out: ...
       -input_parameters:
             [0]:
                  -parameter_name: input_parameter_1
                  -parameter_value: value
             [1]:
                  -parameter_name: input_parameter_2
                  -parameter_value: value
       -output_parameters:
             [0]: ...
             ...
  -[1]:
       -function: another_function
       -thread: 2
       ...

有没有办法在解析时直接重构 parse_results,这样我就不必在之后重构结果了?也许有一些解析动作?还是仅自己解析输入部分和输出部分,然后按线程、时间戳和函数对它们进行排序,并将输入部分和输出部分拼接在一个新对象中会更容易吗?

感谢您的帮助!

编辑:
在分别解析输入部分和输出部分之后,我将对它们进行排序,这似乎更容易。但是,我仍然想知道是否以及如何重组解析结果实例。假设我有以下语法和测试字符串:

from pyparsing import *

ParserElement.inlineLiteralsUsing(Suppress)
key_val_lines = OneOrMore(Group(Word(alphas)('key') + ':' + Word(nums)('val')))('parameters')

special_key_val_lines = OneOrMore(Group(Word(printables)('key') + ':' + Word(alphas)('val')))('special_parameters')

log = OneOrMore(Group(key_val_lines | special_key_val_lines))('contents').setDebug()

test_string ='''
foo             : 1
bar             : 2
special_key1!   : wow
another_special : abc
normalAgain     : 3'''

parse_results = log.parseString(test_string).dump()
print(parse_results)

这会输出以下内容:

- contents: [[['foo', '1'], ['bar', '2']], [['special_key1!', 'wow'], ['another_special', 'abc']], [['normalAgain', '3']]]
  [0]:
    [['foo', '1'], ['bar', '2']]
    - parameters: [['foo', '1'], ['bar', '2']]
      [0]:
        ['foo', '1']
        - key: 'foo'
        - val: '1'
      [1]:
        ['bar', '2']
        - key: 'bar'
        - val: '2'
  [1]:
    [['special_key1!', 'wow'], ['another_special', 'abc']]
    - special_parameters: [['special_key1!', 'wow'], ['another_special', 'abc']]
      [0]:
        ['special_key1!', 'wow']
        - key: 'special_key1!'
        - val: 'wow'
      [1]:
        ['another_special', 'abc']
        - key: 'another_special'
        - val: 'abc'
  [2]:
    [['normalAgain', '3']]
    - parameters: [['normalAgain', '3']]
      [0]:
        ['normalAgain', '3']
        - key: 'normalAgain'
        - val: '3'

如何修改解析器的语法,使 parse_results.contents[2].parameters[0] 变为 parse_results.contents[0].parameters[3]

纯属判断题,分界线,两种风格的解析器我都写过。

在这种特殊情况下,我的直觉告诉我,如果您将解析器和解析操作集中在对各个日志条目的各个部分进行分组、转换和命名,然后使用单独的方法,那么代码会更清晰根据您的各种分组策略重新组织它们。我的理由是日志消息结构已经有些复杂,因此您的解析器将有足够的工作要做以将每条消息提取为统一的形式。此外,您的分组策略可能会有所发展(需要收集一些短时间 window 内的项目,而不仅仅是精确的时间戳匹配),并且在单独的 post 处理方法中执行此操作将本地化这些变化。

从测试的角度来看,这还允许您将重组代码与解析代码分开测试,或许可以使用一个字典或命名元组列表来模拟来自单独日志记录的解析结果。

tl;dr - 对于这种情况,我会使用 post 处理方法来处理您解析的日志记录的最终 sorting/reorganizing。

编辑: 要就地修改解析结果,定义一个接受单个参数的解析操作,我通常将其命名为 tokens,并使用典型的列表或字典修改器就地修改:

def rearrange(tokens):
    # mutate tokens in place
    tokens.contents[0].parameters.append(tokens.contents[2].parameters[0])

log.addParseAction(rearrange)

如果您 return None(如本例所示),则传入的令牌结构将保留为要 returned 的令牌结构。如果您 return 一个非 None 值,那么新的 return 值将替换解析器输出中的给定标记。这就是整数解析器将解析后的字符串转换为实际整数的方式,或者 date/time 解析器将解析后的字符串转换为 Python datetimes.

的方式