tokuhirom's Blog

OWASP dependency-check を利用して脆弱性のある Java ライブラリに依存していないか確認する

依存しているライブラリに脆弱性がある場合、それを検出できると嬉しい。

OWASP dependency-check の gradle プラグインを入れると、簡単に検出が可能となる。

設定は以下のようであり、非常に容易である。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath('org.owasp:dependency-check-gradle:3.2.1')
    }
}
apply plugin: 'org.owasp.dependencycheck'

check.dependsOn dependencyCheckAnalyze

// https://jeremylong.github.io/DependencyCheck/dependency-check-gradle/configuration.html
dependencyCheck {
    // Threshold value to fail build
    // https://www.first.org/cvss/specification-document
    // 0.1-3.9=low 4.0-6.9=medium 7.0-8.9=High 9.0-10.0=Critical
    failBuildOnCVSS=0
}

failBuildOnCVSS の値を設定することで、CVSS Score が 7.0 以上、つまり脆弱性としての評価が High 以上のもんのの場合は FAIL する、というようなルールを設定することが可能だ。

例えば、spring boot 1.5.0 に依存している場合は以下のような出力になる。

$ ./gradlew check
:dependencyCheckAnalyze
Verifying dependencies for project demo
Checking for updates and analyzing vulnerabilities for dependencies
Generating report for project demo
Found 24 vulnerabilities in project demo


One or more dependencies were identified with known vulnerabilities:

spring-boot-starter-security-1.5.0.RELEASE.jar (org.springframework.boot:spring-boot-starter-security:1.5.0.RELEASE, cpe:/a:pivotal_software:spring_boot:1.5.0, cpe:/a:pivotal_software:spring_security:1.5.0) : CVE-2017-8046, CVE-2018-1196
spring-boot-starter-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-starter:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-aop-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-aop:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-security-config-4.2.1.RELEASE.jar (org.springframework.security:spring-security-config:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-security-web-4.2.1.RELEASE.jar (org.springframework.security:spring-security-web:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-boot-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-boot-autoconfigure-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-autoconfigure:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-boot-starter-logging-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-starter-logging:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-core-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-core:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-beans-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-beans:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-security-core-4.2.1.RELEASE.jar (org.springframework.security:spring-security-core:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-context-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-context:4.3.6.RELEASE) : CVE-2018-1199
spring-expression-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-expression:4.3.6.RELEASE) : CVE-2018-1199
spring-web-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-web:4.3.6.RELEASE) : CVE-2018-1199
logback-classic-1.1.9.jar (cpe:/a:logback:logback:1.1.9, ch.qos.logback:logback-classic:1.1.9) : CVE-2017-5929
logback-core-1.1.9.jar (cpe:/a:logback:logback:1.1.9, ch.qos.logback:logback-core:1.1.9) : CVE-2017-5929


See the dependency-check report for more details.


:dependencyCheckAnalyze FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':dependencyCheckAnalyze'.
>

  Dependency-Analyze Failure:
  One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '0.0': CVE-2017-5929, CVE-2017-8046, CVE-2017-4995, CVE-2018-1199, CVE-2018-1196
  See the dependency-check report for more details.



* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 6.561 secs

report は HTML で出力される(JSON, CSV 等の形式も指定可能)。 実際のレポートはこのような感じになる → https://jeremylong.github.io/DependencyCheck/general/SampleReport.html

ant/maven のタスクも用意されている。

commons-math3 の二次元行列用の型についてのメモ

BlockRealMatrix

キャッシュフレンドリーで速いらしい。

Array2DRowRealMatrix

double[][] で表現されるひじょうに素直な実装

DiagonalMatrix

対角行列用の型。対角行列を扱うならコレを使うとメモリを節約できたりする。

OpenMapRealMatrix

open addressed map ベースの実装。sparse な行列ならコレを使うとよい。

初期化コストに関するベンチマーク

雑ですが。。

    @Test
    fun bench() {
        val tries = 10000000

        val startA = System.currentTimeMillis()
        for (i in 0 until tries) {
            Array2DRowRealMatrix(11, 11)
        }
        val endA = System.currentTimeMillis()
        val startB = System.currentTimeMillis()
        for (i in 0 until tries) {
            BlockRealMatrix(11, 11)
        }
        val endB = System.currentTimeMillis()
        println("A=${endA-startA} B=${endB-startB}")
    }

で、

A=6992 B=2435

みたいな感じ。

commons-math3 の BetaDistribution を利用する場合は rng をキャッシュする

commons-math3 の BetaDistribution を利用する場合、alpha/beta が変わるたびに new BetaDistribution( alpha, beta) とかしてはいけない。 new BetaDistribution(rng, alpha, beta, 1.0) は ``new BetaDistribution(new Well19937c(), alpha, beta)` ということになるからだ。

new Well19937c() は乱数生成系だが、この初期化処理は重いので、キャッシュしないと非常に処理が重くなる。

nuxtjs で bootstrap 使いたい

nuxt.js で twitter bootstrap を有効にする方法

https://bootstrap-vue.js.org/docs/#nuxt-js を参考にやれば良い。

npm i bootstrap-vue --save

して、nuxt.config.js に以下を追加。

{
  modules: [
    'bootstrap-vue/nuxt',
  ]
}

lombok 1.16.20 以後で @lombok.Value された値を Jackson で deserialize しようとするとエラーになる

@lombok.Value された bean に対して ObjectMapper で deserialize した場合、以下のようなエラーがある。

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `MyGreatResponse` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (String)"{"FOO":"BAR"}"; line: 1, column: 2]

これは lombok 1.16.20 で以下の変更が入ったためである。

https://projectlombok.org/changelog

lombok 1.16.20 で、immutable 厨したいときは lombok.config に以下のように記述すべし。

lombok.anyConstructor.addConstructorProperties= true

Capturing grafana dashboard and post it to LINE group using LINE Notify

I want to post a screenshot of the Grafana dashboard to LINE Notify.

Grafana distribution includes PhantomJS( Alerting feature uses PhantomJS. https://github.com/grafana/grafana/blob/master/pkg/services/alerting/notifier.go#L96 ).

Then, you can take a snapshot using curl command.

You need to rewrite Grafana dashboard URL. Insert /render prefix for your dashboard URL. For example, when your dashboard URL is http://grafana.example.com/dashboard/db/my-service, PhantomJS URL is http://grafana.example.com/render/dashboard/db/my-service.

If you want to hide a header, add ?kiosk query parameter for the URL.

Grafana supports API keys and Basic Auth. With curl, you can call it like e.g. curl -u 'YOUR_EMAIL:YOUR_PASSWORD' GRAFANA_URL.

It's summarized as follows:

curl  -u 'YOUR_EMAIL:YOUR_PASSWORD' http://grafana.example.com/render/dashboard/db/my-service?kiosk -o img.png

Post to LINE Notify

You can upload dashboard image to LINE Notify using curl command.

curl -X POST https://notify-api.line.me/api/notify 
       -H 'Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN' 
       -F 'message=test' 
       -F '[email protected]'

See https://engineering.linecorp.com/ja/blog/detail/94 for more details.

SEE ALSO

http://moznion.hatenadiary.com/entry/2016/09/03/004038

tableau server の "Query View Image" API のデフォルトキャッシュ期間は長い

http://onlinehelp.tableau.com/v10.3/api/rest_api/en-us/help.htm#REST/rest_api_ref.htm%23Query_View_Image%3FTocPath%3DAPI%2520Reference%7C_____68

If you make multiple requests for an image, subsequent calls return a cached version of the image. This means that the returned image might not include the latest changes to the view. To decrease the amount of time that an image is cached, use tabadmin to reduce the value of the vizportal.rest_api.view_image.max_age setting. For more information, see tabadmin set options in the Tableau Server help.

で、これの default value は 720min=12hours なので、とにかくキャッシュが長い。

なんか cache 破棄させるほうほうもありそうなものだが、いまいちよくわからないので、とりあえずデフォルトのキャッシュ時間を短くして運用しましょうかというよう話になった。

Perl Web application に於ける Data::UUID の利用について

本文書について

本文書では Perl における Data::UUID での UUID 生成について議論する。 特に、Linux 環境に於ける Data::UUID->new->create_str の挙動について考察する。

Data::UUID について

Data::UUID は Alexander Golomshtok 氏が開発したモジュールで、メンテナンスされなくなった結果として、現在は rjbs 氏がパッチの適用などの消極的なメンテナンスを実施している。

このモジュールは以下の文言からドキュメントが開始されている。

This module provides a framework for generating v3 UUIDs

しかし、期待に反して、実際にはほとんどのユーザーは v1 UUID を生成している。 Data::UUID->new->create_str を利用するのが一般的なケースであるためだ。

UUIDv1 の構造について

timestamp 60bit, clock sequence 16bit, node 48bit

となっている。

  UUID                   = time-low "-" time-mid "-"
                           time-high-and-version "-"
                           clock-seq-and-reserved
                           clock-seq-low "-" node

↓つまり以下のような形式となる。

   TIMESTAM-PTIM-ESTA-CLOC-NODEID

/tmp/.UUID_NODEID について

Data::UUID->new で nodeid は生成されます。

/tmp/.UUID_NODEID が存在しない場合、nodeid は md5(gettimeofday(), gethostname()) で生成され、 /tmp/.UUID_NODEID に保存されます。 /tmp/.UUID_NODEID が存在する場合、slurp(/tmp/.UUID_NODEID) + $$ の結果が nodeid となります。

このロジックにより、二回目以後の nodeid 生成は process ごとに独立した nodeid になります。

/tmp/.UUID_STATE について

/tmp/.UUID_STATE に clockseq などが保存されます。これにより、プロセスが再起動した場合などにも動作を続けることができる、という設計に見えます。

このファイルの保存タイミングは以下。

しかし、現実的には、あまり意味ないかも。

Data::UUID を web application で利用した場合に発生する可能性がある問題について

以下では pre-fork モデルのウェブアプリケーションで Data::UUID を利用した場合に発生し得る問題について議論する。 (Starlet, Starman などの web server が pre-forking モデルです)

時刻の巻戻り

UUID v1 の特性上、NTP などにより時刻が巻き戻った場合、重複した uuid が発行される可能性があります。 時刻の巻き戻りが発生した場合に発生する重複が許容出来ない場合、Data::UUID の利用は避けるべきでしょう。

clock sequence を初期化するタイミングで初期値を random にするなどの軽減策は取られているものの、問題が発生する可能性はそこそこあります。

local 攻撃への脆弱性

の2つのファイルが生成されるが、tmp directory に生成されるために、悪意のあるユーザーが設置することが可能。

nodeid の生成ロジックの問題

/tmp/.UUID_NODEID がある場合にだけ pid が nodeid に考慮される。 このため、Data::UUID を一度も利用したことがない新しいサーバーの初回起動時などに nodeid が同一の Data::UUID インスタンスを持つ process が複数発生する可能性がある。

AWS などで頻繁にインスタンスの作り直しを実施している場合、この問題が発生しやすいと考えられる。

Data::UUID->new() を fork 前に呼んではいけない

Data::UUID->new の中で nodeid を初期化しているので、fork 前に呼ぶと同一 nodeid ですべての子プロセスで利用されることになり、相手は死ぬ。

srand の呼び出し

Data::UUID->new の中で、現在時刻をシードに srand() を呼び出し、それ以後は rand() をコールしている。。 現在時刻は srand のソースとして使うにはよろしくないので、これは、他の rand() を利用している XS module に悪影響を与えている可能性がある。 (Perl 本体は drand48() を利用しているので問題ない)

対案

完全に random な UUID 生成方法である UUID v4 を利用しとくか、sfujiwara さんが作ってたみたいなやつ使うのがいいんじゃないすかね。 ただし、UUID v4 の生成系を利用する場合、fork 時の seed 最初期化などについては考慮を忘れずに。

spring-boot で tomcat の設定するときのやり方

server:
  use-forwarded-headers: true
  tomcat:
    accesslog:
      directory: /PATH/TO/logs/tomcat/
      pattern: common
      enabled: true
      rotate: true
      # It's required when using remote-ip-header.
      request-attributes-enabled: true
    remote-ip-header: x-forwarded-for
    protocol-header: x-forwarded-proto

なんかこんな感じ? request-attributes-enabled を設定するの忘れると RemoteIpValve で設定した値が AccessLogValve で拾われないので死ぬ。

apscheduler で add_job するときは misfire_grace_time の指定が必須

python で job の定期実行を実装するには apscheduler が便利だが、misfire_grace_time の指定をしないと job が実行されないことがあるので注意が必要。 指定していないと以下のようなエラーに悩まされることになる。

Run time of job "send_menu (trigger: cron[day_of_week='mon-fri', hour='11'], next run at: 2016-05-31 11:00:00 CEST)" was missed by 0:00:01.51075

これは、予定された実行時間よりも scheduler の起動が遅れてしまったから実行しないようにしましたよ、というメッセージである。 デフォルトでは 1 秒以上実行が遅延した場合、ジョブは実行されない。 https://github.com/museumsvictoria/nodel/issues/13

しかし実際、1.5秒遅れても実行はしてほしいわけなので以下のように misfire_grace_time を指定し、delay の許容時間を指定する方が良い。

scheduler.add_job(func, trigger='cron', hour='10,11,12', misfire_grace_time=60*60)

spring.profiles.include は array で指定可能

https://github.com/spring-projects/spring-boot/issues/6995

spring.profiles.include は従来、string型でしか指定できなかったので以下のようなクソみたいな設定をするしかなかった。

---
spring.profiles: default
spring.profiles.active: local

---
spring.profiles.include: "\
  logging-devel,\
  mybatis-common,\
  freemarker-devel,\
  freemarker-common
"

---
spring.profiles: release
spring.profiles.include: "\
  mybatis-common,\
  freemarker-release,\
  freemarker-common
"

しかし、これは使いづらすぎるので以下のように書けるようにしてくれ、という要望を上げていた。


---
spring.profiles: default
spring.profiles.active: local

---
spring.profiles.include:
  - logging-devel
  - mybatis-common
  - freemarker-develo
  - freemarker-common

---
spring.profiles: release
spring.profiles.include:
  - mybatis-common
  - freemarker-release
  - freemarker-common

これが 2017 年の前半でリリースされてる spring-boot 1.5.0 では取り込まれているので、積極的に使うと良い。

[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

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 使うのが便利っぽいって話になった。。

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

/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

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]  
これ

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

/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。簡単。

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

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

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

netty で http server

RxNetty 触ってると、素の Netty だとどうなんだっけ? というところも把握しておかないと厳しい面もありますので、一応ひさびさに netty で素の http server を書いてみる。

keep-alive まわりの処理とか chunked の処理とか手で全部書かないといけないからやはり生で使うものではないな、という感じ。

package com.example;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;

/**
 * もっとも基本的な HTTP server の実装。
 */
@Slf4j
public class HttpServer {
    public static void main(String[] args) throws InterruptedException {
        int port = 3000;

        NioEventLoopGroup group = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(group)
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(port))
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(
                                new HttpServerCodec(),
                                new HttpObjectAggregator(512 * 1024),
                                new HttpServerHandler());
                    }
                });
        try {
            ChannelFuture f = bootstrap.bind().syncUninterruptibly();
            log.info("Listening: {}", f.channel().localAddress());
            f.channel().closeFuture().syncUninterruptibly();
        } finally {
            group.shutdownGracefully().syncUninterruptibly();
        }
    }

    @Slf4j
    public static class HttpServerHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            FullHttpRequest request = (FullHttpRequest) msg;
            log.info("[{}] {} {} {}",
                    ctx.channel().remoteAddress(),
                    request.protocolVersion(),
                    request.method(),
                    request.uri());
            byte[] content = "Hello".getBytes(StandardCharsets.UTF_8);
            DefaultHttpResponse defaultHttpResponse = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.OK,
                    Unpooled.buffer()
                            .writeBytes(content),
                    new DefaultHttpHeaders()
                            .add(HttpHeaderNames.CONTENT_TYPE, "text/plain")
                            .add(HttpHeaderNames.CONTENT_LENGTH, content.length),
                    EmptyHttpHeaders.INSTANCE);

            if (!HttpUtil.isKeepAlive(request)) {
                ctx.writeAndFlush(defaultHttpResponse)
                        .addListener(ChannelFutureListener.CLOSE);
            } else {
                defaultHttpResponse.headers()
                        .add(HttpHeaderNames.CONNECTION, KEEP_ALIVE);
                ctx.writeAndFlush(defaultHttpResponse);
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx,
                                    Throwable cause) {
            log.warn("Caught unhandled exception", cause);
            ctx.close();
        }
    }
}

一通り書いてから気づいたけど https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/helloworld に実装例が載っているわ。