Akka実践バイブルをゆっくり読み解く　第７章設定とロギングとデプロイ

[Akka実践バイブルをゆっくり読み解く企画](https://qiita.com/louvre2489/items/8a649fa0280d39e144da)の第７章です。
# 第７章 設定とロギングとデプロイ
ScalaやAkkaに限らず、設定周りって誰かが準備してくれていたりすることが多いので、設定全体をしっかりと把握していることは決して多くないです。特にログ周りとか、新規でプロジェクトを作ることがあっても、既存のうまくいっている設定をコピペしてしまうこともあるし・・・
これを気にしっかり反省しないといかんです。


# 設定
Akka独自の設定方法があるとかってわけではなく、Scalaの標準の設定方法に従ってAkkaの設定を行う。

## TypesafeConfigによる設定
`Typesafe Config`はScala標準の設定ライブラリ。複数のファイルフォーマットをサポートしており、優先順位としては以下の順番で読み込まれる。

1. application.conf
2. application.json
3. application.properties

confファイルは[前回](https://qiita.com/louvre2489/items/82efa0b92fb26a89124e)から普通に出てきているけど、可読性が高いのがとても良い。

``` scala:myAppl.confの設定
MyAppl {
  # MyAppl の version
  version = 10

  database {
    # MyAppl の database の connect
    connect = "jdbc:xxx://xxxxxx/xxxx"
    # MyAppl の database の user
    user = "user"
  }
}
```

``` scala:confからの設定取得
// confファイルの読み込み
val config = ConfigFactory.load("myAppl")

// MyAppl の version
val version = config.getInt("MyAppl.version")

val db = config.getConfig("MyAppl.database")
// MyAppl の database の connect
val conn = db.getString("connect")
// MyAppl の database の user
val user = db.getString("user")

// フルパスでの読み取りも可
val user2 = config.getString("MyAppl.database.user")
```

conf、json、propertiesのいずれにもパラメータが設定されていない場合のためのフォールバックの設定を`reference.conf`に持たせることができる。

## Akkaの設定
アクターを生成する際に、デフォルトの設定が読み込まれる。

``` scala:Actorから設定情報を取得する
// アクターシステムの生成
val myActor = ActorSystem("myActor")
// この場合、上記TypesafeConfigのルールに則ってapplication.configなどが読み込まれる
val myConfig = myActor.settings.config
```

以下のようにすると、読み込む設定ファイルを指定することもできる。

``` scala:設定ファイルを指定する
// 対象の設定ファイルを読み込む
val config = ConfigFactory.load("myAppl")
// アクターシステムの生成時に、設定情報を渡す
val myActor = ActorSystem("myActor", config)
// myAppl.configの情報を読み込むことができる
val myConfig = myActor.settings.config
```

また、以下のようにすることで設定ファイルをインクルードすることもできる。

``` scala:設定ファイルのインクルード
# includedFile.confがインクルードされる
include "includedFile"

MyAppl {
  # includedFile.confにversionが設定されていても、10で上書きされる
  version = 10
}
```
より複雑な設定の例として、以下のようなことも可能。

``` scala:Test.scala
"can be lifted" in {
  // lift.confから読み込み
  val configuration = ConfigFactory.load("lift")
  // フォールバックとしてconfigurationの内容を追加
  val mySystem =
    ActorSystem("myFirstLiftTest",
                configuration.getConfig("myTestLift").withFallback(configuration))
```

イメージとして以下のようになる。

``` scala:lift.confの設定内容
myTestLift {
  myTest {
    applicationDesc="My Lift Test"   // ①
  }
  rootParam="root"                   // ②
}

myTest {
  intParam=20                        // ③
  applicationDesc="My Default Test"  // ④
}
```
``` scala:lift.confをフォールバック
myTestLift {
  myTest {
    applicationDesc="My Lift Test"   // ①
  }
  rootParam="root"                   // ②
}

myTest {
  intParam=20                        // ③
  applicationDesc="My Lift Test"     // ⑤ ★設定値の上書きが発生★
}
rootParam="root"                     // ⑥
```

実際の動きとしては、以下のようになる。

``` scala:Test.scala
"can be lifted" in {
  ...
  
  val config = mySystem.settings.config
  // ③の設定内容
  config.getInt("myTest.intParam") must be(20)
  // フォールバックによって、④の内容が⑤の内容で上書き
  config.getString("myTest.applicationDesc") must be("My Lift Test")
  // フォールバックによって、⑥の設定が追加される
  config.getString("rootParam") must be("root")
  // myTestLiftがlift.confから消えるわけではないので、元々の設定値にアクセスすることもできる
  config.getString("myTestLift.rootParam") must be("root")
```

# ロギング
## 基本的な使い方
基本的にはJavaの時と同じように考えればOKとのこと。
Akkaのロギングシステムは特定のロギングライブラリに依存しないように、ロギングアダプタを提供することにより複数のロギングライブラリをサポートしている。あと、ロギング時のI/O待ちにによるブロックを防ぐためにノンブロッキングな仕組みで提供される。

ロギングのイベントハンドラの設定は以下のように行う。

``` scala:confの設定
akka {
  # 標準出力を使用
  loggers = ["akka.event.Logging$DefaultLogger"]
  # SLF4Jを使用
  loggers = ["akka.event.slf4j.Slf4jLogger"]

  # Options: ERROR, WARNING, INFO, DEBUG
  loglevel = "DEBUG"
}
```
提供されているロギングの仕組みだけでは不十分な場合は、独自のイベントハンドラを定義することも可能。

イベントハンドラの設定を行ったら、あとは以下の方法でロギングを実現することができる。

``` scala:loggerインスタンスの生成
import akka.event.Logging
// 第２引数の設定内容が出力元を示す。
// 第２引数に設定可能なのは、以下のとおり。
//  String
//  Actor/ActorRef
//  Class
val log = Logging(context.system, this)
```

`Logging`によるloggerインスタンスの生成は、`ActorLogging`トレイトにお任せすることもできる。

``` scala:ActorLoggingを使用する
class MyAcotr extends Actor with ActorLogging {
  // Loggingの第２引数には自Actorが設定されるため、出力元はMyActorとなる
}
```
以下、`ActorLogging`トレイトの実装内容。Loggingの第２引数には自Actorが設定されているのがわかる。

```scala:Actor.scala
trait ActorLogging { this: Actor ⇒
  private var _log: LoggingAdapter = _

  def log: LoggingAdapter = {
    // only used in Actor, i.e. thread safe
    if (_log eq null)
      // ★Loggingにはthisが設定される★
      _log = akka.event.Logging(context.system, this)
    _log
  }
}
```

## アクターが受け取るメッセージを記録する
アクターが受信したメッセージをログに出力することもできる。

``` scala:受信したメッセージをログに出力する
class MyActor extends Actor with ActorLogging {
  def receive = LogginigReceive {
    case ... => ...
  }
}
```
``` scala:confの設定
akka {
  debug {
    # これによって受信したメッセージをログに出力する設定が有効になる
    receive = on
  }
}
```

# デプロイ
## スタンドアロンアプリケーション作成に向けた設定
スタンドアロンアプリケーションの作成には、`sbt-native-packager`プラグインを使用する。

### plugins.sbt
```scala:project/plugins.sbt
// 2018/03/20時点の`sbt-native-packager`の最新バージョンは1.3.3
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.3")
```

`sbt-native-packager`プラグインを使用する場合は、設定ファイルを`<project home>/src/universal/conf`配下に配置する必要がある。
![範囲を選択_056.png](https://qiita-image-store.s3.amazonaws.com/0/64360/1350163a-83ee-c624-3a38-3857d440fcb3.png)

### build.sbt
``` scala:build.sbt
// nameに設定された名称が起動スクリプトのファイル名になる
name := "deploy"

version := "1.0"

organization := "manning"

scalacOptions ++= Seq(
  "-deprecation",
  "-unchecked",
  "-Xlint",
  "-Ywarn-unused",
  "-Ywarn-dead-code",
  "-feature",
  "-language:_"
)

// スタンドアロンアプリケーションであることを宣言
enablePlugins(JavaAppPackaging)

// 追加しないとapplication.confやlogback.xmlが見つからない。
// stageした際に出力されるstage/binから見たconfの場所を示す。
scriptClasspath +="../conf"

libraryDependencies ++= {
  val akkaVersion = "2.5.4"
  Seq(
    "com.typesafe.akka" %%  "akka-actor"      % akkaVersion,
    "com.typesafe.akka" %%  "akka-slf4j"      % akkaVersion,
    "ch.qos.logback"     %  "logback-classic" % "1.0.13",
    "com.typesafe.akka" %%  "akka-testkit"    % akkaVersion   % "test",
    "org.scalatest"     %%  "scalatest"       % "3.0.0"       % "test"
  )
}
```

## 起動スクリプトの生成
CLIで以下のように入力する。

``` scala
 > sbt                                                                                                                                                                                                                          (git)-[master]
[info] Loading settings from plugins.sbt ...
[info] Loading project definition from /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/project
[info] Loading settings from build.sbt,scala.sbt ...
[info] Set current project to deploy (in build file:/home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/)
[info] sbt server started at local:///home/xxxxxx/.sbt/1.0/server/f8bc3ba3406f8bf12708/sock

sbt:deploy> stage
[info] Packaging /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/deploy_2.12-1.0-sources.jar ...
[info] Done packaging.
[info] Wrote /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/deploy_2.12-1.0.pom
[info] Main Scala API documentation to /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/api...
[info] Packaging /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/deploy_2.12-1.0.jar ...
[info] Done packaging.
model contains 7 documentable templates
[info] Main Scala API documentation successful.
[info] Packaging /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/deploy_2.12-1.0-javadoc.jar ...
[info] Done packaging.
[success] Total time: 6 s, completed 2018/03/20 22:28:45
```

`stage`を実行したら、以下のディレクトリにbin/conf/libが出力される。

``` bash
> pwd
/home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/universal/stage

> ls
bin  conf  lib
```

`stage`ディレクトリ配下に生成されるディレクトリは以下のような構成になっている。

| ディレクトリ | 格納リソース |
|------------|------------|
| bin | Windows用/Unix用の起動スクリプトを含む |
| lib | アプリケーションが依存している全てのjarファイルを含む |
| conf | アプリケーションの全ての設定ファイルを含む |

`bin`の中に起動スクリプトが入っているとのことなので、`bin`の中を確認してみると以下のようになっている。


``` bash
> pwd
/home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/universal/stage/bin

 > ls
deploy  deploy.bat
```

起動スクリプトを以下のように実行することとでスタンドアロンアプリケーションを起動することができる。

``` bash
./deploy
```

# 個人的まとめ
第７章はAkkaやアクターに関する要素はほとんどなかったけど、大事なことが書いてある章だった。どのような設定でアプリケーションが動いているのかを把握することは当然のこと。普段は大量のXMLや散らばった設定ファイルを紐解くのがシンドくてDB接続やログ出力先を雑に把握する程度だったけど、ちゃんと設定は把握しないといけないなぁと、本書の主題とは全然違うところでたいへん意識向上させられる章でした。