2013-03-14
FluentdとGrowthForecastを使って自分の行動をロギング・可視化する
の記事のように、FluentdやGrowthForecastを使ったロギングって面白そうだなーと思って自分でもやってみた。
アプリケーション毎のアクティブな時間を取る
まず考えたのはこれ。キーイベント発火回数ほど細かくなくても、「アクティブにしている時間の割合」が取れたらそれはそれで良いかな、と。
1秒ごとにアクティブなアプリを調べてロギングしていく。Mac OS Xにおいてアクティブなアプリケーションを調べるならAppleScriptが簡単。
name of (info for (path to frontmost application))
もしくは
tell application "System Events" name the first process whose frontmost is true end tell
といった形でプロセス名として取ることもできるらしい。以下のように変形することもできる。
tell application "System Events" to name of first process whose frontmost is true
なんにせよAppleScript一行で取れるので、ワンライナーのコマンドで書ける。fluentdのconfigで
<source>
type exec
command osascript -e 'name of (info for (path to frontmost application))'
keys application
tag application
run_interval 1s
</source>
のように書けば1秒ごとにAppleScript経由でアクティブなアプリケーション名がemitされる。これをdatacounterで1分ごとに集計。
<match application> type datacounter count_key application count_interval 1m aggregate all tag counted.apps pattern1 iTerm iTerm.app pattern2 Emacs Emacs.app pattern3 Chrome Google Chrome.app pattern4 Twitter Twitter.app </match>
これで主要なアプリがどれくらいアクティブになっているかがだいたい取れる。ので、そのままgrowthforecastに投げてしまう。
<match counted.apps> type growthforecast gfapi_url http://gf.*****.my.server/api/ service sugyan section apps name_keys iTerm_percentage,Emacs_percentage,Chrome_percentage,Twitter_percentage,unmatched_percentage </match>
こんなかんじでそれぞれのアプリの単位時間あたりのアクティブ率の変化がグラフで取れて、複合グラフにすると
というかんじで「あぁ、このへんはずっとEmacsで何か書いてて、このへんはずっとTwitter見てるな」とかが分かる。
Emacsの文字入力数を取る
Emacsで色々作業する場合に、どんな言語をどれくらい書いているか…じゃなくても、major-modeごとに集計できたら良いかな、と。
post-self-insert-hookで文字入力した際にhookして非同期プロセスからfluentdに送信する。
(defun my-insert-hook () (start-process "post-fluent" nil "curl" "-X" "POST" "-d" (concat "json={\"mode\":\"" (symbol-name major-mode) "\"}") "localhost:9880/emacs")) (add-hook 'post-self-insert-hook 'my-insert-hook)
上記のようなelispをEmacsの設定に書いておけば、文字が入力されるごとにそのバッファのmajor-mode名がcurlを使ってHTTP経由でemitされる。
start-process関数は非同期にコマンド実行されるので連打していても動作が重くなったりしない。と思う。
<source> type http </source> <match emacs> type datacounter count_key mode count_interval 1m aggregate all tag counted.emacs pattern1 perl cperl-mode pattern2 ruby ruby-mode pattern3 javascript js2-mode pattern4 minibuffer minibuffer-inactive-mode </match> <match counted.emacs> type growthforecast gfapi_url http://gf.*****.my.server/api/ service sugyan section emacs name_keys perl_count,ruby_count,javascript_count,minibuffer_count,unmatched_count </match>
といった感じで、1分間あたりに文字入力が起こった回数をmajor-mode毎に集計してグラフ化できる。
zshの実行コマンドを取る
zshでもシェルコマンド実行前後に処理を挟むことができるので、実行したコマンドで集計したりできる。
function preexec_fluent() { curl -X POST -d 'json={"command":"'${1%% *}'"}' localhost:9880/zsh } preexec_functions=(preexec_fluent)
のようなものを~/.zshrcに書いておけば、コマンド実行するたびにそのコマンド名がemitされる。(ダブルクォートのエスケープとかあんまり考えていないけど)
<source> type http </source> <match zsh> type datacounter count_key command count_interval 1m aggregate all tag counted.zsh pattern1 ls ls pattern2 cd cd </match> <match counted.zsh> type growthforecast gfapi_url http://gf.*****.my.server/api/ service sugyan section zsh name_keys ls_count,cd_count,unmatched_count </match>
コマンドごとに集計する意味はあまり無いかもしれないけど、特定のコマンドをよく使う、という場合は割合を知ることができて良いかも?
合計値を取れば1分あたりどれくらいzshでコマンド叩いているか、の推移は分かると思う。
その他
ほかに何か面白いデータ取れるかなぁ
2013-03-01
Slateを入れてみた
Macで起動中のアプリを切り替えるのに、⌘+Tabだけだとたくさん起動しているときに選択するために連打するのが面倒だし頻繁に使うものは一発で切り替わるようにしたい、というのがあって、
今までずっとそれをQuicksilverのHotKey機能を利用して
みたいに左手の操作だけで切り替えられるようにしていた。
ところで最近「Slateというのが面白い」という話を某所で聞いたので試しに入れてみた。
DivvyやSizeUpといったウィンドウ操作系ツールとして使えるもののようだけど(両方つかったことないから知らない)、
~/.slateに設定ファイルを書くことで様々な操作を規定してキーバインド設定できたりして良い。さらに~/.slate.jsにJavaScriptコードで設定を書くこともできる、というのが面白い。slateもしくはSとUnderscore.jsがglobal objectとして使うことができて、それらを使って色々できる。
JavaScript Configs ? jigish/slate Wiki ? GitHub
S.bind('q:ctrl,cmd', function (win) { win.doOperation(S.operation('focus', { app: 'iTerm' })); });
と書くことで⌘+Ctrl+QでiTermにfocusさせる、といったことが出来る。
これくらいならJSで書く必要もないのだけど、これは起動していないときに何も起こらないので、もし指定したappが立ち上がっていない場合はshellコマンド経由で/usr/bin/openを使って起動させた上でfocusさせるようにする。条件分岐はプログラミング言語で書きやすいので嬉しい。
S.bind('q:ctrl,cmd', function (win) { var apps = []; S.eachApp(function (app) { apps.push(app.name()); }); if (! _.find(apps, function (name) { return name === 'iTerm'; })) { win.doOperation( S.operation('shell', { command: '/usr/bin/open -a iTerm', waithFoeExit: true }) ); } win.doOperation(S.operation('focus', { app: 'iTerm' })); });
S.eachApp()で起動中のappを列挙できるので、その中にfocusしたいappの名前があるか否かを調べて見つからなかったらopen。
あとは共通で同じようなことができるように関数を定義してやって、
var launch_and_focus = function (target) { return function (win) { var apps = []; S.eachApp(function (app) { apps.push(app.name()); }); if (! _.find(apps, function (name) { return name === target; })) { win.doOperation( S.operation('shell', { command: "/usr/bin/open -a " + target, waithFoeExit: true }) ); } win.doOperation(S.operation('focus', { app: target })); }; }; S.bind('q:ctrl,cmd', launch_and_focus('iTerm')); S.bind('w:ctrl,cmd', launch_and_focus('Emacs')); S.bind('e:ctrl,cmd', launch_and_focus('Google Chrome')); S.bind('r:ctrl,cmd', launch_and_focus('Safari')); S.bind('t:ctrl,cmd', launch_and_focus('Twitter'));
という感じでたくさんHotKeyでの切り替えを定義していける。
…というようなことが下記記事に書いてありまして大変参考になりました。
Slateでアプリ切り替えをする設定 - ミントフレーバー緑茶
でもこれ、"Google Chrome"のときにopenコマンドが動かない。無条件でスペースで区切ってコマンドに引数として渡されてしまうらしく、クォートしてもダメ。のようだったので一応pull req送ってみた。。
なんかたまに変なエラーが出ておかしくなるけど、ほぼ問題なく快適に使えている。のでQuicksilverは卒業してlauncherとしてはAlfredを使うようにしてみた。
Slate自体はウィンドウの移動とかサイズ変更とか柔軟に色々できるはずなのでもう少し色々設定して試してみたい。オヌヌメ設定とかあれば教えてください…
2013-01-12
Heroku上からService Account認証でGoogle APIにアクセスする
Googleの様々なAPIを叩く際、認証にOAuth 2.0を用いる。
使用する場面やパターンによって以下のような6つのシナリオが想定されている。
- Login
- Web Server Applications
- Client-side Applications
- Installed Applications
- Devices
- Service Accounts
多くの場合は「ユーザごとに認証させて個別のtokenを発行しリクエストに利用する」という流れなのだけど、中にはたとえばURL短縮APIとか、必ずしもユーザ個別にtokenを発行させる必要がないこともある。
そういう場合には「サービス固有のtoken」だけあれば良い。ということで使えるのが「Service Accounts」という方式。
サービスアカウントに発行されたprivate keyを使って署名したJWTリクエストを生成してAPIを叩く、という仕組みのようだ。
rubyでgoogle-api-clientを使う例
GoogleのAPIを叩くためのライブラリとして、Ruby Gemsではgoogle-api-clientというのがある。
google-api-client | RubyGems.org | your community gem host
JWTを使ったリクエストや認証なども対応してくれている。
これを使って実際にやってみる。
準備
にて、Application typeを"Service account"と選択してClient IDを生成する。
すると、"Email address"が発行され、private keyがダウンロードできるようになるので、これを使う。
書く
Gemfileに
source :rubygems gem 'google-api-client', '0.6.0'
と書いてbundle installして、以下のようなスクリプトを書く。
require 'google/api_client' client = Google::APIClient.new(:application_name => '') key = Google::APIClient::PKCS12.load_key('/Users/sugyan/Downloads/...-privatekey.p12', 'notasecret') client.authorization = Signet::OAuth2::Client.new( :token_credential_uri => 'https://accounts.google.com/o/oauth2/token', :audience => 'https://accounts.google.com/o/oauth2/token', :scope => 'https://www.googleapis.com/auth/urlshortener', :issuer => <発行されたemail address> :signing_key => key, ) client.authorization.fetch_access_token! shortener = client.discovered_api('urlshortener') result = client.execute( :api_method => shortener.url.insert, :body_object => { :longUrl => 'http://d.hatena.ne.jp/sugyan/' }, ) puts result.data.id
ダウンロードしたprivate keyのファイルを読みこんでkeyを生成し、email addressやscopeを指定し、認証。
これで、GoogleのURL短縮APIを叩いて短縮URLを得ることができる。
$ bundle exec ruby shorten.rb http://goo.gl/zLVjD
Herokuに上げるために
で、こんなものをHeroku上で動かそうとすると、API Consoleからダウンロードしたprivate keyのファイルをgit repositoryに含める必要が出てしまう。それはイヤだ。
幸いにも、Google::APIClient::PKCS12でloadしたkeyは文字列として得られる。
$ bundle exec ruby -r 'google/api_client' -e 'puts Google::APIClient::PKCS12.load_key("/Users/sugyan/Downloads/...-privatekey.p12", "notasecret")'
-----BEGIN RSA PRIVATE KEY-----
MIICXQ...
...
-----END RSA PRIVATE KEY-----
なので、これを丸ごとheroku configで渡してしまえばいい。ついでに発行されたemail addressも。
$ heroku config:set GOOGLE_API_KEY="$(bundle exec ruby -r 'google/api_client' -e 'puts Google::APIClient::PKCS12.load_key("/Users/sugyan/Downloads/...-privatekey.p12", "notasecret")')"
$ heroku config:set GOOGLE_API_EMAILADDRESS=...@developer.gserviceaccount.com
で、これを使用してKeyを生成するようにコードを変更する。
require 'google/api_client' client = Google::APIClient.new(:application_name => '') key = OpenSSL::PKey::RSA.new(ENV['GOOGLE_API_KEY']) client.authorization = Signet::OAuth2::Client.new( :token_credential_uri => 'https://accounts.google.com/o/oauth2/token', :audience => 'https://accounts.google.com/o/oauth2/token', :scope => 'https://www.googleapis.com/auth/urlshortener', :issuer => ENV['GOOGLE_API_EMAILADDRESS'], :signing_key => key, ) client.authorization.fetch_access_token! shortener = client.discovered_api('urlshortener') result = client.execute( :api_method => shortener.url.insert, :body_object => { :longUrl => 'http://d.hatena.ne.jp/sugyan/' }, ) puts result.data.id
これで、このへんをcommitしてherokuにpushすると、設定したconfigの文字列を使用してheroku上からService Accounts認証を使ってAPIを叩けるようになる。
$ git push heroku master
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 1.10 KiB, done.
Total 6 (delta 0), reused 0 (delta 0)
-----> Ruby app detected
-----> Installing dependencies using Bundler version 1.3.0.pre.5
Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin --deployment
Fetching gem metadata from http://rubygems.org/.........
Fetching gem metadata from http://rubygems.org/..
Installing addressable (2.3.2)
Installing extlib (0.9.16)
Installing multi_json (1.5.0)
Installing autoparse (0.3.2)
Installing multipart-post (1.1.5)
Installing faraday (0.8.4)
Installing jwt (0.1.5)
Installing launchy (2.1.2)
Installing signet (0.4.4)
Installing uuidtools (2.1.3)
Installing google-api-client (0.6.0)
Using bundler (1.3.0.pre.5)
Your bundle is complete! It was installed into ./vendor/bundle
Cleaning up the bundler cache.
...
* [new branch] master -> master
$ heroku run bundle exec ruby shorten.rb
http://goo.gl/WnqP5
まとめ
…と、そういうようなことが、以下の記事に書いてあった。
2012-10-11
Express 3.xのjadeでHTMLをpretty出力する
結論
先に書くと、ここに答えがある。
node.js - ExpressJS: how to output pretty html - Stack Overflow
問題
現時点での最新express@3.0.0rc5+jade@0.27.6でjadeをレンダリングすると、改行やインデントされずにHTMLが吐き出される。
$ express hoge
create : hoge
create : hoge/package.json
create : hoge/app.js
create : hoge/public
create : hoge/public/javascripts
create : hoge/public/images
create : hoge/public/stylesheets
create : hoge/public/stylesheets/style.css
create : hoge/routes
create : hoge/routes/index.js
create : hoge/routes/user.js
create : hoge/views
create : hoge/views/layout.jade
create : hoge/views/index.jade
install dependencies:
$ cd hoge && npm install
run the app:
$ node app
$ cd hoge && npm install
$ node app &
Express server listening on port 3000
$ curl localhost:3000
GET / 200 24ms - 170
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>
これはこれで問題ないのだけど、開発中とかちゃんと想定通りのHTMLが出力されているのか確かめづらいし、改行やインデントを含んだ出力になってくれると嬉しい。
解決策
jadeはcompile時にprettyというオプションを渡してやることで出力を整形してくれるようになるらしい。
jade/Readme.md at 0.27.6 ? visionmedia/jade ? GitHub
expressのview engineとしてjadeを使っている場合は、例えばrenderメソッドの第2引数に渡すことでそういったオプションを指定することができるようだ。
--- a/routes/index.js +++ b/routes/index.js @@ -4,5 +4,5 @@ */ exports.index = function(req, res){ - res.render('index', { title: 'Express' }); + res.render('index', { title: 'Express', pretty: true }); };
これで下記のように見やすくインデントされた形式でHTMLがレンダリングされる。
<!DOCTYPE html> <html> <head> <title>Express</title> <link rel="stylesheet" href="/stylesheets/style.css"> </head> <body> <h1>Express</h1> <p>Welcome to Express</p> </body> </html>
とはいえ毎回のrenderメソッドにこれを指定するのは面倒。アプリケーション共通の変数を保持するために、Express 3.xではapp.localsというものが使えるらしい。
これを使ってセットしておくことで、いちいちrenderメソッドの第2引数にオプションを渡す必要がなくなる。
--- a/app.js +++ b/app.js @@ -15,6 +15,7 @@ app.configure(function(){ app.set('port', process.env.PORT || 3000); app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); + app.locals.pretty = true; app.use(express.favicon()); app.use(express.logger('dev')); app.use(express.bodyParser());
Express 2.xではview optionsという設定項目があったようだけど、3.xではapp.localsを使いましょう、ということのようだ。
Migrating from 2.x to 3.x ? visionmedia/express Wiki ? GitHub
2012-09-27
以前作ったSinatraアプリをPadrinoで書き直した
ももクロの人気上昇ぶりをグラフで可視化する - すぎゃーんメモ
という記事で作ったアプリをちょっと作り直した。
というかんじでシンプルなSinatraアプリでpgから直接SQLを叩いて云々してhamlでレンダリング、というかたちだったものを、
を使った構成に。
あとはTwitter bootstrapやHighstockのアップデートなど。
- app: http://momoclo-visualizer.herokuapp.com/blog_comments/
- source code: https://github.com/sugyan/momoclo-visualizer
Padrinoメモ
gemでPadrinoをインストールしてpadrino gen project hogeみたいなかんじでスケルトン作成。ORMやtemplate engineは色々選べるのでオプションで指定。Herokuで動かすの想定しているなら-a postgresとか。
padrino startでアプリを起動できる。
モデルやコントローラを追加するときは
$ padrino gen model fuga $ padrino gen controller piyo
とかで作ると自動でテストやヘルパーやmigration用スクリプトなども追加してくれるっぽい。便利。
Padrino app on Heroku
Herokuで動かすために諸々設定。
Procfileには
web: bundle exec padrino start -a thin -p $PORT
とか記述しておく(Gemfileにthin追加しておく)。
データベースはORMがSequelの場合、config/database.rbで
Sequel::Model.db = case Padrino.env when :production then Sequel.connect(ENV['DATABASE_URL'], :loggers => [logger]) end
とか書いておけば繋がるはず。migrationは
$ padrino rake sq:migrate:*
とかでゴニョゴニョできるっぽい。deploy後に本番の方を更新する場合は
$ heroku run padrino rake sq:migrate:auto
とかでいいのかな。
Before After
Heroku Scheduler
コメント数の情報を更新するのに、以前は
というスクリプトを用意して実行させていたけど、今はrake taskとして
というのを定義して、$ padrino rake scrapeで実行するようにした。
DBの接続とか意識せずに書けるしmodelに突っ込むだけなので簡潔になった。
JSON API
Highchartsで使うコメント数の情報は、SQLで引いた結果をJSON APIで返してAJAXで使う、というかんじなのだけど、
と、アレげだったものが
Visualizer.controllers :api do get :blog_comments, :provides => :json, :cache => true do expires_in 60 Entry.for_highstock.to_json end end
と、非常に簡潔に書けるようになった。モデルの方で
class Entry < Sequel::Model unrestrict_primary_key dataset_module do def for_highstock # ... end end end
と、どんな情報を返すかだけ定義してるだけ。コントローラの方では「JSONで返す」「結果は60秒キャッシュする」といったことを簡単に指定できるので便利。
use Memcache
キャッシュにはDalliを使って、app/app.rbで
register Padrino::Cache enable :caching set :cache, Padrino::Cache::Store::Memcache.new( ::Dalli::Client.new( ENV['MEMCACHE_SERVERS'] || '127.0.0.1:11211', :exception_retry_limit => 1, :username => ENV['MEMCACHE_USERNAME'], :password => ENV['MEMCACHE_PASSWORD'], ))
と書いておけばだいたいどこでも使えるはず。
from Haml to Slim
- before(Haml): https://github.com/sugyan/momoclo-visualizer/blob/ff93c6459933433a441c4349a78c80701acb6877/views/layout.haml
- after(Slim): https://github.com/sugyan/momoclo-visualizer/blob/143698ccba5d2128da686bf42940cb1cbe91ae18/app/views/layouts/application.slim
まぁ書き方のクセはあるものの、慣れればどちらも大して変わらないかな?ライトに使う分には。
パフォーマンス的な部分は比較していないし分からない。
まとめ
まぁ、単純にPadrinoに興味があって使ってみたかったので使ってみた、というわけですが。
今回はほぼ1ページだけの簡単なアプリだったのでそれほど恩恵を受けなかったけど、ページ数が多くなったり複雑なことが増えてくるとSinatraだとキツくなってくる部分があると思っていて、Padrinoならそのへんうまくキレイに書けるようになるのでは?と思いました。
あとテストとかも書きやすそうな気がする。




