HRForecast の EMBED でヘッダとラベルを非表示に出来るようになったよ
HRForecast には EMBED の機能があり、iframe で別のページ(別のアプリケーション)にグラフを埋め込むことが出来ます。
ただ、埋め込む先のアプリケーションによっては、グラフ名と 1週間,1ヶ月,1年 などのレンジ指定、CSVエクスポートが置かれているヘッダ領域
、グラフ名と値が表示される右側のラベル領域
が不要な場合もあるかなと思います。
ので、pull-request して取り込んでもらいました。
suppress graph headers or labels when specified respective options #26
指定するクエリストリングと参考画像
iframe の中の URL のクエリストリングによって表示が変わります。
- ヘッダもラベルも消したい場合
graphheader=0&graphlabel=0
- ヘッダだけ消したい場合
graphheader=0
or graphheader=0&graphlabel=1
- ラベルだけ消したい場合
graphlabel=0
or graphheader=1&graphlabel=0
- ヘッダもラベルも消さず、従来どおりにしたい場合
graphlabel
, graphheader
を指定しない or graphheader=1&graphlabel=1
HRForecast についてご存じない方はこちらをどうぞ
fluent-plugin-graphite を書いたよ
graphite にメトリクスをポストする fluent-plugin を書きました
先に github で公開されていた fluent-plugin-graphite がありましたが、イチから書いて gem release いたしました
https://github.com/studio3104/fluent-plugin-graphite
http://rubygems.org/gems/fluent-plugin-graphite
なぜイチから書いたのか
以下のような箇所に懸念があり、修正だと結局まるっと書き直すのと変わらないと思いイチから書いてしまいました
- 先行プラグインは、
新しく書いたプラグインの機能
詳しくは README を見ていただきたく
- メトリクスを持つフィールドを指定して出力対象とする
- グラフ名はタグと出力対象のフィールドのキーから生成
- TCP コネクションを可能な限り使いまわす (
graphite-api.gem
による機能) - バッファリングしておいてある程度まとめてメトリクスを送信 (
graphite-api.gem
による機能)
たとえば dstat からグラフを生成したい場合
こんな感じの設定をすると、
<source> type dstat tag dstat.__HOSTNAME__ option -lnc delay 3 </source> <match dstat.**> type flatten_hash add_tag_prefix graphite. separator . </match> <match graphite.dstat.**> type graphite host localhost port 2003 tag_for prefix remove_tag_prefix graphite.dstat. name_key_pattern ^((?!hostname).)*$ </match>
こんな感じのグラフが出来る
先行プラグインとの互換性
設定ファイルを頑張って書きなおしてもらえれば同じグラフに対してメトリクスを送るようには出来ます
先日の このエントリ を例にすると、以下の様に設定ファイルを書き換えてあげる必要があります
<source> type dstat tag dstat option -lcn delay 5 </source> <match dstat> type copy <store> type map tag "map.dstat.gauges." + record["hostname"] time time record { "loadavg-short" => record["dstat"]["load avg"]["1m"] } </store> <store> type map tag "map.dstat.gauges." + record["hostname"] time time record { "cpu-usr" => record["dstat"]["total cpu usage"]["usr"] } </store> <store> type map tag "map.dstat.gauges." + record["hostname"] time time record { "cpu-sys" => record["dstat"]["total cpu usage"]["sys"] } </store> <store> type map tag "map.dstat.gauges." + record["hostname"] time time record { "cpu-hiq" => record["dstat"]["total cpu usage"]["hiq"] } </store> <store> type map tag "map.dstat.gauges." + record["hostname"] time time record { "cpu-siq" => record["dstat"]["total cpu usage"]["siq"] } </store> <store> type map tag "map.dstat.gauges." + record["hostname"] time time record { "net-recv" => record["dstat"]["net/total"]["recv"] } </store> <store> type map tag "map.dstat.gauges." + record["hostname"] time time record { "net-send" => record["dstat"]["net/total"]["send"] } </store> </match> <match map.dstat.**> type graphite host localhost port 2003 remove_tag_prefix map name_key_pattern .+ </match>
#CROSS2014 で #ぶつかり稽古 やります!
ぶつかり稽古って?
@__kan こんにちは、ペパボです。YAPC::ASIA参加者スペシャル特典にご応募いただき、ありがとうございます! @kentaro とのぶつかりげいこをぜひ開催したく思います。ご都合のよろしい日をいくつかご連絡下さい! URL
2013-10-02 11:08:11 via TweetList! to @__kan
オリジナルは昨年11月に行われたこちらのイベントです。エンジニアとエンジニアとの魂のぶつかり愛。
企画について
オリジナルのほうはペアプロでしたが、今回は趣向を変えて、コードレビューをテーマとしました。
自分はオリジナルのぶつかり稽古の関係者ではないのですが、許可を賜り、今回の企画名に冠しております。
実行委員長の @muddydixon さんがご自身のブログで仰っておられますが、今年の CROSS はこのようなテーマのもと各セッションが準備されてきました。
今年はテーマとして「クロスでススム、クロスで変わる」を掲げました。 「交流」「議論」を通して、明日への知識・人脈をひとつでも得て、変わる機会になってくだされば幸いです。
自分がセッションオーナーを務めさせていただくことになったセッションでは、このようなモチベーションのもと企画しました。
- 普段コードレビュをしてもらっていて、よそのあの人達はどんな感じでやっているのだろうと気になる
- 普段の勉強会の発表やセッションは最適化された手法を人に話す用にブラッシュアップされたものになっていることが多いので、現場感ライブ感のあるセッションを見たい
どんなセッション?
さて肝心の内容ですが、このような感じでお届けする予定です。
- コードレビュをテーマとしたパネルディスカッションを軸として進行
- 気をつけていること
- 使用しているツールについて
- など!など!!
- 実際にレビュー、修正を行いながら進行
- レビュー、修正は各ペアで予め何往復かしておいてもらっているので、普段の開発の一部分を切り取ってお見せする、という感じ
詳しくは実際にセッションにいらっしゃってご覧くださればと存じます。
各弟子に実装していただいた WEB アプリケーションは事前にデプロイし聴衆の方が見られるようにします。 また、各リポジトリの URL も公表しますので、勇気のあるあなたはレビュアーとしてライブで参加することも出来てしまうかもしれません!!!
課題の WEB アプリケーション
仕様はこんな感じです。
コレを各弟子に実装していただきました。
- アイドルグループの人気楽曲投票WEBアプリケーション
お待ちしております
会場でお会いしましょう!!!!!!
HRForecast に値を投げた時に一緒に複合グラフも作っちゃいたい!
HRForecast の API で値を投げた時に返ってくる JSON に、これまではエラーの有無とエラーメッセージだけでしたが、POST 成功時にはグラフの情報が含まれるようになりました。
{ :metricses => [ [0] { :section_name => "test_section", :id => "1", :colors => "[\"#99cc33\"]", :updated_at => nil, :service_name => "test_service", :graph_name => "test_graph", :color => "#99cc33", :meta => "{\"color\":\"#99cc33\"}", :created_at => nil, :sort => "0" } ], :error => 0 }
他にもいくつかの JSON API が追加され、ますます便利になってしまっています。 https://github.com/kazeburo/HRForecast/commit/a5b28908a0fe53c45f9b576687df6a58af385b17
値を投げた時にグラフの情報が返ってくれると例えば何が嬉しいのか
複合グラフを作成する API 自体はないのですが、これまででも POST してあげれば WEB UI からでなくても複合グラフを作成することは出来ました。
しかし複合グラフを作成するには、複合する対象のグラフの ID を送ってあげる必要があります。
ちょっと変な実装
をすればグラフ ID を HTTP リクエストで取得することは出来ますが、トリッキーなことをせずとも複合グラフの作成が出来るようになります。
こんな感じでどうでしょうか
ふたつのグラフ(test_1
, test_2
)に値を投げて、複合グラフ(test_complex
)がなければ作成します。
ちょっと工夫すれば test_complex
がすでに作られていて、そこに test_3
をつっこみたい、なんてこともカンタンに出来るでしょう。
require "net/http" require "json" class HRForecastRequestError < StandardError; end class HRForecast def initialize(fqdn, bind_port, enable_https = false) @base_url = enable_https ? "https://" + fqdn : "http://" + fqdn @bind_port = bind_port end # datetime のサポートするフォーマットはドキュメント参照: @base_url/docs def post_value(service_name, section_name, graph_name, post_bodies) value = post_bodies[:value] datetime = post_bodies[:datetime] ? post_bodies[:datetime] : Time.now graph_path = [service_name, section_name, graph_name].join("/") result = post_request("/api/" + graph_path, number: value, datetime: datetime) # 失敗すると、200 で body にエラーメッセージの入った JSON が返るので成否はそこを見て判断 if result[:error] == 1 raise HRForecastRequestError, "could not post value to #{graph_path} (#{result[:messages].to_s.chomp})" end result[:metricses].first[:id].to_i end def create_complex_graph(service_name, section_name, graph_name, path_data, graph_options = {}) description = graph_options[:description] ? graph_options[:description] : "" stack = graph_options[:stack] ? graph_options[:stack] : 0 sort = graph_options[:sort] ? graph_options[:sort] : 19 result = post_request("/add_complex", { service_name: service_name, section_name: section_name, graph_name: graph_name, description: description, stack: stack, # 0:積み上げないグラフ, 1:積み上げるグラフ sort: sort, # 値の大きいモノ順で list に表示される (0..19) :"path-data" => path_data # 複合グラフに突っ込むグラフの ID を配列で渡す }) # 失敗すると、200 で body にエラーメッセージの入った JSON が返るので成否はそこを見て判断 if result[:error] == 1 graph_path = [service_name, section_name, graph_name].join("/") raise HRForecastRequestError, "could not create complex graph #{graph_path} (#{result[:messages].to_s.chomp})" end end def complex_graph_exist?(service_name, section_name, graph_name) url = URI.parse(@base_url + ":" + @bind_port.to_s + ["/json_complex", service_name, section_name, graph_name].join("/")) http = Net::HTTP.new(url.host, url.port) http.get(url.path).is_a?(Net::HTTPSuccess) end private def post_request(request_path, post_data) url = @base_url + ":" + @bind_port.to_s + request_path response = Net::HTTP.post_form(URI.parse(url), post_data) raise HRForecastRequestError, "post request was not success" unless response.is_a?(Net::HTTPSuccess) JSON.parse(response.body, symbolize_names: true) end end
hf = HRForecast.new("test.hrforecast.com", 5127) service_name = "test_service" section_name = "test_section" graph_name_prefix = "test" test1_graph_id = hf.post_value(service_name, section_name, graph_name_prefix + "_1", value: rand(100) + 1) test2_graph_id = hf.post_value(service_name, section_name, graph_name_prefix + "_2", value: rand(100) + 1) complex_graph_name = graph_name_prefix + "_complex" if !hf.complex_graph_exist?(service_name, section_name, complex_graph_name) && test1_graph_id && test2_graph_id hf.create_complex_graph( service_name, section_name, complex_graph_name, [ test1_graph_id, test2_graph_id ] ) end
余談
前述のとおり、このような変な実装
によってグラフ ID を取得することは出来ます。
def get_graph_id(service_name, section_name, graph_name) # HTTP リクエストでグラフ ID を取得出来るクチが csv ダウンロードリンクしかなかったぽい url = URI.parse(@base_url + ":" + @bind_port.to_s + ["/csv", service_name, section_name, graph_name].join("/")) http = Net::HTTP.new(url.host, url.port) # "d=1" をつけてリクエストすると content-disposition の csv のファイル名からグラフ ID が取得できる response = http.head(url.path + "?d=1") return $1.to_i if response["content-disposition"] =~ %r{attachment; filename=\"metrics_(\d+).csv\"} end
自分で書いてて違和感があったので、「複合グラフを作成するために、値が POSTされたときにグラフ ID を返すようにって出来ますか?」って kazeburo さんに相談したら、数分で実装してくれました。
はじめからお願いすれば良かった感が強いですけど、自分はこの変な実装
をするために結構な時間を費やしてしまったのですが HRForecast のコードを読んでみたりだとか色々勉強になったこともあったのでまぁそれはそれで良かったと考えます。前向きに。
はてなブログ2周年おめでとう!
はてなブログ2周年おめでとう!> id:hatenablog
Percona Cloud Tools を試した
インストールや設定などは、pt-agent のドキュメントの通りに進めれば難なくこなせるはずですので割愛します。
感想
以下の点などを加味して考慮した結果、思っていたほどいいモノではないような気がしました。
ざっと使ってみただけなので誤っている点などあるかも知れませんが、ご容赦を。
「ここちがうよ!」「こうすればもっといいよ!」みたいなのよろしくお願い申し上げます。
pt-agent
というエージェントを root 権限で動かさなくてはならない
pt-agent
の設定は、Percona Cloud Tools
の WEB 画面からも設定変更を行えます。
例えば以下画像だと、How often to report
のところをいじるとサーバ上にある cron のファイルが書き換えられます。
インターネットから操作するのに root 権限あげちゃうのか、、、って感じ。
[追記]
root じゃなくても動かすことは出来る模様。ドキュメントに書いてあった。
でも「面倒な手順で手動インストールが必要で、わかんなかったら Percona に問い合わせてね!」みたいな感じだ。
pt-agent must be installed and ran as root. It is possible to run as a non-root user, but this requires a more complicated and manual installation. Please contact Percona for help if you need to run pt-agent as a non-root user.
pt-agent
が MySQL に作成する pt_agent
なるユーザに SUPER
権限がつく
上の画像を例にすると、Long query time
を書き換えて Save した場合に、サーバ上の MySQL で SET GLOBAL long_query_time=0.000000
とか実行されます。
インターネットから操作するのに SUPER 権限あげちゃうのか、、、って感じ。
mysql> SHOW GRANTS FOR 'pt_agent'@'localhost'; +-----------------------------------------------------------------------------------------------------------------+ | Grants for pt_agent@localhost | +-----------------------------------------------------------------------------------------------------------------+ | GRANT SUPER ON *.* TO 'pt_agent'@'localhost' IDENTIFIED BY PASSWORD '*16CED81DAC5917722E82063353A27E11178399F0' | +-----------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
各 MySQL サーバに pt-agent
を泳がせなければならない
(このへんはあまり深くタッチしてないので、うまいやり方があるのかもしれないです。)
ひとつの pt-agent
プロセスが ひとつの mysqld
と対になるようなので、サーバごとにエージェントを泳がせなくてはなりません。
そうすると、一旦ログを集約サーバなどに集めておいて、そこからだけ投げるみたいなことがやりにくくなります。
ファイルやクエリごとにタグを付けられるわけではなさそうだし、どのクエリがどのサーバのモノなのかを一意に識別させる手段が、どのエージェントから送られてきたログなのか、というところだけなので。
タスクが JSON (/var/lib/pt-agent/services
にあるファイル) で定義されているので、それをうまいことなんとかすればもしかしたらどうにか出来るのかも知れません。未検証。でもやれたとしても面倒な感じがする。
あとDB サーバが直接インターネットに繋ぎにいけないような構成をとっている場合には proxy を経由させたりとかしなくてはならないかも。面倒。
上述のエージェントに与える権限についてだけど、サーバごとにエージェントが要るのであれば、WEB から設定変えられるほうがまぁいいとは思うけど、権限過多ではないかという気がする。 サーバ もイントラで動かせるんだったらよいと思うけど、クラウドだしなぁ。
こんな感じのサマリー画面
ひとつの DB サーバ全体
クエリの種類ごと
これだったら EXPLAIN までしてから結果を表示して欲しい感じがする。。
これも。。
「偽だったら真、真だったら偽」みたいなのわかりにくい
flag ? false : true
みたいなコード見かけて、
!flag ? true : false
わかりにくいからこうしたほうがよさそうだと思ったけど、
!flag
これだけで良いじゃん、って指摘してもらった。確かに。
@studio3104 !flag するなら三項演算子使う必要ない
2013-10-22 17:13:24 via YoruFukurou to @studio3104