LoginSignup
0
1

More than 1 year has passed since last update.

[TypeScript]IndexedDBでログを保存と出力。JS/TS、VueとReactに適用する

Last updated at Posted at 2022-09-06

環境

  • Windows 10
  • Node.js v18.0.0
  • Yarn (npmまたはpnpmも構いません)
  • VS Code
  • Chrome

初めに

  • ある程度のindexedDB基礎知識が必要、学習:LINK
  • 非同期処理(Promiseなど)を使っていないので、ニーズによって追加してください
  • 本記事でVueReactのJSフレームワークをサンプルとして書いたが、JSまたはTSプログラムにも使える

パッケージインストール

  • file-saver:ファイル出力用、@types/file-saverはそのタイプライブラリ
  • dayjs:時間ライブラリ、お好きなライブラリを使ってください
yarn add -D file-saver @types/file-saver dayjs

Vue.js版

App.vue

<script setup lang="ts">
// パッケージインポート
import dayjs from "dayjs";
import { saveAs } from "file-saver";

// コンフィグ設定
const dataBaseVer: number = 1; // バージョン
const dataBaseName: string = "MyIndexedDB"; // データベース名称
const dataBaseStore: string = "MyStore"; // store名称
const dataBaseKeyPath: string = "key"; // 主キー
const dataBaseLimit: number = 10000; // サイズの制限
const filename: string = `log_${dayjs().format("YYYYMMDD")}.txt`; // 出力されたlogファイルの名称

// indexedDB作成
const createIndexedDB = (): void => {
  const indexed: IDBOpenDBRequest = indexedDB.open(dataBaseName, dataBaseVer);

  // 初めて実行する場合は、バージョンがないので、onupgradeneededイベントでstoreと主キーを追加
  indexed.onupgradeneeded = (event: IDBVersionChangeEvent): void => {
    const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
    if (!db.objectStoreNames.contains(dataBaseStore)) {
      db.createObjectStore(dataBaseStore, {
        keyPath: dataBaseKeyPath,
      });
    }
  };

  // 作成失敗
  indexed.onerror = (): void => {
    console.log("Indexed Start Error");
  };
};

// log追加
const addLog = (log: string): void => {
  // タイムスタンプ作成
  const time = dayjs().format("YYYY-MM-DD-HH:mm:ss:SSS");
  const random = Math.ceil(Math.random() * 999);
  const timeStamp = time + "_" + random + "Z";

  // データベースを起動
  const indexed: IDBOpenDBRequest = indexedDB.open(dataBaseName);
  indexed.onsuccess = (event: Event): void => {
    const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
    const trans: IDBTransaction = db.transaction(dataBaseStore, "readwrite");
    const store: IDBObjectStore = trans.objectStore(dataBaseStore);
    const count: IDBRequest<number> = store.count();

    // データの数を数え、追加する前に制限を確認
    count.onsuccess = (): void => {
      store.put({ [dataBaseKeyPath]: `[${timeStamp}] : ${log}` });

      if (Number(count.result) <= dataBaseLimit) {
        // 超えない場合追加だけ
        return;
      } else {
        // 超える場合は一番古いデータから削除
        store.openCursor().onsuccess = (event: Event): void => {
          const cursor: any = (event.target as IDBRequest).result;
          if (cursor) {
            cursor.delete();
          }
        };
      }
    };

    // データの数を数え失败
    count.onerror = (): void => {
      console.log("Count Error");
    };
  };

  // データベースを起動失敗
  indexed.onerror = (): void => {
    console.log("Indexed Open Error");
  };
};

// log読み取り
const readDBandExport = (): void => {
  let tmp: string[] = [];

  // データベースを起動
  const indexed: IDBOpenDBRequest = indexedDB.open(dataBaseName);
  indexed.onsuccess = (event: Event): void => {
    const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
    const trans: IDBTransaction = db.transaction(dataBaseStore, "readonly");
    const store: IDBObjectStore = trans.objectStore(dataBaseStore);

    // データをループ
    store.openCursor().onsuccess = (event: Event): void => {
      const cursor: any = (event.target as IDBRequest).result;
      // データがあれば、tmpに追加、ループをやり続く
      if (cursor) {
        tmp.push(cursor.key);
        tmp.push("\r\n");
        cursor.continue();
      } else {
        // データがなければ、ループが完了。Blobオブジェクトを作成
        const blob: Blob = new Blob(tmp, {
          type: "text/plain;charset=utf-8",
        });

        // 出力
        saveAs(blob, filename);
      }
    };

    // データをループ失敗
    store.openCursor().onerror = (): void => {
      console.log("OpenCursor Error");
    };
  };

  // データベースを起動失敗
  indexed.onerror = (): void => {
    console.log("Indexed Open Error");
  };
};

// データベース削除
const deleteIndexedDB = (): void => {
  const indexed: IDBOpenDBRequest = indexedDB.deleteDatabase(dataBaseName);

  // 削除成功
  indexed.onsuccess = (): void => {
    console.log("Delete Success");
  };

  // 削除失敗
  indexed.onerror = (): void => {
    console.log("Delete Error");
  };
};

// 使用
createIndexedDB();
// テスト
addLog("テスト");
</script>

<template>
  <main>
    <h1>Vue.js</h1>
    <button @click="addLog('ボタンからのテスト')">addLog</button>
    <button @click="readDBandExport">readDBandExport</button>
    <button @click="deleteIndexedDB">deleteIndexedDB</button>
  </main>
</template>

React.js版

App.tsx

// パッケージインポート
import dayjs from "dayjs";
import { saveAs } from "file-saver";

function App() {
  // コンフィグ設定
  const dataBaseVer: number = 1; // バージョン
  const dataBaseName: string = "MyIndexedDB"; // データベース名称
  const dataBaseStore: string = "MyStore"; // store名称
  const dataBaseKeyPath: string = "key"; // 主キー
  const dataBaseLimit: number = 10000; // サイズの制限
  const filename: string = `log_${dayjs().format("YYYYMMDD")}.txt`; // 出力されたlogファイルの名称

  // indexedDB作成
  const createIndexedDB = (): void => {
    const indexed: IDBOpenDBRequest = indexedDB.open(dataBaseName, dataBaseVer);

    // 初めて実行する場合は、バージョンがないので、onupgradeneededイベントでstoreと主キーを追加
    indexed.onupgradeneeded = (event: IDBVersionChangeEvent): void => {
      const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
      if (!db.objectStoreNames.contains(dataBaseStore)) {
        db.createObjectStore(dataBaseStore, {
          keyPath: dataBaseKeyPath,
        });
      }
    };

    // 作成失敗
    indexed.onerror = (): void => {
      console.log("Indexed Start Error");
    };
  };

  // log追加
  const addLog = (log: string): void => {
    // タイムスタンプ作成
    const time = dayjs().format("YYYY-MM-DD-HH:mm:ss:SSS");
    const random = Math.ceil(Math.random() * 999);
    const timeStamp = time + "_" + random + "Z";

    // データベースを起動
    const indexed: IDBOpenDBRequest = indexedDB.open(dataBaseName);
    indexed.onsuccess = (event: Event): void => {
      const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
      const trans: IDBTransaction = db.transaction(dataBaseStore, "readwrite");
      const store: IDBObjectStore = trans.objectStore(dataBaseStore);
      const count: IDBRequest<number> = store.count();

      // データの数を数え、追加する前に制限を確認
      count.onsuccess = (): void => {
        store.put({ [dataBaseKeyPath]: `[${timeStamp}] : ${log}` });

        if (Number(count.result) <= dataBaseLimit) {
          // 超えない場合追加だけ
          return;
        } else {
          // 超える場合は一番古いデータから削除
          store.openCursor().onsuccess = (event: Event): void => {
            const cursor: any = (event.target as IDBRequest).result;
            if (cursor) {
              cursor.delete();
            }
          };
        }
      };

      // データの数を数え失败
      count.onerror = (): void => {
        console.log("Count Error");
      };
    };

    // データベースを起動失敗
    indexed.onerror = (): void => {
      console.log("Indexed Open Error");
    };
  };

  // log読み取り
  const readDBandExport = (): void => {
    let tmp: string[] = [];

    // データベースを起動
    const indexed: IDBOpenDBRequest = indexedDB.open(dataBaseName);
    indexed.onsuccess = (event: Event): void => {
      const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
      const trans: IDBTransaction = db.transaction(dataBaseStore, "readonly");
      const store: IDBObjectStore = trans.objectStore(dataBaseStore);

      // データをループ
      store.openCursor().onsuccess = (event: Event): void => {
        const cursor: any = (event.target as IDBRequest).result;
        // データがあれば、tmpに追加、ループをやり続く
        if (cursor) {
          tmp.push(cursor.key);
          tmp.push("\r\n");
          cursor.continue();
        } else {
          // データがなければ、ループが完了。Blobオブジェクトを作成
          const blob: Blob = new Blob(tmp, {
            type: "text/plain;charset=utf-8",
          });

          // 出力
          saveAs(blob, filename);
        }
      };

      // データをループ失敗
      store.openCursor().onerror = (): void => {
        console.log("OpenCursor Error");
      };
    };

    // データベースを起動失敗
    indexed.onerror = (): void => {
      console.log("Indexed Open Error");
    };
  };

  // データベース削除
  const deleteIndexedDB = (): void => {
    const indexed: IDBOpenDBRequest = indexedDB.deleteDatabase(dataBaseName);

    // 削除成功
    indexed.onsuccess = (): void => {
      console.log("Delete Success");
    };

    // 削除失敗
    indexed.onerror = (): void => {
      console.log("Delete Error");
    };
  };

  // 使用
  createIndexedDB();
  // テスト
  addLog("テスト");

  return (
    <main>
      <h1>React.js</h1>
      <button onClick={() => {addLog("ボタンからのテスト")}}>
        addLog
      </button>
      <button onClick={readDBandExport}>readDBandExport</button>
      <button onClick={deleteIndexedDB}>deleteIndexedDB</button>
    </main>
  );
}

export default App;

検証(Vue.js版で)

起動、データベースが作成された

1.jpg

addLogを推してテストデータを作成して、readDBandExportでログを出力

2.jpg

出力されたファイルの中身を確認、成功

3.jpg

最後

console.logのデータをキャッチして、出力したい場合は↓の関数を使ってください

// logキャッチ
const catchConsoleLog = () => {
  console.oldLog = console.log;

  console.log = (log: string) => {
    console.oldLog(log);
    addLog(log);
  };
};

// 使用
catchConsoleLog()
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1