Kompira Enterprise での文字列のパターンマッチング

Kompira Enterprise がチャネル、メールチャネル、API、kompira_sendevt モジュールなど、外部から受信したデータを解析して、ジョブフローの処理の起動を行ったり、処理条件を変える事があります。このような場合には受信した文字列を解析するために、文字列のパターンマッチングが必要となります。

※ 本稿は Kompira Enterprise 1.6系に準拠した画像を用いています。


動作確認環境

ソフトウェア バージョン
Kompira Enterprise 1.4.10.post10
OS CentOS 6.10

または

ソフトウェア バージョン
Kompira Enterprise 1.5.5.post7
OS CentOS 7.8.2003

または

ソフトウェア バージョン
Kompira Enterprise 1.6.2.post4
OS CentOS 8.2.2004

または

ソフトウェア バージョン
Kompira Enterprise 1.6.8
OS CentOS Stream 8

Kompira Enterprise でパターンマッチングを行う場合には、パターン型オブジェクトを利用します。パターンの種別は、’r’ (正規表現パターン)、’g’ (glob パターン) 、’e’ (完全一致パターン) の3種類があります。また、パターンマッチングを行う場合、大文字、小文字を区別しないモード (‘i’) を組み合わせることもできます。

パターン型オブジェクトを作成するには、組み込み関数 “pattern()” を利用します。

ptn_obj = pattern(pattern, typ='r', mode='')

引数:

■ pattern は、マッチングを行うパターンを指定します。
■ typ はパターンの種別を表し、”r” (正規表現パターン) 、”g” (glob パターン) 、”e” (完全一致パターン) のいずれかを指定することができます。
■ mode には、”i” を指定すると大文字小文字を区別しないパターン照合となります。指定しない場合は、大文字小文字を区別します。

戻り値:
■ パターン型オブジェクト (ptn_obj) を返します。

パターン型オブジェクトは、文字列 s とパターンとのマッチングを試みる match() メソッドを備えています。

ptn_obj.match(s)

引数:

■ s は、マッチング対象の文字列を指定します。

戻り値:

■ マッチした場合は true 、もしくはパターンが正規表現パターンの場合はマッチした情報を格納した辞書を返します。
■ マッチしなかった場合は false を返します。

続いて、ログファイルの行をサンプルにして、各マッチング種別の動きを見ていきましょう。


完全一致パターン

完全一致パターンは、対象の文字列とパターンで指定した文字列が一致するかを確認します。
対象の文字列とパターンで指定した文字列が一致する場合は true 、一致しない場合には false を返します。

| sequence = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending a SIGTERM" |
| ptn_1 = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending a SIGTERM" |
| ptn_2 = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending" |

print("ptn_1 とのマッチング") ->
[match_rst = pattern(ptn_1, typ="e").match(sequence)] -> 
{ if match_rst |
  then: 
    print("マッチ")
  else: 
    print("ミスマッチ")
} ->  
print(match_rst) ->

print("ptn_2 とのマッチング") ->
[match_rst = pattern(ptn_2, typ="e").match(sequence)] -> 
{ if match_rst |
  then: 
    print("マッチ")
  else: 
    print("ミスマッチ")
} ->  
print(match_rst)
 

上記の場合、以下のように  ptn_1 とのマッチング結果は true となり、ptn_2 とのマッチング結果は false となります。

完全一致パターンは、以下のように単純な文字列比較として記述することもできます。

| sequence = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending a SIGTERM" |
| ptn_1 = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending a SIGTERM" |
| ptn_2 = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending" |

print("ptn_1 とのマッチング") ->
{ if sequence == ptn_1 |
  then: 
    print("マッチ")
  else: 
    print("ミスマッチ")
} -> 

print("ptn_2 とのマッチング") ->
{ if sequence == ptn_2 |
  then: 
    print("マッチ")
  else: 
    print("ミスマッチ")
}


glob パターン

glob パターンでは、Unix のシェル形式のワイルドカードを用いることができ、以下の特別な文字に対応しています。

■ 「*」: すべての文字にマッチ
■ 「?」: 任意の一文字にマッチ
■ 「[seq] 」: seq にある任意の文字にマッチ
■ 「[!seq]」: seq にない任意の文字にマッチ

“*” 、”?” 、”[” などの特殊文字をエスケープしたい場合は、[*] 、[?] 、[[] のように [] で囲って記述してください。

ログに “[warn]” という文字列が含まれているかを判別する処理は、以下のように (“*[[]warn[]]*”) 、glob パターンを用いて記述することができます。
match() は一致する場合は true 、一致しない場合には false を返します。

| sequence = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending a SIGTERM" |
| ptn_1 = "*[[]warn[]]*" |
| ptn_2 = "*[[]warn       []]*" |
 
print("ptn_1 とのマッチング") ->
[match_rst = pattern(ptn_1, typ="g").match(sequence)] -> 
{ if match_rst |
  then: 
    print("マッチ")
  else: 
    print("ミスマッチ")
} ->
print(match_rst) ->

print("ptn_2 とのマッチング") ->
[match_rst = pattern(ptn_2, typ="g").match(sequence)] -> 
{ if match_rst |
  then: 
    print("マッチ")
  else: 
    print("ミスマッチ")
} ->
print(match_rst)

上記の場合、以下のように ptn_1 とのマッチング結果は true となり、ptn_2 とのマッチング結果は false となります。

glob パターンは、比較的単純なマッチングにおいて有効な方法です。


正規表現パターン

正規表現パターンは、プログラミング言語 Python の re モジュールの正規表現に準じています。
また、match() メソッドの応答は、完全一致パターンや glob パターンと異なり、マッチした場合は、マッチした情報を格納した辞書を返します。() でくくったグループごとに、マッチした文字列を返します。マッチしない場合は false を返します。
以下の処理では、ログ取得の日時、ログレベル、メッセージの3つの情報にマッチさせて、それぞれ個別に表示します。
マッチした文字列は戻り値内の groups に格納されます。マッチしない場合は、false が返ります。
特殊文字をエスケープしたい場合は、\ をエスケープしたい文字の直前に記述します。

| sequence = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending a SIGTERM" |
| ptn_1 = "\[(.+)\] \[(warn|error)\] (.+)" |
| ptn_2 = "\[(.+)\] \[(warn_|eror)\] (.+)" |
 
print("ptn_1 とのマッチング") ->
# 正規表現によるマッチ
[match_rst = pattern(ptn_1, typ="r").match(sequence)] ->
{ if match_rst |
  then: 
    print("マッチ") -> 
    print("timestamp: ", match_rst.groups[0]) -> 
    print("level    : ", match_rst.groups[1]) -> 
    print("message  : ", match_rst.groups[2])
  else: 
    print("ミスマッチ")
} ->
print(match_rst) ->

print("ptn_2 とのマッチング") ->
# 正規表現によるマッチ
[match_rst = pattern(ptn_2, typ="r").match(sequence)] ->
{ if match_rst |
  then: 
    print("マッチ") -> 
    print("timestamp: ", match_rst.groups[0]) -> 
    print("level    : ", match_rst.groups[1]) -> 
    print("message  : ", match_rst.groups[2])
  else: 
    print("ミスマッチ")
} ->
print(match_rst)

上記の場合、以下のように ptn_1 とのマッチング結果は true となり辞書型が与えられ、ptn_2 とのマッチング結果は false となります。

正規表現でのマッチングは、glob パターンより複雑ですが、より細かい条件設定を行うことが出来ます。


パターンリテラル

これまではマッチングパターンのオブジェクトを作成するために pattern() 関数を利用してきました。
Kompira Enterprise では、pattern() の代わりに、パターンリテラルを用いる事も出来ます。
パターンリテラルは、パターンの種別を表す ‘e’ 、‘g’ 、‘r’ のいずれかの文字に引き続き、一重引用符 (‘) 、もしくは、二重引用符 (”) で囲まれた0個以上の文字 (パターン文字列) で表現されます。

上記の「正規表現パターン」のジョブフローは、パターンリテラルを用いると以下のように記述することができます。

| sequence = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending a SIGTERM" |

print("ptn_1 とのマッチング") ->
# 正規表現のパターンリテラルによるマッチ
[match_rst = r"\[(.+)\] \[(warn|error)\] (.+)".match(sequence)] ->    
{ if match_rst |
  then: 
    print("マッチ") ->
    print("timestamp: ", match_rst.groups[0]) -> 
    print("level    : ", match_rst.groups[1]) -> 
    print("message  : ", match_rst.groups[2])
  else: 
    print("ミスマッチ")
} ->
print(match_rst) ->

print("ptn_2 とのマッチング") ->
# 正規表現のパターンリテラルによるマッチ
[match_rst = r"\[(.+)\] \[(warn_|error)\] (.+)".match(sequence)] ->    
{ if match_rst |
  then: 
    print("マッチ") ->
    print("timestamp: ", match_rst.groups[0]) -> 
    print("level    : ", match_rst.groups[1]) -> 
    print("message  : ", match_rst.groups[2])
  else: 
    print("ミスマッチ")
} ->
print(match_rst)

実行結果は pattern() を使った場合と同様になります。正規表現以外にも完全一致パターン、 glob パターンの場合も同様に記述する事が出来ます。


正規表現パターンの名前付きグループ

正規表現を用いたパターンを使って値を参照しようとする場合、値は groups という配列に格納されているため、パターン中の順序を意識する必要があります。
Kompira Enterprise では、各パターンのグループに明示的に名前をつけることで、名前を用いて値を参照することができます。

名前つきグループのための構文は、固有拡張の一つ「(?P…)」を使います。
上記の「パターンリテラル」のジョブフローを以下のように書き換えることで、名前付きグループを用いることができます。

| sequence = "[Wed Aug 30 09:44:40 2017] [warn] child process 13408 still did not exit, sending a SIGTERM" |
# 正規表現によるマッチ
[match_rst = r"\[(?P<timestamp>.+)\] \[(?P<level>warn|error)\] (?P<message>.+)".match(sequence)] ->     
{ if match_rst |
  then: 
    print("マッチ") -> 
    print("timestamp:" , match_rst.groupdict.timestamp) -> 
    print("level    :" , match_rst.groupdict.level) ->
    print("message  :" , match_rst.groupdict.message)
  else: 
    print("ミスマッチ")
} ->
print(match_rst) 

それぞれの要素に “timestamp” 、”level” 、”message” という名前を付けています。
出力は、以下のようになります。

マッチさせた文字列は、groupdict という辞書に指定した名前をキーにして格納されていますので、上記の例では “match_rst.groupdict.timestamp” のように参照できます。

このようにしてパターン型オブジェクトやパターンリテラルを用いることで、文字列のパターンマッチングを行うことができます。これにより、文字列から必要な情報を取得したりするなどの、文字列の解析処理を行うことができます。

TOP