ログ SDK

@parallelsandbox/log は小さなブラウザ SDK です。ボックス内(または他の場所)で動かすページに付ければ、そのコンソール出力とエラーはあなたのログプロジェクトに届きます。エージェントはコンソール出力を貼ってもらう代わりに、logs_search、logs_errors、logs_tail で読みます。

ブラウザ専用です。ページの console と window のイベントにフックし、ログサービスはプロジェクトが許可したオリジンからのバッチしか受け付けません。サーバー側のロガーではありません。ボックスで直接動かすプログラムは、その出力を直接読んでください(ログサービスを使わない場合を参照)。

プロジェクトとキー

ログプロジェクトは、トークン(API キーか OAuth アクセストークン。キーの入手方法)を使って REST で作ります。ボックスではなく自分のマシンから作成してください:

curl -fsS -X POST https://log.parallelsandbox.com/v1/projects \
  -H "Authorization: Bearer $PSBX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "example-web", "origins": ["http://localhost:8080", "https://*.box.parallelsandbox.com"] }'

レスポンスは {"project": {"id": "prj_...", ...}, "writeKey": "psw_..."} です。各プロジェクトには次があります。

  • プロジェクト id、prj_...。すべてのクエリで使います(ツールはプロジェクト名も受け付けます)。
  • 書き込みキー、psw_...。ブラウザがバッチごとに送ります。表示されるのはこのレスポンスだけです。このプロジェクトにしか書けないので、ページに置いても安全です。POST /v1/projects/{id}/write-key で交換でき、古いキーはすぐに無効になります。
  • オリジン許可リスト。他のオリジンからのバッチは拒否されます。origins を渡さなければ https://*.box.parallelsandbox.com で、ボックスの URL(sceneUrl と、その web サービスの URL)で開いたページが対象です。渡したリストはこの既定値を置き換えるので、両方必要なら両方書いてください。各項目は scheme://host[:port]、https://*.example.com、null、*(すべてのオリジン、オリジンのないリクエストも含む)で、最大 32 件です。PATCH /v1/projects/{id} に {"origins": [...]} で変更します。
  • プロジェクトごと、送信元アドレスごとのレート制限。

GET /v1/projects で自分のプロジェクトを一覧し、DELETE /v1/projects/{id} でログとソースマップごと削除します。プロジェクトごとに独立したロググループがあり、ログはプロジェクトを削除するまで期限なく保持されます。削除すればログとソースマップの保存の課金が止まります。

インストール

0.1.2 以降を使ってください。ボックスのどの URL のホストからもボックス id を読みます:<id>-<key>.box.parallelsandbox.com(sceneUrl。最初に宣言したサービスが web ならその url でもある)と <id>-<key>-<targetPort>.box.parallelsandbox.com(2 つ目以降の web サービスの url)。key は送りません。メッセージ、スタック、route、extra の中の <id>-<key> はすべて、バッチがページを出る前に <id> に置き換わるので、https://<id>-<key>-5173.box.parallelsandbox.com/app.js は https://<id>-5173.box.parallelsandbox.com/app.js として記録されます。ログサービスも受信時に同じ置き換えをします。これより前のバージョンは、web サービスの URL からボックス id を読みません。

バンドラーを使う場合は npm から:

npm install @parallelsandbox/log@^0.1.2

バンドラーなしなら import map で(@0.1 は 0.1.x の最新):

<script type="importmap">
{ "imports": { "@parallelsandbox/log": "https://cdn.jsdelivr.net/npm/@parallelsandbox/log@0.1/+esm" } }
</script>

初期化

import { init } from '@parallelsandbox/log';

const log = init({
  writeKey: 'psw_...',   // プロジェクトの書き込みキー。これでプロジェクトが決まる
  release: 'a1b2c3d',    // ビルド id。ソースマップをアップロードするときの release と一致させる
});

できるだけ早く、一度だけ呼んでください。writeKey が psw_ で始まらないと init は例外を投げます。もう一度呼ぶと前のロガーを置き換えます。オプション:

オプション 既定値 意味
writeKey 必須 プロジェクトの書き込みキー
endpoint https://log.parallelsandbox.com ログサービス
release 空 ビルド id。アップロードしたソースマップと一致したときだけスタックが解決される
boxId ページのホストから ボックス id。ボックスの URL のホスト(<id>-<key>.box.parallelsandbox.com または <id>-<key>-<targetPort>.box.parallelsandbox.com)から読み、それ以外では空
boxDomain box.parallelsandbox.com ボックスのホストのドメイン
captureConsole true console.debug、log、info、warn、error にフックする
captureErrors true window のエラーと未処理の promise rejection を捕捉する
levels ['info', 'warn', 'error'] 送るレベル。console.debug も送るなら 'debug' を加える
flushIntervalMs 2000 送信の間隔
batchSize 50 1 リクエストのエントリ数、最大 200
maxBuffer 1000 メモリに保持するエントリ数。超えると新しいものが捨てられる
platform 'web' 各バッチに付くラベル

init の後:

  • console.log は info として、他のコンソールメソッドはそれぞれのレベルで送られます。未捕捉のエラーは [uncaught] <メッセージ> at <ファイル>:<行>:<列> としてスタック付きで、未処理の rejection は [unhandledrejection] <メッセージ> になります。
  • エントリは flushIntervalMs ごとにまとめて送られ、ページが隠れたりアンロードされたりするときに残りが送られます。
  • 各バッチには release、platform、ユーザーエージェント、ページのパスとクエリ(route)、ボックス id が付きます。ページがボックスの URL から配信されている場合、ボックス id はそのホストから取られ、key はページの外に出ません。バッチ内の <id>-<key> はすべて <id> になり、ログサービスも受信時に同じ置き換えをして、ページのオリジンからボックス id を取ります。ボックス内で localhost として開いたページには、boxId を渡さない限りボックス id は付きません。

手動呼び出し

init はロガーを返します:

log.info('checkout started', { cart: 3 });
log.warn('slow response', { ms: 2400 });

try {
  riskyThing();
} catch (err) {
  log.error(err, { step: 'riskyThing' }); // Error はスタックを保つ
}

await log.flush(); // テストでページを離れる前に

debug、info、warn はメッセージと任意の extra オブジェクトを、error は Error か任意の値と任意の extra を受け取ります。extra は JSON 化できる任意のオブジェクトで、logs_search で検索できます。ロガーには setBox(id)、setRelease(release)、boxId、そして console と window のリスナーを元に戻す uninstall() もあります。

ソースマップ

minify されたスタックはエージェントには役に立ちません。各ビルドのソースマップを、init に渡したのと同じ release でアップロードすると、logs_errors は元のファイルと行に解決されたスタックを返します。

curl -fsS -X POST https://log.parallelsandbox.com/v1/projects/prj_.../sourcemaps \
  -H "Authorization: Bearer $PSBX_TOKEN" \
  -F "release=a1b2c3d" \
  -F "assets/app-ALQGRFWG.js.map=@dist/assets/app-ALQGRFWG.js.map;type=application/json" \
  -F "assets/log-init-K6FPZHJN.js.map=@dist/assets/log-init-K6FPZHJN.js.map;type=application/json"

マップごとに 1 つのファイルパートを送り、パート名はそのマップが配信されるパス(ページのオリジンからの相対。スタックに現れるスクリプトのパスに .map を付けたもの)にします。同じ release のマップは 1 リクエストでまとめて送れます。アップロードには書き込みキーではなくアカウントのトークンを使います。GET /v1/projects/{id}/sourcemaps?release= でアップロード済みのものを一覧できます。example-web-with-logs には dist/ 全体をアップロードするスクリプトがあります。

スタックは、エラーが届いた時点で一度だけ解決されます。対象は、スタックと release を持つ error レベルのエントリです。ページを動かす前にソースマップをアップロードしてください。それより前に届いたエラーは minify されたスタックのままです。

エージェントから問い合わせる

logs_errors { "project": "prj_...", "since": "30m", "boxId": "<box id>" }
logs_search { "project": "prj_...", "since": "1h", "query": "checkout" }
logs_tail   { "project": "prj_...", "boxId": "<box id>" }

各行は ts、ingestedAt、level、box、release、platform、route、msg、stack、stackResolved、extra、stream を持ちます。logs_search と logs_errors は古い順に nextCursor 付きで返し、logs_tail はロングポーリングで、新しい行が届くか waitSec がたつと、次の呼び出し用の cursor 付きで返ります。パラメータはツールリファレンスにあります。

コスト

ログの取り込みと保持も他の使用量と同様に計量されます。

使用量 クレジット
書き込み 1 GB あたり 760
保存 1 GB・1 か月あたり 33

1 日に数百件のエラーを報告するページでも、月に 1 クレジットに届きません。