Nuxtで生成したsourcemapをNew RelicにPublishするGitHub Actionsを用意する
概要
- NuxtとFirebaseとNew Relicを利用しています。デプロイパイプラインにGitHub Actionsを使っています
- New RelicのBrowser monitoringのErrorがトランスパイル後のスタックトレースのため、調査しづらかった
- sourcemapを連携する術を学び、GitHub Actionsに組み込んだ
設定するまでの流れ
デプロイパイプラインを用意する
これです
New Relicで諸々用意する
手順はぐぐればでてくるのでここでは省略します
- Browser monitoringの設定
- scriptファイルの方を選択したので、JavaScriptファイルを
~/public
以下に保存します
- scriptファイルの方を選択したので、JavaScriptファイルを
- Nuxtにscriptファイルをロードする仕組みの用意(例は下記に貼り付けている)
- API Keyの発行
- sourcemapをPublishするときに利用します
export default defineNuxtConfig({
// ...
app: {
head: {
script: [
{
key: 'NewRelic',
src: `/js/newRelic/newrelic.js`,
defer: true,
type: 'text/javascript'
}
]
}
},
// ...
この設定をすることで、トランスパイル後のコードで、 window.newrelic.xxx
と呼び出すことができます
@newrelic/publish-sourcemap
と@newrelic/publish-sourcemap
をインストールする
プロジェクトに
$ yarn add --dev "@newrelic/publish-sourcemap" "@types/new-relic-browser"
- 1つめはsourcemapをNew Relicへpublishするためのパッケージです
- 2つめはブラウザ上で利用するNew Relicの型パッケージです。これがないとVSCode上での赤い波線とTypeCheckで死ぬ
Nuxtでsourcemapを出力する設定を追記
これでよしです
export default defineNuxtConfig({
// ...
sourcemap: {
server: true,
client: true
}
})
app.vueやPluginでNew Relicに連携するreleaseIdとreleaseNameを指定できるようにする
今回僕はPluginでやりました。(App.vueのほうが簡単に設定はできます)
$ mkdir -p ~/plugins/newrelic/
$ touch ~/types/newrelic.d.ts
$ touch ~/plugins/newrelic/newrelic.ts
/// <reference types="new-relic-browser" />
declare global {
interface Window {
newrelic?: typeof newrelic
}
}
export { }
import { getVersion } from './version'
export default defineNuxtPlugin(() => {
if (!process.client) { return }
const nuxtApp = useNuxtApp()
nuxtApp.hook('app:mounted', () => {
const newrelic = window.newrelic || undefined
if (!newrelic) { return }
// ビルド時に生成されるversion.tsからリリース情報を取得
const version = getVersion()
newrelic.addRelease(version.releaseName, version.releaseId)
})
})
- この時点では、
version.ts
がないため、動きません -
newrelic
変数は、レンダリング後でないと利用できないので、nuxtApp.hook('app:mounted', ...)
にしています -
newrelic.addRelease
でリリース時に生成される値をブラウザ上で実行することができます
version.ts
を生成するコマンド(JSファイル)を用意する
import fs from 'fs'
import path from 'path'
// 環境変数からリリース情報を取得
const releaseId = process.env.RELEASE_ID || 'defaultReleaseId'
const releaseName = process.env.RELEASE_NAME || 'defaultReleaseName'
// TypeScriptファイルの内容
const content = `
export const getVersion = (): { releaseId: string, releaseName: string} => {
return { releaseId: '${releaseId}', releaseName: '${releaseName}' }
}
`
console.log('RELEASE_ID: ', releaseId, ', RELEASE_NAME: ', releaseName)
// ファイルを書き出す
const filePath = path.join(__dirname, '../../plugins/newrelic/version.ts')
fs.writeFileSync(filePath, content)
というTypeScriptファイルを用意します
version.ts
をビルド前に生成するhookを設定する
最初、package.json
にコマンドを並べて追加しようと思いましたが、Nuxtのドキュメントを漁ったところ、すばらしい機能がありました
ビルド前にプログラムが実行できるようなので追記します
export default defineNuxtConfig({
// ...
hooks: {
build: {
before () {
if (lifecycle === 'build') {
require('./generate-release')
}
}
}
},
// ...
これで、 $ yarn run build
で、version.ts
が生成されます
GitHub ActionsのJobを用意する
これまで用意したファイルをピタゴラしていきます。今回採用した方法は
sourcemap publshに関連するGitHub ActionsのJobを4つ用意して組み合わせて、コマンド、ソースコードで指定できるようにする
なので
- releaseIdとreleaseNameを自動生成するコマンドを実行し、環境変数として登録?する
- Nuxtビルド時に
version.ts
を出力するコマンドを叩く →version.ts
が自動生成される - 合わせて生成された
publish-sourcemap
コマンドを使って、sourcemapをNew RelicへPublish - アプリケーションをデプロイ
という手はずを取りました めんどい
まず1はこうしました
# ...
- name: Set up newrelic release environment variables
run: |
RELEASE_ID=$(uuidgen)
RELEASE_NAME=${{ needs.setup.outputs.environment }}_${RELEASE_ID}
echo "RELEASE_ID=${RELEASE_ID}" >> $GITHUB_ENV
echo "RELEASE_NAME=${RELEASE_NAME}" >> $GITHUB_ENV
# ...
- Ubuntuのイメージなので、
uuidgen
が利用できるため、こいつを使って自動生成して環境変数にしています - それぞれ次のJobで利用したいので、
$GITHUB_ENV
に追加します
2つめはNuxtのビルドです
# ...
- name: Build Nuxt
run: yarn build
env:
# ...
RELEASE_ID: ${{ env.RELEASE_ID }}
RELEASE_NAME: ${{ env.RELEASE_NAME }}
# ...
- envに先ほど用意した環境変数を足してください
- このタイミングで、version.tsが生成されて、
newrelic.addRelease
に環境変数経由で値が渡ります
3つめはsourcemapのpublishコマンドです。すーぱーごちゃっています
# ...
- name: Upload sourcemap for Newrelic
run: |
APPLICATION_URL=$(echo ${APPLICATION_URL_}/_nuxt | sed -e "s|https://|https:\\\/\\\/|g")
ls -l .nuxt/dist/client/_nuxt/*.js.map | \
sed -e "s|^.*\.nuxt|.nuxt|g" \
-e "s|\(\.nuxt/dist/client/_nuxt\)/\(.*\.js\)\.map|./node_modules/.bin/publish-sourcemap \1/\2.map ${APPLICATION_URL}/\2 --apiKey=${N_API_KEY} --applicationId=${N_APPLICATION_ID} --releaseName ${RELEASE_NAME} --releaseId ${RELEASE_ID}|g" | \
xargs -I{} bash -c {}
env:
NEWRELIC_API_KEY: ${{ vars.N_API_KEY }}
NEWRELIC_APPLICATION_ID: ${{ vars.N_APPLICATION_ID }}
RELEASE_ID: ${{ env.RELEASE_ID }}
RELEASE_NAME: ${{ env.RELEASE_NAME }}
APPLICATION_URL_: ${{ vars.APPLICATION_URL_ }}
# ...
- まず、環境変数を用意します。
APPLICATION_URL
でアプリケーションをデプロイするURLを用意します。これからpublishするsourcemapと公開されているどのJavaScriptが組み合わせなのかが必要なためです- めっちゃ置換するので、sedにてエスケープをつけていますが、これ今思いましたが、
|
で区切り文字しているので、いらないな多分
- めっちゃ置換するので、sedにてエスケープをつけていますが、これ今思いましたが、
-
publish-sourcemap
のコマンドの全体像はこんな感じです↓ これを作っているだけです$ ./node_modules/.bin/publish-sourcemap ${ソースマップファイルのパス} ${公開されている監視したいWebサービスのJavaScriptファイルURL} --applicationId=${N_APPLICATION_ID} --apiKey=${N_API_KEY} --releaseId=${RELEASE_ID} --releaseName=${RELEASE_NAME}
- releaseIdとreleaseNameは必須なはずなのに、optionalなのがハマりポイント
- ビルド時に生成されたmapファイルを
ls -lh
で検索して、行ごとにpublish-sourcemap
コマンドに置換して、最後にxargs
で行単位にbashで実行するというコマンドです
ここまで無事完了したら4番目はデプロイするだけです!
ハマったところ
releaseId
と releaseName
について
先人のおかげで助かりました(というか踏み抜いた)
releaseId
と releaseName
はコマンドとソースコードでどちらでも利用できるようにする
リリース時、最初、@newrelic/publish-sourcemap
をコマンドでガチャガチャ検証してたので、うまくいったのち、newrelic.addRelease
が必要なのね。となり、あれ?これはビルド前に必要だけど、ビルド後にコマンド使ってpublishしないといけないよね?となり、前述に説明した手法でリリースできるようにしました
ただ、あんまりイケていないと思うので、もっと良い案があれば別案を採用したいです
publish-sourcemap
はJavaScriptでの実行もできるので、生成したreleaseIdとreleaseNameは環境変数のままで、どうにかして生成されたsourcemapを。。。って思ったけど、トランスパイル後は後追いできないじゃん。うーんって感じ
環境変数で渡す分には簡単なんですが、Nuxtの場合、あらかじめ nuxt.config.ts
に追加しないといけないので、アカンとなりました。。。あれ???
書いてて思ったんですが、nuxt.config.ts
上では、直接.env
を参照できるので、ダイナミックにruntimeConfig
に渡すことはできるのでは? そしたら、2がいらなくなりますね
今日はもう遅いので明日(今日)以降頑張ります(執筆時00:24)
New Relic Browser monitoringのErrorsでリストアップされるErrorがわからなかった
いざsourcemapをpublishできたとて、本当にうまく動くのかわからなかったです。ガチャガチャ試して無理やりエラー出力さしても、ブラウザのconsole上では確認できるが、New Relicでは確認できねぇとなっていました
ChatGPTに聞きまくり&ドキュメントをあさりまくりでここにたどり着く
また、Query Your Data
画面でクエリで確認することもできます(いつも適当に書いているやつ)
SELECT * FROM JavaScriptError WHERE appName = 'アプリケーションの名前' SINCE 24 HOURS AGO
まだよくわかっていないですが、致命的なエラー(操作中にアプリケーションが停止するれべる)だと通知してくれるとのことらしいです(多分)
そうすると、無理やりエラー出したとしても意味がないらしいです
またドキュメントを漁った結果、よさそうなAPIに出会えたのでこうしました
const err = new Error('test error')
window.newrelic.noticeError(err)
これで無事意図的にエラーを追加することができ、sourcemapがpublishされていることが確認できました!
雑感
- ちょっとつかれた
- これからもっと活用していきたい気持ち
Discussion