grepやawkコマンドでログの調査をする

grepawkコマンドで、ログファイルから欲しい情報を取得する!

ログファイルの中身を調査したい時に使えるコマンドをメモとして残します。

結論

以下のようなコマンドを使ってログファイルから検索/閲覧を行います。

gzip -dc /var/log/access.log-20231203.gz | grep '500 Internal Server Error' | awk -F ' ' '{print $2, $3, $4, $5}' | wc -l

grepコマンド

grep 検索ワード ファイル名と書きます。

grep '500 Internal Server Error' log/test.log

パイプを使って書くこともできます。

cat log/test.log | grep '500 Internal Server Error' 

-eオプション:複数の検索文字列を指定したい時に使用します。

grep -e '500 Internal Server Error' -e '400 Bad Request' log/test.log

-Eオプション:正規表現で“または”という意味の「|」記号使って指定することもできます。(上記-eオプションと同じ結果が得られる。)

grep -E '500 Internal Server Error|400 Bad Request' log/test.log

awkコマンド

入力として受け取った文字列に対して、区切り文字を指定したり加工したりできるコマンドです。

cat log/test.log | grep '500 Internal Server Error' | awk '{print $0}'

-Fオプション:区切り文字を指定することができます。デフォルトはスペース' 'です。タグ区切り("\t")などあります。

cat log/test.log | grep '500 Internal Server Error' | awk -F ' ' '{print $0}'
Completed 500 Internal Server Error in 200ms (ActiveRecord: 0.3ms)

awkコマンドで取得できるのは改行ごとです。
全体をレコードと呼び、-Fオプションで区切られたものをフィールドと言います。
レコード全体には $0 変数が割り当てられ、レコード最初のフィールドには $1 変数が割り当てられます。

cat log/test.log | grep '500 Internal Server Error' | awk -F ' ' '{print $1}'
Completed

$2 変数は2つ目のフィールドが割り当てられていることがわかります。

cat log/test.log | grep '500 Internal Server Error' | awk -F ' ' '{print $2}'
500

wcコマンド

入力として受け取った文字列に対して、行数や単語数、文字数を数えるコマンドです。
例えば以下のような出力結果が得られているとします。

cat log/test.log | grep '500 Internal Server Error' | awk -F ' ' '{print $2, $3, $4, $5}'
500 Internal Server Error
500 Internal Server Error
500 Internal Server Error

wcコマンドの-lオプションを指定すると3が出力されます。
3回ログファイルに「500 Internal Server Error」が出力されていることがわかります。
だけ欲しい場合に役に立ちそうです。

cat log/test.log | grep '500 Internal Server Error' | awk -F ' ' '{print $2, $3, $4, $5}' | wc -l
3

圧縮されている場合

通常、サーバ上のログファイルは圧縮された状態で格納されているかと思います。
例えばgz圧縮されている場合、解凍せずにファイルの中身を閲覧したい時、gzip -dcコマンドを使います。

gzip -dc /var/log/nginx/access.log-20231203.gz | grep '500 Internal Server Error' 

Railsのbuild_associationの挙動について


登録したデータが削除される?!

build_associationで新規登録しようとすると、 既存のデータが削除されてしまい、混乱したのでメモとして残します。

実行環境

ruby 2.6.5
Rails 6.0.2.2

かんたんなケースで動作確認してみる

UserとUserProfileというモデルを作ります。
1対1の関係であるとき、以下のようにmodelで設定されるかと思います。
なおUserProfileテーブル自体にuniqueインデックスが追加されているとします。

class User < ApplicationRecord
  has_one :user_profile
end
class UserProfile < ApplicationRecord
  belongs_to :user
end

UserProfileの登録処理を実装します。
createアクションの中で、
・既存のUserに紐づくUserProfileが存在する場合は、例外を発生させる。存在しない場合は登録成功とする。
のような実装をしたいとします。
最初に思いついたのは以下の実装です。build_associationメソッドを使用しています。 Railsガイド belongs-toで追加されるメソッド

def create
  user = find_user!
  user.build_user_profile(hoge: params[:hoge]).save!

  render_some_page
end

しかし、上記のコードでは、例外は発生しません。
既存のデータは削除され、新規で登録されてしまいまいます。

# 既存のuserに紐づくuser_profileあり
[1] pry(main)> user.user_profile
=> #<UserProfile:0x0000560151435258 id: 4, user_id: 1, hoge: 2, created_at: Sat, 02 Dec 2023 15:50:13 JST +09:00, updated_at: Sat, 02 Dec 2023 15:50:13 JST +09:00>

# id=4がdestroyされています...!
[2] pry(main)> user.build_user_profile(hoge: 2).save!
   (0.8ms)  BEGIN
  UserProfile Destroy (1.0ms)  DELETE FROM `user_profiles` WHERE `user_profiles`.`id` = 4
   (71.2ms)  COMMIT
   (0.4ms)  BEGIN
  UserProfile Create (0.9ms)  INSERT INTO `user_profiles` (`user_id`, `hoge`, `created_at`, `updated_at`) VALUES (1, 2, '2023-12-02 07:19:07', '2023-12-02 07:19:07')
   (44.2ms)  COMMIT
=> true

# id=5で新しくレコードが作成されてしまいます。
[3] pry(main)> user.user_profile
=> #<UserProfile:0x000056015145fc88 id: 5, user_id: 1, hoge: 2, created_at: Sat, 02 Dec 2023 16:19:07 JST +09:00, updated_at: Sat, 02 Dec 2023 16:19:07 JST +09:00>

意図して削除して新規作成したい場合は、上記で良いと思いますが、知らずに使うと期待しない結果になってしまうので、注意が必要だと思いました。

さて、今回のように、
・既存のUserに紐づくUserProfileが存在する場合は、例外を発生させる。存在しない場合は登録成功とする。
を実装したい場合はどうしたら良いのか。

パターン1

newしてsaveをする

[4] pry(main)> user.user_profile
=> #<UserProfile:0x000056015145fc88 id: 5, user_id: 1, hoge: 2, created_at: Sat, 02 Dec 2023 16:19:07 JST +09:00, updated_at: Sat, 02 Dec 2023 16:19:07 JST +09:00>
[5] pry(main)> user_profile =  UserProfile.new(user_id: 1, hoge: 2)
=> #<UserProfile:0x00005601512faf78 id: nil, user_id: 1, hoge: 2, created_at: nil, updated_at: nil>

# 例外が発生する
[6] pry(main)> user_profile.save!
   (0.9ms)  BEGIN
  User Load (1.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  UserProfile Create (2.1ms)  INSERT INTO `user_profiles` (`user_id`, `hoge`, `created_at`, `updated_at`) VALUES (1, 2, '2023-12-02 07:28:56', '2023-12-02 07:28:56')
   (5.6ms)  ROLLBACK
ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry '1' for key 'user_profiles.index_user_profiles_on_user_id'
from /app/.bundle/ruby/2.6.0/gems/mysql2-0.5.3/lib/mysql2/client.rb:131:in `_query'
Caused by Mysql2::Error: Duplicate entry '1' for key 'user_profiles.index_user_profiles_on_user_id'
from /app/.bundle/ruby/2.6.0/gems/mysql2-0.5.3/lib/mysql2/client.rb:131:in `_query'

パターン2

create_user_profile(create_user_profile!)を使う

[7] pry(main)> user.user_profile
=> #<UserProfile:0x000056015145fc88 id: 5, user_id: 1, hoge: 2, created_at: Sat, 02 Dec 2023 16:19:07 JST +09:00, updated_at: Sat, 02 Dec 2023 16:19:07 JST +09:00>

# 例外が発生する
[8] pry(main)> user.create_user_profile!(hoge: 2)
   (0.8ms)  BEGIN
  UserProfile Create (2.2ms)  INSERT INTO `user_profiles` (`user_id`, `hoge`, `created_at`, `updated_at`) VALUES (1, 2, '2023-12-02 07:38:18', '2023-12-02 07:38:18')
   (5.6ms)  ROLLBACK
ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry '1' for key 'user_profiles.index_user_profiles_on_user_id'
from /app/.bundle/ruby/2.6.0/gems/mysql2-0.5.3/lib/mysql2/client.rb:131:in `_query'
Caused by Mysql2::Error: Duplicate entry '1' for key 'user_profiles.index_user_profiles_on_user_id'
from /app/.bundle/ruby/2.6.0/gems/mysql2-0.5.3/lib/mysql2/client.rb:131:in `_query'

※ なおRails7以上だとcreate_associateの挙動が変わっているので注意(検証せずに削除→新規作成される)

github.com

プライバシーポリシー

当サイトに掲載されている広告について

当サイトでは、第三者配信の広告サービス(GoogleアドセンスA8.netAmazonアソシエイトバリューコマース、もしもアフィリエイト、afb、アクセストレード)を利用しています。
このような広告配信事業者は、ユーザーの興味に応じた商品やサービスの広告を表示するため、当サイトや他サイトへのアクセスに関する情報 『Cookie』(氏名、住所、メール アドレス、電話番号は含まれません) を使用することがあります。

またGoogleアドセンスに関して、このプロセスの詳細やこのような情報が広告配信事業者に使用されないようにする方法については、こちらをクリックしてください。

当サイトが使用しているアクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。
このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。
このトラフィックデータは匿名で収集されており、個人を特定するものではありません。

この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。
この規約に関して、詳しくはこちら、またはこちらをクリックしてください。

当サイトへのコメントについて

当サイトでは、スパム・荒らしへの対応として、コメントの際に使用されたIPアドレスを記録しています。
これはブログの標準機能としてサポートされている機能で、スパム・荒らしへの対応以外にこのIPアドレスを使用することはありません。

また、メールアドレスとURLの入力に関しては、任意となっております。
全てのコメントは管理人である いくら が事前にその内容を確認し、承認した上での掲載となりますことをあらかじめご了承下さい。

加えて、次の各号に掲げる内容を含むコメントは管理人の裁量によって承認せず、削除する事があります。

  • 特定の自然人または法人を誹謗し、中傷するもの。
  • 極度にわいせつな内容を含むもの。
  • 禁制品の取引に関するものや、他者を害する行為の依頼など、法律によって禁止されている物品、行為の依頼や斡旋などに関するもの。
  • その他、公序良俗に反し、または管理人によって承認すべきでないと認められるもの。

免責事項

当サイトで掲載している画像の著作権・肖像権等は各権利所有者に帰属致します。権利を侵害する目的ではございません。記事の内容や掲載画像等に問題がございましたら、各権利所有者様本人が直接メールでご連絡下さい。確認後、対応させて頂きます。

当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。
当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。
当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

プライバシーポリシーの変更について

当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。
修正された最新のプライバシーポリシーは常に本ページにて開示されます。

運営者:いくら