tokuhirom's blog

[kotlin] ktor で config を読みたい

前提として、ktor のサンプルコードは、

    embeddedServer(Jetty, 8080, module = Application::module)
            .start()        

のように書かれているものが多いのですが、embeddedServer() を呼んだだけでは設定ファイルは読まれません。

fun main(args: Array<String>) {
    val applicationEnvironment = commandLineEnvironment(args)
    JettyApplicationHost(applicationEnvironment).start()
}

のように commandLineEnvironment を利用するのがよさそうです。

こうすることで、コマンドライン引数もよしなに扱ってくれるようになり、たとえば -port=8080 のように port 番号をコマンドライン引数で指定できるようになります。

で、application.conf を src/main/resources/application.conf に起きます。ktor サーバーの port 番号や、起動時のエントリポイントなどの指定もここでおこなえますし、アプリケーション独自の設定もここに書くことができます。

ktor {
  deployment {
    port = 8080
  }
  application {
    modules = [ com.example.HelloKt.module ]
  }
}



myapp {
  foobar {
    baz = 5963
  }
}

あとは以下のようにすれば設定を読み込み可能です。簡単ですね。

    val baz = environment.config.property("myapp.foobar.baz").getString()
    environment.log.info("baz: $baz")

完全な例はこちらです。 https://github.com/tokuhirom/ktor-samples/tree/master

Created: 2017-05-30 23:12:05 +0000
Updated: 2017-05-30 23:12:05 +0000

[kotlin] ktor で basic auth したい

ktor-auth コンポーネントで簡単に設定できます。

    compile "org.jetbrains.ktor:ktor-auth:$ktor_version"

で、あとは以下のように認証を設定する。設定した principal をコントローラ内でも取得可能。

        route("/") {
                authentication {
                    basicAuthentication("your-app-realm") { credentials ->
                        val username = credentials.name
                        val password = credentials.password
                        if (checkAuth(username, password)) {
                            UserIdPrincipal(username, password)
                        } else {
                            null
                        }
                    }
                }

            get("/hello") {
                val name = call.principal<UserIdPrincipal>()!!.name
                call.respondText("Hello $name")
            }
        }

簡単ですね。

Created: 2017-05-26 23:45:20 +0000
Updated: 2017-05-26 23:45:20 +0000

kotlin で instance_eval 的なことをしたい

kotlin では ruby 的な block つきメソッド呼び出しができるので、以下のようにすると instance_eval 的な、rubyish な DSL もめっちゃ簡単に実現できる〜〜

class Foo {
    fun bar(cb: Foo.() -> Unit) {
        apply(cb)
    }

    fun berk() {
        println("berk")
    }
}

fun main(args: Array<String>) {
    Foo().bar() {
        berk()
    }
}
Created: 2017-05-24 13:17:09 +0000
Updated: 2017-05-24 13:17:09 +0000

osx の notification を kotlin で呼びたい

osascript で呼ぶのがいいかな。apple script の escape 方法はよくわからないので JavaScript で書くことにして、 JSON で serialize して渡すのが良さそう。

fun sendNotification(message: String, title: String=message, subtitle: String=message, soundName: String = "Frog") {
    // https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/DisplayNotifications.html
    val params = jsonObject(
            "withTitle" to title,
            "subtitle" to subtitle,
            "soundName" to soundName
    )

    Runtime.getRuntime().exec(arrayOf(
            "osascript",
            "-l",
            "JavaScript",
            "-e",
            """var app = Application.currentApplication();
                app.includeStandardAdditions = true
                app.displayNotification(${message.toJson()}, $params)"""
    ))
}

JSON serialize は kotoson が便利。

compile 'com.github.salomonbrys.kotson:kotson:2.5.0'

で、こういうのちょっと頑張って書いてみたけど、LINE Notify 使うのが便利っぽいって話になった。。

Created: 2017-05-22 04:18:26 +0000
Updated: 2017-05-22 04:18:26 +0000

kotlin native で外部のライブラリとリンクする

kotlin native は外部のライブラリとリンクするのも簡単にできます。

以下、例として openssl にアクセスしてみます。

openssl.def として以下のように記載します。

headers = openssl/crypto.h openssl/opensslv.h

openssl.def を利用するコードとして以下のように記載(型の推移がわかりやすいように冗長にかいてます)。

import openssl.*
import kotlinx.cinterop.*

fun main(args: Array<String>) {
    val ver: CPointer<ByteVar>? = SSLeay_version(SSLEAY_VERSION)
    val verStr: String = ver!!.toKString()
    println(verStr)
}

あとは、以下のように Makefile でビルドしたら出来上がり。

all: sample.kexe

CCFLAGS=-I/usr/local/opt/openssl/include

clean:
        rm -rf openssl.kt.bc-build/ openssl.kt.bc/ openssl.kt.bc.klib sample.kexe "sample}.kt.bc"

openssl.kt.bc: openssl.def
        cinterop -copt "${CCFLAGS}" -def openssl.def -o openssl.kt.bc

sample.kexe: sample.kt openssl.kt.bc
        kotlinc-native -linkerArgs "-L/usr/local/opt/openssl/lib -lcrypto" -library openssl.kt.bc sample.kt -o sample

.PHONY: all clean

Makefile じゃなくて gradle でビルドするようにしたほうが良いが、めんどくさくて使い慣れた Makefile で今回はビルドしてみた。

実行結果は以下のようになる。

tokuhirom@mba ~/dev/kotlin-native-samples/openssl-version $ ./sample.kexe
OpenSSL 1.0.2k  26 Jan 2017

まとめ

kotlin native 意外と動く。しかしコンパイルは遅い。

Created: 2017-05-19 00:57:03 +0000
Updated: 2017-05-19 00:57:03 +0000

kotlin で native code を生成 kotlin native を楽しみたい

android で kotlin が話題なので、kotlin native を試してみました。 kotlin native を利用すると、kotlin のコードをネイティブコードにコンパイルできます。

https://github.com/JetBrains/kotlin-native

git clone git@github.com:JetBrains/kotlin-native.git

しておいて

./gradlew dependencies:update
./gradlew dist

す。Hello.kt を作成する。

fun main(args : Array<String>) {
    println("Hello, world")
}

以下のように、コンパイルする。

./dist/bin/kotlinc Hello.kt -o hello
[tokuhirom@dev2 kotlin-native]$ time ./program.kexe
Hello, world

real    0m0.002s
user    0m0.001s
sys     0m0.000s

CGI で実行できそうなぐらい速い。便利!!!

C library へのアクセス

C library には *.def ファイルを作ってアクセスすれば良い。 https://github.com/JetBrains/kotlin-native/blob/master/samples/tetris/sdl.def

header ファイルをスキャンしてるのかな?

ためしに stdio.def というファイルを作成する。

headers = stdio.h
excludeDependentModules.osx = true

実際にこのファイルからビルドしてみる。

[tokuhirom@dev2 kotlin-native]$ ./dist/bin/cinterop -def stdio.def -o stdio.kt.bc
JetFile: stdio.kt

このライブラリを利用した Hello2.kt は以下のようになる。

import stdio.puts

fun main(args : Array<String>) {
    puts("Hello, world")
}

さて、stdio.kt を利用したスクリプトをビルドしてみる。

[tokuhirom@dev2 kotlin-native]$ ./dist/bin/konanc Hello2.kt -library stdio.kt.bc -o Hello2.kexe
JetFile: Hello2.kt
[tokuhirom@dev2 kotlin-native]$ ./Hello2.kexe
Hello, world

動いた!!

結論

kotlin native わりと動く。

Created: 2017-05-18 07:20:25 +0000
Updated: 2017-05-18 07:20:25 +0000

Furl の timeout と SSL

Furl は、XS モジュールへの依存を排除するために IO::Socket::SSL のロードを実行時に行っております。 結果として SSL を利用したアクセスのうち、初回のアクセス時は若干の遅延が生じます。 use IO::Socket::SSL には 0.05sec〜0.1sec 程度かかります。

通常は問題がありませんが、timeout を0.1secなどに設定していると、モジュールのロードだけで timeout sec を消費してしまう可能性があります。

HTTPS で通信する場合には use IO::Socket::SSL しておくことを推奨します。

なお、この動作は仕様です。

Created: 2017-05-17 00:26:49 +0000
Updated: 2017-05-17 00:26:49 +0000

parallelStream の利用スレッド数

Created: 2017-05-16 03:49:54 +0000
Updated: 2017-05-16 03:49:54 +0000

Java でちょっとしたパーサーを実装するときは ANTLR4 が便利

Java でちょっとしたDSLパーサーを実装するときはANTLR4 が最近もデファクトスタンダードなのかなあ。と思っています。

ANTLR4 はパーサージェネレーターです。BNF っぽい記法で書いたらいい感じに生成してくれます。手書きパーサーとかに比べると管理しやすい気がします。

gradle はコアプラグインに antlr プラグインがあるので簡単に利用できます。 https://docs.gradle.org/current/userguide/antlr_plugin.html

group 'com.example'

apply plugin: 'java'
apply plugin: 'antlr'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    antlr "org.antlr:antlr4:4.7"
    testCompile 'junit:junit:4.12'
}

とかして、src/main/antlr に文法定義ファイルを置くだけ。

四則演算なら以下のような感じ。

grammar Expr;

@header {
    package com.example;
}

prog: expr;
expr: term (('+'|'-') term)*;
term: factor (('*'|'/') factor)*;
factor: INT
    | '(' expr ')'
    ;
INT     : [0-9]+ ;

利用コードは以下のような感じ。

package com.example;

import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

import java.io.IOException;
import java.util.List;

public class Test {
    public static void main(String[] args) throws IOException {
//        CharStreams.fromFile() とかもあるよ
        ExprLexer lexer = new ExprLexer(CharStreams.fromString("3+2*4"));
        CommonTokenStream stream = new CommonTokenStream(lexer);
        ExprParser exprParser = new ExprParser(stream);
        ExprParser.ExprContext expr = exprParser.expr();
        System.out.println("toInfoString : " + expr.toInfoString(exprParser));
        System.out.println("toString : " + expr.toString());
        System.out.println("toStringTree : " + expr.toStringTree());
        System.out.println("\n");

        // AST はこういう形で辿れる
        List<ExprParser.TermContext> term = expr.term();
        System.out.println(term.get(0).factor(0).INT());

       // ビジターパターンで処理することも可能
        ParseTreeWalker walker = new ParseTreeWalker();
        walker.walk(new ExprBaseListener(), expr);
    }
}

全体のコードはこちら。 https://github.com/tokuhirom/antlr-demo

Created: 2017-05-15 22:42:29 +0000
Updated: 2017-05-15 22:42:29 +0000

Sierra で key のスワップしながら cmd 単体だと英数にしたりする方法

現時点では karabiner が sierra だと使えない。 karabiner elements 使えば、キーのスワップはできるが、cmd 単体押した場合に英数にする、みたいなことは karabiner elements ではできない。 karabiner elements + cmdえいかなでできるかなと思いきや、karabiner elements と cmd えいかなは食合せが悪いっぽい。

僕はギョームでは realforce を使っているので、key の swap も必須なのであった。。

というわけで困っていて、ctrl+shift+; と ctrl+shift+j で切り替えていたのだが、karabiner elements の fork 版を使えばお望みの機能は使えるということをバーの常連の人に聞いたので、利用するようしたら、sierra 以前と同等の入力環境を得ることができ、hammerspoon を使って lua を書いたりしなくて済んだ。 http://qiita.com/Linda_pp/items/32c83feb82faa9d73764

Created: 2017-04-26 09:12:04 +0000
Updated: 2017-04-26 09:12:04 +0000

pidstat で見れるような数字を perl で取ってみる

pid ファイルを読んで procfs からバーンと取って集計とるようなやつを書いてみた。 1secごとに60回データを取って、平均を取るみたいなのを子プロセス含めてバーンとヤる感じをやってみた。 書きながらオーバーヘッドがどうかな〜とか考え始めちゃって、結局こういうの、go で書いたほうが良いなということを感じる。

use strict;
use warnings;
use v5.10.0;
use Getopt::Long;
use Pod::Usage;
use warnings FATAL => 'recursion';

{
    package ProcStat::Stat;

    use File::Basename qw/basename/;
    use List::Util qw/sum0/;

    sub new {
        my $class = shift;
        bless {
        }, $class;
    }

    sub read {
        my ($self) = @_;
        open my $fh, '<', '/proc/stat' or die "Cannot read /proc/stat: $!";
        my $line = <$fh>;
        $line =~ /^cpu\s+(.*)/;
        return sum0(split / /, $1);
    }
}

{
    package ProcStat::Process;

    use File::Basename qw/basename/;
    use List::Util qw/sum0/;

    # https://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html
    use constant {
        PROC_UTIME  => 13,
        PROC_STIME  => 14,
        PROC_CUTIME => 15,
        PROC_CSTIME => 16,

        IO_RCHAR       => 0,
        IO_WCHAR       => 1,
        IO_SYSCR       => 2,
        IO_SYSCW       => 3,
        IO_READ_BYTES  => 4,
        IO_WRITE_BYTES => 5,

        PREV_STAT   => 0,
        PREV_IO     => 1,
        PREV_SMAPS  => 2,
    };

    sub new {
        my ($class, $pid_file, $logger) = @_;

        my $self = bless {
            pid_file    => $pid_file,
            history => [],
            logger      => $logger,
        }, $class;
        $self->{name} = $self->_build_name();
        return $self;
    }

    sub pid_file { shift->{pid_file} }
    sub name     { shift->{name} }
    sub logger   { shift->{logger} }

    sub _build_name {
        my $self = shift;
        my $name = basename($self->pid_file);
        $name =~ s/\.pid$//;
        $name;
    }

    sub read_pid {
        my $self = shift;
        local $/;
        open my $fh, '<', $self->pid_file
            or do {
                $self->logger->warnf("Cannot read pid file: $self->{pid_file}: $!");
                return;
            };
        my $s = <$fh>;
        $s =~ s/\s+//g;
        $s;
    }

    sub clear_history {
        my ($self) = @_;
        $self->{history} = [];
    }

    sub aggregate {
        my ($self, $elapsed, $child_mgr) = @_;

        $self->logger->infof("Now loading %s", $self->pid_file);
        my $pid = $self->read_pid
            or return;
        my $proc_stat = $self->read_stat($pid)
            or return;

        my @pids = ($pid, $self->child_pids($pid));

        my $current_io = $self->aggregate_io(@pids);
        my $current_smaps = $self->aggregate_smaps($pid);
        if ($self->{prev}) {
            my $utime       = $proc_stat->[PROC_UTIME]      - $self->{prev}->[PREV_STAT]->[PROC_UTIME];
            my $stime       = $proc_stat->[PROC_STIME]      - $self->{prev}->[PREV_STAT]->[PROC_STIME];
            my $cutime      = $proc_stat->[PROC_CUTIME]     - $self->{prev}->[PREV_STAT]->[PROC_CUTIME];
            my $cstime      = $proc_stat->[PROC_CSTIME]     - $self->{prev}->[PREV_STAT]->[PROC_CSTIME];
            my $rchar       = $current_io->[IO_RCHAR]       - $self->{prev}->[PREV_IO]->[IO_RCHAR];
            my $wchar       = $current_io->[IO_WCHAR]       - $self->{prev}->[PREV_IO]->[IO_WCHAR];
            my $read_bytes  = $current_io->[IO_READ_BYTES]  - $self->{prev}->[PREV_IO]->[IO_READ_BYTES];
            my $write_bytes = $current_io->[IO_WRITE_BYTES] - $self->{prev}->[PREV_IO]->[IO_WRITE_BYTES];
            my $pss         = $current_smaps;
            push @{$self->{history}}, [
                ($utime+$cutime)/$elapsed,
                ($stime+$cstime)/$elapsed,
                $rchar,
                $wchar,
                $read_bytes,
                $write_bytes,
                $pss,
            ];
        }
        $self->{prev} = [$proc_stat, $current_io, $current_smaps];
    }

    sub aggregate_smaps {
        my ($self, @pids) = @_;
        return sum0 grep { defined $_ } map { $self->read_smaps($_) } @pids;
    }

    sub read_smaps {
        my ($self, $pid) = @_;
        open my $fh, '<', "/proc/$pid/smaps"
            or return;
        my $pss = 0;
        my $src = do { local $/; <$fh> };
        unless (defined $src) {
            warn "Cannot red /proc/$pid/smaps: $!";
            return;
        }
        $src =~ s/Pss:\s+(\d+)/$pss += $1/gesm;
        return $pss;
    }

    sub aggregate_io {
        my ($self, @pids) = @_;
        my @io = grep { $_ } map { $self->read_io($_) } @pids;
        my @retval = (0,0,0,0,0,0);
        for my $row (@io) {
            for my $i (0..@$row-1) {
                $retval[$i] += $row->[$i];
            }
        }
        return \@retval;
    }

    sub read_io {
        my ($self, $pid) = @_;
        open my $fh, '<', "/proc/$pid/io"
            or return;
        my $src = do { local $/; <$fh>; };
        if ($src =~ /rchar: (\d+)\nwchar: (\d+)\nsyscr: (\d+)\nsyscw: (\d+)\nread_bytes: (\d+)\nwrite_bytes: (\d+)/) {
            return [$1, $2, $3, $4, $5, $6];
        } else {
            return;
        }
    }

    sub child_pids {
        my ($self, $pid) = @_;
        my @retval;
        # /proc/[pid]/task/[tid]/children since Linux 3.5
        LOOP:
        for my $children (glob("/proc/$pid/task/*/children")) {
            open my $fh, '<', $children
                or do {
                    $self->logger->info("Cannot read $children: $!");
                    next LOOP;
                };
            local $/;
            my @pids = split / /, <$fh>;
            push @retval, @pids;
            push @retval, map { $self->child_pids($_) } @pids;
        }
        return @retval;
    }

    sub result {
        my ($self) = @_;
        if (@{$self->{history}}) {
            return [map { $self->sum_history($_) } 0..6];
        } else {
            return [0,0,0,0,0,0,0];
        }
    }

    sub sum_history {
        my ($self, $idx) = @_;
        return (sum0 map { $_->[$idx] } @{$self->{history}}) / @{$self->{history}};
    }

    sub read_stat {
        my ($self, $pid) = @_;

        my $statfile = "/proc/$pid/stat";
        open my $fh, '<', $statfile
            or do {
                $self->logger->warnf("Cannot read %s: %s", $statfile, $!);
                return;
            };
        return [split / /, <$fh>];
    }
}

{
    package ProcStat::Logger;
    use Time::Piece qw/localtime/;

    sub new {
        my ($class, $level) = @_;
        $level = lc($level);
        bless {
            debug => $level eq 'debug',
            info  => $level eq 'debug' || $level eq 'info',
            warn  => $level eq 'debug' || $level eq 'info' || $level eq 'warn',
        }, $class;
    }

    sub warnf {
        my $self = shift;
        $self->_log('WARN', @_) if $self->{warn};
    }

    sub infof {
        my $self = shift;
        $self->_log('INFO', @_) if $self->{info};
    }

    sub debugf {
        my $self = shift;
        $self->_log('DEBUG', @_) if $self->{debug};
    }

    sub _log {
        my ($self, $level, $fmt, @args) = @_;
        my $body = sprintf($fmt, @args);
        $body =~ s/\n/\\n/g;
        print {*STDERR} localtime->strftime("[%Y-%m-%dT%H:%M:%S%Z] [$level] $body\n");
    }
}

{
    package ProcStat::ChildProcessManager;

    sub new {
        my $class = shift;
        my $self = bless {
        }, $class;
        $self->{ppid2pids} = $self->_build_ppid2pids;
        $self;
    }

    sub _build_ppid2pids {
        my $self = shift;
        my %ppid2pids;
        for my $file (glob("/proc/*/stat")) {
            local $/;
            open my $fh, '<', $file
                or next;
            my $line = <$fh> // next;
            my @stat = split / /, $line;
            my $pid = $stat[0];
            my $ppid = $stat[3];
            push @{$ppid2pids{$ppid}}, $pid;
        }
        return \%ppid2pids;
    }
}

{
    package ProcStat;
    use File::Basename qw/basename/;
    use Time::HiRes ();

    sub new {
        my $class = shift;
        my %args = @_;
        bless {
            pid_dir   => $args{pid_dir},
            interval  => $args{interval},
            freq      => $args{freq} // 60,
            logger    => $args{logger},
            procs     => +{},                 # pid file name => proc obj
            counter   => 0,
            prev_stat => undef,
            stat      => ProcStat::Stat->new(),
        }, $class;
    }

    sub logger { shift->{logger} }
    sub stat   { shift->{stat} }

    sub run {
        my $self = shift;

        while (1) {
            my $result = $self->aggregate();
            if ($self->{counter}++ % $self->{freq} == 0) {
                $self->send($result);
            }
            Time::HiRes::sleep($self->{interval});
        }
    }

    sub aggregate {
        my $self = shift;

        my $current = $self->stat->read;

        if ($self->{prev}) {
            my $child_mgr = ProcStat::ChildProcessManager->new;

            my $elapsed = $current - $self->{prev};
            my @proc_files = glob("$self->{pid_dir}/*.pid");
            for my $proc_file (@proc_files) {
                my $proc = ($self->{procs}->{$proc_file} //= ProcStat::Process->new($proc_file, $self->logger));
                $proc->aggregate($elapsed, $child_mgr);
            }
        }

        $self->{prev} = $current;
    }

    sub send {
        my ($self) = @_;
        $self->logger->infof("Sending information");
        for my $proc (values %{$self->{procs}}) {
            my $commify = sub {
                local $_ = shift;
                1 while s/^([-+]?\d+)(\d\d\d)/$1,$2/;
                $_;
            };
            my $name = $proc->name;
            my $pid = $proc->read_pid;
            my $result = $proc->result;

            my $user        = sprintf '%.2f', $result->[0] * 100;
            my $sys         = sprintf '%.2f', $result->[1] * 100;
            my $rchar       = $commify->($result->[2]);
            my $wchar       = $commify->($result->[3]);
            my $read_bytes  = $commify->($result->[4]);
            my $write_bytes = $commify->($result->[5]);
            my $pss         = $commify->($result->[6]);

            printf("%-10d %-10s %6s %6s %15s %15s %15s %15s %15s [kb]\n", $pid, $name, $user, $sys, $rchar, $wchar, $read_bytes, $write_bytes, $pss);
            $proc->clear_history;
        }
    }
}

my $interval = 1.0;
my $freq = 60;
GetOptions(
    'pid-dir=s'  => \my $pid_dir,
    'interval=i' => \$interval,
    'f|freq=i'   => \$freq,
    'd|debug!'   => \my $debug,
    'q|quiet!'   => \my $quiet,
);
$pid_dir // pod2usage();

ProcStat->new(
    pid_dir  => $pid_dir,
    interval => $interval,
    freq     => $freq,
    logger   => ProcStat::Logger->new(
        $debug ? 'debug' : ($quiet ? 'warn' : 'info'),
    ),
)->run;

__END__

=head1 SYNOPSIS

procstatd - Read process status. And send it to mackerel.

=head1 DESCRIPTION


Created: 2017-03-12 04:26:27 +0000
Updated: 2017-03-12 04:26:27 +0000

/proc/$$/smaps の読み方のメモ

http://unix.stackexchange.com/questions/33381/getting-information-about-a-process-memory-usage-from-proc-pid-smaps

#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

static void show_smaps(pid_t pid) {
  char cmd[1024];
  snprintf(cmd, sizeof(cmd), "cat /proc/%d/smaps > /tmp/out/%d", pid, pid);
  system(cmd);
}

int main() {
  system("rm -rf /tmp/out; mkdir -p /tmp/out");

  int i;
  char* buf = malloc(10*1024*1024);
  for (i=0; i<10*1024*1024; i++) {
    buf[i]=1;
  }

  // show smaps once.
  show_smaps(getpid());

  pid_t pid = fork();
  if (pid == 0) {
    sleep(1);
    show_smaps(getpid());
    for (i=0; i<5*1024*1024; i++) {
      buf[i]=2;
    }
    return 0;
  } else if (pid > 0) {
    sleep(2);
    show_smaps(getpid());
    printf("Parent pid: %d Child pid: %d\n", getpid(), pid);
    assert(kill(pid, SIGTERM) == 0);
    int status;
    assert(wait(&status) == pid);
    system("perl -E 'for my $f (</tmp/out/*>) { open my $fh, q!<!, $f; my %h; while (<$fh>) { $h{$1}+=$2 if /(Pss|Rss): +([0-9]+)/ }  say qq!$f:
 Rss:$h{Rss} Pss:$h{Pss}! }'");
    return 0;
  } else {
    perror("fork failed");
    return 1;
  }
}
[tokuhirom@dev2 pidstatd]$ gcc -Wall stat.c
[tokuhirom@dev2 pidstatd]$ ./a.out
Parent pid: 30910 Child pid: 30916
/tmp/out/30910: Rss:10736 Pss:10347
/tmp/out/30916: Rss:10452 Pss:5186
Created: 2017-03-12 03:43:38 +0000
Updated: 2017-03-12 03:43:38 +0000

golang プロセスのモニタリングってみんなどうしてるんですかね、という話

[9:43 AM]  
tokuhirom plack のアプリだと  Plack::Middleware::ServerStatus::Lite とかで監視したりしますけど、go だとそういうのってどうやるのが一般的なんでしょうか?

[9:47 AM]  
macopy https://github.com/fukata/golang-stats-api-handler
github.com
GitHub - fukata/golang-stats-api-handler: Golang cpu, memory, gc, etc information api handler.
golang-stats-api-handler - Golang cpu, memory, gc, etc information api handler.

[9:47 AM]  
https://golang.org/pkg/net/http/pprof/

[9:48 AM]  
このあたりでしょうか、ちょっと取れる情報違う気もしますが

[9:48 AM]  
lestrrat kazeburo wareがあるんじゃないのか、と一瞬

[9:48 AM]  
(探してないけど)

[9:49 AM]  
なんだったら書いてもいいですけど!(tokuhiromが書いた方が早そう

[9:49 AM]  
tokuhirom kazeburo ware ありそうだ

[9:50 AM]  
mattn dave 御大のがいい

[9:51 AM]  
https://github.com/davecheney/gcvis
github.com
GitHub - davecheney/gcvis: Visualise Go program GC trace data in real time
gcvis - Visualise Go program GC trace data in real time

[9:51 AM]  
JSON だけなら golang-stats-api-handler でいい


[9:54 AM]  
この辺、いろいろ調べていろいろ試した僕なりの結果としては

[9:54 AM]  
mackerel すばらしいという結論に至った。

[9:54 AM]  
suzuken w

[10:03 AM]  
IFTTT APP
Google Online Security Blog: Another option for file sharing
Google Online Security Blog: Another option for file sharing http://ift.tt/2lDCbAz


[10:19 AM]  
tokuhirom mackerel でとれるんです?

[10:27 AM]  
suzuken GCのmetricsがとりたいのですかね・・?mackerelだと https://github.com/mackerelio/mackerel-agent-plugins/tree/master/mackerel-plugin-gostats  あたり・・?
github.com
mackerel-agent-plugins/mackerel-plugin-gostats at master · mackerelio/mackerel-agent-plugins · GitHub
mackerel-agent-plugins - Plugins for mackerel-agent

[10:28 AM]  
弊社だとPrometheusの https://github.com/prometheus/client_golang  をhandlerにはやしてます
github.com
GitHub - prometheus/client_golang: Prometheus instrumentation library for Go applications
client_golang - Prometheus instrumentation library for Go applications

[10:28 AM]  
`/metrics` みたいな

[10:28 AM]  
(もとの Plack::Middleware::ServerStatus::Lite の機能をよくしらないので的はずれな回答をしているかも・・

[10:29 AM]  
goroutine数とかをさくっとみるならgolang-stats-api-handlerがかんたんです

[10:30 AM]  
pprofみたいなのでprofiling含めてしたいのであれば http/pprof つかうとremoteサーバのprofilingができたりもしますが、メトリクス収集用途であれば golang-stats-api-handler でいい気がします

[10:31 AM]  
それとざくっとやるのであれば expvarとか

[10:31 AM]  
http://blog.ralch.com/tutorial/golang-metrics-with-expvar/

[10:31 AM]  
https://golang.org/pkg/expvar/

[10:31 AM]  
fujiwara Apacheのmod_statusみたいなのですね > ServerStatus::Lite

[10:32 AM]  
suzuken なるほどです

[10:36 AM]  
tokuhirom そうすね。いや、一般的に何をみんなとってるのかなーっていうレベルの疑問でした

[10:37 AM]  
kazuho 最大同時接続数が多い環境だと、mod_status的なものの必要性は下がるよね

[10:37 AM]  
node.js とかでもないんじゃないかな

[10:38 AM]  
tokuhirom nrhd

[10:38 AM]  
みんな気にして取ってるのは gc count ぐらいなのかな。

[10:38 AM]  
prom のなら req/sec とかまでとれそうですが。

[10:39 AM]  
kazuho もちろん nice-to-have だとは思います

[10:47 AM]  
songmu groutineがリークしてないか一番気をつけてるかなー。

[10:47 AM]  
上記のやつで可視化して

[11:04 AM]  
mattn ローカルで軽く確認するなら dave 御大ので充分すね

[11:04 AM]  
あとは gops にしてコマンドから確認するって方法もある

[11:04 AM]  
http://qiita.com/mattn/items/a92f69ff18eb5cbcdd59
Qiita
golang で書かれたプロセスのリソースを外部から監視/操作できる「gops」 - Qiita
この記事は [Go Advent Calendar 2016](http://qiita.com/advent-calendar/2016/go) の3日目の記事です。 # はじめに 今回は gops という Google 製のツ...


[11:04 AM]  
これとか

[11:04 AM]  
http://qiita.com/mattn/items/882a1924a1d706d127a2
Qiita
稼働中のバッチを監視したくなったら Mackerel Custom Metrics が便利 - Qiita
この記事は [Mackerel Advent Calendar 2016](http://qiita.com/advent-calendar/2016/mackerel) の 12/13 日の記事です。 --- # はじめに ![監...


[11:04 AM]  
これ

まとめようと思ったけどめんどくさくなってそのまま貼った。

Created: 2017-02-22 07:29:20 +0000
Updated: 2017-02-22 07:29:20 +0000

memcached の conn_yields について

memcached の conn_yields が上がっていた場合、conn_yield がでなくなるまで起動時オプションの -R を上げろって主張している人がいます。

実際問題、結局のところどういう主張なのかわかりづらくて、調べた。

コードを検索すると、1つのコネクションでコマンドを発行しまくっている場合にここに到達するようだ。 https://github.com/memcached/memcached/blob/d9dfbe0e2613b9c20cb3c4fdd3c55d1bf3a8c8bd/memcached.c#L4667

「1つのクライアントがリクエストを投げすぎている場合に、スレッドを専有してしまってそれにともなって memcached にアクセスしているクライアント全体がパフォーマンス劣化するという問題に対応する」ために導入されているようだ。

1つのコネクションでコマンドを投げまくっていると、conn_yields が発生する。 eventsperrequest(-R で渡してる値) が低すぎると、バッチリクエストの最中に yield 発生しまくって非効率なので、conn_yields が上がっている場合には適宜増やした方がいい。

ただし、上げすぎると、安全弁としての役割を果たさなく鳴るので、すごい勢いで memcached を呼びまくるクライアントが一台いただけで、全体的なパフォーマンスが悪化するかも。という気もする。


Created: 2017-02-03 05:27:51 +0000
Updated: 2017-02-03 05:27:51 +0000

cursor.executemany(operation, seq_of_params) の挙動

python の cursor.executemany(operation, seq_of_params) について、

data = [
  ('Jane', date(2005, 2, 12)),
  ('Joe', date(2006, 5, 23)),
  ('John', date(2010, 10, 3)),
]
stmt = "INSERT INTO employees (first_name, hire_date) VALUES (%s, %s)"
cursor.executemany(stmt, data)

みたいなケースでは、いい感じに

INSERT INTO employees (first_name, hire_date)
VALUES ('Jane', '2005-02-12'), ('Joe', '2006-05-23'), ('John', '2010-10-03')

のようなクエリが実行される。

この機構は以下のような regexp にマッチした場合にのみ動作する、とのこと。

restr = r"""
    \s
    values
    \s*
    (
        \(
            [^()']*
            (?:
                (?:
                        (?:\(
                            # ( - editor hightlighting helper
                            .*
                        \))
                    |
                        '
                            [^\\']*
                            (?:\\.[^\\']*)*
                        '
                )
                [^()']*
            )*
        \)
    )
"""

https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-executemany.html

Created: 2017-01-30 14:01:29 +0000
Updated: 2017-01-30 14:01:29 +0000

vagrant を centos linux で動かしたい

http://qiita.com/Itomaki/items/9a6a314a853cdcd00f80

↑のエントリどおりに動かしたら動いた。

Created: 2017-01-24 05:17:00 +0000
Updated: 2017-01-24 05:17:00 +0000

MySQLでタイムゾーンがどう設定されているか確認したい

mysql> select @@system_time_zone, @@session.time_zone;
+--------------------+---------------------+
| @@system_time_zone | @@session.time_zone |
+--------------------+---------------------+
| UTC                | SYSTEM              |
+--------------------+---------------------+
1 row in set (0.00 sec)

で。

Created: 2017-01-07 02:52:44 +0000
Updated: 2017-01-07 02:52:44 +0000

/dev/random のエントロピーが足りないときは Haveged を使う

/dev/random を利用した乱数生成器を利用する場合、VPS などの場合にはエントロピーが足りなくて stuck する場合があります。

たとえば以下のような感じ。

"localhost-startStop-1" #10 daemon prio=5 os_prio=0 tid=0x00007f63e8001800 nid=0x2697 runnable [0x00007f640251f000]
   java.lang.Thread.State: RUNNABLE
    at java.io.FileInputStream.readBytes(Native Method)
    at java.io.FileInputStream.read(FileInputStream.java:255)
    at sun.security.provider.SeedGenerator$URLSeedGenerator.getSeedBytes(SeedGenerator.java:539)
    at sun.security.provider.SeedGenerator.generateSeed(SeedGenerator.java:144)
    at sun.security.provider.SecureRandom$SeederHolder.<clinit>(SecureRandom.java:203)
    at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:221)
    - locked <0x00000000f8368610> (a sun.security.provider.SecureRandom)
    at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
    - locked <0x00000000f8369080> (a java.security.SecureRandom)
    at java.security.SecureRandom.next(SecureRandom.java:491)
    at java.util.Random.nextInt(Random.java:329)
    at org.apache.catalina.util.SessionIdGenerator.createSecureRandom(SessionIdGenerator.java:246)
    at org.apache.catalina.util.SessionIdGenerator.getRandomBytes(SessionIdGenerator.java:183)
    at org.apache.catalina.util.SessionIdGenerator.generateSessionId(SessionIdGenerator.java:153)
    at org.apache.catalina.session.ManagerBase.startInternal(ManagerBase.java:573)
    at org.apache.catalina.session.StandardManager.startInternal(StandardManager.java:485)
    - locked <0x00000000f82d88f0> (a org.apache.catalina.session.StandardManager)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    - locked <0x00000000f82d88f0> (a org.apache.catalina.session.StandardManager)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5501)
    - locked <0x00000000fadf5850> (a org.apache.catalina.core.StandardContext)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    - locked <0x00000000fadf5850> (a org.apache.catalina.core.StandardContext)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1575)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1565)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

http://stackoverflow.com/questions/26227344/oracle-java-8-x64-for-linux-and-randomsource

このような場合、haveged を使うのが簡単です。 https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged

apt-get install haveged
update-rc.d haveged defaults

とかでOK。簡単。

Created: 2017-01-06 08:56:41 +0000
Updated: 2017-01-06 08:56:41 +0000

Keynote の数式エディタは何が良いのか問題

色々派閥があるようだ。。 とりあえず latexit 入れてみるか。。

Created: 2016-12-12 05:22:55 +0000
Updated: 2016-12-12 05:22:55 +0000

DBD::mysql のメンテナンス体制が変わっていた

レポジトリが https://github.com/perl5-dbi/DBD-mysql ここに変わっていた。

https://github.com/CaptTofu/DBD-mysql/issues/51 この issue は buffer overflow なので、ちょっとまあアレなんだけど2年前から放置されてて困ったなーと思っていたが、なんか新体制で別のレポジトリで解消されていた。

具体的には以下のようなコードで、バッファオーバーフロー発生していた。sprintf でエラーメッセージを生成している部分で、固定バッファ使ってるというわりとよくあるISSUE。 perl -MDBI -e 'DBI->connect("dbi:mysql:dbname=test", "root")->prepare("?")->bind_param(1, "2014-01-01 00:00", 4)'

2016-10-03 Patrick Galbraith, Michiel Beijen, DBI/DBD community (4.037)
* Security release to patch possible buffer overflow in unsafe sprintf with
  variable length. Reported and fixed by Pali Rohár. This vulnerability
  is present in all releases at least back to versions 3.0 of the
  driver, which were released in 2005.
  The CVE identifier for this vulnerability is CVE-2016-1246.

というわけで、4.037 以後にしておくのがオススメ。

Created: 2016-12-12 02:14:44 +0000
Updated: 2016-12-12 02:14:44 +0000

YAPC Hokkaido で「Perl6 と Web 開発と」というタイトルで発表しました #yapcjapan

Created: 2016-12-10 03:03:13 +0000
Updated: 2016-12-10 03:03:13 +0000
Next page