Skip to content

リンクカード:ステージングで特定のリンクカードが展開に失敗している件に対処する#10058

Closed
mousu-a wants to merge 2 commits into
mainfrom
bug/add-metadata-fetch-debug-logs
Closed

リンクカード:ステージングで特定のリンクカードが展開に失敗している件に対処する#10058
mousu-a wants to merge 2 commits into
mainfrom
bug/add-metadata-fetch-debug-logs

Conversation

@mousu-a

@mousu-a mousu-a commented May 22, 2026

Copy link
Copy Markdown
Contributor

Issue

issueではないですがこちらに端を発しています。

#9778 (comment)

概要

こちらのPRマージ後、ステージングにて動作確認を行ってもらったところリンクカードの展開に失敗していました。
このPRは、ステージングでの問題切り分けのため怪しいところにログ出力を埋め込むものです。

Summary by CodeRabbit

  • バグ修正
    • ドメイン検証時のエラーハンドリングを強化し、エラー情報をログに記録するようにしました
    • メタデータ取得時のレスポンス処理を改善し、エラー時の詳細情報を記録するようにしました
    • メタデータパース失敗時のエラー情報をログに記録するようになりました

Review Change Stack

Flakewatch Report

Flakewatch Report

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

LinkCheckerとMetadataのエラーハンドリング箇所にRails.loggerによるログ出力を追加。DNS解決失敗、HTTPレスポンス非OK、OpenGraphパース失敗時に関連情報を記録して早期returnするようにした。

Changes

エラーハンドリングのロギング強化

Layer / File(s) Summary
LinkChecker例外ログ出力
app/models/link_checker/checker.rb
valid_domain?Resolv::ResolvError=> e で受け、[LinkChecker] プレフィックスで e.classdomainRails.logger.info に出力する処理を追加(production 環境では出力しない)。
Metadataのレスポンス・パース失敗時ログ
app/models/metadata.rb
fetchresponse.message != 'OK' の場合に statusurl をログ出力して早期 return。parseOpenGraphReader.parsenil の場合に @url とページタイトル(Nokogiri::HTML(html).title)をログ出力してその場で nil を返すよう変更。

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • fjordllc/bootcamp#9765: 同じ Metadata#fetch / Metadata#parse 周辺の変更が含まれており、挙動やログ出力に関連する可能性あり。

Suggested Reviewers

  • komagata

Poem

🐰 小さな耳で聞いたログの歌
ドメインの声を丁寧に書き留める
ステータスとタイトルがそっと並び
バグの影は少しずつ薄くなり
デバッグの道ににんじん灯す ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive PRの説明は Issue 番号、概要の記載はありますが、テンプレートで要求されている「変更確認方法」と「Screenshot」セクションが不完全です。 「変更確認方法」セクションに具体的な手順を追記し、「Screenshot」セクション(変更前・変更後)の情報を提供してください。
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed タイトルはステージング環境でのリンクカード展開失敗に対処する変更であり、提供されたコード変更の主な内容(ログ出力追加)と一致している。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bug/add-metadata-fetch-debug-logs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/models/metadata.rb (1)

30-32: ⚡ Quick win

ログ出力のためだけにHTMLをパースするのはコスト高です。

Nokogiri::HTML(html).title は、ログメッセージに含めるためだけにHTML全体をパースしています。大きなHTMLドキュメントの場合、このパース処理がパフォーマンスに影響を与える可能性があります。

デバッグ目的であれば、タイトル取得を省略するか、HTMLのサイズが小さい場合のみタイトルを取得するなどの最適化を検討してください。

♻️ 最適化案:HTMLサイズチェックを追加
   unless object
-    Rails.logger.info("[Metadata] OpenGraphReader parse failed: url=#{`@url`}, title=#{Nokogiri::HTML(html).title}")
+    title = html.size < 10_000 ? Nokogiri::HTML(html).title : '(HTML too large to parse)'
+    Rails.logger.info("[Metadata] OpenGraphReader parse failed: url=#{`@url`}, title=#{title}")
     return
   end

または、よりシンプルにタイトルを省略:

   unless object
-    Rails.logger.info("[Metadata] OpenGraphReader parse failed: url=#{`@url`}, title=#{Nokogiri::HTML(html).title}")
+    Rails.logger.info("[Metadata] OpenGraphReader parse failed: url=#{`@url`}")
     return
   end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/models/metadata.rb` around lines 30 - 32, The log currently forces a full
HTML parse via Nokogiri::HTML(html).title just to enrich the Rails.logger.info
line when object is nil; change this to avoid costly parsing by either omitting
the title or gating title extraction behind a size check (e.g., only call
Nokogiri::HTML(html).title if html.bytesize is below a threshold), and update
the logging in the object-nil branch (the Rails.logger.info call referencing
`@url` and Nokogiri::HTML(html).title) to use the safe/conditional approach so we
don't parse large documents purely for logging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/models/metadata.rb`:
- Around line 18-21: The log in the response check (the block around
response.message != 'OK' that calls Rails.logger.info with url=`@url`) currently
writes the full `@url` which may contain sensitive query params; change it to
avoid emitting secrets by either (a) omitting the query string or masking query
values (e.g. parse `@url` with URI and log only scheme+host+path or replace query
values with "[REDACTED]") or (b) only include the full URL in non-production
environments (check Rails.env.production?). Update the Rails.logger.info call to
use a sanitized_url (no query or masked params) or to skip URL logging in
production to prevent leaking tokens/user IDs.

---

Nitpick comments:
In `@app/models/metadata.rb`:
- Around line 30-32: The log currently forces a full HTML parse via
Nokogiri::HTML(html).title just to enrich the Rails.logger.info line when object
is nil; change this to avoid costly parsing by either omitting the title or
gating title extraction behind a size check (e.g., only call
Nokogiri::HTML(html).title if html.bytesize is below a threshold), and update
the logging in the object-nil branch (the Rails.logger.info call referencing
`@url` and Nokogiri::HTML(html).title) to use the safe/conditional approach so we
don't parse large documents purely for logging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 67449a9f-dbd9-4933-9476-303d15217a26

📥 Commits

Reviewing files that changed from the base of the PR and between 459fe42 and a860c06.

📒 Files selected for processing (2)
  • app/models/link_checker/checker.rb
  • app/models/metadata.rb

Comment thread app/models/metadata.rb

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/models/link_checker/checker.rb (1)

42-44: 💤 Low value

例外クラスの出力が冗長です。

rescue Resolv::ResolvError で既に例外の型を指定しているため、e.class は常に Resolv::ResolvError になります。ログ出力から e.class を省いても情報の損失はありません。

♻️ 簡略化の提案
-rescue Resolv::ResolvError => e
-  Rails.logger.info("[LinkChecker] #{e.class}: domain=#{domain}") unless Rails.env.production?
+rescue Resolv::ResolvError
+  Rails.logger.info("[LinkChecker] Resolv::ResolvError: domain=#{domain}") unless Rails.env.production?
   false

または例外メッセージを含める場合:

 rescue Resolv::ResolvError => e
-  Rails.logger.info("[LinkChecker] #{e.class}: domain=#{domain}") unless Rails.env.production?
+  Rails.logger.info("[LinkChecker] Resolv::ResolvError: domain=#{domain}, message=#{e.message}") unless Rails.env.production?
   false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/models/link_checker/checker.rb` around lines 42 - 44, In the rescue block
that matches "rescue Resolv::ResolvError => e" in
app/models/link_checker/checker.rb, remove the redundant e.class from the
Rails.logger.info call (since the exception type is already specified) and
either log just the domain or, if you want the exception details, include
e.message instead; ensure the existing environment guard (unless
Rails.env.production?) remains in place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/models/link_checker/checker.rb`:
- Around line 42-44: In the rescue block that matches "rescue
Resolv::ResolvError => e" in app/models/link_checker/checker.rb, remove the
redundant e.class from the Rails.logger.info call (since the exception type is
already specified) and either log just the domain or, if you want the exception
details, include e.message instead; ensure the existing environment guard
(unless Rails.env.production?) remains in place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: da093c89-a072-4a07-b133-7d1b1f8b1aae

📥 Commits

Reviewing files that changed from the base of the PR and between a860c06 and cfe4eae.

📒 Files selected for processing (2)
  • app/models/link_checker/checker.rb
  • app/models/metadata.rb

@github-actions

Copy link
Copy Markdown

🚀 Review App

URL: https://bootcamp-pr-10058-fvlfu45apq-an.a.run.app

Basic認証: fjord / (ステージングと同じ)
PR更新時に自動で再デプロイされます。

@mousu-a

mousu-a commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

@komagata
お疲れ様です。ステージングでの確認ありがとうございました🙇‍♂️
発生しているバグ、ステージングで特定のリンクカードが展開失敗している件について、調べたことをまとめます。

ログで気になったところ

いただいたログに関してですが、以下の文が気になりました。

rollbar.min.js:1 リンクカードの埋め込みに失敗しました: Error: Error: 400

400が返されていることから、JSからRailsへのfetchは成功しているが、Railsからbad_requestが返されていることが考えられます。
fetch先のMetadataControllerは以下のようになっており、なんらかの理由でmetadataに中身が入っておらず、head :bad_requestを返しているものと考えています。

class API::MetadataController < ApplicationController
...
    card = LinkCard::Card.new(params[:url], params[:tweet])
    metadata = card.metadata
    if metadata
      render json: metadata, status: :ok
    else
      head :bad_request
...

考えられる原因、仮説

3つほど考えています。

  1. YouTubeから200以外のステータスコード、もしくはbot判定のHTMLがレスポンスされてmetadatanilになっている(この場合bad_requestが返される)
  2. Railsのリンクカード処理のどこかで例外が走り、rescueしてnilになっている(この場合bad_requestが返される)
    この場合ステージングでのみ例外が走っているので、タイミング次第で落ちうる、冪等でない処理をしている箇所が怪しいと考えられます。
    LinkChecker::valid_domain?など。(DNS解決失敗, 一時的な名前解決失敗, ステージング環境のDNS/ネットワーク問題)
  3. タイミングが悪く、Youtubeのサーバーが落ちていた。

これら3つの仮説を考えました。
本PRはこれらの仮説を検証するためにログ出力を埋め込むものです。
レビューをお願いします。

@komagata

Copy link
Copy Markdown
Member

@mousu-a すみません、急ぎだったので僕の方で修正してしまっておりました。

@mousu-a

mousu-a commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

@komagata
とんでもないです!とても助かります🙇‍♂️
すみませんが、このままステージングでの確認をお願いしても良いでしょうか?

@mousu-a mousu-a closed this May 22, 2026
@komagata komagata deleted the bug/add-metadata-fetch-debug-logs branch June 2, 2026 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants