【kintone】カスタマイズで発生したエラーをアプリに保存する

# kintoneのカスタマイズのエラーについて
kintoneのカスタマイズはブラウザのJavaScriptを記述することになります。なので、エラーは各々のユーザーのブラウザ上で起こり、サーバーではないので、エラーを捕まえて分析するのがとても難しいです。

今回はkintoneにエラーをためるアプリを作り、そこにエラー内容を登録してしまう方法について考えてみます。

# 作るもの
- エラーをためるkintoneアプリ
- 実際に運用されるkintoneアプリ
- カスタマイズ内容であるJavaScriptファイル

![qiita01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3712902/3048f714-e65d-f146-dc5a-4a5ba0990793.gif)

実際に運用されるkintoneアプリはなんでもいいです。今回はEOL材料管理にしてみました。特に意味はありません😌

# 実装
それでは作ってみましょう。

## エラーをためるkintoneアプリ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3712902/8dc024ea-ef13-5bbe-527f-e985e3d2b496.png)

上記のような内容でアプリを作ります。項目は以下のようにしてみました。
- アプリID
    - エラーを起こしたアプリのアプリID
- URL
    - エラーが起きた画面のURL
- スタックトレース
    - エラーの内容
- ユーザー
    - エラーに遭遇したユーザー

URLは、アプリIDを取得してもどの画面で起きたか分からないので、取得します。ただし、統計情報などを取得するときにアプリIDは欲しいので、別の項目にしています。

ユーザーを保存する理由は、エラーを貯めるアプリは管理者権限にするので、一般ユーザーからは見えません。なので、APITokenを利用したアクセスにより、データを作成することになります。

APITokenを利用したデータ作成では、作成者がAdministratorになってしまい、実際にエラーに遭遇したユーザーが分からなくなってしまいます。

そのため、ユーザーを別項目にしています。

## カスタマイズ
エラーを登録する関数は以下のようになります。

```javascript
/**
 * エラーを登録する
 * @param {Error} e 
 */
async function persistError(e) {

    //kintoneアプリにエラーを登録する。
    const client = new KintoneRestAPIClient({
        auth: {
            apiToken: ERROR_TRACE_APP_TOKEN
        }
    });

    await client.record.addRecord({
        app: ERROR_TRACE_APP,
        record: {
            "ユーザー": {
                //APITokenを使っているので、ログインユーザーがわからないので、ログインユーザー情報を保存
                value: [kintone.getLoginUser()]
            },
            "アプリID": {
                //どこのアプリか
                value: kintone.app.getId(),
            },
            "スタックトレース": {
                //スタックトレースを保存
                value: e.stack
            },
            "URL": {
                //どの画面で発生したか
                value: location.href
            },
        }
    });
}


```
エラーオブジェクトの.stackにスタックトレースが格納されています。

アクセスはKintoneRestAPIClientを利用します。このライブラリでは、authプロパティに接続方式を記述します。今回はAPITokenを利用するので、apiTokenをセットしています。

```javascript
//kintoneアプリにエラーを登録するためのボタンを作成
const $$button = document.createElement('button');
$$button.id = `error-trace-button`;
$$button.textContent = 'Hello Error';
$$button.addEventListener('click', async () => {
    try {
        alert('このダイアログを閉じるとエラーが発生します。');

        //エラーを発生させる。
        throw new Error('Hello Error');

    } catch (e) {
        console.warn(e);
        await persistError(e);
    }
});


kintone.events.on(
    ['app.record.index.show'],
    async (ev) => {
        //一覧画面の、ヘッダーに追加します。
        const $$el = kintone.app.getHeaderMenuSpaceElement();
        if (!$$el.contains($$button)) {
            $$el.appendChild($$button);
        }
        return ev;
    }
)
```
エラーを起こすコードはこんな感じです。
ボタンを押すと、エラーをthrowする感じですね。

## コード全文
```javascript

(() => {
    'use strict';

    // エラーを記録するアプリのID
    const ERROR_TRACE_APP = `291`; 
    // エラーを記録するアプリのトークン
    const ERROR_TRACE_APP_TOKEN = `JAfuTfxDH0uuuuuuuuuuuuuuuuuuuw`; 

    //kintoneアプリにエラーを登録するためのボタンを作成
    const $$button = document.createElement('button');
    $$button.id = `error-trace-button`;
    $$button.textContent = 'Hello Error';
    $$button.addEventListener('click', async () => {
        try {
            alert('このダイアログを閉じるとエラーが発生します。');

            //エラーを発生させる。
            throw new Error('Hello Error');

        } catch (e) {
            console.warn(e);
            await persistError(e);
        }
    });


    kintone.events.on(
        ['app.record.index.show'],
        async (ev) => {
            const $$el = kintone.app.getHeaderMenuSpaceElement();
            if (!$$el.contains($$button)) {
                $$el.appendChild($$button);
            }
            return ev;
        }
    )

    /**
     * エラーを登録する
     * @param {Error} e 
     */
    async function persistError(e) {

        //kintoneアプリにエラーを登録する。
        const client = new KintoneRestAPIClient({
            auth: {
                apiToken: ERROR_TRACE_APP_TOKEN
            }
        });

        await client.record.addRecord({
            app: ERROR_TRACE_APP,
            record: {
                "ユーザー": {
                    //APITokenを使っているので、ログインユーザーがわからないので、ログインユーザー情報を保存
                    value: [kintone.getLoginUser()]
                },
                "アプリID": {
                    //どこのアプリか
                    value: kintone.app.getId(),
                },
                "スタックトレース": {
                    //スタックトレースを保存
                    value: e.stack
                },
                "URL": {
                    //どの画面で発生したか
                    value: location.href
                },
            }
        });
    }
})();
```

# まとめ
- Error.stackにスタックトレースが格納されている
- kintoneにエラー内容を保存することができる
- KintoneRestAPIの認証形式をApiTokenにしてくことで、権限を気にせずレコードを追加できる

すべての場面でエラーをアプリに登録することが有効というわけではありませんが、原因不明なガンコな不具合に、利用されてもよいのではないかと思っています。

注意）アプリのAPIトークンについては、セキュリティ上の問題から、ハードコードしないほうがいいです。今回は分かりやすさのために、コードを掲載しています。

---
この記事は以上です。ありがとうございました。

https://www.pulsar-works.jp/

kintoneのプラグイン開発や研修などを行っています。
お仕事のお話はこちらまで。