BedrockにはCloudWatch Logsにログを記録する機能があります。
ただ、記録したログをそのまま確認しようとしても、目当てのログを見つけるには、ログをひとつひとつ開いて見ていく必要があります。
CloudWatch Logs Insights をつかって、見やすくする方法をご紹介します。
最後に私が作ったクエリーを記載してますので、どうぞ最後まで御覧ください。
また、改良してより良いクエリーが作成できたら、ぜひコメントで教えてください!
手順
CloudWatch Logs Insightsの画面に遷移するには、Bedrockのログイベントを表示した状態で、「アクション」メニューの「Logs Insightsで表示」をクリックします。
赤枠の部分にクエリーを入力して、ログの検索や出力を加工できます。
初期状態では以下のクエリーが入力されています。
filter @logStream = 'aws/bedrock/modelinvocations'
| fields @timestamp, @message
少しずつ見やすくしていきましょう。
Bedrockのログ出力は以下のようなJSON形式です。
{
"schemaType": "ModelInvocationLog",
"schemaVersion": "1.0",
"timestamp": "2025-01-10T14:08:32Z",
"accountId": "***",
"identity": {
},
"region": "us-east-1",
"requestId": "fe5ac981-b9e8-43f1-aa30-21cca7682de4",
"operation": "Converse",
"modelId": "amazon.nova-micro-v1:0",
"input": {
},
"output": {
}
}
JSONのキーを、クエリーのfields
に指定するとその項目が抽出されて、表示されます。
requestId
、operation
、modelId
を表示させてみましょう
filter @logStream = 'aws/bedrock/modelinvocations'
| fields @timestamp, requestId, operation, modelId, @message
少し見やすくなりましたね!
ネストされている項目を表示したい(文字列や数値)
Bedrockのログのoutput項目に、使用したトークンの情報があるので、これを表示させてみましょう。
"output": {
"outputContentType": "application/json",
"outputBodyJson": {
"output": {
"message": {
"role": "assistant",
"content": [
{
"text": ""
}
]
}
},
"stopReason": "end_turn",
"metrics": {
"latencyMs": 619
},
"usage": {
"inputTokens": 913,
"outputTokens": 72,
"totalTokens": 985
}
},
"outputTokenCount": 72
}
ネストされている項目は、ドット区切りで指定すると表示させられます。
また、項目名はas
を使って指定が可能です。
filter @logStream = 'aws/bedrock/modelinvocations'
| fields @timestamp,
requestId, operation, modelId,
output.outputBodyJson.usage.inputTokens as inputTokens,
output.outputBodyJson.usage.outputTokens,
output.outputBodyJson.usage.totalTokens,
@message
inputTokensだけ、as
で別名を付けるとこんな感じです。
ネストされている項目を表示したい(複雑な構造)
先程の方法では、どうも、JSONの値に該当する部分が「文字列」や「数値」のときしか取れないようでして、オブジェクトや配列の場合はうまく取得できません。
filter @logStream = 'aws/bedrock/modelinvocations'
| fields @timestamp,
requestId, operation, modelId,
output.outputContentType as outputContentType, # 文字列なので出力される
output.outputBodyJson, # ディクショナリーなので出力されない
output.outputBodyJson.output.message.role, # 文字列なので出力される
output.outputBodyJson.output.message.content, # 配列なので出力されない
output.outputBodyJson.output.message.content[0].text, # 配列が間に挟まってるので出力されない
@message
このような場合は、一度jsonParse
したうえで、unnest
する必要があります。
filter @logStream = 'aws/bedrock/modelinvocations'
| fields @timestamp,
requestId, operation, modelId,
jsonParse(@message) as json_message,
output.outputContentType as outputContentType,
output.outputBodyJson.output.message.role,
@message
| unnest json_message.output.outputBodyJson into outputBodyJson
| unnest json_message.output.outputBodyJson.output.message.content into output_content
| unnest json_message.output.outputBodyJson.output.message.content[0].text into output_text
フィルター、リミット
Bedrockの呼び出しには埋め込みモデルもありますが、おそらくログを確認したいことはあまりないのではないでしょうか?そのような場合は、フィルター条件を追加し、除外することができます。
方法としては以下の二通り実現可能です。
-
Operationが
Converse
かConverseStream
のものだけを表示するfilter @logStream = 'aws/bedrock/modelinvocations' | fields @timestamp, requestId, operation, modelId, @message | filter operation in ["Converse", "ConverseStream"]
-
modelIdが
amazon.titan-embed-text-v2:0
のものを除外するfilter @logStream = 'aws/bedrock/modelinvocations' | fields @timestamp, requestId, operation, modelId, @message | filter modelId != "amazon.titan-embed-text-v2:0"
他にも色々使えそうです。
ログの取得上限はlimit
で指定できます。CloudWatch Logs Insightsの料金はスキャンしたデータ量で決まるので、limit
を上手に使ってスキャン量を減らすと良いですね。
filter @logStream = 'aws/bedrock/modelinvocations'
| fields @timestamp,
requestId, operation, modelId,
@message
| filter modelId != "amazon.titan-embed-text-v2:0"
| sort @timestamp desc | limit 1
最終的にたどり着いたクエリーはこちら
filter @logStream = 'aws/bedrock/modelinvocations'
| fields jsonParse(@message) as json_message,
requestId, operation, modelId,
output.outputBodyJson.stopReason as stopReason,
output.outputBodyJson.metrics.latencyMs as latencyMs,
output.outputBodyJson.usage.inputTokens as inputTokens,
output.outputBodyJson.usage.outputTokens as outputTokens,
output.outputBodyJson.usage.totalTokens as totalTokens
| unnest json_message.input.inputBodyJson.messages into inputMessages
| unnest json_message.input.inputBodyJson.system[0].text into system
| unnest json_message.input.inputBodyJson.inferenceConfig into inferenceConfig
| unnest json_message.input.inputBodyJson.additionalModelRequestFields into additionalModelRequestFields
| unnest json_message.output.outputBodyJson.output.message.content[0].text into outputMessage
| filter operation = "Converse"
| sort @timestamp desc | limit 10
| display
@timestamp, requestId, operation, modelId,
latencyMs,
inputTokens, outputTokens, totalTokens,
system,
inputMessages,
inferenceConfig,
additionalModelRequestFields,
outputMessage,
stopReason
inputMessagesを分解して、「最後のメッセージ」を表示したかったのですが、どうしても「最後の」の指定方法がわからずでした。