その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Springの起動時に好きな画像をAAにして表示する

Spring Boot 1.4.1 1.4.0 (訂正しました.詳細は id:dayflower さんのコメントを参照下さい) の機能として,Springの起動時に任意の画像をAAとして表示できる機能が追加されたようです.
なんかここに書いてた.

www.infoq.com

やり方は非常に簡単で,

  • banner.jpgbanner.pngあるいはbanner.gifという名前の画像ファイルを/src/main/resourcesの直下に置く (あるいはbanner.image.locationで設定した場所に置く)

以上.早速やってみましょう.

f:id:moznion:20161024232806p:plain

普段のSpringの起動後のターミナルといえばこんなのでしたが……

f:id:moznion:20161024232835p:plain

この機能を使うとこの通り! ちなみに元画像は僕のアイコンのこれです.

f:id:moznion:20161024232911p:plain

若干ホラー感がある……と思いきや遠目からAAが中々の再現度であることがわかります.すごい!


当初はこの機能について「なぜウェブアプリケーションフレームワークの機能として入ったんだ……」と疑いの眼差しを向けていたのですが,実際に使ってみると想像以上に気分が上がります.自分のプロジェクトのロゴなんかを入れると良い感じですね!

以上です.


[追記]
画像が大きいとAAも大きくなります.「そんなAA大きくなくて良い!」という時は,application.propertiesのbanner.image.widthもしくはbanner.image.heightを設定することでサイズの調整ができます.便利便利.

ソフトウェアライセンスのURLが軒並みhttpsが使えた

READMEだとかLICENSEファイルだとかに書くソフトウェアライセンスの文言には,そのライセンスのURLが含まれていることが多いと思う.
最近ソフトウェアライセンス文言をめちゃめちゃ書く事があって,その時にふと「ライセンスのURLは軒並みhttpで表記されてるけど実はhttpsでいけるんじゃないのか?」と思ってざっくり調べたら色々なライセンスがhttpsのコンテンツを提供していることがわかった.全部書くと大変なので一部だけれどこんな感じ.


ライセンスの文言って大体コピペとかでパパっと済ませてしまうのでURLが元のままだったりするのだけど,実はhttpsで提供されている事があるので今後はそっちを使っていこうと思った.


[追記]
id:r7kamuraさんに教えてもらったんだけれど,opensource.orgSSLに対応していてHTTPでアクセスするとHTTPSにリダイレクトされる様子.時代はSSL

雑に特定のホストの特定のポートと疎通できてるかどうか確かめる

新しくサーバ立てた時やサーバ追加した時に,そのサーバが他のホストの特定のポートと疎通できるかどうかチェックする必要が出てくる時がある.ACLとかの兼ね合い.
そういうのは本番の環境だと監視のシステム等に組み込まれていたり,あるいはserverspecとかで確認されていることが多いと思うのだけれど,その場で雑に確認したくなることがあると思う.そういう時はtelnetで繋いで,quitして,というのを繰り返していく感じになりがちなのだけれど,対象となるホストの数が多くなってくるとそういうことを手でやるのも大変になってくる……というわけでこれです.

$ (sleep 0.1; echo quit) | telnet $HOST $PORT)

こういう風にしておくとtelnetで繋いだ後にquitを発行するということを自動でやってくれる.
とはいうものの,こうすると正しく繋げているかどうかを目視で確認する必要が出てくる (つまり以下の様な文字列が正しく出力されているかどうかを目視で確かめる必要がある).

Trying ::1...
Connected to localhost.
Escape character is '^]'.

更にこの方法だと Connection closed by foreign host となるために,exit codeが1となってしまう.その為,スクリプト中でexit codeを使って良い感じにするのが難しい.


というような話をしていたら,id:karupaneruraさんから「それncで出来るよ」と教えてもらって以下の様な感じになった.

if [ -z $((sleep 0.1; echo "\nquit") | nc $HOST $PORT) ]; then
    echo "Fail $HOST:$PORT"
fi

見た目としてはtelnetの代わりにncを使って,送り込む文字列を quit から \ncode にしたという様子.
前者はまあその通りという感じ.後者について少し説明すると,ncで正しく繋げている場合に先に改行文字を送り込むと ERROR というレスポンスがncから返ってきた後にquitする.一方で正しく繋げていない場合はncから何もレスポンスが返ってこないという挙動になるので,それを利用して文字列が空か空ではないかで正しく繋げているか否かを判断している (つまり文字列が空でなければ正しく繋げていると判断している).

なお補足すると,ncだとこういう風に動くんだけど,CentOS6のncatコマンドだとquit時に+OKという文字列が出てくるので最初に改行文字を送り込む必要は無かったりする.みたいな感じで微妙にnc (ncat) コマンドの挙動に差があったりするので,そこら辺はまあ臨機応変に対応しましょうという感じ.


これを適当にforの中に組み込んでおくと雑に疎通のチェックが出来て便利,という話でした.ncを使うという発想がなかったので勉強になった.


[追記]

コメントでid:bearminiさんが /dev/tcp を使う方法を教えてくれたんですが,これめちゃめちゃ便利ですね.知らなかった……

tinyorm 1.11.0 is out

tinyormのversion 1.11.0が出ました.

Maven Repository: me.geso » tinyorm » 1.11.0

新しい機能としては

に書いたように

  • Connection を2つ持てるようになった
  • Lazily な connection borrowing に対応した

という2つが挙げられます.
rc1の時点では中々にバギーだったんですが,改善を重ねていった結果,プロダクションでも安定して動作するようになったのでこの度rcを外して正式なバージョンとしてリリースしました *1

今回入った機能についてはConnectionというセクションのドキュメントでも説明しているので併せて参照ください.

*1:その結果rc7までバージョンが育ったという逸話があります

ltsv_exporter書いた

ltsv_exporterというPrometheusのexporterを書いた.

その名の通り,LTSV形式のテキストを読み込んでPrometheusが解釈できる形式に吐き出すexporter.

利用方法としては2つのパターンを想定している.

1つはリモートで配信されているLTSVを読むというスタイル.

$ ltsv_exporter --url https://example.com/sample.ltsv

このようにすると https://example.com/sample.ltsv で配信されているLTSVを読み込んでそれをPrometheus形式で出力するようになる.

もう1つはローカルに設置されているLTSVを読むというスタイル.

$ ltsv_exporter --file /path/to/sample.ltsv

このようにすると /path/to/sample.ltsv に設置されているLTSVを読み込んでPrometheusの形式で出力するようになる.

Exporterの出力としてはこのような感じ.

# HELP ltsv_value LTSV value
# TYPE ltsv_value gauge
ltsv_value{key="duration"} 4.097
ltsv_value{key="size"} 123

key というラベルにLTSVのkeyが入り,値の部分にはそのままLTSVのvalueが入る.なお,LTSVのvalueが数値ではなかった場合は,exporterはその値を出力に含めずに読み飛ばす.
あとはこのexporterのhttp越しの出力をPrometheusで収集すれば良い.


というわけでメトリクスを取りたいものがあったり監視したいものがあった時は,雑にサーバでLTSVとして配信したり,もっと雑にLTSVとしてローカルファイルに吐き出したりするだけでltsv_exporterで読み込んでPrometheus形式で出力できるようになった.
LTSVはシリアライズ(?)する時に特別なシリアライザが不要で,シンプルに文字列組み立てだけで作れるので楽で良いですね.


今後としては,最後の1行だけ読み込んでパーズするモードなんかがあると便利なのかな〜と思っている.

CasperJSを使ってGrafanaのグラフのスクリーンキャプチャを撮る

最近はサーバのメトリクス収集にPrometheus,その可視化の為のフロントエンドとしてGrafanaをハードに使っている.Grafanaは予想よりもはるかに格好良いグラフが生成されるのでやる気が出て良い.デモを触るとなんとなく温度感がわかると思う.
そうしているうちに,これは便利なのでサーバのメトリクス以外の情報も入れてみましょうという発想があり,サービスのKPIに関するような情報も入れ始めている.
とまあここまでは良いのだけれど,そうやって収集したデータも,格好良く可視化したグラフも,人に見られなければ一切の意味が無いのでそうした無意味を避けるためにデイリーでグラフを社内チャットに放流することにした.


という事になるとGrafanaで描画したグラフを画像としてエクスポートする必要が出てくる.
Grafanaにはグラフを画像としてシェアする機能があるのだけれど,これはブラウザをポチポチする必要があるみたいで,APIもぱっと見つからなかった (もしあったら教えて下さい) ので利用を見送った.あとどうやらこの画像データはSQLiteに入るみたいでホイホイ使って良いのかどうかあまり判断が付かなかった (間違ってたら教えて下さい).
というわけで今回はCasperJSを使ってGrafanaのグラフをキャプチャすることにした (結局CasperJS使うんだったらそれでGrafanaの画像シェア機能をポチポチすれば良かったんではないか,という意見もあることだろう.確かに!).
CasperJSはご存知の通りPhantomJS (かSlimerJS) を使ってスクレイピングやテストの実行をするのを助ける便利ライブラリ.CSSセレクタで指定すると,その範囲だけスクリーンキャプチャを撮ってくれるという機能があって大変便利なので使うことにした.

以下の様な感じでキャプチャが撮れる.

var casper = require('casper').create();

// login処理
casper.start('https://your-grafana.example.com/dashboard/db/your-dashboard?from=now-24h', function () {
    this.fill('div.login-inner-box > form', {
        username: 'Grafana user name',
        password: 'Grafana password',
    }, true);
});
casper.then(function () {
    this.click('div.gf-form-button-row > button');
});

// キャプチャ撮る
var waitMillis = 2000;
casper.viewport(1536, 1536).wait(waitMillis, function () {
    this.captureSelector('capture.png', 'div.grafana-row');
});

casper.run();

ログイン処理の部分は最初にfillしてからログインボタンをclickするように書いてある.シンプルなフォームだとfillの部分だけで認証が通るのだけれど,Grafanaはリッチな感じ (buttonのng-clickで処理が走るっぽい) なのでマニュアルでclickする処理を入れている.
認証が通ったらキャプチャを撮る処理を走らせる.ここで即座にキャプチャを撮ってしまうとグラフが未描画の場合があるので,気持ち待ってからキャプチャを撮る処理を開始している (例だと2秒).


で,これらをやる上で考えるべき点としては

  • グラフの時間スケール
  • viewportのサイズ
  • 対象となるグラフのCSSセレクタ

というのが挙げられる.


グラフの時間スケールと言うのは,どの時間スケール (直近5分とか直近24時間とか) のグラフを取得するかという点.Grafanaは時間スケールの情報はダッシュボード自身と紐付いていて,自分あるいは自分以外のユーザが時間スケールを変更すると全てのユーザに影響する.
そうした状態で時間スケールを特に指定せずにグラフを取得してキャプチャを撮ってしまうとめちゃくちゃになってしまうのでちゃんと指定してやる必要がある,という感じ.
例で言う所の https://your-grafana.example.com/dashboard/db/your-dashboard?from=now-24h?from=now-24h の部分がそれに当たる.


viewpointのサイズというのはCasperJSの話.要は仮想的なブラウザの大きさをどうするかみたいなもので,デフォルトの状態のままだとかなり小さめに設定されている.従ってほぼ確実に調整が必要になる部分だと思う.
Grafanaのグラフの大きさはブラウザの画面サイズに応じて可変なので,良い感じの数値を探っていくという泥臭い作業が要求されるのだけれど,まあ適当に妥協しましょう.


対象となるグラフのCSSセレクタというのは若干厄介.
今回は div.grafana-row というのを指定しているのだけれど,これはGrafanaのダッシュボードのrowそのものを表しているので,そのrowに属するグラフ等のパネルを全てキャプチャできる.
div.panel-container というのを指定すると個別のパネルをキャプチャできるので,用途に応じて使い分けると良い……という感じなのだがそう簡単には済まない.
なぜかというと,例えばrowが2つ有ると div.grafana-row というCSSセレクタも2つhtmlに表れてしまう為,先に出た方が優先されてしまう.つまりどちらか片方しかキャプチャができない.id要素などが付いているわけではない,本当に同一のCSSセレクタが出てきてしまうので面倒くさい……という感じ.もちろんパネルが複数個あればその個数分だけ div.panel-container が出てくるので問題は変わらない.
まあここら辺をなんとかする方法としては

  • 気合
  • キャプチャ用のダッシュボードを新たに作る

などといった解決方法があるので頑張れば問題がない.頑張りましょう.



こんな感じでGrafanaのグラフをキャプチャできるようになった.それを社内Gyazoに投げつけた上で社内チャットに毎日放流するという事が自動行えるようになって便利!
なお自動化はcronでやってます.

そして物語は何度目かのアプリ内通知再実装を迎える

というタイトルでKyoto.なんか #2で発表してきました.


そして物語は何度目かのアプリ内通知再実装を迎える / Reimplement in app notification // Speaker Deck

スライドの内容としては,アプリ内通知 (Twitter appで言うところの「通知」タブにあたる部分) のサーバサイドを実装する際にどういう問題があって,それをどういう風に実装したかという葛藤の記録となっています.

Webアプリケーションやスマートフォンアプリケーションを書いていると,そこそこの確率でアプリ内通知を書くことになると思うんですが,ところがどっこい「実際にどういう風に実装しているか」みたいな知見が共有されている感じがあまりありません.みんな実装しているはずなのに,ググってもあまり情報が出てこなくて寂しい.地味な機能だから?
という思いがあり,そこら辺アプリ内通知周辺の技術交流・意見交換などしたいなあというモチベーションでこの度発表してみた次第です.賽は投げられた.


ところで,この発表の最後で言おうと思っていたのに言っていない事があったのでこの場に書きますが,実際は「アプリ内通知」というか「通知タブ」のようなものはアプリケーションが個別に自作すべきものではないのでは? と思っています.
iOSAndroidには受け取ったpush通知を貯めて表示する通知センター的な領域が用意されているので,そうしたOSが提供してくれている機能に乗るようにアプリケーションを作成すればこんなに悩まなくても済みます (例えば今回の場合だと,push通知を送るだけ送るようにしておいて,クライアントが通知を貯めておくようにしておけばサーバサイドで通知情報を保持する必要がなくなる).


通知に限らず,他にもそうしたコンポーネントと言うのは様々あると思っていて,OSが提供してくれている機能で要件を満足できるのであればそれを活用するのが良いんじゃないかな,と思っています.
とはいえアプリケーション・サービスの特性によってはそうも言っていられないので,今回のように自作することになります.ケースバイケースと言えるでしょう.OSの通知センター使いたかった……




Kyoto.なんかは色々な技術的な話を聞けてとても有益でした.
特にid:r7kamuraさんのamakanのシリーズ判定の話がすこぶる面白かった.

r7kamura.hatenablog.com

主催のid:hakobe932さん,id:hitode909さんありがとうございました.また参加します.