LoginSignup
0
0

More than 3 years have passed since last update.

ログ解析者がScalaをかじった結果。。。

Last updated at Posted at 2020-10-16

Scalaは、いわゆる関数型、命令型どっちでもいける全方位型言語である。

一方で、全容を理解するのが難しい言語ではある。(奥行きがある)
しかし、ログ解析者からすると、パーサーの部分だけ理解できればよい、という面もある。

↓のようなINIファイルのパースのみに集中する。。。


     6[dataStorage1]
     7indexer=Elasticsearch
     8url=localhost:9200
     9user=admin
    10password=qiita
    11
    12[dataStorage2]
    13driver=Splunk
    14url=local:8089
    15user=admin
    16password=qiita

Scalaには、ケースクラスという非常に強力なスペックがある。
ケースクラスとはなにか、とは難しいだが、ログ解析者からすればパターンマッチに派生強化したクラスだと理解する。

Scalaは、ケースクラスによるパターンマッチが非常に強力な上にパーサーコンビネータという機能があり、これを組み合わせるとAST(抽象構文木)を駆使するのが楽にできる。

ASTの何がいいかというと、ログ解析者的には、JSON,mapなどに変換できるところである。

scala-ast.png

↑に対応するコードをcase classを用いて書く。。

60// String
61case class String_of_AST(string: String) extends AST
62// Property
63case class Property_of_AST(key: String_of_AST, value: String_of_AST) extends AST
64// Section
65case class Section_of_AST(name: String_of_AST, properties: List[Property_of_AST]) extends AST
66// Section(s)
67case class Sections_of_AST(sections: List[Section_of_AST]) extends AST

scala-syntax.png

↑に対応するコードをパーサーコンビネータで書く。。

35class IniParser extends RegexParsers {
36
37  // String
38  def string :Parser[String_of_AST] = """[^\[\]=\s]*""".r^^{
39    case value => String_of_AST(value)
40  }
41    42  // Property
43  def property :Parser[Property_of_AST] = string~"="~string^^{
44    case (key~_~value) => Property_of_AST(key, value)
45  }
46
47  // Section
48  def section :Parser[Section_of_AST] = "["~>string~"]"~rep(property)^^{
49    case (section~_~properties) => Section_of_AST(section, properties)
50  }
51
52  // Section(s)
53  def sections :Parser[Sections_of_AST] = rep(section)^^{
54    case sections => Sections_of_AST(sections)
55  }

パーサーの最終的な目的はなにか?

ログ解析者的には、
意味(Semantics)が同じものを異なる形式(Syntax)にいろいろ変換するためである。

コードを見てみる。。。

 1object ParserSample extends App {
 2
 3  val parser = new IniParser
 4
 5  val result = parser.parseAll(parser.sections, """
 6[db1]
 7indexer=Elasticsearch
 8url=localhost:9200
 9user=admin
10password=qiita
11
12[db2]
13driver=Splunk
14url=local:8089
15user=admin
16password=qiita
17""")
18
19  val sections = result.get
20
21  println(sections)
22  println("")
23
24  val map = sections.sections.map { section =>
25    (section.name.string -> section.properties.map { property =>
26      (property.key.string -> property.value.string)
27    }.toMap)
28  }.toMap
29
30  println(map)
31}
32
33import scala.util.parsing.combinator.RegexParsers
34
35class IniParser extends RegexParsers {
36
37  // String
38  def string :Parser[String_of_AST] = """[^\[\]=\s]*""".r^^{
39    case value => String_of_AST(value)
40  }
41    42  // Property
43  def property :Parser[Property_of_AST] = string~"="~string^^{
44    case (key~_~value) => Property_of_AST(key, value)
45  }
46
47  // Section
48  def section :Parser[Section_of_AST] = "["~>string~"]"~rep(property)^^{
49    case (section~_~properties) => Section_of_AST(section, properties)
50  }
51
52  // Section(s)
53  def sections :Parser[Sections_of_AST] = rep(section)^^{
54    case sections => Sections_of_AST(sections)
55  }
56}
57
58trait AST
59
60// String
61case class String_of_AST(string: String) extends AST
62// Property
63case class Property_of_AST(key: String_of_AST, value: String_of_AST) extends AST
64// Section
65case class Section_of_AST(name: String_of_AST, properties: List[Property_of_AST]) extends AST
66// Section(s)
67case class Sections_of_AST(sections: List[Section_of_AST]) extends AST

実行してみる。。


$ scala Parser.scala
Sections_of_AST(List(Section_of_AST(String_of_AST(db1),List(Property_of_AST(String_of_AST(indexer),String_of_AST(Elasticsearch)), Property_of_AST(String_of_AST(url),String_of_AST(localhost:9200)), Property_of_AST(String_of_AST(user),String_of_AST(admin)), Property_of_AST(String_of_AST(password),String_of_AST(qiita)))), Section_of_AST(String_of_AST(db2),List(Property_of_AST(String_of_AST(driver),String_of_AST(Splunk)), Property_of_AST(String_of_AST(url),String_of_AST(local:8089)), Property_of_AST(String_of_AST(user),String_of_AST(admin)), Property_of_AST(String_of_AST(password),String_of_AST(qiita))))))

Map(db1 -> Map(indexer -> Elasticsearch, url -> localhost:9200, user -> admin, password -> qiita), db2 -> Map(driver -> Splunk, url -> local:8089, user -> admin, password -> qiita))

(`ー´)b

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0