コマンド・ワン・ライナーにかなりのこだわりがある、ユゲタです。
「サーバーコマンド」とかけまして、
「標準じゃない体格の人の着る服」と、ときます。
そのココロは・・・
"LS"が定番です。
難題は突然やってくる
SQLデータベースはなるべく利用したくないので、json形式でテキストログを追記形式にして溜め込んでいるんですが、linuxの"/var/log/*"と、同じような状態になっている事は、理解しやすいかと思います。
そのjsonデータに、ログ取得した人(サイトにアクセスした人)のタイプが書き込まれてあり、タイプ別にアクセス数を取得したり、そのランキングを集計したいと、アクセス解析をやらなければいけなくなりました。
その際に、jsonデータは、jqコマンドに任せて、整形された集計データを返せばいいと考えていたのですが、思いの外この手の処理が難しい事がわかりました。
タイプ1つ1つの処理をjsonログから探して個数を出す処理はさほど難しくないのですが、ログのボリュームが大きくなった時に、タイプ数分のjqコマンドを実行すると、単純に1回に5秒ぐらいかかってしまうと、タイプが10個あれば、10倍の処理速度が有してしまいます。
できれば、データをなめるのは、1回にとどめたいと思うのが、システムエンジニアであれば誰でもそう考えるはずです。
jqコマンドとの格闘
まず、元データは、こんな感じ。
{"user_type":0,"name":"red","date":"20200401"}
{"user_type":1,"name":"blue","date":"20200402"}
{"user_type":null,"name":"yellow","date":"20200403"}
{"user_type":1,"name":"green","date":"20200404"}
{"user_type":2,"name":"pink","date":"20200405"}
{"user_type":1,"name":"perple","date":"20200405"}
そして、とりあえず、user_typeのみを抜き出してみる。
jq --slurp --compact-output '.[] | .user_type' data.log
> 0
> 1
> null
> 1
> 2
> 1
こんな書き方もできます。
jq --slurp --compact-output 'reduce .[] as $item([]; . + [$item.user_type] )' data.log
> [0,1,null,1,2,1]
配列で返すぐらいでこの2つはさほど差がありません。
ちなみに、nullの値は、ユーザータイプ0とマージしたいという、困難仕様。
分岐処理を使うとnull問題は解決しそうだ。
jq --slurp --compact-output '.[] | if .user_type == 1 then 1 elif .user_type == 2 then 2 else 0 end' data.log
> 0
> 1
> 0
> 1
> 2
> 1
これをそれぞれの値のlengthを取得して、json形式に整形しようとしたのだが、jqコマンドでは、ここに限界があるようだ。
仕方なく、別コマンドと連携してみる。
jq --slurp --compact-output '.[] | if .user_type == 1 then 1 elif .user_type == 2 then 2 else 0 end' data.log | sort | uniq -c
> 2 0
> 3 1
> 1 2
左の数値が発生個数で、右の数値が、user_typeです。
レスのスペース文字も気になるし、なんだかjsonでも、csvでもない、この状態が気持ち悪いので、awkを使って整形
jq --slurp --compact-output '.[] | if .user_type == 1 then 1 elif .user_type == 2 then 2 else 0 end' data.log | sort | uniq -c | awk '{print $2","$1}'
> 0,2
> 1,3
> 2,1
なんちゃってcsv形式での出力に成功です。そして、左側にuser_type、右側に発生数にして、key,valueっぽくしてみました。
でも、このままでは、ランキング結果が出せないので、ランキングに対応するには、以下のようにします。
jq --slurp --compact-output '.[] | if .user_type == 1 then 1 elif .user_type == 2 then 2 else 0 end' data.log | sort | uniq -c | sort -k 1 -nr | awk '{print $2","$1}'
> 1,3
> 0,2
> 2,1
これで完成!
コマンド・ワンライナーの活用方法
サンプルは6件しかデータがないのですが、数万件ぐらいであれば、結構すぐに結果がでるので、簡単にランキングを集計することができますね。
ちなみに、この方法を活用すると、通常のlinuxログに対して、nginxやapacheのログからIPアドレス別アクセスランキングなどを出すこともできて、不正に大量アクセスが会った場合など、すぐに割り出すことができます。
こうした、コマンド・ワンライナーって、覚えておくと非常に仕事がはかどる仕組みなんですね。
0 件のコメント:
コメントを投稿