Lambdaでサーバーのレスポンスタイムを計測しGoogle Spreadsheetに記録する

# はじめに

[1つ前の記事](https://qiita.com/0828/items/87abfb676b2d06ed4cd8) でLambdaについてなんとなく把握したので、今回はWebサイトのレスポンスタイムの計測をLambdaで実施してみることにします

Google SpreadSheetに計測結果を記録しておけば、簡単にグラフ化できそうなので便利そうです。
ということで、結果は[Sheets API](https://developers.google.com/sheets/api/reference/rest/)を使って記録することにします

# SpreadSheetの準備

1. Sheets APIを使うために必要なので、[Google API Console(旧称)](https://console.developers.google.com/)でサービスアカウントキーを発行して、jsonファイルをダウンロードしておきます  
参考：https://www.magellanic-clouds.com/blocks/guide/create-gcp-service-account-key/  
ファイル名は`credentials.json`とします
2. Google SpreadSheetを新規作成し、「シート1」を雛形として以下のようにします
![sheet sample.png](https://qiita-image-store.s3.amazonaws.com/0/75486/485027ff-721e-537a-a798-1d18d037ff68.png)
このシートを日毎にコピーして結果を記録していくことにします
3. このSpreadSheetの共有設定で、1で発行したサービスアカウントのメールアドレスに対して書き込み権限を与えます

# npm

package.jsonを作ります

```
npm init -y
```

Sheets APIの実行で`googleapis`、
日付の操作で`date-utils`、
curlコマンドを実行するのに`child_process`
が必要なのでインストールします

```
yarn add googleapis date-utils child_process
```

これで準備完了

# コード

SSLを使っていて、ウェブサーバー2台構成それぞれがグローバルIPを持っていて、それぞれのサーバーの計測をすることを想定しています
[Sheets APIのNode.jsサンプル](https://developers.google.com/sheets/api/quickstart/nodejs)はOAuthのものしかなくて苦労しました
Sheets APIのドキュメントは最初は理解しづらいけど、いちど見方がわかばなんとかなります

```javascript:index.js
'use strict';

const { google } = require('googleapis');
require('date-utils');

const spreadsheetId = process.env.SPREADSHEETID;
const credentials = './credentials.json';

const hostname = 'expamle.com';
const hosts = {
  'server1': '123.123.123.123',
  'server2': '123.124.124.124'
};

exports.handler = async () => {
  let response;
  let worksheetTitle = new Date().toFormat('YYYY/MM/DD');
  let datetime = new Date().toFormat('YYYY/MM/DD HH24:MI:SS');
  // サービスアカウントキーを使って認証
  const client = await google.auth.getClient({
    keyFile: credentials,
    scopes: 'https://www.googleapis.com/auth/spreadsheets'
  });
  // SheetsAPI
  const sheets = google.sheets({
    version: 'v4',
    auth: client
  });

  // sheet一覧の取得
  // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/get
  response = await sheets.spreadsheets.get({
    spreadsheetId
  });

  // すでに当日のsheetがあるか確認する
  let sheetsExists = false;
  await response.data.sheets.some(worksheet => {
    if (worksheet.properties.title == worksheetTitle) {
      sheetsExists = true;
      return;
    }
  });
  if (sheetsExists === false) {
    // sheetId = 0 をコピーする
    // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#duplicatesheetrequest
    response = await sheets.spreadsheets.batchUpdate({
      spreadsheetId,
      resource: {
        requests: [{
          'duplicateSheet': {
            sourceSheetId: 0,
            newSheetName: worksheetTitle
          }
        }]
      }
    });
    const newSheetId = response.data.replies[0].duplicateSheet.properties.sheetId;
    await console.log('sheet duplicated: ' + newSheetId);
  }

  // curlを実行して計測する
  const exec = require('child_process').execSync;
  let responseTimes = [datetime];
  for (let key in hosts) {
    let $ip = hosts[key];
    let curl_result = await exec('curl --connect-timeout 5 --max-time 10 -o /dev/null -s -w "%{time_namelookup}\n%{time_connect}\n%{time_appconnect}\n%{time_pretransfer}\n%{time_starttransfer}\n%{time_total}" --resolv ' + hostname + ':443:' + $ip + ' https://' + hostname).toString();
    let curl_result_array = await curl_result.split('\n');
    await Array.prototype.push.apply(responseTimes, curl_result_array);
  }

  await console.log(responseTimes);

  // sheetに記録する
  // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append
  await sheets.spreadsheets.values.append({
    spreadsheetId,
    range: worksheetTitle + '!A1',
    valueInputOption: 'USER_ENTERED',
    requestBody: {
      values: [responseTimes]
    }
  });
  return 'success';
};
```

このままだと、SpreadSheetにUTCで記録されてしまうので、Lambdaの環境変数`TZ`に`Asia/Tokyo`と設定する必要があります
Lambdaに割り当てるメモリが多いほどレスポンスタイムが良くなります。でも1024MBを超えると大して変わりません
あとは、これをCloudWatch Eventsから好きなタイミングで呼ぶようにすればOKです

# 注意点

SpreadSheetの制限として、200万cellまでしか作れないようです
ずっと放置していると

> Invalid requests[0].duplicateSheet: This action would increase the number of cells in the workbook above the limit of 2000000 cells.

と言われてsheetのコピーができなくなります
雛形となるシートの行数・列数を最小にしておくなどの工夫も必要でしょう