GASのconsole.logを書き換えようと思ったらできなかった話

# 何がしたかったのか

下記記事を参考に、`console.log`の内容を掠め取ろうと思いました
→GASには`Logger.log`と`Logger.getLog`という機能があるため、不要かとも思われるのですが、`log`しか使えないため、
　下記の内容+errorやwarnも追加すれば便利になるのでは？！と思った

https://qiita.com/standard-software/items/5636cc69970a5be00bc0

# レッツトライ(失敗します)

<details>
    <summary>JavaScriptコード(TypeScriptからコンパイルしたやつ)</summary>

JSDocはChatGPTに任せました

```JavaScript
"use strict";
/**
 * 渡された値がオブジェクトかどうかを判定します。
 * @param {any} value 判定対象の値
 * @returns {boolean} 渡された値がオブジェクトである場合は true、そうでない場合は false を返します。
 */
const isObject = (value) =>
  Object.prototype.toString.call(value) === "[object Object]" && !Array.isArray(value) && value !== null && typeof value !== "undefined";
/**
 * オブジェクトを文字列に変換する関数
 * @param {object} obj - 変換するオブジェクト
 * @return {string} 変換された文字列
 */
const objToString = (obj) =>
  JSON.stringify(obj)
    .split(",")
    .map((element) =>
      element.replace(/(.+:)(.*)/, (string, capture1, capture2) => capture1.replace(/\"/g, "") + capture2).replace(/:/g, ": ")
    )
    .join(", ");
/**
 * 拡張コンソールオブジェクト
 * @typedef {Object} ConsoleExt
 * @property {Function} originalConsoleLog - オリジナルのconsole.log
 * @property {Function} originalConsoleInfo - オリジナルのconsole.info
 * @property {Function} originalConsoleWarn - オリジナルのconsole.warn
 * @property {Function} originalConsoleError - オリジナルのconsole.error
 * @property {Array} result - コンソールに出力されたログの配列
 * @property {Boolean} logOutput - コンソールにログを出力するかどうかのフラグ
 * @property {Function} log - ログを出力する
 * @property {Function} info - 情報を出力する
 * @property {Function} warn - 警告を出力する
 * @property {Function} error - エラーを出力する
 * @property {Function} hook - オリジナルのconsoleオブジェクトに拡張メソッドをフックする
 * @property {Function} unhook - オリジナルのconsoleオブジェクトから拡張メソッドのフックを解除する
 * @property {Function} setup - ログの出力フラグを設定する
 */
const consoleExt = {
  originalConsoleLog: console.log,
  originalConsoleInfo: console.info,
  originalConsoleWarn: console.warn,
  originalConsoleError: console.error,
  original: {...console},
  result: [],
  logOutput: true,
  log: function (message) {
    if (consoleExt.logOutput) consoleExt.originalConsoleLog(message);
    consoleExt.result.push({
      type: "LOG",
      message: isObject(message) ? objToString(message) : message,
      time: new Date(),
    });
  },
  info: function (message) {
    if (consoleExt.logOutput) consoleExt.originalConsoleInfo(message);
    consoleExt.result.push({
      type: "INFO",
      message: isObject(message) ? objToString(message) : message,
      time: new Date(),
    });
  },
  warn: function (message) {
    if (consoleExt.logOutput) consoleExt.originalConsoleWarn(message);
    consoleExt.result.push({
      type: "WARN",
      message: isObject(message) ? objToString(message) : message,
      time: new Date(),
    });
  },
  error: function (message) {
    if (consoleExt.logOutput) consoleExt.originalConsoleError(message);
    consoleExt.result.push({
      type: "ERROR",
      message: isObject(message) ? objToString(message) : message,
      time: new Date(),
    });
  },
  hook: function () {
    if (consoleExt.originalConsoleLog === console.log) console.log = consoleExt.log;
    if (consoleExt.originalConsoleInfo === console.info) console.info = consoleExt.info;
    if (consoleExt.originalConsoleWarn === console.warn) console.warn = consoleExt.warn;
    if (consoleExt.originalConsoleError === console.error) console.error = consoleExt.error;
  },
  unhook: function () {
    if (consoleExt.originalConsoleLog !== console.log) console.log = consoleExt.originalConsoleLog;
    if (consoleExt.originalConsoleInfo !== console.info) console.info = consoleExt.originalConsoleInfo;
    if (consoleExt.originalConsoleWarn !== console.warn) console.warn = consoleExt.originalConsoleWarn;
    if (consoleExt.originalConsoleError !== console.error) console.error = consoleExt.originalConsoleError;
  },
  setup: function ({ logOutput }) {
    if (logOutput !== undefined) consoleExt.logOutput = logOutput;
  },
};
```
</details>

```JavaScript:GAS
/* consoleExtは記載済み */
function myFunction() {
  consoleExt.hook();
  console.log("logging");
  console.warn("warning");
  console.info("infomation");
  console.error("errrrrrrrr");
  consoleExt.unhook();
  console.log(consoleExt.result.map(v=>`[${v.type}]\t${Utilities.formatDate(v.time, "JST", "yyyy/MM/dd HH:mm:ss:SSS")}\t${v.message}`).join("\n"));
}
```

実行してみる

![スクリーンショット 2023-02-19 23.11.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565925/0c1f13ae-eb1f-3729-8d0f-e3ca52ebafdd.png)

**(*’ω’*)ん?**

おかしいなーと思い、`consoleExt.result.length`を確認してみると0

`console`より`Logger`の方が詳しい内容が見れるので`Logger.log`でhook前後の`console.log`の値を確認してみる

```JavaScript:GAS
function myFunction() {
  Logger.log(console.log);
  consoleExt.hook();
  Logger.log(console.log);
}
```

![スクリーンショット 2023-02-19 23.14.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565925/e7075422-8573-a213-7dbd-17c15d1a67d9.png)


**変わってね〜o(´д`o)**

直接`console.log`に何か入れたら変わるかな？(変わらない)

```javascript:GAS
function myFunction(){
  console.log = "warawara";
  Logger.log(console.log);
}
```


# まとめ

**無理だった**

`console.log`のまま掠め取ることは厳しそうなので、
大人しくオブジェクトから呼び出せばロギングしつつ最後まとめてログ出力できるしな〜ってことで諦め。
`console.log`や`console.warn`のまま利用することで、
エラーになった時のメール文にHTMLビューと折り合わせていい感じのログ出力をさせたかったんですが、、、**（Ⅲ-ω-）**


## ChatGPTに聞いてみた

> GASでconsole.logを書き換えようと思ったら、書き換えれなかったのですが、なぜかわかりますか

GASの実行環境では、標準の`console.log`などのグローバルオブジェクトは、実際にはGoogleが提供する`Logger`クラスにマッピングされています。このため、通常のJavaScriptとは異なり、`console.log`を直接書き換えることはできません。

しかし、代わりに`Logger`クラスを使用してログを出力することができます。例えば、次のようにしてログを出力することができます。

```javascript
Logger.log("Hello, world!");
```
また、`console.log`を書き換えたい理由が、単に出力されるログを加工したい場合であれば、以下のようなライブラリを使用することができます。

BetterLog: https://github.com/peterherrmann/BetterLog
このライブラリを使用すると、通常の`console.log`と同じようにログを出力することができますが、ログのフォーマットをカスタマイズすることができます。ただし、BetterLogを使用するには、`Logger`クラスにアクセスする必要があります。

## ChatGPT凄いね

以上。