PhoenixのLogger

とりあえず、アプリが起動する状態になった。

Phoenix・Elixir をちょっとさわって思ったのは、コード上に型が現れてれていないとなにかと調べにくい、ということ。馴染みのある言語なら、型が現れてなくてもなんとかなっちゃうんだけど、馴染みのない言語だとそうはいかない。

結局コード見ててもわからないので、ログを出しまくって、どういう動きをするか、どういうデータが入力・出力されるのかを追っているところ(デバッガみたいなのはある?)。まあ、始めはログの出し方もわからなかったのだけれど。

Logger

ドキュメントはこちら

debug, info, warn, error の4レベルがある。

使い方としてはこんな感じ(サンプルはここで作ったhello_phoenixプロジェクト)。

defmodule HelloPhoenix.PageController do
  use HelloPhoenix.Web, :controller

  require Logger

  def index(conn, _params) do
    Logger.error "Error!!!"
    Logger.warn  "Warn!!"
    Logger.info  "Info!"
    Logger.debug "Debug"

    render conn, "index.html"
  end
end

ログの設定は config の下の設定ファイルで変えられる。

出力先はデフォルトで :console となっている。 ファイルに落とすにはリダイレクトするのかな...?と思って調べたら Backend というものがあって、そこにファイルに直接ログを出力する拡張機能を追加すれば、リダイレクトは不要。標準ではファイルに落とす拡張機能は用意されていないが、logger_file_backendが公開されていたので使ってみる。

まずは mix.exs 依存関係を追加して、% mix deps.get でダウンロードする。

--- mix.exs.orig 2015-08-15 15:12:58.149425240 +0900
+++ mix.exs   2015-08-15 15:13:18.074069599 +0900
@@ -31,6 +31,7 @@
     [{:phoenix, "~> 0.16"},
      {:phoenix_html, "~> 2.0"},
      {:phoenix_live_reload, "~> 0.6", only: :dev},
+     {:logger_file_backend, "~> 0.0.3"},
      {:cowboy, "~> 1.0"}]
   end
 end

設定ファイルはこんな感じ。

--- config/dev.exs.orig  2015-08-15 15:08:00.514099611 +0900
+++ config/dev.exs    2015-08-15 15:10:06.139743337 +0900
@@ -24,4 +24,7 @@
   ]

 # Do not include metadata nor timestamps in development logs
-config :logger, :console, format: "[$level] $message\n"
+config :logger, backends: [{LoggerFileBackend, :file}]
+config :logger, :file,
+  path: "/tmp/hello_phoenix.log",
+  level: :debug

結果

% cat /tmp/hello_phoenix.log
15:16:29.472 [info] Running HelloPhoenix.Endpoint with Cowboy on http://localhost:4000
15:16:49.496 [info] GET /
15:16:49.510 [debug] Processing by HelloPhoenix.PageController.index/2
  Parameters: %{"format" => "html"}
  Pipelines: [:browser]
15:16:49.526 [info] Sent 200 in 29ms

Phoenixを始めてみた

Phoenix が気になったので、さわり始めた。

Phoenix は Elixir で書かれていて、アプリを作る場合も Elixir で書く。Phoenix もElixir も今まで使ったことがなく、初めての言語、初めてのフレームワークで、いろいろとつまづくことが多そう。その反面、面白いこともありそうなわけで。

とりあえず環境を整えよう。

環境構築

Erlang, Elixir, Phoenix をインストールする。

Elang, Elixir は anyenv を使ってインストールする。

今回インストールしたバージョンは

  • Erlaang/OTP 18
  • Elixir 1.0.5
  • Phoenix 0.16.0

OS は Ubuntu 14.04.3。

*env

anyenv のインストールはREADME.md を参照。

% anyenv install erlenv
% anyenv install exenv
Erlangのインストール

Erlangerlang-build みたいなのがないので、自分でビルドする。

% curl -O http://www.erlang.org/download/otp_src_18.0.tar.gz
% tar xzf otp_src_18.0.tar.gz
% cd otp_src_18.0
% ./configure --prefix=/home/ymmtmsys/.anyenv/envs/erlenv/releases/18.0
% make -j4
% make install
% erlenv global 18.0

ソースは消してOK.

Elixirのインストール
% exenv  install -l     # 最新バージョンの確認のため
% exenv install 1.0.5
% exenv global 1.0.5
Phoenix のインストール

公式ドキュメントを参照

node.js はすでに入っているので省略。

Up And Running

ここ を参考に、サンプルのアプリが起動するか確認。

% mix phoenix.new hello_phoenix
% hello_phoenix
% mix phoenix.server
.
.
.
[info] Running HelloPhoenix.Endpoint with Cowboy on http://localhost:4000
[warning] the :size option when configuring HelloPhoenix.Repo is deprecated, please use :pool_size instead
14 Aug 11:03:56 - info: compiled 5 files into 2 files, copied 3 in 2675ms
[info] GET /
[debug] Processing by HelloPhoenix.PageController.index/2
  Parameters: %{"format" => "html"}
  Pipelines: [:browser]
[info] Sent 200 in 17ms

Ok.

warning は設定ファイルのフィールド名を変えれば出なくなる。

--- config/dev.exs.orig  2015-08-14 11:21:57.671526209 +0900
+++ config/dev.exs    2015-08-14 11:22:01.059526209 +0900
@@ -32,4 +32,4 @@
   username: "postgres",
   password: "postgres",
   database: "hello_phoenix_dev",
-  size: 10 # The amount of database connections in the pool
+  pool_size: 10 # The amount of database connections in the pool

mipmap

問題

R.drawable.ic_launcher と書くと Cannot resolve symbol ic_launcher というエラーが出る。

解決

drawable ではなく mipmap を使う。R.mipmap.ic_launcher

googledevjp.blogspot.jp


現在使用している Android Studio のバージョンは 1.2.2。プロジェクトのディレクトリをよくよく見ると res/drawable の下には何もなく、res/mipmap の下にはきちんとアイコンが用意されていた。

本に載っていたコードを写していたのだけれど、写しているだけで、そこまで見れていなかった。

Google Play services out of date

問題

Androidアプリ開発時以下のようなログが出力され、アプリが落ちることがあった。

Google Play services out of date. Requires xxxxxxx but found yyyyyyy

xxxxxxx や yyyyyyy バージョン番号。

解決

Google Play 開発者サービス」アプリをアップデートする。

もしかしたら、アップデート後 Android Studio を再起動する必要があるかも。 なぜかというと、アップデート後 マニフェストファイルにパーミッションを追加せよ、というエラーが出力された。でも、そのパーミッションはすでに追加されていた。Android Studio を再起動したところその問題は解消されたので。

リンク

Google Play開発者サービス - Google Play の Android アプリ

UNIXの哲学と関数型プログラミング

UNIXの哲学と関数型プログラミングは共通しているところがあるよね、というのを最近同僚と話した。

ここでいうUNIXの哲学とは

  • スモール・イズ・ビューティフル
  • 一つのプログラムには一つのことを うまくやらせる
  • ソフトウェアの挺子を有効に活用する

など(「UNIXという考え方-その設計思想と哲学」より)。

それらと、関数型プログラミングの、一つの機能に絞った小さな関数を組み合わせ、大きな関数を作っていくという部分が似ているなと。

とりわけ強くそう思うのが、UNIXでコマンドをパイプで繋げて自分の期待する結果を得るところ。関数型プログラミングだと、関数合成にあたる。

UNIXの操作に慣れている人なら、関数型プログラムもしっくりくるのでは。

不正なHTTPヘッダ

リバースプロキシとしてアプリケーションの前にnginxを置いている.

あるとき, アプリケーションにリクエストヘッダが渡らないんですけど, という問い合わせがきた.

うーん, 自分が使っている分にはヘッダは渡ってきてるんだけどな...

とりあえず, どんなリクエストを投げてるのか教えてもらったので, 試してみたところ, 報告通りヘッダが渡ってこない.

nginx を通さずに直接アプリケーションにそのリクエストを投げるとヘッダが渡ってきた. 原因はnginxにあることが判明した.

どうなっているかを把握するため debug ログを出すことにした.

/etc/nginx/nginx.conf

- error_log  /var/log/nginx/error.log warn;
+ error_log  /var/log/nginx/error.log debug;

すると以下のログが出た.

2015/01/21 09:00:16 [info] 3057#0: *2 client sent invalid header line: "X-Hoge : 1234" while reading client request headers, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "localhost"

client sent invalid header line ...不正なヘッダを送っているため、nginx で落とされているようだ.

rfc2616 によると Message Header は

message-header = field-name ":" [ field-value ]
field-name     = token

となっていて, token は

token          = 1*<any CHAR except CTLs or separators>
separators     = "(" | ")" | "<" | ">" | "@"
               | "," | ";" | ":" | "\" | <">
               | "/" | "[" | "]" | "?" | "="
               | "{" | "}" | SP | HT

となっている.

field-name と ":" の間にコントローコードやセパレータががあってはいけない.

今回は field-name と ":" の間に空白があるため, 不正なヘッダとして扱われたようだ.

nginx の設定で ignore_invalid_headers をoff にすればヘッダを落とさずにアプリケーション側に投げてくれる.

けど, まあ空白なしがRFC的に正しいので, 空白を削除してリクエストを投げてもらうことで解決.