🦕

Denoでコンソールとログファイルにログを出力する

2021/06/20に公開1

Denoでコンソール出力をログファイルにも残しておきたいと思いました。
ミニマルなやり方の日本語解説記事が見当たらなかったので解説します。

std/logを使う

標準ライブラリに用意されています。
現在の最新版は@0.99.0です。

https://deno.land/std@0.99.0/log

consoleとfileに出力する

上記ライブラリのREADMEを参考にログを作成します。
要件は以下とします。

  • ログレベルはDEBUG以上(つまりすべて)
  • 出力ファイル名は./app.log決め打ち
  • 出力フォーマットは[日時] [レベル] [内容]

こんな感じでlogger.tsを作ります。

logger.ts
import * as log from "https://deno.land/std@0.99.0/log/mod.ts";

// 出力ファイル名
const filename = "./app.log";

// 出力フォーマット
const formatter = "{datetime} {levelName} {msg}";

await log.setup({
  handlers: {
    // console出力形式の定義
    console: new log.handlers.ConsoleHandler("DEBUG", {
      formatter,
    }),

    // file出力形式の定義
    file: new log.handlers.FileHandler("DEBUG", {
      filename,
      formatter,
    }),
  },

  loggers: {
    default: {
      level: "DEBUG",
      handlers: ["console", "file"],
    },
  },
});

// getLogger()を無引数で実行すると"default"のloggerを取得する
const Logger = log.getLogger();
console.log(`logfile: ${filename}`);

export { Logger };

これを読み込んで使います。

main.ts
import { Logger } from "./logger.ts";

Logger.debug("This log is debug!");
Logger.info("This log is info!");
Logger.warning("This log is warning!");
Logger.error("This log is error!");

実行します。ログファイルに書き込むため、--allow-write=app.log権限が必要です。

❯ deno run --allow-write=app.log main.ts
Check file:///Users/kawarimidoll/ghq/github.com/kawarimidoll/deno-log-practice/main.ts
logfile: ./app.log
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) DEBUG This log is debug!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) INFO This log is info!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) WARNING This log is warning!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) ERROR This log is error!

ちゃんとレベルごとに色がついて出力されます。

app.logの内容も確認しておきましょう。

❯ cat app.log 
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) DEBUG This log is debug!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) INFO This log is info!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) WARNING This log is warning!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) ERROR This log is error!

ログをファイルに残すことが出来ました。

フォーマットを調整する

各Handlerのformatterオプションには関数を渡すことができるので、より細かいフォーマットの調整ができます。

まず、datetimeの出力がSun Jun 20 2021 09:35:23 GMT+0900 (日本標準時)のようにDate型そのままの出力になっているのがイケてません。
このままでは読みづらいし後から集計もしづらいので、 JavaScript ISO8601形式の日時表記を生成 - Qiita を参考にフォーマットします。

また、小さなことですが、DEBUG INFO WARNING ERRORでログ本文の開始位置がずれてしまうのも気になるので、padEnd()を使ってこの差を吸収します。

logger.tsを以下のように修正します。

logger.ts
import * as log from "https://deno.land/std@0.99.0/log/mod.ts";
+ import { LogRecord } from "https://deno.land/std@0.99.0/log/logger.ts";

// 出力ファイル名
const filename = "./app.log";

// 出力フォーマット
-  const formatter = "{datetime} {levelName} {msg}";
+ const formatter = (logRecord: LogRecord) => {
+   const { datetime, levelName, msg } = logRecord;
+ 
+   // 日付の形式を調整
+   const d = new Date(datetime.getTime() - datetime.getTimezoneOffset() * 6e4);
+   const logTime = d.toISOString().slice(0, -5) +
+     d.toString().replace(/^.*GMT([-+]\d{2})(\d{2}).*$/, "$1:$2");
+ 
+   // levelの文字数を調整
+  return `${logTime} ${levelName.padEnd(7)} ${msg}`;
+ };

await log.setup({
// 以降省略

これで再度実行します。

❯ deno run --allow-write=app.log main.ts
Check file:///Users/kawarimidoll/ghq/github.com/kawarimidoll/deno-log-practice/main.ts
logfile: ./app.log
2021-06-20T09:39:03+09:00 DEBUG   This log is debug!
2021-06-20T09:39:03+09:00 INFO    This log is info!
2021-06-20T09:39:03+09:00 WARNING This log is warning!
2021-06-20T09:39:03+09:00 ERROR   This log is error!

日付フォーマットが見やすくなっており、ログ本文の頭も揃っています。良さげです。

app.logも確認します。

❯ cat app.log 
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) DEBUG This log is debug!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) INFO This log is info!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) WARNING This log is warning!
Sun Jun 20 2021 09:35:23 GMT+0900 (日本標準時) ERROR This log is error!
2021-06-20T09:39:03+09:00 DEBUG   This log is debug!
2021-06-20T09:39:03+09:00 INFO    This log is info!
2021-06-20T09:39:03+09:00 WARNING This log is warning!
2021-06-20T09:39:03+09:00 ERROR   This log is error!

FileHandlerは標準ではファイルを上書きではなく追記するため、前のフォーマットのログも残っています。
以後は整形されたログで記録されていきます。

おわりに

非常に簡単なものですが、フォーマットしたログをコンソールとファイルへ出力することができました。
割と汎用性があると思うのでDenoでコードを書くときは使えそうです。
より実践的にするなら、出力先ファイルを指定できるようにしたり、default以外のログを作ったり、RotatingFileHandlerを使ったりすると良いかもしれません。

今回のリポジトリはこちらです。
https://github.com/kawarimidoll/deno-log-practice

Discussion

フシハラフシハラ

出力フォーマットに"{datetime} {levelName} {msg}"こんな簡単な書き方があったのか!と思ったけど
バージョン0.214から remove string formatter で消えてしまったらしい。残念!
https://github.com/denoland/deno_std/releases/tag/0.214.0

でもこの記事に0.99.0というバージョンが書いてあったので、0.99.0の頃はあった事が確認できたのは助かった!