📝

AWS SDK for JavaScript v3 でデバッグログを出力する

2024/10/01に公開

これは何

  • AWS SDK for JavaScript v3 でデバッグログを出力する方法を調べたメモ
  • 以下のドキュメントを読み解いた

https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/logging-sdk-calls.html

適用に Node.js プロジェクトをセットアップ

mkdir app && cd app
npm init -y
touch index.js

AWS SDK for JavaScript v3 を使用するコードを書く

https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/command/GetObjectCommand/

S3 オブジェクトを取得する例

const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3");

async function main() {
    const client = new S3Client({});
    
    const input = {
        "Bucket": "mybucket",
        "Key": "hello.txt"
    };
    
    const command = new GetObjectCommand(input);
    const response = await client.send(command);
    
    return response;
}

(async() => {
    try {
        const res = await main()
        console.log(res)
    } catch (e) {
        console.error(e)
    }
})();

デバッグログを有効化

client を定義する際に logger として console を指定すれば OK

const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3");

async function main() {
    const client = new S3Client({
        logger: {
            ...console
        },
    });
    
    const input = {
        "Bucket": "mybucket",
        "Key": "hello.txt"
    };
    
    const command = new GetObjectCommand(input);
    const response = await client.send(command);
    
    return response;
}

(async() => {
    try {
        const res = await main()
        console.log(res)
    } catch (e) {
        console.error(e)
    }
})();

debug レベルのログだけは出力したくない場合は、以下のような実装で OK

const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3");

async function main() {
    const client = new S3Client({
        logger: {
            ...console,
            debug(...args) {},
        },
    });
    
    const input = {
        "Bucket": "mybucket",
        "Key": "hello.txt"
    };
    
    const command = new GetObjectCommand(input);
    const response = await client.send(command);
    
    return response;
}

(async() => {
    try {
        const res = await main()
        console.log(res)
    } catch (e) {
        console.error(e)
    }
})();

対応するログレベルは Logger interface として定義されている

https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-smithy-types/Interface/Logger/

各種ログレベルに応じたメソッドを上書きすることが出来るので、 debug レベルのログを出力したくない場合には何の処理も行わない debug メソッドを実装するという感じ

middleware を使用したロギング

  • オペレーションコールのライフサイクルを制御するために middleware が使用されている
  • スタック内の各 middleware はリクエストオブジェクトに変更を加えた後、次の middleware を呼び出す
  • これによってエラーに至るまでに、どの middleware が呼び出されたのかを正確に確認できるため、スタック内のデバッグが容易になる
const { S3Client,GetObjectCommand } = require("@aws-sdk/client-s3");

async function main() {
    const client = new S3Client({});
    
    client.middlewareStack.add(
        (next, context) => async (args) => {
            console.log("AWS SDK context", context.clientName, context.commandName);
            console.log("AWS SDK request input", args.input);
            const result = await next(args);
            console.log("AWS SDK request output:", result.output);
            return result;
        },
        {
            name: "MyMiddleware",
            step: "build",
            override: true,
        }
    );

    const input = {
        "Bucket": "mybucket",
        "Key": "hello.txt"
    };
    
    const command = new GetObjectCommand(input);
    const response = await client.send(command);
    
    return response;
}

(async() => {
    try {
        const res = await main()
        console.log(res)
    } catch (e) {
        console.error(e)
    }
})();
  • next: スタック内で次に呼び出す middleware
  • context: 呼び出されているオペレーションに関する情報を含むオブジェクト
  • この関数は args を受け取る関数を返し args を使用して次の middleware を呼び出した結果を返す
  • args: オペレーションに渡されたパラメーターとリクエストを含むオブジェクト

context の中身

{
  logger: NoOpLogger {},
  clientName: 'S3Client',
  commandName: 'GetObjectCommand',
  inputFilterSensitiveLog: [Function: GetObjectRequestFilterSensitiveLog],
  outputFilterSensitiveLog: [Function: GetObjectOutputFilterSensitiveLog],
  __smithy_context: {
    commandInstance: GetObjectCommand {
      middlewareStack: [Object],
      serialize: [AsyncFunction: se_GetObjectCommand],
      deserialize: [AsyncFunction: de_GetObjectCommand],
      input: [Object]
    },
    service: 'AmazonS3',
    operation: 'GetObject',
    selectedHttpAuthScheme: {
      httpAuthOption: [Object],
      identity: [Object],
      signer: AwsSdkSigV4Signer {}
    }
  },
  endpointV2: {
    headers: {},
    properties: { authSchemes: [Array] },
    url: URL {
      href: 'https://mybucket.s3.ap-northeast-1.amazonaws.com/',
      origin: 'https://mybucket.s3.ap-northeast-1.amazonaws.com',
      protocol: 'https:',
      username: '',
      password: '',
      host: 'mybucket.s3.ap-northeast-1.amazonaws.com',
      hostname: 'mybucket.s3.ap-northeast-1.amazonaws.com',
      port: '',
      pathname: '/',
      search: '',
      searchParams: URLSearchParams {},
      hash: ''
    }
  },
  authSchemes: [
    {
      disableDoubleEncoding: true,
      name: 'sigv4',
      signingName: 's3',
      signingRegion: 'ap-northeast-1'
    }
  ],
  signing_region: 'ap-northeast-1',
  signing_service: 's3'
}

middleware の詳細については公式のブログ記事がある

https://aws.amazon.com/jp/blogs/developer/middleware-stack-modular-aws-sdk-js/

リファレンスは以下

https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-smithy-middleware-stack/

試した環境

$ sw_vers
ProductName:            macOS
ProductVersion:         14.7
BuildVersion:           23H124
$ node --version
v20.12.2
$ npm --version
10.8.1

package.json

{
  "name": "app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@aws-sdk/client-s3": "^3.658.1"
  }
}

Discussion