HTTP リクエスト ID
この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。
最終更新日 2023年06月14日(水)
Table of Contents
HTTP リクエスト ID を使用すると、特定の Web リクエストのルーターログを、その同じリクエストの Web dyno のログに関連付けることができます。
仕組み
Heroku ルーターは、受信したすべての受信 HTTP リクエストに対して一意のリクエスト ID を生成します。この一意の ID は次に、X-Request-ID
と呼ばれる HTTP ヘッダーとしてアプリケーションに渡されます。
あるいは、リクエストを作成するときに X-Request-ID
ヘッダーを指定できます。この値は 20 ~ 200 文字であり、かつ ASCII 文字、数字、または文字 +
、/
、=
、-
で構成されている必要があります。無効な ID は無視され、生成された ID に置き換えられます。
その後、アプリのコードはこの ID を読み取り、ログに記録したり、その他の目的に使用したりできます。
ルーターログ
これらのリクエスト ID は、Heroku ルーターのログでも request_id
として表示されます。 次に例を示します。
$ heroku logs --ps router
2013-03-10T19:53:10+00:00 heroku[router]: at=info method=GET path=/ host=example-app-1234567890ab.herokuapp.com request_id=f9ed4675f1c53513c61a3b3b4e25b4c0 fwd="215.90.1.17" dyno=web.4 connect=0ms service=61ms status=200 bytes=1382
例: H13 エラーをアプリのバックトレースに関連付ける
シナリオ: アプリが不定期の H13 エラーを報告しています。ルーターは dyno が接続を閉じたことを報告しています。どのようにすれば、これらのリクエストのいずれかに関連付けられた Web dyno のログが見つかるでしょうか。この場合、特に多くのデータがログに含まれているビジーなアプリでは、リクエスト ID が役立ちます。
H13 のルーターログの行を見つけます。
2010-10-06T21:51:37-07:00 heroku[router]: at=error code=H13 desc="Connection closed without response" method=GET path=/ host=example-app-1234567890ab.herokuapp.com request_id=30f14c6c1fc85cba12bfd093aa8f90e3 fwd="215.90.1.17" dyno=web.15 connect=3030ms service=9767ms status=503 bytes=0
リクエスト ID (この例では 30f14c6c1fc85cba12bfd093aa8f90e3
) に注意してください。次に、その ID のログを (Papertrail やその他のログ検索方法を使用して) 検索し、前のセクションに示されているミドルウェアによってログに記録されたリクエストを見つけることができます。
2010-10-06T21:51:37-07:00 heroku[web.15]: request_id=30f14c6c1fc85cba12bfd093aa8f90e3
2010-10-06T21:51:37-07:00 heroku[web.15]: /usr/local/heroku/vendor/gems/excon-0.14.0/lib/excon/ssl_socket.rb:74: [BUG] Segmentation fault
ここでは、SSL ライブラリか Ruby インタープリタ、またはその両方のバグのために、このリクエストが dyno をクラッシュさせていることがわかります。新しいバージョンの Ruby へのアップグレードが役立つ可能性があります。そうでない場合は、バグを提出するために使用できるバックトレースがあります。
Rails での使用法
デフォルトでは、Rails 3.2 以上では X-Request-ID
が取得され、リクエストの UUID として設定されます。このリクエスト ID をアプリケーションログに関連付けるには、config/environments/production.rb
に次の行を追加します。
config.log_tags = [ :uuid ]
Rails 5.0 以上では、次のようになります。
config.log_tags = [ :request_id ]
これにより、ログの値がリクエスト ID にタグ付けされます。
2014-02-07T00:58:00.978819+00:00 app[web.1]: [6a7f7b2f-889b-4ae8-b849-db1f635c971c] Started GET "/" for 99.61.71.11 at 2014-02-07 00:58:00 +0000
ここでは、6a7f7b2f-889b-4ae8-b849-db1f635c971c
がリクエスト ID です。Rails で UUID にタグ付けされたログ記録についての詳細は、ここで入手できます。使用可能な X-Request-ID
が存在しない場合は Rails が自動的に UUID を生成するため、これを開発環境で有効にすると、X-Request-ID
ヘッダーで提供されていなくても “リクエスト ID” が表示されることに注意してください。
Ruby Rack ミドルウェアでの使用法
リクエスト ID をログ記録するためのサンプルの Rack モジュールを次に示します。
module Rack::LogRequestID
def initialize(app); @app = app; end
def call(env)
puts "request_id=#{env['HTTP_X_REQUEST_ID']}"
@app.call(env)
end
end
Rails なしで Rack を使用している場合は、より包括的な Rack ミドルウェアとして、Heroku Request ID gem をお勧めします。Rails を使用している場合は、この gem を使用しないでください。この gem により、Rails のリクエストのログ記録機能が失敗します。この gem を Rack と共に使用する場合は、Rack::Runtime
が有効になっているとタイミング情報が提供されます。
use Rack::Runtime
Node.js での使用法
logfmt Node モジュール >=0.21.0
を使用している場合、X-Request-Id
ヘッダーは、すべての Web リクエストで request_id
として自動的にログに出力されます。
Node アプリで logfmt
を使用したくない場合は、Node のコア HTTP モジュールではヘッダーキーが小文字になるため、ヘッダーが req.headers['x-request-id']
と指定されることに注意してください。
Django での使用法
Django では、優れた django-log-request-id
ライブラリを使用して、リクエスト ID のすべての機能を簡単に使用できます。最初に、それを requirements.txt
ファイルに追加することによってインストールする必要があります。
django-log-request-id==1.0.0
次に、新しく入手可能になった Django ミドルウェアをアプリケーションの settings.py
にインストールする必要があります。それが最初に指定されたミドルウェアであることを確認してください。そうでないと、正しく機能しない可能性があります。
MIDDLEWARE_CLASSES = (
'log_request_id.middleware.RequestIDMiddleware',
...
最後に、settings.py
を変更して、送信リクエストを stdout
に正しく出力するように Django を設定する必要があります。
# Support for X-Request-ID
LOG_REQUEST_ID_HEADER = 'HTTP_X_REQUEST_ID'
LOG_REQUESTS = True
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'request_id': {
'()': 'log_request_id.filters.RequestIDFilter'
}
},
'formatters': {
'standard': {
'format': '%(levelname)-8s [%(asctime)s] [%(request_id)s] %(name)s: %(message)s'
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'filters': ['request_id'],
'formatter': 'standard',
},
},
'loggers': {
'log_request_id.middleware': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False,
},
}
Java での使用法
Java でリクエスト ID にアクセスしたり、それをログに記録したりする方法は、アプリケーションで使用しているフレームワークによって異なる可能性があります。これは、Java Servlet フィルターを使用してヘッダーにアクセスし、それを System.out
に出力する簡素化された例です。
RequestIdLoggingFilter.java
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class RequestIdLoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (servletRequest instanceof HttpServletRequest) {
final String requestId = ((HttpServletRequest) servletRequest).getHeader("X-Request-ID");
if (requestId != null) {
System.out.println("request_id=" + requestId);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<filter>
<filter-name>RequestIdLoggingFilter</filter-name>
<filter-class>com.example.config.RequestIdLoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RequestIdLoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
PHP での使用法
優れた Monolog パッケージを使用している場合は、X-Request-Id
HTTP ヘッダーのコンテンツをレコードの extra
フィールドの配列に追加するカスタムプロセッサーを簡単に登録できます。
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
require('vendor/autoload.php');
$logger = new Logger('my_logger');
$logger->pushHandler(new StreamHandler('php://stderr', Logger::DEBUG));
$logger->pushProcessor(function ($record) {
$record['extra']['request_id'] = isset($_SERVER['HTTP_X_REQUEST_ID']) ? $_SERVER['HTTP_X_REQUEST_ID'] : null;
return $record;
});
$logger->addInfo('Hello World');
その後、ログに記録されたメッセージの heroku logs
ストリームにはリクエスト ID が含まれます。
2014-11-19T15:10:32.027154+00:00 app[web.1]: [2014-11-19 15:10:31] my_logger.INFO: Hello World [] {"request_id":"a0a86a10-2a3a-4852-ae80-893e5d4e8348"}
詳細は、プロセッサーに関するドキュメントのセクション、または Symfony を使用した Monolog プロセッサーの統合に関するフレームワーク固有のドキュメントを確認してください。
Scala での使用法
Scala でリクエスト ID にアクセスしたり、それをログに記録したりする方法は、アプリケーションで使用しているフレームワークによって異なる可能性があります。これは、Play Framework フィルターを使用してヘッダーにアクセスし、それをログに記録する簡素化された例です。
import play.api.mvc._
import play.api.Logger
import scala.concurrent.Future
import play.api.libs.concurrent.Execution.Implicits.defaultContext
object RequestIdLoggingFilter extends Filter {
def apply(nextFilter: (RequestHeader) => Future[Result])
(requestHeader: RequestHeader): Future[Result] = {
Logger.info(s"request_id=${requestHeader.headers.get("X-Request-ID").getOrElse("None")}")
nextFilter(requestHeader)
}
}
このフィルターは、Global オブジェクトを宣言することによって使用できます。
object Global extends WithFilters(RequestIdLoggingFilter) {
// ...
}
その後、ログに記録されたメッセージの heroku logs
ストリームにはリクエスト ID が含まれます。
2014-11-19T16:08:59.427428+00:00 app[web.1]: [info] application - request_id=2bcf9cec-7717-40e3-9bcf-b438b93b7770
Clojure での使用法
Clojure でリクエスト ID にアクセスしたり、それをログに記録したりする方法は、アプリケーションで使用しているフレームワークによって異なる可能性があります。これは、Compojure ルーティングライブラリを使用してヘッダーにアクセスし、それをログに記録する例です。
(defroutes request-id-logger
(ANY "*" {:keys [headers params body] :as request}
(println (format "request_id=%s" (get headers "x-request-id")))))
このルートが他のルートの前に宣言されている限り、ルーターが他のいずれかのルートに一致するように移動する前に、このルートが実行されます。
その後、ログに記録されたメッセージの heroku logs
ストリームにはリクエスト ID が含まれます。
2014-12-20T23:23:56.782823+00:00 app[web.1]: request_id=0e748c63-70ef-4d45-ab97-96510ddcdf5a