VercelのログをCloud Loggingに送りたい!! with Cloudflare Workers
この記事は モニクル Advent Calendar 2023 の 8 日目の記事です。
@beaverjrです。
モニクルでSREをしています。
はじめに
Vercelでは、出力したログを各プロジェクトのFunctions画面でリアルタイム表示させることができますが、保存はされません。
ログを永続的に保持しておくためには
Vercelが連携している外部loggerサービス
または
LogDrainの機能
が利用できます。
連携している外部loggerサービスはIntegrationの設定で簡単に利用できるようになるため便利です。
しかし、Google Cloudにログを集約したい!など、Cloud Loggingにログを送りたいケースもあると思います。
本記事ではLogDrainの機能を使用し、VercelのログをCloudflare Workersを経由してCloud Loggingに転送する方法を紹介します。
構成
全体の構成は下記の図の通りです。
LogDrainの機能だけでは直接Cloud Loggingにログを転送することはできず、ヘッダーの追加とログの整形を行う必要があります。
Cloudflare Workersを採用した理由は
- 早い!0ms Cold Starts
- 安い!
https://www.cloudflare.com/ja-jp/developer-platform/workers/ - Cloudflare Workersの制約も問題なさそう
これらに加え、ちょうどCloudflare使ってみたい熱も高まっていたためです。
設定手順
※Cloudflare Workers, KVの作成方法は公式の手順を参考にしてください
1.Google Cloudのトークン取得用のCloudflare Workersを作成
- Google Cloudのアクセストークンを取得し、Cloudflare Workers KVに保存する(スコープは
logging.write
)- 下記のライブラリを使用して取得し、KVに保存します。
https://github.com/Schachte/cloudflare-google-auth
- 下記のライブラリを使用して取得し、KVに保存します。
- トークンの有効期限が切れる前に、定期的に新しいトークンを取得する
- Cron Triggersで定期的に実行するようにします。
import GoogleAuth, { GoogleKey } from 'cloudflare-workers-and-google-oauth'
export interface Env {
GCP_SERVICE_ACCOUNT: string;
<KV名>: KVNamespace;
}
export default {
async scheduled(
Event: ScheduledEvent,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
// https://developers.google.com/identity/protocols/oauth2/scopes
const scopes: string[] = ['https://www.googleapis.com/auth/logging.write']
const googleAuth: GoogleKey = JSON.parse(env.GCP_SERVICE_ACCOUNT)
// 取得したトークンをKVに保存する
const oauth = new GoogleAuth(googleAuth, scopes)
const token: any = await oauth.getGoogleAuthToken()
await env.<KV名>.put('key', token);
const key: string | null = await env.<KV名>.get('key');
return new Response(key);
},
};
2.ヘッダー追加・ログ整形用のCloudflare Workersを作成
- Vercel→Cloudflare Workersの認証
- 手順1でKVに保存したトークンを取得
- ヘッダー追加・ログ整形
- Cloud Logging APIを使用して、整形したログを送信
- Vercelではエンドポイントの所有権を確認するため、Vercelが指定するヘッダーを返す
を行います。
// VercelからCloudflare Workersへのアクセスを認証するために、認証ヘッダーを使用
// 認証ヘッダーは、Vercel側で指定
// Cloudflare Workers側では認証ヘッダーを検証して、認証されたリクエストのみ処理
export default {
async fetch(request: Request, env: any, ctx: any) {
const AUTH_HEADER_KEY = env.AUTH_HEADER_KEY;
const AUTH_HEADER_VALUE = env.AUTH_HEADER_VALUE;
const psk = request.headers.get(AUTH_HEADER_KEY);
if (psk === AUTH_HEADER_VALUE) {
return handleRequest(request, env);
}
return new Response('Sorry, you have supplied an invalid key.', {
status: 403,
});
},
};
async function handleRequest(request: Request, env: any) {
// ログの形式をJSON形式に変換する
// ログに必要な情報を追加する
const logData1: any = await request.json();
const logEntries = logData1.map((entry: any) => {
const timestamp = new Date(entry.timestamp).toISOString();
return {
logName: 'projects/<プロジェクト名>/logs/<logname>',
resource: {
type: 'global',
},
severity: 'INFO',
timestamp: timestamp,
jsonPayload: {
id: entry.id,
message: entry.message,
source: entry.source,
requestId: entry.requestId,
statusCode: entry.statusCode,
proxy: entry.proxy,
projectId: entry.projectId,
deploymentId: entry.deploymentId,
host: entry.host,
path: entry.path,
},
};
});
// KVからトークンを取得する
const newAccessToken = await env.<KV名>.get('key');
// Cloud Logging APIを使用して、ログを送信
// ログは、指定したプロジェクトとログ名のログに書き込まれる
const response = await fetch('https://logging.googleapis.com/v2/entries:write', {
method: 'POST',
headers: {
Authorization: `Bearer ${newAccessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ entries: [logEntries] }),
});
if (!response.ok) {
return new Response('Sorry, an error occurred.', {
status: 500,
});
}
return returnResponse200(env);
}
// Vercelが指定するヘッダーを返す
function returnResponse200(env: any) {
const response = new Response('Hello from Cloudflare Workers!', {
status: 200,
headers: {
'x-vercel-verify': env.X_VERCEL_VERIFY,
},
});
return response;
}
3.VercelでLogDrainを作成する
Configure a log drainの手順に沿って設定します。
-
希望のSource、Delivery Format(今回はJSONを選択しました)、Projectを選択します。
-
Custom Headersで認証用のヘッダーを追加します。
-
エンドポイントに手順2のCloudflare WorkersのURLを指定します。
-
手順2のCloudflare側で環境変数(例:X_VERCEL_VERIFY)にvercel-verification-stringを設定します。
- Verifyをクリックし、検証します。
- Test Log Drainで実際にテストログを送信します。
- 問題なければ、Add Log Drainをクリックして完了です。
- ログがCloud Loggingに転送されはじめます🎉
まとめ
Cloudflare Workersを使用して、VercelのログをCloud Loggingに転送することができました。
転送されたログは、構造化ログとして書き込まれるため、検索やデータの活用が容易になります。
個人的には今年Cloudflareを初めて業務で使い、こちらの記事を書くことができたので感慨深いです。
アドバイスいただいた@k2さん、ありがとうございました!!!
明日は @FAMAsoon さんです!よろしくお願いします!
参考
Discussion