New Relic CodeStream をNext.js & Remote Containersな環境で使ってみた!(だが時間切れで断念した => できてました!)

本記事はNew Relic Advent Calendar 2021の8日目です。結論から言うとタイトルにある通り時間切れで挫折しました。

なのでここからのブログの内容は最終的に時間切れで挫折に至るまでの戦いの記録です。あまり参考にはならない。

=> 見るべき場所を間違っていただけで実はちゃんとできていた!とうわけで追加しました。

New Relic CodeStreamとは

CodeStreamというのはNew Relicが新たにリリースした開発者向けのツールで、開発ワークフローを支援するものである。具体的にはGitHub等のリポジトリと接続してプルリク作ったり、課題管理系のツールと連携したり、New Relic Oneとの連携ができるようになっている。New Relicといえばモニタリングおよびオブザーバビリティを実現するサービスといった感じだけどCodeStreamはVS CodeなどのIDEにインストールすることで開発ワークフローが強化されるというもの。あと、オブザーバビリティとの連携ってことでNew Relicで検知したエラーからソースコードに飛んだりするのが簡単に行えるらしい。正直なところプルリクうんぬんとか課題管理系ツールとの連携はそれほど強い興味をひかなかったんだけどNew Relic Oneとの連携は興味深い。

Remote Containers

さて、僕は普段から開発にはVS Codeを使っているのだけど、それだけでなくRemote Containersを活用している。Remote Containersについては以前の投稿に詳しいのでこちらを参照。M1 Macbook Proに移行していろいろあったものの、依然としてRemote Containers最高だと思ってるので今回のNew Relic CodeStreamも単にVS Codeで動くというだけでなく、僕の普段の開発環境下で動くかどうかが気になるところなので試してみた。

まずはインストール

テスト用の環境としてNext.jsのアプリケーションのリポジトリとNode.jsのコンテナを用意した。サンプルリポジトリこちら

CodeStream自体のインストールは簡単でVS CodeのExtensionで導入するだけ。そしてGitHub連携をすると特定リポジトリだけでなくGitHubアカウント全体が見えるようになる

なんかよく見たらRemote Containersでもほげほげって買いてある

f:id:Keisuke69:20211206222845p:plain

動く…、動くぞコイツ…Remote Containersでも動くぞーーーーー!

というわけで目的を果たせたので本ブログは終了。

せっかくなので試す

Remote Containersでも特に問題なく動くことがわかったので実際に試してみることにした。

繰り返しになるがCodeStreamで何ができるかって言うと大きく以下の5つだ。

  • Pull Request Integration
  • Feedback Request
  • Code Discussion
  • Issue Management
  • Observability

この中でNew Relicと言えばオブザーバビリティということでNew Relicとの連携機能が一番真価を発揮するポイントでしょうということでこれを試す。まずはNew Relicのほうも今回の検証用にセットアップしていく。

Next.jsで作ったWebアプリケーションをモニタリングする

早速New Relicにサインアップします。New Relicはサインアップすると基本的にすべての機能が利用できて、課金の対象はユーザ数と保存するデータの量です、たしか。サインアップ自体はよくある感じのやつで何も難しくないので割愛。

サインアップするとインストレーションのガイドが出てくるんですが今回みたいなSPAなWebアプリケーションだけの場合はどれを選択するのが最適なんでしょうね。 f:id:Keisuke69:20211207094056p:plain

表示されているのはどれもサーバーサイドの言語でどれを選べいいかわからなかったので "See other options"をクリックしてみた。

が、これもわからない。 f:id:Keisuke69:20211207094317p:plain

わからないので中ほどにある "Browser Metrics" を選択しておいた。あとで変更できるでしょ。で、次の画面上で "Deployment Method" として "Copy/Paste Javascript code" を選択。 f:id:Keisuke69:20211207094612p:plain

次に進むとコピペすべきコードが表示されるんだけど、この長いコードを <HEAD /> の前に差し込む必要があるらしい。なんか手っ取り早いいい方法はないものかと思ってNext.jsでNew Relic使う場合のサンプルとかググってみたら以下のような内容が。

github.com

うーん、なんか違う。ということで大人しくJSのコードを仕込みます。

まずはコピペ用のコードをそのまま newrelic.js とうファイル名で public の下に保存。これを全ページでロードするために _app.tsx に仕込みます。といっても静的なJSファイルを各ページで読み込むだけなので何も難しくない。

import "../styles/globals.css";
import type { AppProps } from "next/app";
import Script from "next/script";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script src="newrelic.js"></Script>
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

そして、実際に登録するとこんな感じで表示されるようになる。これは普通にyarn devで開発サーバを起動してリクエストしてるだけ。 f:id:Keisuke69:20211208004330p:plain

Next.jsのアプリを作成して起動しただけだとエラーとかも起きず面白みもないのでちょっとだけサンプルアプリに手を加える。

猫の画像をランダムに取得できるREST APIがあるのでそこにリクエストして表示するようにしてみる。ちなみに別に猫好きではない。どちらかというと犬派だ。

さて、 'pages/index.ts を編集する。これの実行前には yarn add axios でaxiosをインストールしておいてね。Next.js使ってるけど今回は適当なSPAとして実装した。

import type { NextPage } from "next";
import axios from "axios";
import { useEffect, useState } from "react";

const Home: NextPage = () => {
  const [url, setUrl] = useState();

  useEffect(async () => {
    const url = "https://thatcopy.pw/catapi/rest/";
    const res = await axios.get(url);
    setUrl(await res.data.url);
  }, []);

  return (
    <div>
      <img src={url} width="100%"></img>
    </div>
  );
};

export default Home;

やってることは単純でAPIにリクエストするとランダムで猫画像の情報が返ってくるので、そこから画像のURLを取り出してページ全体に画像を表示してるだけだ。

なお、next/imageを使っているので、srcに外部URLを指定する場合はnext.config.jsにそのドメインを許可する設定が必要なのでこれも追加する。

module.exports = {
  reactStrictMode: true,
  images: {
    domains: ["thatcopy.github.io"],
  },
};

この猫APIは以下のリポジトリで見つけたんだが、このリポジトリがとても便利。Public APIのまとめになっていてAuthの要不要も記載されている。今回のようなサンプルアプリとか作ったりするのに使う適当なAPIを探したりするのにとても便利。

github.com

なお、コミットするとこんな感じで自動でPRが作成されるようだが実際には試していないのでわからない。

f:id:Keisuke69:20211208002934p:plain

New Relic Oneとつなぐ

VS CodeにExtensionを入れると上述のPRとかの機能は使える。が、繰り返して言うがこのサービスの真骨頂はNew Relic Oneと連携してエラーからソースコード上の該当箇所を特定できるということだと思う。それには当然ながらNew Relicでアプリケーションのモニタリングをしている必要がある。そして、そのNew Relic アカウントと連携をする必要がある。

先ほどNew Relic Browserでこのアプリケーションのモニタリングは開始した。なのでそれを連携する。実際につなぐにあたってはAPIキーを発行してそれを登録する必要がある。 f:id:Keisuke69:20211208003311p:plain

New Relic OneのAPIキーはNew Relic Oneのユーザメニューから簡単に作成できるのだがポイントはキーのタイプを User にしておくこと。

f:id:Keisuke69:20211208003726p:plain

作成したらコピーして、VSCode側のAPIキー入力欄に入力して "Connect~"を押す。

f:id:Keisuke69:20211208003935p:plain

紐付ける

f:id:Keisuke69:20211208004036p:plain

だがしかし何も表示されない。。。

f:id:Keisuke69:20211208004133p:plain

とりあえずエラーを表示させねば、ということでサンプルアプリで無理やり例外を投げる。return の直前でthrow するだけだ。

import type { NextPage } from "next";
import axios from "axios";
import { useEffect, useState } from "react";

const Home: NextPage = () => {
  const [url, setUrl] = useState();

  useEffect(async () => {
    const url = "https://thatcopy.pw/catapi/rest/";
    const res = await axios.get(url);
    setUrl(await res.data.url);
  }, []);

  throw "Sample Error";
  return (
    <div>
      <img src={url} width="100%"></img>
    </div>
  );
};

export default Home;

これで実行してみた。

New Relic側はこんな感じでエラーが表示されている。

f:id:Keisuke69:20211208005336p:plain

ちなみにこんな感じでスタックトレースなんかも表示される。

f:id:Keisuke69:20211208005815p:plain

あとはこれがCodeStreamで連携できるか。

だが、VSCode上ではNo Error。リロードらしきボタンを押して何度かリロードしてみたが表示されない。

f:id:Keisuke69:20211208005511p:plain

これはどういうことか。。。もしやSPAなフロントエンドアプリだとCodeStreamの真価を発揮することは叶わないということなのか?

はい、そうです。今の所APMにしか対応しておらずBrowserだと無理っぽい。このドキュメントにもAPMと連携って書いてある。ちなみにAPMってのはApplication Performance Monitoringの略なんだろう。要はインフラではなくアプリケーション観点でのモニタリングができる機能とでも思っていけばいい。余談だけで数年前までは今みたいに包括的なモニタリングソリューションを展開していなかったように思う。New RelicといえばAPMといったイメージだった。

Performance monitoring with CodeStream | New Relic Documentation

ドキュメントはちゃんと確認しましょうね。

というわけでここまでセットアップしたNew Relic BrowserだとCodeStreamの機能は試せなかった。SPAこそこういう機能があればいいなーと思ってたのでこれは少し残念。

SSRでやってみる

さて、ここでやめるも微妙だし、じゃあどうしたものかとしばし考えた結果APMならできる、つまりNode.jsが動いていればできるということでNext.js使ってるんだしServer Side Rendringする形にしたらいいのでは?ということで早速コードを修正。

import type { NextPage } from "next";
import axios from "axios";
import { useEffect, useState } from "react";
import Image from "next/image";

export const getServerSideProps = async () => {
  const url = "https://thatcopy.pw/catapi/rest/";
  const res = await axios.get(url).catch((err) => {
    return err.response;
  });

  const catUrl = await res.data.url;
  return {
    props: {
      url: catUrl,
    },
  };
};

const Home = ({ url }: { url: string }) => {
  return (
    <div>
      <Image src={url} alt="cat" layout="fill"></Image>
    </div>
  );
};

export default Home;

そしてCodeStream側でもAPMを使うようにする。CodeStreamのObservabilityの設定からInstrument my Appを選択。 f:id:Keisuke69:20211208092109p:plain

そのまま次へを押していくとNode.jsがすでに検知されているのでこれもまたそのまま次へ

f:id:Keisuke69:20211208092202p:plain

そしてこんな感じでアプリ名やらライセンスキーやらを設定する。あとnewrelicのライブラリを入れる必要もある。 f:id:Keisuke69:20211208092600p:plain

一通り終わったら起動してみる。なんとなくyarn devではなくyarn buildしてからyarn startしてみる。

…だがしかしNew Relic側には何も表示されないではないか。

Node.jsでAPM使う場合のドキュメントをよく見ると、babelとか使ってる場合はサーバの起動を node -r newrelic ./dist/server.js で起動しろって書いてあるじゃないか。この-rってオプションは特定のモジュールをプリロードするときに使う。というわけで今回はNext.jsのアプリなので./dist/server.jsの部分をNext.jsのコマンドに置き換えて以下みたいな感じで実行してみる。

node -r newrelic node_modules/.bin/next start

無事に起動できてNew Relic側でのモニタリングも開始された模様。

f:id:Keisuke69:20211208203751p:plain

今度こそCodeStream連携を試してみる

エラーが出たらそこから該当のソースコードを開いてくれるというのを試してみたいところ。まずはエラーを発生させるべく以下のコードを追加。具体的にはgetServerSideProps内のreturnの直前に例外(ここではSyntaxError)を投げるようにしただけだ。

<略>

  const catUrl = await res.data.url;
  throw new SyntaxError("This is sample error");
  return {
    props: {
      url: catUrl,
    },
  };

<略>

だがNew Relicの画面を見てもStack Traceが出力されてこない。Stack Traceが出力されないと肝心の機能が試せないのだ。そうしたら以下のページを見つけた。

discuss.newrelic.com

なるほど。普通に例外を投げてもNode.jsがハンドリングして500エラーを返すだけだと。そうするとNew Relicもハンドリングしようがないということらしい。で、それを回避するにはAPM agentの設定ファイルである newrelic.jsに以下の記述を追加すればいいらしい。ちなみに自分の環境では前述の通りクライアント側のモニタリングをするためのコードをpublic/newrelic.jsという名前で保存してしまっているので紛らわしい。失敗した。

error_collector: {
  ignore_status_codes: [404, 500]
}

設定したら再起動。

だが…

エラーが表示されない!!!!

何がだめなのかちょっと調べただけではわからない。何か重大な見落としや勘違いをしている予感もするけどそれが何かわからない。

いろいろ調べたところNew RelicのAPM AgentのAPIでエラーを発行できるらしいのでこれを試してみる。出力する場所はさっきと同じだ。こんな感じで追加する。

(略)

  const catUrl = await res.data.url;

  const err = new SyntaxError("This is sample error");
  const newrelic = require("newrelic");
  newrelic.noticeError(err);
  
  return {
    props: {
      url: catUrl,
    },
  };

(略)

これで再度実行してみると無事に出力された模様。

f:id:Keisuke69:20211208235638p:plain

開いてみるとStack Traceも出力されてる!

f:id:Keisuke69:20211208235756p:plain

だがしかし、IDEで開く的なボタンが表示されるはずなのに表示されてないではないか…

## 結果 最終的に志半ばで時間切れとなりました。きっと何か重要な設定を忘れているんだと思う。時間切れなのでいったんここまでとするもののこれではやってみたブログにすらなっていないので近いうちにリベンジしようと思います。

ここから追記

当初、IDEで開くというボタンが見つからず困り果てた結果時間切れと判断したのですが、実は見るべき場所を間違っていただけでした。

見るべき場所はErrors Inboxだった。というわけでボタンがちゃんとあります。 f:id:Keisuke69:20211209105300p:plain

というわけで Open in IDEをおもむろにクリック。するとこんな感じのダイアログが出て、 f:id:Keisuke69:20211209105606p:plain

次にVS Code側でこんな感じのダイアログが出て、目的のものに近づいてきてる感じがします。

f:id:Keisuke69:20211209105644p:plain

そしてOpenを押すと紐付けるリポジトリ選択の画面が出ますがここはスクショ取るの忘れました。

そしてついに、

f:id:Keisuke69:20211209105729p:plain

こんな感じでエラーの該当箇所が表示されます!

実際の出力はサーバで実行されてる実際のコード、つまり今回の環境ではWebpackでバンドルされた後のコードに紐付いて表示されるようですね。

これとても便利じゃないですか?ローカルの環境で開発中はデバッガ仕込むのも容易ですがプロダクション環境で発生したエラーを調べるのって結構面倒じゃないですか。それがNew Relicでエラー検知してそのままコードの該当箇所まで飛べるのって結構便利。

加えてこんな感じで該当のエラーの担当者を設定したり、resolve/unresolveを設定したり、コメント残したりもできます。

f:id:Keisuke69:20211209110253p:plain

残したコメントなどはNew Relic One側でもこんな感じで見れます。

f:id:Keisuke69:20211209111023p:plain

ただ、一点気になるのは今回試した環境はTypeScriptで書かれてます。TypeScriptってことはビルドのタイミングでJSにコンパイルされて実行されるわけです。で、このCodeStreamはそのコンパイル後のJSのソースに対してポイントするんです。つまり先ほどのコメントとかも大元のtsxファイルには紐付かない。実際にはビルド後の.next/server/pages/index.jsみたいなファイルに紐づきます。

このファイルはビルドすると更新されてしまうし、当然ながらリポジトリにコミットもしないです。なので別の人の環境とかだとどうなるのかなと思って実際に試してみました。結論から言うと特に何も変更せずに.nextを消してビルドし直しただけなら特に問題なかったです。でも元のソースをちょっといじってビルドしなおしたら最初にエラーが出た行にポイントされるようで、エラーが出た後に更新が入ってたらずれてしまう。

f:id:Keisuke69:20211209111619p:plain

これってTypeScriptに限らず、プロダクションで実行されてるバージョンと手元で開発中のバージョンが違うってのは普通によくあることだと思うのでそのあたりをどう扱えばいいのか。

今度こそ結論

というわけど、一度は時間切れで挫折かと思ったのですが実は問題なく使えるようになってました。

モニタリングしてるNew Relicの画面でエラーからIDEを開いて該当のコードに飛べるのはやっぱり便利です。一方でソースコードのバージョンとの兼ね合いとかもうまいこと扱えるとより便利になるような気がします。これは僕がちゃんと設定できていないだけかもしれないのでもうちょっと調べてみようと思います。

あと、結果的にタイトルにあるRemote Containersどうこうは何も苦労することがなかったです。そしてぜひNew Relic Browserとの連携もしてもらえるといいなと思います。

©Keisuke Nishitani, 2020   プライバシーポリシー