Parsing 使用scala的日志解析策略

Parsing 使用scala的日志解析策略,parsing,scala,design-patterns,pattern-matching,case-class,Parsing,Scala,Design Patterns,Pattern Matching,Case Class,所以昨天我有一个日志文件,在提供的log.txt中有逗号分隔的条目,如下所示: entry1.1,entry1.2,entry1.3 entry2.1,entry2,2,entry2.3 .......................... line.split match { case Array(entry1,entry2,entry3) => LogEntry(entry1,entry2,entry3) } 因此,我怀着极大的快乐,继续创建了一个案例课程: case clas

所以昨天我有一个日志文件,在提供的log.txt中有逗号分隔的条目,如下所示:

entry1.1,entry1.2,entry1.3
entry2.1,entry2,2,entry2.3
..........................
line.split match {
  case Array(entry1,entry2,entry3) => LogEntry(entry1,entry2,entry3)
}
因此,我怀着极大的快乐,继续创建了一个案例课程:

case class LogEntry(
  entry1:String,
  entry2:String,
  entry3:String
)
case class LogEntry(
    time: java.util.Date,
    userId: Int,
    host: String,
    port: Int
)
并在读取案例类时填充案例类,如下所示:

entry1.1,entry1.2,entry1.3
entry2.1,entry2,2,entry2.3
..........................
line.split match {
  case Array(entry1,entry2,entry3) => LogEntry(entry1,entry2,entry3)
}
现在,当我今天运行代码时,问题出现了,我注意到日志条目对象并没有被创建

我查看了今天提供给我的log.txt,意识到条目已更改:

我现在有:

entry1.1,entry1.2,entry1.3,entry1.4
entry2.1,entry2,2,entry2.3,entry2.4
...................................
我现在在每行中都有第四个条目。看起来没什么大不了的,只要用第四个条目(代码味道1)更改我的case类,然后更改模式匹配(代码味道2)

有人能建议我应该如何编写代码来处理这种情况吗。我想扩展我的代码,而不是修改它


谢谢

当您想要正确地抽象日志行时,要做的第一件事是给出单个元素的名称和正确的类型,而不是通过名称(即entry1、entry2等)来提供伪索引。因此,我们将有:

case class LogEntry(
    time: java.util.Date,
    userId: Int,
    host: String
)
键入的严格程度取决于您的具体用例(请注意,
host
这里不是
java.net.InetAddress

将新列添加到日志文件时,可能需要保留以下两项:

  • 需要旧式日志项的函数
  • 即使添加了新功能,也要处理旧日志文件
  • 案例1很简单。只需将字段添加到案例类:

    case class LogEntry(
      entry1:String,
      entry2:String,
      entry3:String
    )
    
    case class LogEntry(
        time: java.util.Date,
        userId: Int,
        host: String,
        port: Int
    )
    
    由于您有名称,您甚至可以更改字段的顺序,旧代码仍然可以工作,因为它期望的字段仍然在这里。因为您不想再处理旧的日志文件,所以只需修改读入代码即可

    案例2有点棘手:您必须反映这样一个事实,即在运行的应用程序中有新旧日志条目。您可以:

  • 将其他字段设置为可选,并提供默认值:

    case class LogEntry(
        time: java.util.Date,
        userId: Int,
        host: String,
        port: Option[Int] = None
    ) 
    
  • 在层次结构中反映日志格式的版本(注意:您不应该从case类继承,因此必须从它们中生成普通类):

  • 选项1允许处理日志条目的代码处理条目类型之间的差异(即,所有函数的类型仍然是
    LogEntry=>?
    )。还请注意,它可能允许不一致的日志条目(添加两个字段,两个字段都不在格式1中,格式2需要,您仍然可以将其中一个字段设置为
    None

    选项2允许调用代码处理条目类型之间的差异。如果高级函数需要一个
    ExtendedLogEntry
    ,它的类型将是
    ExtendedLogEntry=>。
    ,因此您不能向该函数提供
    LogEntry
    (备选方案:函数中的模式匹配并提供默认/回退行为)。此外,它还防止了选项1可能出现的不一致性


    关于读入:选项1将在读取旧式日志文件时自动设置默认参数,在选项2中,旧式日志文件将像以前一样生成
    LogEntry
    。因此,在这两种情况下,旧的读入代码不需要更改,但可能需要进行调整,以便检测新样式或旧样式日志并生成适当的条目。(请注意,这可能是选项2的一个问题,因为如果不想强制转换,日志的样式将通过键入系统静态强制执行)。

    如果要在对象中表示日志文件,则可能需要在文件更改时更改对象。您可以始终将内容存储为
    列表[String]
    。文件不应存储为对象,单独的行需要表示为对象,因为它们会进一步推动逻辑的计算。我只想知道日志行条目的适当抽象技术,以便在保持向后兼容性的同时适应文件模式的变化。我不确定将行条目存储为字符串列表会有什么帮助?@sc\u-ray您需要澄清如何处理
    LogEntry
    类。现在我看不出比@bmorris591所建议的使用变长
    List[String]
    有什么好处。变长列表[String]如何映射到数据库表的列?