Log4j2をとりあえず javacで動かす話

## はじめに
この記事は、Javaの動作を実感することを目的として作成しています。そのため、
イマドキがどうとか、Mavenがどうとか、IDEがどうとか。そういった議論は一切省きます。これを知って初めて効率のよいやり方に移らないと、根本的な事が抜け落ちてしまうからです。

## 資料の目的
誰かがかいたライブラリを自分のプログラムに組込むまでの手順を整理し、クラスパスやパッケージ
importなどの概念を獲得することが目的です。そのために、「プログラム実行時に動作を目で見て確認できる」Log出力機能を選定します。選定ライブラリは今を時めくLog4j2です。僕のお友達が、仕事でよくLog4j2の名前を聞くということで。せっかくだからこいつを使おうと決めました。だから**Log4j2の使い方とか、設定方法とかも全くの範囲外です**

## 前提条件

+ jdkが既に導入されていること(javac/javaコマンドが利用可能であること)
+ インターネットに接続できること。
+ Windows でやってね。（コマンドプロンプト） Linuxは適宜読み替えてください。


```
>javac --version
javac 17
```

JDKが入っていれば、バージョン情報が出力される。

## 準備作業

まずは、作業の準備から。いつものようにコマンドプロンプトで

```
> mkdir c:\dev\workspace\java\libconfirm 
> cd c:\dev\workspace\java\libconfirm
> mkdir src\main\java\ 
> mkdir src\main\java\cococlub
> mkdir lib
> curl -o log4j.zip https://dlcdn.apache.org/logging/log4j/2.3.2/apache-log4j-2.3.2-bin.zip
> jar xvf log4j.zip
> copy apache-log4j-2.3.2-bin lib\
> del /s apache-log4j-2.3.2-bin
> rmdir apache-log4j-2.3.2-bin
> del log4j.zip
> set classpath=
```

## コードを作成しよう

まずは。自分の作りたいコードを作るところからです。

```
>notepad src\main\java\cococlub\MyApp.java
(ダイアログで新しく作るか聴かれるので、作成ボタンを押下してください。)
```
入力するコードは以下の通りです。

```java
public class MyApp{

}

```

さっそくコンパイルしてみましょう

```
> javac -d bin src\main\java\cococlub\MyApp.java
コンパイルが終了するので

> dir bin
 c:\dev\workspace\java\libconfirm\bin のディレクトリ

2022/01/30  17:48    <DIR>          .
2022/01/30  17:48    <DIR>          ..
2022/01/30  17:48               184 MyApp.class

```
ここで -d の意味ですが、コンパイルした結果を -d で指定したディレクトリに保存してくださいという意味です。え？何がいいのかわからない。では指定していない場合の結果と見比べてみましょう

まず指定した場合

```
libconfirm>dir /S /A-D /B src bin
c:\dev\workspace\java\libconfirm\src\main\java\cococlub\MyApp.java
c:\dev\workspace\java\libconfirm\bin\MyApp.class
```

指定していない場合

```
libconfirm>dir /S /A-D /B src bin
c:\dev\workspace\java\libconfirm\src\main\java\cococlub\MyApp.class
c:\dev\workspace\java\libconfirm\src\main\java\cococlub\MyApp.java
★ソースコードと同じ場所に.classファイルができちゃいます。
```

これは、「前の結果を忘れて綺麗な状態でコンパイルしたい」って場合にとても都合が悪いのです。
指定していれば、binディレクトリそのものを消してしまえばいいのですが、指定しない場合。*.classファイルを探し出して消さなければならない。消し漏らすと、問題なケースなどもあるので、都合が悪いのです。以後

**dオプションは必ず指定して実行するようにしましょう。**

さて、こんな何にもないコードでも、いろいろ分かることがあります。こうやって、少しずついろいろ変えながら理解することは初学者にとってとても大切なステップです。ここをおろそかにするとあとから全くわからないことがでてきます。**遠きに行くには必ず邇きよりす** 武田先生。貴方すごくいい言葉を教えてくれていますよ！！！

## パッケージを作ってみよう

実はJava は、パッケージに属さない（デフォルトパッケージ）クラスを推奨していません。まずは最初にパッケージをつけてみます。パッケージでは

* .class を格納するフォルダはパッケージ名と一致さえる必要がある

という鉄のおきてがあります。つまりパッケージを付けると、生成される.classは<package名のフォルダ＞ に格納されます。そのため、ソースコードも、パッケージ名と格納先をそろえておくのがいいでしょう。(エラーの内容から、どのソースが問題だかすぐに分かるようにしておきたいのです。)


```
cococlub.net ⇒ ...../cococlub/net にそのソースが置いておく。
```
という事です。今ソースは「cococlub」にあるので、cococlubというパッケージにします。出来上がるソースは

```java
package cococlub;

public class MyApp{

}
```
です。では早速コンパイルしてみましょう

```
>javac -d bin src\main\java\cococlub\MyApp.java
>dir /S /B /A-D bin
c:\dev\workspace\java\libconfirm\bin\cococlub\MyApp.class
cococlub配下にMyApp.classが生成されています。

```

## Log4j2を使ってみよう

### プログラムを書こう!!

まずは、本格的に使う前に、Log4jにおいて、ログを記録する役割をになう**org.apache.logging.log4j.Logger**型の変数を宣言しましょう。

``` java 
package cococlub;

public class MyApp{
	public static void main(String[] args){
		org.apache.logging.log4j.Logger logger;
        //★↑ちょっと長くて面倒くさいので、別の方法を後述します。
	}
}

```

さてと、コンパイルコンパイル

```
set classpath=
javac -encoding UTF-8 -d bin src\main\java\cococlub\MyApp.java
★以下実行結果
src\main\java\cococlub\MyApp.java:5: エラー: パッケージorg.apache.logging.log4jは存在しません
                org.apache.logging.log4j.Logger logger;
                                        ^
エラー1個
```
``-encoding UTF-8``サンプルプログラムに日本語のコメントをいれたので、ファイルの文字コードがUTF-8であることをjavacコマンドにおしえている。これを教えないと、javacはシステムのデフォルト文字コードwindows-３１ｊ であることを仮定して動作するため、実際の文字コードと異なりエラーとなる。

えらー・・・・ですと・・・だってさっきlibに解凍したのに・・・・・と思いますよね？でも。そのlibフォルダにlog4jが置いてあるなんて、コンパイラはいつ知るのでしょうか？知りえないんですよね。。

**知らぬなら、教えてあげようjarの位置**

```
javac -encoding UTF-8 -d bin  --class-path=lib\log4j-api-2.3.2.jar src\main\java\cococlub\MyApp.java
```

今度はうまくいきました。class-pathはjavac/javaに「ソースにないClass=外部ライブラリの位置はここだよ～」と教えげ上げるための仕掛けです。今回は「org.apache.logging.log4j.Logger」が入っている「lib\log4j-api-2.3.2.jar」を追加しています。さてもう少しコードを追加しましょう。


```java
package cococlub;

public class MyApp{
	public static void main(String[] args){
		org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(MyApp.class);
        logger.error("An Error is Occured!");

	}
}
```
さぁコンパイルコンパイル♪さっき、class-pathも追加したし、今回は・・・・

```
> javac -encoding UTF-8 -d bin  --class-path=lib\log4j-api-2.3.2.jar src\main\java\cococlub\MyApp.java
```

お！！！とおりましたよ！！！ やった！！やったよ！！

### 動かしてみよう

では動かしてみましょう。

```
>java cococlub.MyApp
エラー: メイン・クラスcococlub.MyAppが見つからなかったかロードできませんでした
```
ええええそりゃないよおとっつぁん。さっきコンパイルしたじゃん！！だってほら

```
> dir /A-D /B /S bin
c:\dev\workspace\java\libconfirm\bin\cococlub\MyApp.class
```

いるじゃん・・・・はっ！！！このクラスがbin に置いてあるなんて、愛しのJavaチャンにだれが伝えたのか。。。つたえてねー－－－－－－。そりゃ呼べないよ。。java(javacであれjavaであれ)に「クラスの在処」を教えるためには、classpathを使えばよかったのです。では積んでみましょう

``` java 
set classpath=bin
java -cp %classpath% cococlub.MyApp
★以下実行結果
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/log4j/LogManager
        at cococlub.MyApp.main(MyApp.java:5)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.LogManager
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)

```
-cp がjava（javacではなく） にクラスパスを与える方法です。覚えておこうね。どうやらMyAppは呼べたようですが。。なんと、log4jが・・・log4j忘れてたごめんよ。log4j。。。だって教えてないんだもん。「og4j-api-2.3.2.jar」を使えばいいんだよね。もう慣れてるから簡単さ。

``` java 
set classpath=
set classpath=bin
set classpath=%classpath%;lib\log4j-api-2.3.2.jar

java -cp %classpath% cococlub.MyApp
★以下実行結果
ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
ERROR MyApp An Error is Occured!←ただメッセージが出ただけで、ログのフォーマットとしてだめだめ。
```

>あ、怒られた。log4j-coreをclasspathにいれろだって・・・なんでだ・・・コンパイル時にはいらなかったじゃないか！！

これ、javaあるあるなので気をつけましょう。

* コンパイル時に必要なライブラリ(jar)＝プログラムが直接呼ぶライブラリ(jar)
* 実行時に必要なライブラリ(jar)＝プログラムが直接呼ぶライブラリ(jar)が呼ぶライブラリ(jar)を全部

なので、呼んだ先で必要なライブラリを要求されているのです。場合によっては先の先の先でとかすごい数のjarが必要いなったりもします。今回はlog4jの自己申告通りlog4-coreを追加してあげましょう

```java
set classpath=
set classpath=bin
set classpath=%classpath%;lib\log4j-api-2.3.2.jar;lib\log4j-core-2.3.2.jar

java -cp %classpath% cococlub.MyApp
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. 
★log4j2の設定ファイルがないから、デフォルトの動作として Errorレベルだけ標準出力に出力するよ。設定ファイル面倒なので今回は割愛。見逃し。

22:33:18.852 [main] ERROR cococlub.MyApp - An Error is Occured!←お、どのコードからいつ出されたのか一目瞭然。
```
見事エラーレベルのログが出力されました。参考まで、infoも追加してみましょう。

```java
package cococlub;

public class MyApp{
	public static void main(String[] args){
		org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(MyApp.class);
        	logger.error("An Error is Occured!");
		    logger.info("It is an information");
	}
}
```

早速、コンパイル。

```java 
javac -encoding UTF-8 -d bin  --class-path=lib\log4j-api-2.3.2.jar src\main\java\cococlub\MyApp.java
```
さて実行


```java
set classpath=
set classpath=bin
set classpath=%classpath%;lib\log4j-api-2.3.2.jar;lib\log4j-core-2.3.2.jar
java -cp %classpath% cococlub.MyApp
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
22:41:14.331 [main] ERROR cococlub.MyApp - An Error is Occured!

```

きちんとErrorレベルのログだけが出されていますね。すばらしい。

### import を使おうよ

さて、今まで作ってたこのコード。ちょっときついですよね。

```java
package cococlub;

public class MyApp{
	public static void main(String[] args){
		org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(MyApp.class);
        	logger.error("An Error is Occured!");
		    logger.info("It is an information");
	}
}
```

なにがって、org.apache.logging.log4j.Loggerとorg.apache.logging.log4j.LoggerをFQDN で指定するのはつらいです。1か所ならまだしも、複数個所でてきたら目もあてられません。こういう「なんども書くだろう長い表記」というのは普通に省略する方法があります。javaではimport文です。**importしたクラスは、クラス名だけで呼び出しできる**というありがたー－－－－い仕様がjavaにはあるのです。使わない手はありません。

```java
package cococlub;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class MyApp{
	public static void main(String[] args){
		Logger logger = LogManager.getLogger(MyApp.class);
        	logger.error("An Error is Occured!");
		    logger.info("It is an information");
	}
}
```

あ、かなりすっきり。これのコンパイルと実行は今までと代わり映えしないので割愛します。

## まとめ

+ jarをもってきて利用する方法が分かったよ
+ packageの使い方もわかったよ
+ 実行時に依存するすべてのライブラリを指定するのは結構面倒だよ。
+ ライブラリ（Jar） を各サイトから集まるのも結構手間だと思うよ