jq を使用したGitLabログの解析
- プラン: Free、Premium、Ultimate
- 提供形態: GitLab Self-Managed
可能な限り、KibanaやSplunkのようなログ集計および検索ツールを使用することをお勧めしますが、利用できない場合は、GitLabログをjqでJSON形式で解析できます。
特に、エラーイベントと基本的な使用状況統計を要約するために、GitLabサポートチームは特殊なfast-statsツールを提供しています。
JQとは?
マニュアルに記載されているように、jqはコマンドラインJSONプロセッサーです。次の例では、GitLabログファイルを解析するためのユースケースが含まれています。
ログの解析
以下に示す例では、それぞれのログファイルが、それぞれのLinuxパッケージインストールパスとデフォルトのファイル名で示されています。それぞれのフルパスは、GitLabログのセクションにあります。
圧縮ログ
ログファイルがローテーションされると、Unixタイムスタンプ形式で名前が変更され、gzipで圧縮されます。結果のファイル名は@40000000624492fa18da6f34.sのようになります。これらのファイルは、より新しいログファイルよりも前に解析する前に、異なる方法で処理する必要があります:
- ファイルを解凍するには、
gunzip -S .s @40000000624492fa18da6f34.sを使用して、ファイル名を圧縮ログファイルの名前に置き換えます。 - ファイルを直接読み取るか、パイプ処理するには、
zcatまたはzlessを使用します。 - ファイルの内容を検索するには、
zgrepを使用します。
一般的なコマンド
色分けされたjq出力をlessにパイプ処理します
jq . <FILE> -C | less -R用語を検索して、一致するすべての行をpretty印刷します
grep <TERM> <FILE> | jq .JSONの無効な行をスキップする
jq -cR 'fromjson?' file.json | jq <COMMAND>デフォルトでは、jqは有効なJSONではない行を検出するとエラーになります。これにより、無効な行はすべてスキップされ、残りが解析されます。
JSONログの時間範囲を印刷する
cat log.json | (head -1; tail -1) | jq '.time'ファイルがローテーションおよび圧縮されている場合は、zcatを使用します:
zcat @400000006026b71d1a7af804.s | (head -1; tail -1) | jq '.time'
zcat some_json.log.25.gz | (head -1; tail -1) | jq '.time'時系列順に複数のJSONログにわたる相関IDのアクティビティーを取得する
grep -hR <correlationID> | jq -c -R 'fromjson?' | jq -C -s 'sort_by(.time)' | less -Rgitlab-rails/production_json.logおよびgitlab-rails/api_json.logの解析
5XXステータスコードのすべてのリクエストを検索する
jq 'select(.status >= 500)' <FILE>上位10件の最も遅いリクエスト
jq -s 'sort_by(-.duration_s) | limit(10; .[])' <FILE>プロジェクトに関連するすべてのリクエストを検索して、pretty印刷する
grep <PROJECT_NAME> <FILE> | jq .合計期間が5秒を超えるすべてのリクエストを検索する
jq 'select(.duration_s > 5000)' <FILE>5回を超えるGitaly呼び出しがあるすべてのプロジェクトリクエストを検索する
grep <PROJECT_NAME> <FILE> | jq 'select(.gitaly_calls > 5)'Gitalyの期間が10秒を超えるすべてのリクエストを検索する
jq 'select(.gitaly_duration_s > 10000)' <FILE>キューの期間が10秒を超えるすべてのリクエストを検索する
jq 'select(.queue_duration_s > 10000)' <FILE>Gitaly呼び出しの#による上位10件のリクエスト
jq -s 'map(select(.gitaly_calls != null)) | sort_by(-.gitaly_calls) | limit(10; .[])' <FILE>特定の時間範囲を出力する
jq 'select(.time >= "2023-01-10T00:00:00Z" and .time <= "2023-01-10T12:00:00Z")' <FILE>gitlab-rails/production_json.logの解析
リクエストボリュームによる上位3つのコントローラーメソッドと、それらの3つの最長期間を印刷する
jq -s -r 'group_by(.controller+.action) | sort_by(-length) | limit(3; .[]) | sort_by(-.duration_s) | "CT: \(length)\tMETHOD: \(.[0].controller)#\(.[0].action)\tDURS: \(.[0].duration_s), \(.[1].duration_s), \(.[2].duration_s)"' production_json.log出力例
CT: 2721 METHOD: SessionsController#new DURS: 844.06, 713.81, 704.66
CT: 2435 METHOD: MetricsController#index DURS: 299.29, 284.01, 158.57
CT: 1328 METHOD: Projects::NotesController#index DURS: 403.99, 386.29, 384.39gitlab-rails/api_json.logの解析
リクエスト数と3つの最長期間を含む上位3つのルートを印刷する
jq -s -r 'group_by(.route) | sort_by(-length) | limit(3; .[]) | sort_by(-.duration_s) | "CT: \(length)\tROUTE: \(.[0].route)\tDURS: \(.[0].duration_s), \(.[1].duration_s), \(.[2].duration_s)"' api_json.log出力例
CT: 2472 ROUTE: /api/:version/internal/allowed DURS: 56402.65, 38411.43, 19500.41
CT: 297 ROUTE: /api/:version/projects/:id/repository/tags DURS: 731.39, 685.57, 480.86
CT: 190 ROUTE: /api/:version/projects/:id/repository/commits DURS: 1079.02, 979.68, 958.21上位APIユーザーエージェントを印刷する
jq --raw-output '
select(.remote_ip != "127.0.0.1") | [
(.time | split(".")[0] | strptime("%Y-%m-%dT%H:%M:%S") | strftime("…%m-%dT%H…")),
."meta.caller_id", .username, .ua
] | @tsv' api_json.log | sort | uniq -c \
| grep --invert-match --extended-regexp '^\s+\d{1,3}\b'出力例:
1234 …01-12T01… GET /api/:version/projects/:id/pipelines some_user # plus browser details; OK
54321 …01-12T01… POST /api/:version/projects/:id/repository/files/:file_path/raw some_bot
5678 …01-12T01… PATCH /api/:version/jobs/:id/trace gitlab-runner # plus version details; OKこの例は、カスタムツールまたはスクリプトが、予期しない高いリクエストレート(>15 RPS)をGitalyで引き起こしていることを示しています。この状況のユーザーエージェントは、特殊なサードパーティクライアント、またはcurlのような一般的なツールである可能性があります。
毎時の集計は、以下に役立ちます:
- Prometheusのようなモニタリングツールからのデータに、ボットまたはユーザーアクティビティーのスパイクを関連付けます。
- レート制限を評価します。
これらのユーザーまたはボットのパフォーマンス統計を抽出するには、fast-stats top (ページ上部を参照)を使用することもできます。
gitlab-rails/importer.logの解析
プロジェクトのインポートまたは移行をトラブルシューティングするには、次のコマンドラインを実行します:
jq 'select(.project_path == "<namespace>/<project>").error_messages' importer.log一般的な問題については、トラブルシューティングを参照してください。
gitlab-workhorse/currentの解析
上位のWorkhorseユーザーエージェントを印刷する
jq --raw-output '
select(.remote_ip != "127.0.0.1") | [
(.time | split(".")[0] | strptime("%Y-%m-%dT%H:%M:%S") | strftime("…%m-%dT%H…")),
.remote_ip, .uri, .user_agent
] | @tsv' current |
sort | uniq -cAPI uaの例と同様に、この出力で予期しないユーザーエージェントが多数ある場合は、最適化されていないスクリプトを示しています。予期されるユーザーエージェントには、gitlab-runner、GitLab-Shell、およびブラウザーが含まれます。
check_interval設定を増やすことで、新しいジョブをチェックするRunnerのパフォーマンスへの影響を軽減できます。
gitlab-rails/geo.logの解析
最も一般的なGeo同期エラーを検索する
geo:status Rakeタスクが、一部のアイテムが100%に達しないことを繰り返しレポートする場合は、次のコマンドラインを実行すると、最も一般的なエラーに焦点を当てることができます。
jq --raw-output 'select(.severity == "ERROR") | [
(.time | split(".")[0] | strptime("%Y-%m-%dT%H:%M:%S") | strftime("…%m-%dT%H:%M…")),
.class, .id, .message, .error
] | @tsv' geo.log \
| sort | uniq -c特定のエラーメッセージに関するアドバイスについては、Geoトラブルシューティングページを参照してください。
gitaly/currentの解析
次の例を使用して、Gitalyのトラブルシューティングを行います。
ウェブUIから送信されたすべてのGitalyリクエストを検索する
jq 'select(."grpc.meta.client_name" == "gitlab-web")' current失敗したすべてのGitalyリクエストを検索する
jq 'select(."grpc.code" != null and ."grpc.code" != "OK")' current30秒以上かかったすべてのリクエストを検索する
jq 'select(."grpc.time_ms" > 30000)' currentリクエストボリュームによる上位10件のプロジェクトと、それらの3つの最長期間を印刷する
jq --raw-output --slurp '
map(
select(
."grpc.request.glProjectPath" != null
and ."grpc.request.glProjectPath" != ""
and ."grpc.time_ms" != null
)
)
| group_by(."grpc.request.glProjectPath")
| sort_by(-length)
| limit(10; .[])
| sort_by(-."grpc.time_ms")
| [
length,
.[0]."grpc.time_ms",
.[1]."grpc.time_ms",
.[2]."grpc.time_ms",
.[0]."grpc.request.glProjectPath"
]
| @sh' current |
awk 'BEGIN { printf "%7s %10s %10s %10s\t%s\n", "CT", "MAX DURS", "", "", "PROJECT" }
{ printf "%7u %7u ms, %7u ms, %7u ms\t%s\n", $1, $2, $3, $4, $5 }'出力例
CT MAX DURS PROJECT
206 4898 ms, 1101 ms, 1032 ms 'groupD/project4'
109 1420 ms, 962 ms, 875 ms 'groupEF/project56'
663 106 ms, 96 ms, 94 ms 'groupABC/project123'
...ユーザーとプロジェクトのアクティビティーの概要
jq --raw-output '[
(.time | split(".")[0] | strptime("%Y-%m-%dT%H:%M:%S") | strftime("…%m-%dT%H…")),
.username, ."grpc.method", ."grpc.request.glProjectPath"
] | @tsv' current | sort | uniq -c \
| grep --invert-match --extended-regexp '^\s+\d{1,3}\b'出力例:
5678 …01-12T01… ReferenceTransactionHook # Praefect operation; OK
54321 …01-12T01… some_bot GetBlobs namespace/subgroup/project
1234 …01-12T01… some_user FindCommit namespace/subgroup/projectこの例は、カスタムツールまたはスクリプトが、Gitalyで予期しない高いリクエストレート(>15 RPS)を引き起こしていることを示しています。毎時の集計は、以下に役立ちます:
- Prometheusのようなモニタリングツールからのデータに、ボットまたはユーザーアクティビティーのスパイクを関連付けます。
- レート制限を評価します。
これらのユーザーまたはボットのパフォーマンス統計を抽出するには、fast-stats top (ページ上部を参照)を使用することもできます。
致命的なGitの問題の影響を受けるすべてのプロジェクトを検索する
grep "fatal: " current |
jq '."grpc.request.glProjectPath"' |
sort | uniqgitlab-shell/gitlab-shell.logの解析
SSH経由でのGit呼び出しを調査する場合。
プロジェクトとユーザーごとの上位20件の呼び出しを検索します:
jq --raw-output --slurp '
map(
select(
.username != null and
.gl_project_path !=null
)
)
| group_by(.username+.gl_project_path)
| sort_by(-length)
| limit(20; .[])
| "count: \(length)\tuser: \(.[0].username)\tproject: \(.[0].gl_project_path)" ' \
gitlab-shell.logプロジェクト、ユーザー、およびコマンドごとの上位20件の呼び出しを検索します:
jq --raw-output --slurp '
map(
select(
.command != null and
.username != null and
.gl_project_path !=null
)
)
| group_by(.username+.gl_project_path+.command)
| sort_by(-length)
| limit(20; .[])
| "count: \(length)\tcommand: \(.[0].command)\tuser: \(.[0].username)\tproject: \(.[0].gl_project_path)" ' \
gitlab-shell.log