最近お問い合わせ頂いたサポートチケットより、node.jsのAPM AgentでNew Relic Browserを有効化できない場合のチェックポイントと解決策についてご案内します。

対象ユースケース

  • New Relic APM node.js AgentでNew Relic Browserを有効にする場合

問題

  • New Relic Browserで計測できない
  • newrelic.getBrowserTimingHeader()がエラーになったり、返り値が<!-- NREUM: (1) -->になったりして正しいタグを取得できない

解決策

New Relic APM AgentはNew Relic Browserの計測を有効にする機能を持っています。node.jsの場合はこのドキュメントに記載されている通り、newrelic.getBrowserTimingHeader()の結果をHTMLのheadタグ内に埋め込む処理を記述する必要があります。ところがこのAPIを実行しようとするとnewrelic is not definedというエラーが出たり、返り値が<!-- NREUM: (1) -->のような値となり正しいタグを得られないことがあります。この状況ではNew Relic Browserは正しくインストールされていないため計測もできません。

まず、APMによるBrowserの有効化ができる前提として、APM Agentが正しくインストールされトランザクションを計測できている必要があります。正しくインストールされていない場合にnewrelic is not definedのようなエラーがでる可能性があります。特に注意すべき点は以下の2点です。

  1. newrelic.jsをルートディレクトリ(多くの場合nodeコマンドの引数にわたすファイルと同じディレクトリ階層)に配置する。
  2. 最初に実行されるファイル(多くの場合nodeコマンドの引数にわたすファイル)の先頭にrequire('newrelic')を記述する。

また、アプリケーション設計と関連する問題として、HTMLのコードをAPMをインストールしたnode.jsプロセスが返している必要があります。どのような場合に適用できないかというと、Single Page Application (SPA)で完全な静的HTMLコードを配布し、node.jsはAJAXのようなAPI通信のみでレスポンスを返している場合です。このような場合は静的HTMLコードにCopy/Pasteによる方法でタグを追加し、New Relic Browserを有効化する必要があります。

ここまで確認できたら、newrelic.getBrowserTimingHeader()が何を返しているか確認しましょう。おそらく正しく計測できていない場合は、<!-- NREUM: (1) -->のような値が返ってきているはずです。()内の数字は異なることがありますが、これはエラーコードを示しており、詳細な理由はAgentのログファイルに出力されています。

{"v":0,"level":40,"name":"newrelic","hostname":"C02ZQ5CPMD6P","pid":31780,"time":"2020-10-06T08:58:09.005Z","msg":"NREUM: transaction missing or ignored while generating browser monitoring headers","component":"api"}

ログにはエラーコードが出ていないため時刻で紐付けする必要がありますが、もし確認したい場合はソースコードを参照してください。

このエラーのうち、Browserが有効になっていないであったり、キーが不正、といったあたりはインストール手順を再度確認して修正できます。ここでは、<!-- NREUM: (1) -->「transaction missing or ignored while generating browser monitoring headers」の対応を取り上げます。

このエラーはnewrelic.getBrowserTimingHeader()を実行する際にトランザクションを計測できていない場合に発生します。一つはAPM Agentはインストールされているものの、トランザクションの計測ができていないケースです。これはサポートしていないWebフレームワークを利用している場合に起きる可能性があるため、custom instrumentationを適用し計測できるようにする必要があります。もう一つのケースがこのAPIを初期化時しか呼ばないケースです。一例として、テンプレートエンジンとしてEJSを使っている場合で説明しますが、任意のテンプレートエンジンで起きる可能性があります。

const template = `
<!DOCTYPE html>
<html lang="en">
  <head>
  <title><%= config.TITLE %></title>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  ${newrelic.getBrowserTimingHeader()}
//以下略
`;

return (req, res, next) => {
  return res.send(ejs.render(template, {
    config: settings
  }));
};

テンプレートエンジンにわたすHTMLのテンプレートを定数として定義しており、その中でAPIを呼び出しています。このように記述すると、node.jsのWebサーバーが起動する際に一度だけAPIが実行され、その際はトランザクションが計測されていないので、<!-- NREUM: (1) -->が返り、Browserの計測ができません。

これを回避するためには、テンプレートをHTMLに変換する処理のたびにこの関数を実行するように記述します。例えば、 <%- newrelic.getBrowserTimingHeader() %> と記述できます。ただし、この場合newrelicというオブジェクトをテンプレートエンジン内で解決できなければなりません。ExpressとJadeやSwigの組み合わせの場合はapp.localsを使って実現する方法がドキュメントのExampleに記載されています。

もう一つの方法がテンプレート変数として渡す方法になります。先程のコードであれば次のように変更します。

const template = `
<!DOCTYPE html>
<html lang="en">
  <head>
  <title><%= config.TITLE %></title>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  <%- newrelicBrowser ->
//以下略
`;

return (req, res, next) => {
  return res.send(ejs.render(template, {
    config: settings,
    newrelicBrowser: newrelic.getBrowserTimingHeader()
  }));
};

すると、期待どおりのタグを取得し、Browserの計測ができるようになりました。

もし、この通りやったけどうまくいかないであったり、具体的なやり方がわからない、といった場合はお気軽にお問い合わせください。

ソフトウェアエンジニア、インフラエンジニアなど自社開発や自社運用の現場で経験を積んだのち外資系ソフトウェアベンダーでのテクニカルサポートを経て現職。New Relicユーザーだった経験あり。コミュニティでの登壇活動も多く、Microsoft MVPを7年連続受賞中。Microsoft Certified Azure Solutions Architect Expert。得意分野はC#をはじめとするソフトウェア開発、Kubernetes関連技術およびパブリッククラウド。 View posts by .