TitaniumMobile勉強記

Titanium Mobileもだけど、関連してJavaScript、サーバ側の開発(Ruby中心)、BDD的な話題とかについても振れていきたい本業は転職コンサルタントな非プログラマー

Fluentdでログ集約して、TitaniumMobile使ってiPhone上でグラフ表示

この前Fluentdの勉強会参加してお話を聞いたその翌日に


ということをつぶやいていました。折角妄想したので、ひとまず作ってみました。

f:id:h5y1m141:20120521163540p:plain

サーバ側でJSON生成

Fluentdの設定はこんな感じにしました。

<source>
type tail	
path /var/log/apache2/access.log
tag apache.access
format apache
</source>

<source>
type tail	
path /var/log/auth.log
tag syslog.access
format syslog
</source>

<match **.access>
# plugin type
type mongo

# mongodb db + collection
database apache
collection access

# mongodb host + port
host localhost
port 27017

# interval
flush_interval 10s

</match>

上記設定で、MongoDBにログが集約されたので、JSONを生成するSinatraアプリをこんな具合にしました。

require 'sinatra'
require 'mongo'
require 'json'

def connect_mongo(db,collection)
  db = Mongo::Connection.new("localhost", 27017).db(db)
  entries = db.collection(collection)
  return entries
end

def convert_to_json(items)
  items.each_with_index do |item,i|
    content_type :json
    body.push(item.to_json) 
  end
  result = '[' + body.join(',') + ']'
  return result
end
get '/mongo' do
  access = connect_mongo('apache','access')
  items = access.find()
  return convert_to_json(items)
end

Titanium Mobile+グラフモジュール使ってiPhone側でグラフ表示

以下2つをポイントに作成しました

  • @goando さんが作成されたグラフモジュール(詳しくはこちら)があるのでそれを活用
  • 取得したJSONに対してiPhone側であれこれ加工した方が使い勝手良さそう&勉強のためにunderscore.jsを使って条件抽出

出来たソースはこんな感じ

var win = Ti.UI.createWindow({backgroundColor:"#CCC"});
var _ = require('lib/underscore');
var Charts = require('com.gnddesign.prettycharts');

httpClient('http://website/mongo',function(result){
  var statusCode200 = _.chain(result)
      .filter(function(item){return item.code ==='200';}).value();

  var statusCode404 = _.chain(result)
      .filter(function(item){return item.code ==='404';}).value();

  var statusCode500 = _.chain(result)
      .filter(function(item){return item.code ==='500';}).value();

  var chartData = Charts.createChart({
    leftChartType:0,
    leftChartColor:2,
    leftDataTitle:"status code",
    leftDataUnit:'件',
    leftDataArray:[statusCode200.length,statusCode404.length,statusCode500.length],
    width:320,
    height:480,
    top:0,
    left:0
  });

  win.add(chartData);
  win.open();
});

function httpClient(url,callback){
  if(Titanium.Network.online===true){
    var xhr = Ti.Network.createHTTPClient();
    xhr.open('GET',url);
    xhr.onload = function(){
      var result = JSON.parse(this.responseText);
      callback(result);
    };
    xhr.error = function(){
      var dialog = Ti.UI.createAlertDialog({
        title: "ネットワーク接続エラー",
        message: "ネットワーク接続が確立されていません。再度お試しください"
      });
      dialog.show();
      myApps.ui.actInd.hide();
    };
    xhr.send();
  }else{
    var dialog = Ti.UI.createAlertDialog({
      title: "ネットワーク接続できていません"
    });
    dialog.show();
    myApps.ui.actInd.hide();
  }
}

作ってみた感想とか課題

  • あまり細かい条件抽出してないのでここまでは意外と簡単に出来た
  • 時間帯別でログの表示をしたいので、MongoDB側でdb.apache.find({time:{$gt:xxx}})みたいな処理を考えないとダメ
  • グラフをタッチした時に、関連の詳細情報を表示するView/TableViewへ画面遷移というようなことがprettychartsだと出来なそう?(間違っていたらゴメンナサイ)
  • MarketPlaceにCharts Moduleがあって、15日間試用出来るけど、$29.99 /Seat /Month となんとなく高く感じる

当面やらないといけないことが多いけど、気が向いたら続きのエントリ書くかも

Fluentd Casual Talksに参加してきました。

f:id:h5y1m141:20120519064434j:plain

前置き

定員130名に対して250名近くの予約があったFluentd Casual Talks気づいたのが遅く、キャンセル待ち状態で申し込んだら、当日までに参加できない人が思っていた以上にいて繰り上がったから無事参加できました。

会場は最近ヒカリエに移転されたDeNAさんにて行われました。受付等での対応をされていたDeNAの社員の方+運営に関わっていらしたみなさんにまずはありがとうございました&おつかれさまでしたとお伝えしたいと思います。

会場はいかにもセミナールームで、カチっとした感じですが、空気としては適度にゆる~い感じで、なんとなくqpstudyっぽいのを感じたのは自分だけかな??

今回参加した背景&目的など

  • 現在Titanium Mobileで作ってるiPhoneアプリを今後リリースした場合に、バックエンド側のサーバのログ(Apacheやsyslog)管理をしっかりとやりたいというのがあって今回参加
  • メインセッションの中で「小さく始めるFluentd」というのがあったので、肩肘張らずに使えるものなのかなというイメージを持ったので実際の所はどうなのか確認したかった
  • Fluentd は自分は使ったことがないけど、参加前に、Software Designを読んで予習しておいたので、概念はひと通り理解したつもりで参加

※事前に予習しておいたから、話にスムーズについていけた

「fluentdはじめました」と「fluentdでwebサイト運用を楽にする」より

twitterやブログを拝見してるお2人を実際に見るのが初めで、ちょっとあこがれの人にあった気分でしたw

印象に残った話を以下箇条書きで

  • ビジネス的に意味のある数値をリアルタイムに可視化したい
  • 性能劣化の早期検知(nginxのログを1分単位でリアルタイム解析できるので、重いクエリー、バッチ処理などの影響がわかる。)
  • ユーザからの問い合わせ対応向け(ソーシャルゲームだと例えば「迷惑行為を受けてる」という報告あっても実際にそういう記録があるのかどうかはログをみないとわからない。そういうサポート部門にいる人向けに便利なツールを提供した)
  • 実際運用してみて動作は安定してるが枯れているわけではないので、バージョンアップした時に内部的に実装がかわってることがある。

小さく始めるFluentd

Treasure Dataの方からのお話。「小さくはじめる」というタイトルがあったので、何気にこれを一番期待して参加していました。

いくつかお話があった中で、Fluentdの設計思想というか目指している所についてのお話がありました。具体的には毎日やってる面倒くさい作業を解決するために作った。使い捨てのスクリプトを書いてたものを整理したかったというのが根底にあるそうです。

Fluentdのプラグインを使っての拡張性とか柔軟性みたいな所がこういう設計思想から表されているんでしょうね。

この後、@ume3_のLTを聞いたり大分理解できたので、残りのLTを聞きつつ



ということもしてました。

さてブログ書き終わったので、これからさくらVPS上のDebianのFluentdの設定やってみようと思います

TitaniumMobileでmustache.js活用できないか試してみた

フロントエンドでHTMLをレンダリングする技術全般を黙々とハックするclient-side templating勉強会というイベントに昨夜参加してきました。

完全に場違いであるのは事前にわかっていたけど、行ってみるとすごい人ばかり参加してたのと、知らないことが多く勉強不足な点を思い知った一日でした

mustache.jsって?

さて、ここから本題のmustache.jsの話。GtiHubのREADMEによると

mustache.js - Logic-less {{mustache}} templates with JavaScript

ということで、具体的には

var view = {
  title: "Joe",
  calc: function () {
    return 2 + 4;
  }
};

var output = Mustache.render("{{title}} spends {{calc}}", view);

みたいなことが出来るテンプレートエンジンなのですが、CommonJSのモジュールとしても使えるので

var Mustache = require("mustache");
var info = {
  name:'John'
};
var tmpl = "My name is {{ name }}"; // 結果はMy name is Johnとなる

みたいなことが出来ます。

CommonJSモジュールとして利用できるならTitanium Mobileからも利用できるよね?

client-side templating勉強会に申し込む時に、CommonJSなmustache.jsをTitanium Mobileで使えないか勉強してみたいと思いますと書いていたので、それをひたすらモクモクとやってました。

使おうと思った背景をちょっとだけ書いておくと、今作ってるアプリで

var exports = {
  createComposeRow :function(){
    var composeRow = Ti.UI.createTableViewRow($$.composeRow);
    composeRow.showd = true;
    var mail = Ti.UI.createImageView($$.lightMailBtn);
    composeRow.add(mail);

    var readLater = Ti.UI.createImageView($$.lightTextBtn);
    composeRow.add(readLater);

    var star = Ti.UI.createImageView($$.lightStarBtn);
    composeRow.add(star);

    var tweet = Ti.UI.createImageView($$.lightCommentBtn);
    composeRow.add(tweet);

    return composeRow;
};

みたいな処理がちょいちょい出てきます。ひたすらTi.UI.createImageViewする処理が多いのでこの部分をもうちょっとスマートに書けないかなと思ってmustache.jsでうまいこと出来ないかと思ってモクモクしようと思ってました

結果的には、上記問題解決まではいたらず、とりあえずのサンプル程度しか出来ませんでしたがひとまずコードを晒しておきます


buildするとこうなります
f:id:h5y1m141:20120509090037p:plain

Titaniumから少し浮気してRubyMotionいじってみた感想など

タイトルそのままですが、キャンペーン価格で1万円ちょっとで、自己投資と考えてポチって少しだけいじった状態の感想を

自分のスキル

  • Objective-Cでの開発経験無し
  • Titanium Mobileで1年半ほどいじってる。あすなろBLOGをオフラインでも閲覧できるようなアプリを作っててもうすぐリリースできそうな所まで来た
  • Rubyは初めてのRubyを読んで、次にどの本を読むべきか悩んでるレベル

初めてのRuby
初めてのRuby
posted with amazlet at 12.05.05
Yugui
オライリージャパン
売り上げランキング: 19735

いじる前に期待していたこと

Titanium Mobileみたいな感じで、iOS SDKAPIをラップしたようなものが提供されてて、それがRubyMotionから手軽に利用できるものを何となく期待していました。

自分の環境

実際やったこと

アプリケーションのインストールは特に問題もなく、RubyMotionの公式ドキュメント 見ながらHelloWorld的なアプリも一応動きました。

その後GitHubのRubyMotionサンプルのTwitterクライアント的なやつを単に写経してbuildしてこんな感じで動きました
f:id:h5y1m141:20120505162307p:plain

使った感想や気づきなど

Titanium Mobileの感覚で開発出来ることを期待してた自分にとってはちょっと敷居高く感じました。

そう感じた一番の要因はiOS SDKAPI Reference見たことが無いからで、ひげろぐさんが書かれているエントリで以下のような一文があったのでちょっと引用します。

RubyMotionは基本的にiOS SDKAPIを忠実になぞった出来になっていて、iOS SDKのクラス名やメソッド名がRubyのコードの中でそのまま使える。そのためiOS SDKの持つすべての機能を使うことができ、Objective-Cでの開発経験がある開発者なら今までの知識を生かしつつ、Objective-Cの煩わしさを捨ててRubyでサクサク書いていくことができる。

こういう経験ある人だと、きっとサクサク開発進むんでしょうね。

以前こんなことを書いたことがありますがTitanium Mobileの内部構造をもう少し理解したいという漠然とした考えを持っていました。

今回RubyMotion使って気づいたのは、iOS SDKのクラス群がどんなものがあって、TitaniumMobileからそれらがどう使われてるかを理解したいっていうことだったのかもしれません。

基本的にはTitanium Mobileでアプリ開発をしつつ、もう少しiOS SDKついて理解を深める手段として RubyMotionを使うというのが今のところ自分にとっての活用方法して最適なのかなと感じたので、ちょっとだけ浮気したけどTitanium Mobileメインにいじっていこうと思います

環境構築で最近はまった事のまとめ

サーバ側の環境構築してる時に最近はまっていたことをいくつか

Apacheのプロセスがどのユーザで起動されているか確認する

Linux/BSDな環境のサーバ管理している人には常識なんだろうけど、実はこれがすぐわからなかった。こちらのページ見たらpsコマンドの一覧まとめていて「u」オプション付けることでプロセスの実行ユーザ確認できるとのこと。なので

ps aux | grep apa

みたいな感じにすればOK

Sinatraのクエリーパラメータ取得した後に、数値→時刻への変換処理でErrorになる

実はこれに随分悩まされた。やりたかったことは

http://h5y1m141.info/entry/oyamada/1330000000

みたいなURLにアクセスした時に、

  • ブロガー名:oyamada
  • ブログの投稿日:Unix time(=1330000000)を変換して得られる結果。具体的には2012-02-23 21:26:40 +0900 以降。

というクエリーを発行して該当するエントリをDBから得ることをやろうと思ってました。

require 'sinatra'
get '/entry/:blogger/:num' do
  t = Time.at(params[:num])

みたいな感じでクエリーパラメータ取得して変換すればOKかと思っていたのですが「TypeError - can't convert String into an exact number:」と怒られてしまいました。params[:num]を数値変換すれば良かったみたいで、具体的には

require 'sinatra'
get '/entry/:blogger/:num' do
  t = Time.at(params[:num].to_i)

で解決しました。

ただ、http://h5y1m141.info/entry/oyamada/1330000000 みたいなURLにアクセスしてデータ取得するよりも、DBのlimit句などを駆使してpagination実装したほうがシンプルなことについさっき気づいたので、このURLにアクセスするのはやめることにしようと思います

MongoDBのpagination

どうやるんだろうとググっていたら、Stack Overflowにそのものズバリなことがあったけど1年ほど前にすでにはてブしてたのを忘れていた・・

MongoDBインストールしたLinux上でmongoシェル起動して

db.entries.find({"blogger":"oyamada"},{"title":1})
{ "_id" : ObjectId("4f860b45b8dac929af000001"), "title" : "まずは、自己紹介" }
{ "_id" : ObjectId("4f86114db8dac92c71000001"), "title" : "まずは、自己紹介" }
{ "_id" : ObjectId("4f86114fb8dac92c71000003"), "title" : "新しい職場で働く時" }
{ "_id" : ObjectId("4f861152b8dac92c71000005"), "title" : "Relax" }
{ "_id" : ObjectId("4f861154b8dac92c71000007"), "title" : "イチローにみるリーダーシップ" }
{ "_id" : ObjectId("4f861156b8dac92c71000009"), "title" : "Sun Grid から見るネットワークインフラ展望" }
{ "_id" : ObjectId("4f861158b8dac92c7100000b"), "title" : "何を勉強するか考える前に" }
{ "_id" : ObjectId("4f86115bb8dac92c7100000d"), "title" : "MicrosoftのOfficeの牙城を切り崩そうとするサービスの登場" }
{ "_id" : ObjectId("4f86115db8dac92c7100000f"), "title" : "メールとの付き合い方" }
{ "_id" : ObjectId("4f86115fb8dac92c71000011"), "title" : "お金のことについて" }
{ "_id" : ObjectId("4f861161b8dac92c71000013"), "title" : "セミナーやイベント参加時のちょっとした準備" }
{ "_id" : ObjectId("4f861163b8dac92c71000015"), "title" : "オープンソースといえば?" }
{ "_id" : ObjectId("4f861166b8dac92c71000017"), "title" : "お客さんの期待感" }
{ "_id" : ObjectId("4f861168b8dac92c71000019"), "title" : "身になる読書のための5つのTips" }
{ "_id" : ObjectId("4f86116ab8dac92c7100001b"), "title" : "パッケージングの妙" }
{ "_id" : ObjectId("4f86116cb8dac92c7100001d"), "title" : "実務経験xx年以上という”モノサシ”" }
{ "_id" : ObjectId("4f86116fb8dac92c7100001f"), "title" : "Winny対策PC導入。でもその前に・・" }
{ "_id" : ObjectId("4f861171b8dac92c71000021"), "title" : "時間の流れの見方を変えてみませんか?" }
{ "_id" : ObjectId("4f861173b8dac92c71000023"), "title" : "情報漏えい対策セミナー" }
{ "_id" : ObjectId("4f861175b8dac92c71000025"), "title" : "情報漏えい対策セミナーまとめ" }

となっている状況で先頭の5件取得するには

db.entries.find({"blogger":"oyamada"},{"title":1}).skip(0).limit(5)
{ "_id" : ObjectId("4f860b45b8dac929af000001"), "title" : "まずは、自己紹介" }
{ "_id" : ObjectId("4f86114db8dac92c71000001"), "title" : "まずは、自己紹介" }
{ "_id" : ObjectId("4f86114fb8dac92c71000003"), "title" : "新しい職場で働く時" }
{ "_id" : ObjectId("4f861152b8dac92c71000005"), "title" : "Relax" }
{ "_id" : ObjectId("4f861154b8dac92c71000007"), "title" : "イチローにみるリーダーシップ" }

でOK。6件目から10件目を取得したい場合には

db.entries.find({"blogger":"oyamada"},{"title":1}).skip(5).limit(5)
{ "_id" : ObjectId("4f861156b8dac92c71000009"), "title" : "Sun Grid から見るネットワークインフラ展望" }
{ "_id" : ObjectId("4f861158b8dac92c7100000b"), "title" : "何を勉強するか考える前に" }
{ "_id" : ObjectId("4f86115bb8dac92c7100000d"), "title" : "MicrosoftのOfficeの牙城を切り崩そうとするサービスの登場" }
{ "_id" : ObjectId("4f86115db8dac92c7100000f"), "title" : "メールとの付き合い方" }
{ "_id" : ObjectId("4f86115fb8dac92c71000011"), "title" : "お金のことについて" }

でOK。

WebViewの表示をする際にResources以下のCSSを読み込むようにする方法

UIの配色を試行錯誤してましたが、基本的にはこんな感じでいこうかと思ってます。
f:id:h5y1m141:20120425092721p:plain
遷移後の画面はこんな感じ
f:id:h5y1m141:20120425092706p:plain

一覧から任意のエントリをタッチした時に画面遷移し、エントリ詳細情報をWebView利用して表示してますが

「Titanium StudioのプロジェクトのResources以下に配置したcssをWebViewに適用する方法ってどうやるんだろう?」

というのが気になってちょっと調べてみました。

Local CSS not used in Local HTML Webviewを読んだらどうやるかおおよそイメージついたのでこんな感じで実装すれば大丈夫でした。

cssファイルの場所はResources/ui/html/main.cssとしてます
f:id:h5y1m141:20120425093426p:plain

// prepare css for iphone
var file = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory, '/ui/html/main.css');
var css = file.read();
var webView = Ti.UI.createWebView($$.webView);
var htmlHeaderElement = '<html><head><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"><style type="text/css">'+ css + '</style></head>';
var html_body = '以下省略・・'
webView.html = htmlHeaderElement
        +'<body>'
        + html_body
        + '</body></html>';
webView.show();

CSSはこんな感じ

body {
    margin:10;
    padding:10;
    line-height:1.6;
    font-size:1.1em;
    background-color:#ededed;
    color:#333;
    width:90%;
}

FacebookのiPhoneアプリ風のメニューをパクってみた



ということをつぶやていましたが、具体的にはこんな感じのUIを作ってみました。基本的にはastronaughtsさんが以前書いたエントリをそのまま流用しただけなんですけどねw

起動時の状態

普通にエントリー一覧が表示されています。
f:id:h5y1m141:20120420095424p:plain
左上のボタンをタッチすることで、エントリー一覧のTableViewが右にスライドして、あらかじめ下側にレイアウトしておいたブロガー一覧のTableViewが表示される
f:id:h5y1m141:20120420095502p:plain

ソースコード

GitHubにあるのですが、ちょっとやっつけ仕事な所が残ってるのと、UIの配色をこちらを見ながら試行錯誤してる段階なので、ちょっとづつ修正する予定です。