Stormの出力するログファイルについて

Stormにおける標準のログファイル出力機構についてのメモ。

# 基本ルール

* Stormではlog出力機構としてはslf4j+logbackが利用されている。

  + logback自身がローテーションおよび消し込みをしてくれる。
  + Stormでは性能面を考えて`FixedWindowRollingPolicy`(Size100MB毎)を
    利用している。　

```XML:cluster.xml
<configuration scan="true" scanPeriod="60 seconds">
 <appender name="A1" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>/var/log/storm/${logfile.name}</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>/var/log/storm/${logfile.name}-%d{yyyy-MM-dd}.%i</fileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <!-- or whenever the file size reaches 100MB -->
        <maxFileSize>1MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      <maxHistory>9</maxHistory>
    </rollingPolicy>
    <!-- 
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>/var/log/storm/${logfile.name}.%i</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>9</maxIndex>
    </rollingPolicy>

    </rollingPolicy>
      <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <maxFileSize>100MB</maxFileSize>
      </triggeringPolicy>
    </rollingPolicy>
    -->


    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n</pattern>
    </encoder>
 </appender>
```

* この場合、`nimbus.log-2014-08-07.1`のような名前でローテーションする。
* `maxHistory`は日数でカウントされ、同日中にサイズ起因でファイル数が`maxHistory`を越えても消されるわけではない。
* 大抵のまともなシステムの場合、何日間分は保存するみたいなシステム仕様があったりするので上記設定だと力不足かも。

# access.log

`backtype.storm.security.auth.authorizer`パッケージ配下のクラスが出すログが出力されるファイル。
このパッケージは0.9.2-incubatingでは利用されていないが`cluster.xml`に設定があるため0バイトのファイルができる。
(現在、stormのsecurity機能が活発に開発されているのでそのあたりで使うのかトポロジ側から使うものなのかはちゃんと調べていない。)

# nimbus.log

nimbusのログ。stormコマンド中に`-Dlogfile.name=nimbus.log`とハードコードされているためこの名前になる。
変更はstormコマンドをいじらないと綺麗にはできない。おなくじ、logbackの設定ファイル名も同様にコマンド中にハードコードされているので変更はやや難しい。

あまり変えたくなることも無いのでいいか、という感じ。

# supervisor.log, ui.log, drpc.log

nimbusと同様。

# worker-(port番号).log

supervisor.cljの中から起動される際にコード中の文字列リテラルで指定されるため名前は変更不可。

```clojure:storm-core/src/clj/backtype/storm/daemon/supervisor.clj:462
(defmethod launch-worker
    :distributed [supervisor storm-id port worker-id]
    (let [conf (:conf supervisor)
          storm-home (System/getProperty "storm.home")
          stormroot (supervisor-stormdist-root conf storm-id)
          jlp (jlp stormroot conf)
          stormjar (supervisor-stormjar-path stormroot)
          storm-conf (read-supervisor-storm-conf conf storm-id)
          classpath (add-to-classpath (current-classpath) [stormjar])
          worker-childopts (when-let [s (conf WORKER-CHILDOPTS)]
                             (substitute-worker-childopts s port))
          topo-worker-childopts (when-let [s (storm-conf TOPOLOGY-WORKER-CHILDOPTS)]
                                  (substitute-worker-childopts s port))
          logfilename (str "worker-" port ".log") <- ここ
          command (concat
                    [(java-cmd) "-server"]
                    worker-childopts
                    topo-worker-childopts
                    [(str "-Djava.library.path=" jlp)
                     (str "-Dlogfile.name=" logfilename)
                     (str "-Dstorm.home=" storm-home)
                     (str "-Dlogback.configurationFile=" storm-home "/logback/cluster.xml")
                     (str "-Dstorm.id=" storm-id)
                     (str "-Dworker.id=" worker-id)
                     (str "-Dworker.port=" port)
                     "-cp" classpath
                     "backtype.storm.daemon.worker"
                     storm-id
                     (:assignment-id supervisor)
                     port
                     worker-id])
          command (->> command (map str) (filter (complement empty?)))
          shell-cmd (->> command
                         (map #(str \' (clojure.string/escape % {\' "\\'"}) \'))
                         (clojure.string/join " "))]
      (log-message "Launching worker with command: " shell-cmd)
      (launch-process command :environment {"LD_LIBRARY_PATH" jlp})
      ))
```

ただし、`worker.childopts`や`topology.worker.childopts`プロパティに記載された%ID%という文字列はworkerのポート番号に自動的に差し替えをしてくれる。

```clojure:storm-core/src/clj/backtype/storm/daemon/supervisor.clj:448
(defn- substitute-worker-childopts [value port]
  (let [sub-fn (fn [s] (.replaceAll s "%ID%" (str port)))]
    (if (list? value)
      (map sub-fn value)
      (-> value sub-fn (.split " ")))))
```

# nimbus.out, supervisor.out, ui.out, drpc.out

これらのファイルはlogback経由で出力されるわけではなく、デーモンプロセス自体の標準出力である点に注意。

stormプロジェクトは`/etc/init.d`配下のスタートストップスクリプト等は配布していないので自分で`nohup`コマンド等を利用して必要に応じて取る必要がある。
もちろん、ローテーションや消し込みも自分で実装する必要あり。
stormのスタートストップスクリプトを配布している人もチラホラいるようなのでそこから取ってくれば楽かも。

# worker-(port番号).gc

workerはsupervisorプロセスが内部で起動するので `-Xloggc:filename`等のJVMオプションをworker.childoptsプロパティ等に指定してできる範囲のログ保存しかできない。

port番号の部分は、worker-(port番号).logと同様に%ID%という文字を使って差し替えると良い。