[jqコマンド] jsonデータログから発生順位ランキングを取得する方法

2020年4月13日

jq テクノロジー

t f B! P L
コマンド・ワン・ライナーにかなりのこだわりがある、ユゲタです。 「サーバーコマンド」とかけまして、 「標準じゃない体格の人の着る服」と、ときます。 そのココロは・・・ "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アドレス別アクセスランキングなどを出すこともできて、不正に大量アクセスが会った場合など、すぐに割り出すことができます。 こうした、コマンド・ワンライナーって、覚えておくと非常に仕事がはかどる仕組みなんですね。

このブログを検索

プロフィール

自分の写真
プログラミングとサーバーを心の底から楽しむクリエーターです。 経営者であり、開発者でもありますが、得意としているのは、アイデア創出で、出来高は無限大です。

ブログ アーカイブ

QooQ