2012年3月3日土曜日

HomebrewでインストールしたJenkinsのHTTP listen portを変更する方法

Homebrewでインストールする方法自体はこのあたりを参考に。
~/Library/LaunchAgents/にコピーしたhomebrew.mxcl.jenkins.plistファイルの中を見るとこんな感じになっている。


jenkins.war-jarオプションで起動しているので、これはWinstoneをサーブレットコンテナとして動作している。なので、Winstoneの起動オプションを追加すればそれを読み取ってくれるようだ。以下はlisten portの変更を変更するオプションを追加したもの。


別途試していたScalatraが先に8080を使っていたので、とりあえず18080に引っ越してもらった。

変更はプロセスの再起動後に反映される。

launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist

本格運用するには不十分だろうが、とりあえず試してみるにはこれで十分かと。

2012年2月28日火曜日

mapBetween関数Scala版 その4(完結編)

昨日のポストのコードに対して、がくぞさんからまさにそれが欲しかったという修正を頂いたので掲載。



traitとして別途宣言せず、mapBetween関数が追加された無名クラスのインスタンスを返すようにimplicit conversionを定義する。(3〜7行目)
Seqのサブクラスとして何が来ようと、そのクラスを拡張した形で無名クラスが定義されるため、返り値がmutableになってしまうこともない。何より大変シンプルになってわかりやすい。

今度のseqToExtSeq関数の返り値は無名クラスのインスタンスになるため、返り値の型を明示できない。そのためか、main関数の後に定義すると、以下のようなエラーが出てコンパイル出来なかった。

value mapBetween is not a member of scala.collection.immutable.Range.Inclusive  Note: implicit method seqToExtSeq is not applicable here because it comes after the application point and it lacks an explicit result type mapBetween.scala
順番が入れ替わっているのはそのため。

とりあえず最初の手習いとしてはこれくらいにして、次は別のことをやってみる。

2012年2月27日月曜日

mapBetween関数Scala版 その3

前回で関数の実装はまともになったので、クラスを拡張する方向に検討をしてみる。


とりあえずmapBetween関数をtraitにして分離。(20〜24行目)
前回までは、型パラメタAを下限とする型パラメタTを関数の引数の型としていたが、traitにしたことでまたうまく関数の型推論ができなくなってかえって使いにくいため、Aそのものを引数の型に変更した。匿名関数が手軽に定義できる以上、JavaのComparatorのような問題は起きないと思って良いのかもしれない。

使う際にはSeqのサブクラスをnewする時にwith ExtSeq[A]でtraitをミックスインする(3行目)ことで、mapBetween関数を呼び出すことができるようになる。(4〜6行目)
ミックスインできるのはインスタンス化するときなので、生成されたインスタンスを返すRichIntクラスのtoメソッドは使えない。Rangeクラスのコンストラクタはfrom, to, stepの3つの引数を必ず要求するため、ちょっと使いにくい。

そこで、Seqのサブクラスのインスタンスを trait ExtSeqをミックスインしたクラスのインスタンスに変換する、暗黙の型変換を行う関数を定義する。(14〜17行目)

この暗黙の型変換は、mapBetweenオブジェクトのスコープ内で有効なため、Rangeクラスのインスタンスを普通に生成(8行目)しても、そのインスタンスに対してmapBetween関数を呼び出そうとすることで、コンパイル時に暗黙的にseqToExtSeq関数の呼び出しが加えられる。それによって、あたかもRangeクラスに元々mapBetween関数が定義されていたかのように見える。(9〜12行目)

このseqToExtSeq関数を定義する際に注意することは、返り値をExtSeqにしなければならないのを誤ってSeqのインスタンスを返すように実装(例えば++=の代わりに++を呼んでしまった等)しても、コンパイルが通ってしまうことだ。そうした場合、実行時にseqToExtSeq関数が呼び出されると、Seqのインスタンスを本来の返り値の型であるExtSeqに暗黙的に変換しようとして、自分を再帰的に呼び出してしまい、無限ループに陥る。しかもタチの悪いことに、末尾最適化されてしまってスタックがオーバフローしないため、ただダンマリになってしまってしばらく何が起きたのかわからなかった。実装時はimplicit修飾子を付けないで実装して、後から修飾子を付けたほうが間違いが起こりにくいかも。

ところで、immutableなパッケージでミックスインできるクラスが見つけられなかったためやむを得ずArrayBufferを使ってしまったが、このままでは暗黙の型変換をした後のインスタンスがmutableになってしまう。ココは改善する必要があるのだが、とりあえずノーアイディア。

2012年2月24日金曜日

mapBetween関数Scala版 その2

前回のコードをrpscalaの方々に見て頂いたところ、「それ、slidingでできるよ!」と教えていただいたので修正。


sliding関数は引数で指定された個数を1つのブロックとして、sliding windowを提供する。例えばSeq(1,2,3,4)に対してsliding(2)を呼び出すと、Seq(1,2),Seq(2,3),Seq(3,4)と返すIteratorが返される。sliding(3)ならSeq(1,2,3),Seq(2,3,4)となる。
これはmapBetween関数でやりたいことほぼそのものなので、結果としてえらいシンプルになってしまった。

前回適用する関数に対して型推論が効かないと愚痴ったが、これはカリー化することで対応できることを教えてもらった。カリー化前だと型パラメタAとTを同時に推論しなければならず、それが上手くいかない原因だった模様。カリー化することで先に型Aが決定するため、型Tが上手く推論できるようになるらしい。嘘かも。型推論はもうちょっとまともに勉強したいなぁ。。。

2012年1月27日金曜日

mapBetween関数Scala版

dankogai先生の投稿 algorithm - mapBetween - 配列の隣接する2項にそれぞれ演算を施した配列 にあるmapBetween関数をScalaで書いてみる。
クラスを拡張する形で書きたかったのだが、いっぺんにやろうとするときっとこんがらがるのでとりあえずSeqを引数で受ける形で実装。


とりあえず動くけど、果たして効率の良い実装になっているのか。。。
隣り合う2項に適用する関数のパラメタに、型を指定しなければコンパイルが通らない。第1引数Seqの型パラメタから推論して欲しいところだけどどうもそうはならないみたい。
foldLeftの左項のタプルの1つめの要素で、Seq[B]()の代わりにNilを指定したかったのだが、型の不一致でこれもまたコンパイルが通らない。まぁこちらはなんとなく仕方がない気がする。

2011年1月25日火曜日

topコマンドのログ解析(Python手習い) その2

その後OrderedDictというクラスがcollectionsモジュールにあることが判明したため、これを使ってもうちょっとマシなファイルハンドル操作を実装してみた。

ついでにファイルオープン時に致命的なバグがあったため、合わせて修正。

from collections import OrderedDict
from datetime import datetime, timedelta
from functools import reduce
from os.path import exists
from re import match, search, split
from sys import argv
from shutil import rmtree
from os import makedirs

MAX_FILE_HANDLE = 500
WORK_DIR = 'work'

if len(argv) != 3:
    print("\nUsage:\npython", argv[0], "logfile yyyy/mm/dd")
    exit()

rmtree(WORK_DIR, True)
if not exists(WORK_DIR):
    makedirs(WORK_DIR)

mydate = datetime.strptime(argv[2], '%Y/%m/%d')
mydateStr = mydate.strftime('%Y/%m/%d ')

try:
    f = open(argv[1])
    mem = open(WORK_DIR + "/mem.log", 'w')
    mem.write("time,mem av,mem used,mem free,mem shard,mem buff,mem actv, mem in_d,swap av,swap used,swap free,swap cached\n")
    processes = OrderedDict()
    maxtime = ""

    for line in f:
        line = line.strip()
        if match(r"^\d\d:\d\d:\d\d", line):
            if line[0:2] == "00" and timestamp[0:2] != "00":
                mydate = mydate + timedelta(1)
                mydateStr = mydate.strftime('%Y/%m/%d ')
            timestamp = line[0:8]
        elif match(r"^Mem", line):
            data = split(r"\s+", line)[1:9:2]
        elif match(r"^\d+k", line):
            data = data + split(r"\s+", line)[0:5:2]
        elif match(r"^Swap:", line):
            data = data + split(r"\s+", line)[1:8:2]
            prefix = mydateStr + timestamp
            result = reduce((lambda x,y: x + ',' + y.rstrip('k')), data, prefix)
            mem.write(result + "\n")
        elif search(r"java", line):
            data = split(r"\s+", line)
            pid = data[0]

            process = processes.get(pid)
            if process == None:
                while len(processes) >= MAX_FILE_HANDLE:
                    processes.popitem(last=False)[1].close()
                filename = WORK_DIR + "/Pid-" + pid + ".log"
                if exists(filename):
                    process = open(filename, 'a')
                else:
                    process = open(filename, 'w')
                    process.write("time,PID,USER,PRI,NI,SIZE,RSS,SHARE,STAT,%CPU,%MEM,TIME,CPU,COMMAND\n")
            else:
                del processes[pid]
            processes[pid] = process

            prefix = mydateStr + timestamp
            result = reduce((lambda x,y: x + ',' + y), data, prefix)
            process.write(result + "\n")
            if data[10] > maxtime:
                maxtime = data[10]
                maxpid = pid

    print("maxpid:", maxpid)

finally:
    if f != None:
        f.close()
    if mem != None:
        mem.close()
    map(lambda x: x.close(), processes)

キモは51~63行あたり。

OrderedDictは名が示すとおり、(key, value)セットの追加順序を記憶する辞書型である。ただし、一度追加した(key, value)は、valueの更新が行われても順序を入れ替えない。今回はLRUを実装しなければならないため、既に追加されている(key, value)にアクセスした場合は必ず一度削除してから再度追加するようにしている。

さらに、もし管理しているファイルハンドルがMAX_FILE_HANDLEを超えそうになったら、一番使われていないファイルハンドルを取得しクローズ処理を行なっている(54行目)。popitemメソッドは、引数がtrueの場合はLIFO、falseの場合はFIFOとして動作する。

前回バグっていたのを直したのは56~60行目あたり。前回はopen関数の第2引数を常に'w'と指定していたため、ファイルハンドルが増えたために一旦クローズしてしまったファイルについて、再度書きこみを行おうとした場合にクローズ前までのデータを消してしまっていた。オープン時にファイルが存在する場合は追記('a')するように修正。

150MB程度のログファイルの処理に、僕の非常に非力なマシン(PentiumM 1.2GHz 752MB RAM)で約2分。生成されるファイル数は4,000あまりというところ。

時間があったら、次はもう少しモジュール化してみたい。

2011年1月24日月曜日

topコマンドのログ解析(Python手習い)

topコマンドのログをExcelでグラフ化できるようにCSV形式に変換するPythonスクリプトを書いてみた。とりあえず動いたが、色々知らないまま書いているのでたぶんもっと良い書き方があるはず。

JavaのPID毎に別ファイルに出力するようにしたかったのだが、Windows上では500個ちょっとオープンしたところで"Too Many Open Files"エラーが出たため、500を超えたら一旦すべてクローズするように暫定対処した。できれば参照の少ないファイルから先にクローズしていくようにしたい。Javaで言うところのLinkedHashMapみたいなものは無いのだろうか。

スクリプト言語としてはずいぶん昔にPerlを触った以来だが、やっつけで書きやすい割にPerlよりは可読性が高いように感じる。

import sys
import re
from datetime import datetime, timedelta
from functools import reduce

if len(sys.argv) != 3 :
 print("\nUsage:\npython", sys.argv[0], "logfile yyyy/mm/dd")
 exit()

mydate = datetime.strptime(sys.argv[2], '%Y/%m/%d')
mydateStr = mydate.strftime('%Y/%m/%d ')

try :
 f = open(sys.argv[1])
 mem = open("mem.log", 'w')
 mem.write("time,mem av,mem used,mem free,mem shard,mem buff,mem actv, mem in_d,swap av,swap used,swap free,swap cached\n")
 processes = {}
 maxtime = ""

 for line in f :
  line = line.strip()
  if re.match(r"^\d\d:\d\d:\d\d", line) :
   if line[0:2] == "00" and timestamp[0:2] != "00" :
    mydate = mydate + timedelta(1)
    mydateStr = mydate.strftime('%Y/%m/%d ')
   timestamp = line[0:8]
  elif re.match(r"^Mem", line) :
   data = re.split(r"\s+", line)[1:9:2]
  elif re.match(r"^\d+k", line) :
   data = data + re.split(r"\s+", line)[0:5:2]
  elif re.match(r"^Swap:", line) :
   data = data + re.split(r"\s+", line)[1:8:2]
   prefix = mydateStr + timestamp
   result = reduce((lambda x,y: x + ',' + y.rstrip('k')), data, prefix)
   mem.write(result + "\n")
  elif re.search(r"java", line) :
   data = re.split(r"\s+", line)
   if data[0] not in processes :
    processes[data[0]] = open("Pid-" + data[0] + ".log", 'w')
    processes[data[0]].write("time,PID,USER,PRI,NI,SIZE,RSS,SHARE,STAT,%CPU,%MEM,TIME,CPU,COMMAND\n")
   prefix = mydateStr + timestamp
   result = reduce((lambda x,y: x + ',' + y), data, prefix)
   processes[data[0]].write(result + "\n")
   if data[10] > maxtime :
    maxtime = data[10]
    maxpid = data[0]

  if len(processes) > 500 :
   map(lambda x: x.close(), processes)
   processes = {}

 print("maxpid:", maxpid)

finally :
 if f != None :
  f.close()
 if mem != None :
  mem.close()
 map(lambda x: x.close(), processes)