概要
今までログファイルからパターンファイルを使用して目的の行を抽出するときはgrep -f
を使用していました。
今回対象のログとパターンが多く、メモリ不足で遅かったためコマンドラインツールの作成練習もかねて
全文検索のツールを作ろうと思いました。
使用言語はpythonです。
0.開発環境
$ python --version
Python 3.6.1
1.PipEnvをインストール
pipenvの練習もかねてpipenvを使用して仮想環境を構築します。
$ pip install pipenv
また、プロジェクト直下に.venv
を作りたいので環境変数にPIPENV_VENV_IN_PROJECT=true
を設定しておきます。
PyCharmを使っている人は.venv
がプロジェクト直下にあるとインタプリタの設定がしやすいと思います。
2.仮想環境の構築
$ pipenv install
$ ls -a
Pipfile Pipfile.lock .venv
3.ライブラリのインストール
今回は全文検索にwhoosh
を使用します。
また、コマンドラインアプリとして使用したいのでfire
も入れます
$ pipenv install whoosh
$ pipenv install fire
4.コードを書いてみる
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import whoosh.index as index
import whoosh.qparser as qparser
import whoosh.fields as fields
import glob
import fire
"""A simple full text search example Fire CLI.
Example usage:
pipenv run fts.py create_index
pipenv run fts.py search pattern.txt
"""
def create_index(index_dir_name="./index", log_file_name="./logs/*.log"):
ix = get_index(index_dir_name)
writer = ix.writer()
for abspath in list_abspath(log_file_name):
with open(abspath, "r") as fp:
for line in fp:
writer.add_document(path=abspath, body=line)
writer.commit()
ix.close()
def search(pattern_file_name, index_dir_name="./index"):
ix = get_index(index_dir_name)
searcher = ix.searcher()
query = qparser.QueryParser("body", schema=ix.schema)
with open(pattern_file_name, "r") as fp:
for line in fp:
for search_result in searcher.search(query.parse(line)):
print(search_result["path"], search_result["body"], end="")
ix.close()
def get_index(idx):
if os.path.exists(idx):
ix = index.open_dir(idx)
else:
schema = fields.Schema(
path=fields.ID(stored=True),
body=fields.NGRAM(stored=True)
)
os.mkdir(idx)
ix = index.create_in(idx, schema)
return ix
def list_abspath(path):
return [os.path.abspath(p) for p in glob.glob(path)]
def main():
fire.Fire(name='fts')
if __name__ == '__main__':
main()
5.ログファイルと検索パターンファイルを準備する
ログっぽいファイルを用意します。
$ ls ./logs
01.log 02.log
$ cat ./logs/01.log
2018-04-20 06:52:57,613 [main] INFO (Task#begin():176) -
2018-04-20 06:52:57,614 [main] INFO (Task#begin():177) -
2018-04-20 06:52:57,721 [main] INFO (Task#begin():185) -
2018-04-20 06:52:57,722 [main] INFO (Task#begin():186) -
2018-04-20 06:52:57,723 [main] INFO (Task#begin():176) -
2018-04-20 06:52:57,724 [main] INFO (Task#begin():177) -
2018-04-20 06:52:57,725 [main] INFO (Task#begin():185) -
2018-04-20 06:52:57,736 [main] INFO (Task#begin():186) -
2018-04-20 06:52:57,737 [main] ERROR (Task#begin():176) -
2018-04-20 06:52:57,738 [main] INFO (Task#begin():177) -
2018-04-20 06:52:57,739 [main] INFO (Task#begin():185) -
2018-04-20 06:52:57,753 [main] INFO (Task#begin():186) -
$ cat ./logs/02.log
2018-04-23 06:52:57,613 [main] INFO (Task#begin():176) -
2018-04-23 06:52:57,614 [main] INFO (Task#begin():177) -
2018-04-23 06:52:57,721 [main] ERROR (Task#begin():185) -
2018-04-23 06:52:57,722 [main] INFO (Task#begin():186) -
2018-04-23 06:52:57,723 [main] INFO (Task#begin():176) -
2018-04-23 06:52:57,724 [main] INFO (Task#begin():177) -
2018-04-23 06:52:57,725 [main] INFO (Task#begin():185) -
2018-04-23 06:52:57,736 [main] INFO (Task#begin():186) -
2018-04-23 06:52:57,737 [main] INFO (Task#begin():176) -
2018-04-23 06:52:57,738 [main] INFO (Task#begin():177) -
2018-04-23 06:52:57,739 [main] WARN (Task#begin():185) -
2018-04-23 06:52:57,753 [main] INFO (Task#begin():186) -
次に検索するパターンファイルを用意します。
$ ls
Pipfile Pipfile.lock fts.py logs pattern.txt
$ cat pattern.txt
ERROR
WARN
6.検索してみる
$ pipenv run fts.py create_index
$ pipenv run fts.py search pattern.txt
*****\logs\01.log 2018-04-20 06:52:57,737 [main] ERROR (Task#begin():176) -
*****\logs\02.log 2018-04-23 06:52:57,721 [main] ERROR (Task#begin():185) -
*****\logs\02.log 2018-04-23 06:52:57,739 [main] WARN (Task#begin():185) -
7.まとめ
思ったより簡単にできました。
ツールは作ろうとするたびに何かを忘れて手間取ることが多いのでちょこちょこメモしていこうと思います。