47
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

TypescriptでLog4jsを組み込んでロギングしまくる

Last updated at Posted at 2017-08-26

TypescriptでもLog4jみたいなことをしたい!

ありますよ、 Log4js が。

この記事で書くこと

  • Log4jsってなにそれおいしいの?
  • configってなにそれおいしいの?
  • 巷にあるLog4jsの設定は当てにならない!?
  • Typescriptに組み込むならば……

Log4jsとはなんぞ

Log4js

Java使いであれば超定番のロガーライブラリLog4JのJavascript版です。
ログ処理を設定ファイルであれこれ制御できるので便利。
Typescript(Javascriptでもですが)で設定ファイルを記載するとなると便利になるのがconfigというモジュールです。

configとはなんぞ

config

NODE_ENV環境変数に応じて読み込む設定ファイルを変更してくれる、かつ、簡単にソースから設定を取得できるモジュール。

今回お話する内容でのディレクトリ構成

なお、Node.jsアプリの構成です。

  • node-root/ ( npm init したところ)
    • bin/
      • app/
        • config/ -> ../../config
        • hoge.js
        • hoge.js.map
      • test/
        • hoge.test.js
        • hoge.test.js.map
    • src/
      • app/
      • hoge.ts
      • test/
      • hoge.test.ts
    • config/
      • development.json

※configはnode-rootの直下のディレクトリにないとtscコンパイルがうまく行かなかったです。
かつ、トランスパイル後のファイルからも読める必要があるので、node-root/bin/app配下にシンボリックリンクでconfigを用意しています。
なお、NODE_ENVはdevelopmentに設定しています。

Log4jsとconfigのバージョン

library version
Log4js 2.3.3
config 1.26.2

まずはconfigを使えるようにする

まずはnpmでインストール。

console
npm install config
npm install @types/config

んで、config/${NODE_ENV}.jsonを用意。

一旦はコレでconfigの下地は完成。あとはコレを使うLog4jsを導入。

いざLog4js……?

こちらもまずはインストール。

console
npm install log4js
npm install @types/log4js

各モジュールをインストールした後は、

  1. configの設定を記述
  2. ソースからconfigを読み込むコードを実装
  3. configから取得したJSONを元にLog4jsを初期化
  4. ログを吐きまくる

という流れです。

では早速、configを書き始めます。 ワタシはここでハマった

先人たちのナレッジを参考に設定するわけですねまずは。

log4jsの覚え書き - log4.jsを使ってみた -

よくある設定ですと、こんな感じになります。

development.json.よくあるやつ
{
    "log4js": {
        "appenders": [
            {
                "category": "system",
                "type":     "dateFile",
                "filename": "/var/log/app/system.log"
            },
            {
                "category": "access",
                "type":     "dateFile",
                "filename": "/var/logs/app/access.log"
            },
            {
                "category": "error",
                "type":     "dateFile",
                "filename": "/var/logs/app/error.log"
            }
    }
}

そして、実装の中でこう使うんですね。

code
import * as Config from "config";
import * as Path from "path";

var configure = Config.util.loadFileConfigs(Path.join(__dirname,"config")).log4js;

……
……
……あれ、エラーする。
「typeがObjectであるプロパティappendersが見つからない」

appendersプロパティは設定しているのに……

ん?
「typeがObjectであるプロパティ」……

Migrating from log4js versions older than 2.x

The main changes are a need for you to name your appenders, and you also have to define the default category.

Log4js v1.Xではappendersは配列だったけれど、Log4js v2.Xではappendersはオブジェクト
という大きな仕様変更があったみたいです。

ドキュメントを整理をすると、

  • log4js v2ではappenndersプロパティ、categoriesプロパティが必須のJSONで設定を行う。
  • appendersプロパティはオブジェクトとして、 設定名:{出力設定} というプロパティの列挙で各出力設定を用意する。
  • categoriesプロパティはオブジェクトとして、 カテゴリ名:{適用設定,出力ログレベル} というプロパティの列挙でログ定義をする。
  • categories::defaultプロパティは必須。

コレを上の設定ファイルに当てはめると以下のようになりました。

development.json.うごくやつ
{
    "log4js": {
        "appenders": {
            "access": {
                "type":     "dateFile",
                "filename": "/var/log/app/access.log"
            },
            "error": {
                "type":     "dateFile",
                "filename": "/var/logs/app/error.log"
            },
            "system": {
                "type":     "dateFile",
                "filename": "/var/log/app/system.log"
            },
            "console": {
                "type": "console"
            },
            "stdout": {
              "type": "stdout"
            }
        },
        "categories": {
            "default": {
                "appenders": [
                    "access"
                    ,"console"
                    ,"stdout"
                ]
                ,"level": "INFO"
            },
            "access": {
                "appenders": [
                    "access"
                    ,"console"
                    ,"stdout"
                ]
                ,"level": "INFO"
            },
            "system": {
                "appenders": [
                    "system"
                    ,"console"
                    ,"stdout"
                ]
                ,"level": "ALL"
            },
            "error": {
                "appenders": [
                    "error"
                    ,"console"
                    ,"stdout"
                ]
                ,"level": "WARN"
            }
        }
    }
}

この設定では、出力設定としては

  • access
  • error
  • system
  • console
  • stdout

の5つを用意していて、ログ設定としては

  • access
    • 出力設定→access,console,stdout
  • error
    • 出力設定→error,console,stdout
  • system
    • 出力設定→system,console,stdout

の3つを用意しています。この時、アプリケーションから使えるログはaccess,error,systemです。
categoriesプロパティで設定する項目がログ項目となるわけです。

この設定をして、

import * as Config from "config";
import * as Path from "path";

var configure = Config.util.loadFileConfigs(Path.join(__dirname,"config")).log4js;

が機能するようになりました。

いざ本当にLog4js

Log4jsを利用する流れは大きく2段階です。

  1. 初期化
  2. ロギングしまくる

初期化

code
import * as Config from "config";
import * as Path from "path";
import * as Log4js from "log4js";


var configure = Config.util.loadFileConfigs(Path.join(__dirname,"config")).log4js;
Log4js.configure(<Log4js.IConfig>configure);

@types/configで定義されているutil.loadFileCOnfigs(path: string).<<定義キー>>で必要なJSONを取得します。
今回、log4jsをキーとしているため、util.loadFileCOnfigs(path: string).log4jsとしてます。
そして、取得したJSONオブジェクトを@types/log4jsで定義されているconfigure(config: IConfig): voidを実行し設定を行います。
Log4js.configure(<Log4js.IConfig>configure);で強制的な型変換を行っているのは、util.loadFileCOnfigs(path: string).<<定義キー>>で取得できるオブジェクトはTypescript上any型だからです。
普通に行うとconfigure(config: IConfig): voidではなくconfigure(path: string): voidに向かってしまい、正しく動いてくれないのです。

ロギングしまくる

初期化を終えたらあとは気が向くままにロギングするだけです。

code
import * as Config from "config";
import * as Path from "path";
import * as Log4js from "log4js";

let message = "hogehoge"

let logger = Log4js.getLogger("access");
logger.info(message);

getLogger(logname: string): Loggerメソッドで対応するロガーインスタンスを取得します。lognameは設定ファイルのcategoriesで用意した名称を指定します。
取得したLoggerインスタンスに対して、インスタンス.<<ログレベル>>(message: string)でロギングします。
<<ログレベル>>はLog4jsのv1と変わらない感じなので、ここは他の紹介ページを参考にして問題ないです。
上記コードでは、ログ名称accessのLoggerインスタンスを取得して、infoレベルのメッセージをロギングしています。

最終的な実装なこんな感じ

Typescriptに組み込んだ時、このような実装になりました。

logger.ts
import * as Path from "path";
import * as Config from "config";
import * as Log4js from "log4js";


export class Logger {

    public static initialize() {
        let configure = Config.util.loadFileConfigs(Path.join(__dirname,"config")).log4js;
        Log4js.configure(configure as Log4js.Configuration);
    }

    public static LogAccessInfo(message: string): void {

        let logger = Log4js.getLogger("access");
        logger.info(message);
    }

    public static LogAccessWarning(message: string): void {

        let logger = Log4js.getLogger("access");
        logger.warn(message);
    }

    public static LogAccessError(message: string): void {

        let logger = Log4js.getLogger("access");
        logger.error(message);
    }

    public static LogSystemInfo(message: string): void {

        let logger = Log4js.getLogger("system");
        logger.info(message);
    }

    public static LogSystemWarning(message: string): void {

        let logger = Log4js.getLogger("system");
        logger.warn(message);
    }

    public static LogSystemError(message: string): void {

        let logger = Log4js.getLogger("system");
        logger.error(message);
    }

    public static LogError(message: string): void {

        let logger = Log4js.getLogger("error");
        logger.error(message);
    }

}
呼び出し元コード.ts
import {Logger} from "./logger";


public main() {
    Logger.initialize();
    execute();
}

private execute() {
    Logger.LogAccessInfo("accessログのINFOメッセージ");
    Logger.LogSystemError("systemログのERRORメッセージ");
}

main()

エントリポイントとなる箇所でLogger.initialize(): voidを実行して初期化を行い、必要に応じてLogger.LogXXXXを実行する実装です。

47
35
3

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
47
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?