perl で複数のプロセスからログを吐くときは :unix layer で開いてもいい

File::RotateLogs が大変便利でよく使っているのだが、これはファイルへ書き込むときに print を呼んでいる。

一方

によると長いログを吐くときは syswrite にしないと混ざるとある。

よって File::RotateLogs も実は混ざることがあるのかなと思い、複数プロセスから長いログを吐きまくってみたが一向に混ざらなかった。

いろいろ調べた結果、open layer に最も低級な layer である :unix を指定すれば print 時も 1 つの write(2) になるようだった。

> cat test.pl
open my $fh, ">:unix", "test.txt";
print $fh "a" x (1024 * 100);

> strace perl test.pl 2>&1 | grep write
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 102400) = 102400

そして、File::RotateLogs はしっかりと :utf8:unix で open していた。流石である。

まとめ

perl複数のプロセスからログを吐くときは syswrite, もしくは open layer を :unix にすべき。

余談

標準 open layer は大抵の場合 :unix:perlio のため、:perlio を外すという意味で

open my $fh, ">:pop", "file";

としてもよさそうではある。

メモリ使用量をログに吐く

cron で 5分毎に動かしているプログラムでメモリ使用量が気になるものがあったため、 下記のようにしてメモリ使用量をログに吐いたところ、いろいろ捗った。

use Log::Minimal;

sub rss {
    my $rss = `ps -p $$ -o rss=` || 0;
    $rss =~ s/\s+//gsm;
    $rss; # KB
}

infof "memory usage: %dMB", rss() / 1024;
__END__
2015-01-31T11:24:58 [INFO] memory usage: 4MB at rss.pl line 9

perl を build するとき man page を作らない

-Dman1dir=none -Dman3dir=none をつければいいらしい。

$ ./Configure -Dprefix=$HOME/hoge -des -Dman1dir=none -Dman3dir=none
$ make -j`nproc` install

plenv の場合も

$ plenv install 5.20.1 -j`nproc` -Dman1dir=none -Dman3dir=none

でいい。

perldoc があるので、man page はほとんど無意味である。

remote サーバで pbcopy for iTerm2

pbcopy は MacOSX 用の標準入力をクリップボードにコピーしてくれる便利ツールだが、 local でしか利用できない。

しかしながら実際のところ、OSC52 エスケープシーケンスを使えば remote サーバからも クリップボードにアクセスできる。下記 url に詳しく書いてある。

ということで iTerm2 前提の remote サーバ用 pbcopy を用意してみた。

以下、おそらく不安定な nightly の iTerm2 を使い、 さらにセキュリティ上の問題をはらむ OSC52 を使う。

インストール

まず OSC52 で読み込めるデータサイズ制限をなくした iTerm2 をインストールする (そうしないと base64 encode 後の1024 byte しかコピーできず役に立たない)。 http://iterm2.com/downloads/nightly から nightly を ダウンロードし、解凍してでてきた iTerm.app を /Applications におけばいい (もっといいインストール方法がある気がする)。

また iTerm2 の Preference で "Allow clipboard access to terminal apps" にチェックをいれておく。

f:id:ks0608:20150118230050p:plain

あとは remote サーバで github から pbcopy をダウンロードすればいい。

[remote] $ wget https://raw.githubusercontent.com/shoichikaji/remote-pbcopy-iterm2/master/pbcopy
[remote] $ chmod +x pbcopy
[remote] $ mv pbcopy /path/to/bin/

使い方

普通の pbcopy と同じように使える。

[remote] $ echo "copy data to clipboard from remote!" | pbcopy

[local] $ pbpaste
copy data to clipboard from remote!

See also

iTerm2 の "OSC52 で読み込めるデータサイズ制限" に関する issue:

TeraTerm 版 pbcopy

cpan module を symlink で管理できるか

昨日 吉祥寺 pm に参加し、songmu さん、charsbar さんと miyagawa さんの開発している carmel についてお話しすることができた。(主催していただいたmagnoliaさん、ありがとうございました!)

その中で、PERL5LIB にたくさんの path を突っ込むのではなく、symlink で一つのところにまとめてその path のみを設定するのはどうか、という話になった。

これは以前から話題にはなるらしいが、charsbar さんいわく 「ある module の属する distribution が変わったときが大変」とのこと。確かに。

しかし、一度やってみないとその良さ、悪さが自分には理解できないので作ってみた。

これは symlinklib ディレクトリ以下に symlink を作りまくりその path を PERL5LIB に設定し exec できるやつである。

使い方:

> carmel install Plack@1.0030
> cat "requires 'Plack', '== 1.0030';" > cpanfile

> what-happens exec perl -MPlack -e 'printf "Plack %s in %s\n", Plack->VERSION, $INC{"Plack.pm"}'
Plack 1.0030 in /Users/skaji/symlinklib/lib/Plack.pm

> what-happens exec env | grep PERL5LIB
PERL5LIB=/Users/skaji/symlinklib/lib

そんなに悪くない気がする。(charsbar さんに指摘していただいた問題はちゃんと artifact を選べれば大丈夫か?)

Carmel 使ってみる

https://github.com/miyagawa/Carmel を使ってみる!

注:miyagawa さんに教えていただき cpanm インストール方法、例ともに修正しました https://gist.github.com/shoichikaji/5a951ab796ed0e596c47/revisions

まず copy-build-artifacts ブランチの cpanm をインストール

$ cpanm File::Copy::Recursive # 依存してるのではじめに入れておく
$ cpanm git://github.com/miyagawa/cpanminus.git@copy-build-artifacts

Carmel をインストール

$ cpanm git://github.com/miyagawa/Carmel.git

Plack のバージョン1.0033, 1.0030を切り替えてみる。

$ carmel install Plack@1.0033
$ carmel install Plack@1.0030

$ ls -F ~/.cpanm/builds/ | grep Plack
Plack-1.0030/
Plack-1.0033/

$ echo "requires 'Plack', '== 1.0033';" > cpanfile
$ carmel exec perl -MPlack -e 'printf "Plack %s in %s\n", Plack->VERSION, $INC{"Plack.pm"}'
Plack 1.0033 in /Users/skaji/.cpanm/builds/Plack-1.0033/blib/lib/Plack.pm

$ echo "requires 'Plack', '== 1.0030';" > cpanfile
$ carmel exec perl -MPlack -e 'printf "Plack %s in %s\n", Plack->VERSION, $INC{"Plack.pm"}'
Plack 1.0030 in /Users/skaji/.cpanm/builds/Plack-1.0030/blib/lib/Plack.pm

carmel exec は PERL5LIB にPlackが依存する ~/.cpanm/builds/*/blib/{arch,lib} を加えることでこれを実現している:

$ echo "requires 'Plack', '== 1.0030';" > cpanfile
$ carmel exec env | grep PERL5LIB
PERL5LIB=/Users/skaji/.cpanm/builds/Plack-1.0030/blib/arch:/Users/skaji/.cpanm/builds/Plack-1.0030/blib/lib:/Users/skaji/.cpanm/builds/Apache-LogFormat-Compiler-0.32/blib/arch:/Users/skaji/.cpanm/builds/Apache-LogFormat-Compiler-0.32/blib/lib:/Users/skaji/.cpanm/builds/Devel-StackTrace-2.00/blib/arch:/Users/skaji/.cpanm/builds/Devel-StackTrace-2.00/blib/lib ...

Note

  • bundler の挙動に近くなった。
  • 同一モジュールの異なるバージョンを保持できて、簡単にスイッチできるようになった。すばらしい!
  • ~/.cpanm/builds を共有するのでインストールの時間が節約されそう。
  • cpanfile.snapshot を使えば依存も含めてバージョン指定できる。
  • 環境変数 PATH, PERL5LIB が相当長くなりそう。cf: http://d.hatena.ne.jp/gfx/20130118/1358476166

App::FatPacker::Simple

App::FatPacker::Simple というのを書いた。

これはモジュールの依存関係は 使う人が理解しているという前提 で fatpack するところだけいい感じでやってくるやつである。

INSTALL

> cpanm git://github.com/shoichikaji/App-FatPacker-Simple.git

App::FatPacker との関係

App::FatPacker は、あるスクリプトを その依存モジュールをすべて含めた形にまとめてくれるツールである。

利点としては、

  • 依存もすべてまとめた一枚スクリプトになるのでポータビリティーが格段にあがる。
  • あとで FatPacker でまとめればいいという安心感から、スクリプトを幾つかの module に分けて書ける。

がある。実際 cpanminus は fatpack された形で配布されていて その利点を多くの人が享受しているように思う。

一方で一度やってみればわかるが App::FatPacker は、はまりどころにあふれていてなかなか使いこなせない。

App::FatPacker は大きくわけて

  1. あるスクリプトが依存しているモジュールを調べて、
  2. 依存モジュールを含めた fatpacked スクリプトを作る

をやっている。 この 1. が特にはまりどころ満載で、 2. については単に融通が利かないという感じである。

App::FatPacker::Simple は 1. については使う人がしっかり理解しているという前提で、2. だけの面倒を見る。

個人的に 1. を機械的に解決するのは無理だと思っている。

使い方

(以下の例は https://github.com/shoichikaji/fatpack-example にある)

例えば hello.pl があり、それは lib/Hello.pm, lib/Hello/CLI.pm を元にしているとしよう。 また外部依存モジュールは cpanfile に書かれているとする。

> find .
.
./cpanfile
./hello.pl
./lib
./lib/Hello
./lib/Hello/CLI.pm
./lib/Hello.pm

> cat cpanfile
requires "Sub::Retry";
requires "HTTP::Tiny";

App::FatPacker::Simple およびそのフロントエンド fatpack-simple は カレントディレクトリの lib, local, fatlib, extlib ディレクトリにあるモジュールを perl-strip して fatpacked スクリプトを作るだけである。

よってまず 依存モジュールを local, extlib などにインストールする。

> carton install
Installing modules using /Users/skaji/cpanfile
Successfully installed CPAN-Meta-2.143240 (upgraded from 2.140640)
Successfully installed Module-Build-0.4210 (upgraded from 0.4205)
Successfully installed Sub-Retry-0.06
3 distributions installed

# Sub::Retry は parent モジュールに依存すると **あなたが理解してる前提**
# HTTP::Tiny も fatpack すべきだと **あなたが理解してる前提**
> cpanm -nq --reinstall -lextlib parent HTTP::Tiny
Successfully reinstalled parent-0.228
Successfully installed HTTP-Tiny-0.053 (upgraded from 0.049)
2 distributions installed

その後、fatpack-simple を実行すればいい。ただしこのままだと build 時しか入らない Module::Build なども入るので それは exclude する。

> fatpack-simple --exclude Module::Build,CPAN::Meta hello.pl
-> perl-strip lib/perl5/parent.pm
-> perl-strip lib/perl5/HTTP/Tiny.pm
-> perl-strip Hello.pm
-> perl-strip Hello/CLI.pm
-> exclude CPAN/Meta.pm
...
-> exclude Module/Build.pm
...
-> perl-strip Sub/Retry.pm

すると hello.fatpack.pl ができる。

ちなみに以上の作業を Daikufile にまとめておくと便利。

> daiku fatpack
# get hello.fatpack.pl

その他

こうしてできた hello.fatpack.pl を実際実行させたい perl (例えば 5.8.8 とか) でテストすべき。

fatpack 自体を perl 5.8.8 などでやるという考えもあるが、 個人的に最新の perl で fatpack を作り、5.8.8 でテストの方がいいと思っている。

Enjoy fatpack!