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 のコードを読んでみたりだとか色々勉強になったこともあったのでまぁそれはそれで良かったと考えます。前向きに。