はじめに
様々な理由から、LinuxのTOPコマンドでのパフォーマンス測定を余儀なくされている同志の方々に送ります。
目的
LinuxのTOPコマンドをバッチモードで出力したファイルを、
python3を使ってcsvファイルに整形します。
timestamp,PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,COMMAND
10:00:00,1000,root,20,0,160000,2000,1640,R,10.0,0.2,0:00.02,top
10:00:00,3400,httpd,20,0,150000,2000,1700,S,0.0,0.3,0:07.98,nginx:
用意するもの
TOPのバッチモードで出力したファイルを用意します
top -b -d 20 -c > top_org.log
top - 10:00:00 up 1 days, 44 min, 2 users, load average: 0.00, 0.01, 0.01
Tasks: 100 total, 1 running, 99 sleeping, 0 stopped, 1 zombie
%Cpu(s): 2.0 us, 4.0 sy, 0.0 ni, 80.0 id, 5.0 wa, 0.0 hi, 2.0 si, 0.0 st
KiB Mem : 1000000 total, 60000 free, 700000 used, 200000 buff/cache
KiB Swap: 2000000 total, 90000 free, 2000000 used. 70000 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1000 root 20 0 160000 2000 1640 R 10.0 0.2 0:00.02 top -b -d 20 -c
4500 apache 20 0 440000 1000 8 S 0.0 0.1 0:00.01 /usr/sbin/httpd
17000 mysql 20 0 1130000 7000 0 S 0.0 0.7 20:00.00 /usr/sbin/mysqld
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 [kthreadd]
4 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kworker/0:0H]
6 root 20 0 0 0 0 S 0.0 0.0 0:00.00 [ksoftirqd/0]
・・・(以下略)
お急ぎの方へ
本記事末尾に、分割されていないソースコードがあります。そちらを参照下さい。
その際、以下の部分だけは変更して下さい。
毎回変更する部分
・抽出対象のUSER
・「TOPコマンドファイル」の場所
・「TOPコマンドファイルを整形したCSVファイル」の場所
を以下の部分で設定しています。
希望条件や、環境に合わせて変更してください。
'''
設定情報
'''
# 抽出するUSER(未設定だと全USERで抽出。,区切りで設定)
user_array =['root']
# カレントDir取得
current_dir = os.getcwd()
# inputファイル名(Full PATH)
input_file_name=f"{current_dir}\\before\\top_org.log"
# outputファイル名(Full PATH)
output_file_name=f"{current_dir}\\after\\top_csv.csv"
ソースコード
解説
- 必要情報の設定
- TOPコマンドファイルの読み込み
- 先頭カラムに使うtimestampを抽出
- 対象USERのプロセスを抽出
- CSVファイル出力
という順番で進んでいきます。
1. 必要情報の設定
抽出対象となるUSERや、TOPコマンドファイルが配置している場所の指定を行います。
# -*- coding: utf-8
import re
import os
import csv
'''
設定情報
'''
# 抽出するUSER(未設定だと全USERで抽出。,区切りで設定)
user_array =['root']
# カレントDir取得
current_dir = os.getcwd()
# inputファイル名(Full PATH)
input_file_name=f"{current_dir}\\before\\top.log"
# outputファイル名(Full PATH)
output_file_name=f"{current_dir}\\after\\top.log"
# 1回のTOPコマンド結果の中でプロセス行の始まりは何行目か
process_row_start = 8
# TOPコマンド結果でUSERカラムの位置は何カラム目か
user_column = 2
# TOPコマンド結果でCOMMANDカラムの位置は何カラム目か
command_column = 12
2. TOPコマンドファイルの読み込み
# inputファイル読み込み
f = open(f"{input_file_name}", "r")
toplog_lines = f.readlines()
f.close()
inputファイル(TOPコマンドファイル)を、1行ずつ読み込んで変数toplog_lines
に格納します。
3. 先頭カラムに使うtimestampを抽出
# timestamp正規表現
r_top_timestamp = re.compile("top - ([0-9:]+)+")
timestamp_list = []
roop_cnt = 0
for toplog_line in toplog_lines :
# 正規表現でtimestamp一致したらTOPコマンドファイルの行番号を行番号を配列に入れる
if r_top_timestamp.search(toplog_line) != None:
timestamp_list.append(roop_cnt)
roop_cnt += 1
ここでは、timestamp(top - 10:00:00 up 1 days, 44 min, 2 users, load average: 0.00, 0.01, 0.01
)が、
TOPコマンドファイルの何行目に入っているのか確認し、timestamp_list
に行番号を格納しています。
4. 対象USERのプロセスを抽出
まずは、プロセス抽出ループの中で必要な変数を定義します。
# TOPコマンドでプロセス行の始まりの位置
process_row_start = 8
# TOPコマンドでプロセス行のUSERカラムの位置
user_column = 2
# TOPコマンドでプロセス行のCOMMANDカラムの位置
command_column = 12
# 1回のTOPコマンド行数カウント用変数
rows_count = 0
# timestamp格納用変数
tmp_timestamp = ''
# csvに出力する文字列を格納するための配列(tmp)
tmp_output_csv_list = []
# csvに出力する文字列を格納するための配列(実際の書き出しに利用)
output_csv_list = []
次はTOPコマンドファイルを1行ずつループします。
以下を実施しています。
・TOPコマンド実施時間(タイムスタンプ)を抽出
・プロセスを1カラムずつ確認し抽出対象USERであれば、csv出力対象とする
for toplog_line in toplog_lines :
# 1回のTOP行数カウント +1
rows_count +=1
# 行末尾の改行を削除
toplog_line = toplog_line.rstrip()
# 改行のみの場合、次の行へ
if not toplog_line :
continue
# タイムスタンプ行の場合、リストに追加し、次の行へ
if r_top_timestamp.search(toplog_line) != None:
print(toplog_line)
tmp_timestamp = r_top_timestamp.search(toplog_line).group(1)
# 1回のTOPコマンド結果、行数カウント初期化
rows_count = 1
continue
# プロセス行の場合、Userが一致すれば抽出する
if rows_count >= process_row_start:
column_number = 0
row_data = toplog_line.split(" ")
# タイムスタンプを設定
tmp_output_csv_list = [tmp_timestamp]
# 行末まで繰り返す
for column_data in row_data:
if column_data =="":
# 空白の場合次のカラムへ
continue
column_number += 1
# COMMANDカラムまでのデータをtmpリストに入れる
if column_number <= command_column :
tmp_output_csv_list.append(column_data)
else :
continue
# 抽出対象レコードかをチェックする
# 抽出対象USER、または、user_array指定なしのとき抽出対象レコード選定
if column_number == user_column :
user_key_flg = True
for key_user in user_array:
# 抽出対象USER、または、user_array指定なしのとき抽出対象レコード
if ( str(column_data) == key_user ):
user_key_flg = True
break
else:
user_key_flg = False
if user_key_flg == True :
pass
else:
break
# for-else :条件を満たしたときのみ、CSV抽出リストに追加する
else:
output_csv_list.append(tmp_output_csv_list)
5. CSVファイル出力
csv抽出リスト output_csv_list
の中身をcsvに出力します。
# CSV書き出し
csv_header = ['timestamp','USER','PR','NI','VIRT','RES','SHR','%CPU','%MEM','TIME+','COMMAND']
with open(f'{output_file_name}','w') as f:
csv_writer = csv.writer( f, delimiter = ',', lineterminator = '\n')
csv_writer.writerow(csv_header)
csv_writer.writerows(output_csv_list)
csv_writer = csv.writer( f, delimiter = ',', lineterminator = '\n')
の、
delimiter = ','
を、
delimiter = '\t'
にすれば、tsvファイル(タブ区切りファイル)にもなります。
お好みでどうぞ。
ソースコードまとめ
'''
Topコマンドのプロセス行をUSERで抽出しcsvファイルに出力する
timestamp,USER,PR,NI,VIRT,RES,SHR,%CPU,%MEM,TIME+,COMMAND
'''
import re
import os
import csv
'''
設定情報
'''
# 抽出するUSER(未設定だと全USERで抽出。,区切りで設定)
user_array =['apache','httpd']
# カレントDir取得
current_dir = os.getcwd()
# inputファイル名(Full PATH)
input_file_name=f"{current_dir}\\before\\top.log"
# outputファイル名(Full PATH)
output_file_name=f"{current_dir}\\after\\top.log"
# TOPコマンドでプロセス行の始まりの位置
process_row_start = 8
# TOPコマンドでプロセス行のUSERカラムの位置
user_column = 2
# TOPコマンドでプロセス行のCOMMANDカラムの位置
command_column = 12
# timestamp正規表現
r_top_timestamp = re.compile("top - ([0-9:]+)+")
######## main 処理 #############
if __name__ == '__main__' :
'''----------------------
toplogファイル読み込み
----------------------'''
# inputファイル読み込み
f = open(f"{input_file_name}", "r")
toplog_lines = f.readlines()
f.close()
'''----------------------
timestamp行の抽出
----------------------'''
timestamp_list = []
roop_cnt = 0
for toplog_line in toplog_lines :
# 正規表現でtimestamp一致したら行番号を配列に入れる
if r_top_timestamp.search(toplog_line) != None:
timestamp_list.append(roop_cnt)
roop_cnt += 1
'''--------------------------
対象Userのプロセスを抽出
--------------------------'''
rows_count = 0
tmp_timestamp = ''
tmp_output_csv_list = []
output_csv_list = []
for toplog_line in toplog_lines :
# 1回のTOP行数カウント +1
rows_count +=1
# 行末尾の改行を削除
toplog_line = toplog_line.rstrip()
# 改行のみの場合次の行へ
if not toplog_line :
continue
# タイムスタンプ行の場合、リストに追加する
if r_top_timestamp.search(toplog_line) != None:
print(toplog_line)
tmp_timestamp = r_top_timestamp.search(toplog_line).group(1)
# 1回のTOPコマンド結果、行数カウント初期化
rows_count = 1
continue
# プロセス行の場合、Userが一致すれば抽出する
if rows_count >= process_row_start:
column_number = 0
row_data = toplog_line.split(" ")
# タイムスタンプを設定
tmp_output_csv_list = [tmp_timestamp]
# 行末まで繰り返す
for column_data in row_data:
if column_data =="":
# 空白の場合次のカラムへ
continue
column_number += 1
# COMMANDカラムまでのデータをtmpリストに入れる
if column_number <= command_column :
tmp_output_csv_list.append(column_data)
else :
continue
# 抽出対象レコードかをチェックする
# 抽出対象USER、または、user_array指定なしのとき抽出対象レコード
if column_number == user_column :
user_key_flg = True
for key_user in user_array:
# 抽出対象USER、または、user_array指定なしのとき抽出対象レコード
if ( str(column_data) == key_user ):
user_key_flg = True
break
else:
user_key_flg = False
if user_key_flg == True :
pass
else:
break
# for文をbreak以外で抜けたらCSV抽出リストに追加する
else:
output_csv_list.append(tmp_output_csv_list)
'''--------------------------
ファイル書き込み
--------------------------'''
# CSV書き出し
csv_header = ['timestamp','USER','PR','NI','VIRT','RES','SHR','%CPU','%MEM','TIME+','COMMAND']
with open(f'{output_file_name}','w') as f:
csv_writer = csv.writer( f, delimiter = ',', lineterminator = '\n')
csv_writer.writerow(csv_header)
csv_writer.writerows(output_csv_list)
2020/07/09 # -*- coding: utf-8
はpython3では非推奨でしたので削除しました。
参考記事:https://qiita.com/KEINOS/items/6efc1147b917d7811b5b
以上です。
感想
ツールもなく、TOPコマンド結果だけ渡されて、「まとめといて」と言われる方々の負担が減りますように。