作業ログ
2024/06/01
エクセル操作の様にbashを使う話
目的
- 脱エクセル依存。エクセル一本足打法。エクセル以外でも簡単な集計できるように。
- bashと親しむ。
- 時短。
- 冪等性あり、複数回集計でも楽になる。
想定状況
- tomcatのログから処理速度の平均を求めたい。
- 一つ何十㎆もあるログを分析したい。
- Windowsのエディタで開こうとしたら凄く時間かかった、メモリ不足で開けなかった。
概要
centosにtomcatを起動し、適当にアクセスしてログを生成。
集計をbashで行ってみる。
最後グラフにするところはエディタ操作で加工、エクセルのグラフ機能で。
試験環境準備
以下のVagrantfileで、tomcat起動まで実施する。
vagrantの説明が必要な場合は以下をご確認ください。
centos8のdnfがもうできなそうなので、boxをbento/centos-stream-9に変更してみます。
mkdir bashtst
cd bashtst\
vagrant box add bento/centos-stream-9
vagrant init bento/centos-stream-9
Vagrantfileを以下の内容で更新し、vagrant up。
# -*- mode: ruby -*-
# vi: set ft=ruby :
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--vrde", "off"]
end
config.vm.define :centosstr9 do | centosstr9 |
config.vm.boot_timeout = 600
if Vagrant.has_plugin?("vagrant-vbguest")
config.vbguest.no_remote = true
config.vbguest.auto_update = false
end
centosstr9.vm.box = "bento/centos-stream-9"
centosstr9.vm.hostname = "centosstr9"
centosstr9.vm.network "private_network", ip: "192.168.55.101", :netmask => "255.255.255.0"
centosstr9.vm.provision "shell", inline: <<-SHELL1
# download and install tomcat.
whoami ; echo "whoami exit with : $?"
pwd ; echo "pwd exit with : $?"
timedatectl set-timezone Asia/Tokyo
date
# sed -i -e "s/^mirrorlist/#mirrorlist/g" -e "s@^#baseurl=http://mirror@baseurl=http://vault@g" /etc/yum.repos.d/CentOS-*repo ; echo "exit with : $?"
echo "dnf update. This may take a few minutes... please wait..."
dnf -qy update ; echo "dnf -qy update exit with : $?"
date
sleep 5
# sed -i -e "s/^mirrorlist/#mirrorlist/g" -e "s@^#baseurl=http://mirror@baseurl=http://vault@g" /etc/yum.repos.d/CentOS-*repo ; echo "exit with : $?"
# sleep 5
# echo "repo edit end"
dnf -qy install java-11-openjdk-devel > /dev/null ; echo "dnf -qy install java-11-openjdk-devel exit with : $?"
dnf -qy install httpd > /dev/null ; echo "dnf -qy install httpd exit with : $?"
curl -O https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.89/bin/apache-tomcat-9.0.89.tar.gz > /dev/null 2>&1 ; echo "curl exit with : $?"
ls -l `pwd`/*
file *.gz
tar xzf apache-tomcat-9.0.89.tar.gz -C /opt/
useradd -s /sbin/nologin tomcat ; echo "useradd exit with : $?"
chown -R tomcat:tomcat /opt/apache-tomcat-9.0.89
ln -s /opt/apache-tomcat-9.0.89 /opt/tomcat
chown -R tomcat:tomcat /opt/tomcat
cat << EOS >> /etc/profile
### JAVA Setting Start
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export PATH=$PATH:$JAVA_HOME/bin
export CATALINA_HOME=/opt/tomcat
export PATH=$PATH:$CATALINA_HOME/bin
### JAVA Setting End
EOS
cat << EOS > /etc/systemd/system/tomcat.service
[Unit]
Description=Apache Tomcat
After=network.target
[Service]
User=tomcat
Group=tomcat
Type=oneshot
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOS
sudo sed -i -e "s/^SELINUX=enforcing$/SELINUX=disabled/g" /etc/selinux/config
sudo sed -i 's/%b/%D/g' /opt/tomcat/conf/server.xml
chmod 755 /etc/systemd/system/tomcat.service
systemctl daemon-reload ; echo "systemctl daemon-reload exit with : $?"
systemctl enable tomcat.service > /dev/null 2>&1 ; echo "systemctl enable tomcat.service exit with : $?"
systemctl start tomcat.service ; echo "systemctl start tomcat.service exit with : $?"
systemctl is-active tomcat.service ; echo "systemctl is-active tomcat.service exit with : $?"
sleep 10
curl -s localhost:8080 -o /dev/null -w 'result:%{http_code} ' ; echo "curl -s localhost:8080 exit with : $?"
cat /opt/tomcat/logs/localhost_access_log.*
echo 'centosstr9tomcat9 provision over.'
date
for i in `seq 1 10` ; do curl -s localhost:8080 -o /dev/null -w 'result:%{http_code} '; echo loop:${i} ; done
date
SHELL1
end
end
centos8の時のVagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.define :centos8 do | centos8 |
config.vm.boot_timeout = 600
if Vagrant.has_plugin?("vagrant-vbguest")
config.vbguest.no_remote = true
config.vbguest.auto_update = false
end
centos8.vm.box = "bento/centos-8.1"
centos8.vm.hostname = "centos8"
centos8.vm.network "private_network", ip: "192.168.55.101", :netmask => "255.255.255.0"
centos8.vm.provision "shell", inline: <<-SHELL1
# download and install tomcat.
whoami
timedatectl set-timezone Asia/Tokyo
date
sed -i -e "s/^mirrorlist/#mirrorlist/g" -e "s@^#baseurl=http://mirror@baseurl=http://vault@g" /etc/yum.repos.d/CentOS-*repo ; echo "exit with : $?"
echo "dnf update. This may take a few minutes... please wait..."
dnf -qy update ; echo "exit with : $?"
date
sleep 5
sed -i -e "s/^mirrorlist/#mirrorlist/g" -e "s@^#baseurl=http://mirror@baseurl=http://vault@g" /etc/yum.repos.d/CentOS-*repo ; echo "exit with : $?"
sleep 5
echo "repo edit end"
dnf -qy install java-11-openjdk-devel > /dev/null ; echo "exit with : $?"
dnf -qy install httpd > /dev/null ; echo "exit with : $?"
curl -O https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.89/bin/apache-tomcat-9.0.89.tar.gz
tar xzf apache-tomcat-9.0.89.tar.gz -C /opt/
useradd -s /sbin/nologin tomcat
chown -R tomcat:tomcat /opt/apache-tomcat-9.0.89
ln -s /opt/apache-tomcat-9.0.89 /opt/tomcat
chown -R tomcat:tomcat /opt/tomcat
cat << EOS >> /etc/profile
### JAVA Setting Start
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export PATH=$PATH:$JAVA_HOME/bin
export CATALINA_HOME=/opt/tomcat
export PATH=$PATH:$CATALINA_HOME/bin
### JAVA Setting End
EOS
cat << EOS > /etc/systemd/system/tomcat.service
[Unit]
Description=Apache Tomcat
After=network.target
[Service]
User=tomcat
Group=tomcat
Type=oneshot
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOS
sudo sed -i -e "s/^SELINUX=enforcing$/SELINUX=disabled/g" /etc/selinux/config
sudo sed -i 's/%b/%D/g' /opt/tomcat/conf/server.xml
chmod 755 /etc/systemd/system/tomcat.service
systemctl daemon-reload ; echo "exit with : $?"
systemctl enable tomcat.service ; echo "exit with : $?"
systemctl start tomcat.service ; echo "exit with : $?"
systemctl is-active tomcat.service ; echo "exit with : $?"
sleep 10
curl -s localhost:8080 -o /dev/null -w 'result:%{http_code} ' ; echo "exit with : $?"
cat /opt/tomcat/logs/localhost_access_log.*
echo 'centos8tomcat9 provision over.'
date
for i in `seq 1 10` ; do curl -s localhost:8080 -o /dev/null -w 'result:%{http_code} '; echo loop:${i} ; done
date
SHELL1
end
end
負荷発生
ApacheBenchでログを稼ぎます。
- 負荷はお手元の環境の様子見ながら調整願います。
su -
ab -n 1000000 -c 1000 http://localhost:8080/
簡単な集計
cut -d " " -f 4,10 /opt/tomcat/logs/localhost_access_log.*.txt | cut -d ":" -f 2-4 | tail
cut -d " " -f 4,10 /opt/tomcat/logs/localhost_access_log.*.txt | cut -d ":" -f 2-4 | sort -nk 2 | tail
cut -d " " -f 4,10 /opt/tomcat/logs/localhost_access_log.*.txt | cut -d ":" -f 2-4 | awk '{m+=$2} END{print "avg "m/NR" ms";}'
違うURLへの負荷発生
ab -n 1000000 -c 1000 http://localhost:8080/examples/
ab -n 1000000 -c 1000 http://localhost:8080/manager/
ab -n 1000000 -c 1000 http://localhost:8080/host-manager/
ab -n 1000000 -c 1000 http://localhost:8080/docs/
ab -n 1000000 -c 1000 http://localhost:8080/NG/
簡単な集計
grep "/examples/" /opt/tomcat/logs/localhost_access_log.*.txt | cut -d " " -f 4,10 | cut -d ":" -f 2-4 | awk '{m+=$2} END{print "avg "m/NR" ms";}'
grep "/docs/" /opt/tomcat/logs/localhost_access_log.*.txt | cut -d " " -f 4,10 | cut -d ":" -f 2-4 | awk 'BEGIN {sum=0} {sum+=$2} END{print "avg "sum/NR" ms";}'
grep -v -e "/examples/" /opt/tomcat/logs/localhost_access_log.*.txt | wc -l
簡単な説明
ここまでで、サーバ上げて、
何かtomcat上げて、
何か大量にアクセスさせて、
何か大量にログ吐かせて、
で、この何十MB、何百MBもあるファイルを、、
Windows持っていってエクセルで解析だぁ!!
とはなりませんよね?
-
そもそも、
- 大容量テキストファイルはLinux上で処理した方が速くて楽だと思っています。
- bashでは基本的にファイル内容確認はlessしましょう。
- NW転送するならIO考慮してtarなりtar.gzしましょう。
- どうしても分割したかったらsplitしましょう。
- 内容見るなら初手はcatじゃなくてhead、tailしましょう。
- 何行あるかはwc -lで確認しましょう。
- gzなどでも、いちいち展開はせず、zcatなどとless、head、tail、wc -lを組み合わせましょう。
- テキストなのかバイナリなのか何の形式なのか良く分からなかったらfileを使いましょう。
-
エクセルの列選択の操作の様に、
- cutを活用しましょう。
echo "range cut test" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 1-9 | head
echo "field cut test" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 6,7 | head
- エクセルのフィルタ操作の様に、
- grep、sort、uniqを活用しましょう。
- それぞれのコマンドの挙動を把握しましょう。
- コマンド結果をパイプ「❘」で繋いで繋いで繋いで、意図する形になるように試行錯誤しましょう。
- 特定文字列を含む、含まないと言うエクセルのフィルタ操作のように、grep、grep -vを活用しましょう。
grep "examples" /opt/tomcat/logs/localhost_access_log.`date +%Y-%m-%d --date '1 day ago'`* | wc -l
grep -v -e "NG" /opt/tomcat/logs/localhost_access_log.* | wc -l
-
-
- 昇順、降順並べ替えと言うエクセルのフィルタ操作のように、sort、sort -r、sort -nを活用しましょう。
-
echo "sort test asc" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 7 | sort | head
echo "sort test desc" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 7 | sort -r | head
-
-
- 重複排除、重複件数カウントと言うエクセルの操作のように、uniq、uniq -cを活用しましょう。
-
echo "uniq test" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 7 | sort | uniq
echo "uniq count test" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 7 | sort -r | uniq -c
echo "uniq count sort desc as number test" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 7 | sort -r | uniq -c | sort -nr
- エクセルの一括置換の操作の様に、
- sedを活用しましょう。
echo "sort test desc, sed replace all" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 7 | sort -r | sed "s/\///g" | head
- エクセルのavg関数の様に、
- awkを活用しましょう。
echo " count lines all logs" ; cat /opt/tomcat/logs/localhost_access_log.* | wc -l
echo " speed sum all logs" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 10 | awk '{sum+=$1} END {print(sum)}'
echo " speed avg all logs" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 10 | awk '{sum+=$1} END {print(sum/NR)}'
# bc <<< "scale=2; 2669604 / 9030039" | xargs printf "%.2f\n" # chk example
- エクセルのマクロ機能の様に、
- ttlを活用しましょう。
HOSTADOR = 'localhost'
USERNAME = 'vagrant'
PASSWORD = 'vagrant'
WINDOWTITLE = 'login ttl test'
COMMAND = HOSTADOR
strconcat COMMAND ':2222 /ssh /2 /auth=password /user='
strconcat COMMAND USERNAME
strconcat COMMAND ' /passwd='
strconcat COMMAND PASSWORD
connect COMMAND
wait USERNAME
settitle WINDOWTITLE
sendln 'echo "ttl login ok"'
end
- ttlではクオートのエスケープに気を付けましょう。
HOSTADOR = 'localhost'
USERNAME = 'vagrant'
PASSWORD = 'vagrant'
WINDOWTITLE = 'chk_tps'
COMMAND = HOSTADOR
strconcat COMMAND ':2222 /ssh /2 /auth=password /user='
strconcat COMMAND USERNAME
strconcat COMMAND ' /passwd='
strconcat COMMAND PASSWORD
connect COMMAND
wait USERNAME
settitle WINDOWTITLE
sendln 'su -'
wait 'Password'
sendln PASSWORD
wait 'root'
sendln 'echo " tps today to examples top 10" ; grep "examples" /opt/tomcat/logs/localhost_access_log.`date +%Y-%m-%d`* | cut -d " " -f 4 | cut -d "/" -f 3 | cut -d ":" -f 2-4 | sort | uniq -c | sort -nr | head'
wait 'root'
sendln 'echo " tps yesterday to manager top 10" ; grep "manager" /opt/tomcat/logs/localhost_access_log.`date +%Y-%m-%d --date ' #$27 '1 day ago' #$27 '`* | cut -d " " -f 4 | cut -d "/" -f 3 | cut -d ":" -f 2-4 | sort | uniq -c | sort -nr | head'
wait 'root'
sendln 'echo " tps all logs to manager top 10" ; grep "manager" /opt/tomcat/logs/localhost_access_log* | sed "s/\[/tps@/g" | cut -d " " -f 4 | sort | uniq -c | sort -nr | head'
end
- その他色々ttlの断片を試してみましょう。watchは便利です。
wait 'root'
sendln 'echo " count url all logs" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 7 | sort | uniq -c'
wait 'root'
sendln 'echo " count target lines" ; cat /opt/tomcat/logs/localhost_access_log.* | wc -l'
wait 'root'
sendln 'echo " speed histogram all logs 10ms order" ; cat /opt/tomcat/logs/localhost_access_log.* | cut -d " " -f 10 | sort -nr | awk ' #$27 '{printf("%d\n", $1 / 10)}' #$27 ' |awk ' #$27 '{printf("%d\n", $1 * 10)}' #$27 ' | uniq -c'
wait 'root'
sendln 'watch -n5 ' #$27 'echo " tps now" ; cat /opt/tomcat/logs/localhost_access_log.`date +%Y-%m-%d`* | cut -d " " -f 4 | cut -d "/" -f 3 | cut -d ":" -f 2-4 | sort | uniq -c | sort -nr | head' #$27 ''
-
他にも色々探してみましょう。
- 複数連続したスペースを一つにまとめるならsedを使ったり。
- tps集計、処理速度1000msごと集計など、同じように対応可能です。
- grep結果をパイプ「❘」でlessに渡すのも便利です。検索とかvi操作ができるのでlessは安全で便利です。
-
最終的にはエクセルに必要な列として、タイムスタンプとtpsや処理時間だけを貼り付けて、グラフにして可視化したりしましょう。エクセル説明は略。
-
毎回全部エクセルでやるより、ttlにしてbashで集計とすると、変化を追いやすく、冪等性もあり、メリットは大きいと思います。
-
ワンライナーの書き方によってはsortなど要求リソースが多くなるかもなので、作成の際はtopを別窓でかけておいて、自分のttlの影響確認はしておきましょう。
以上