|
1 |
| -require 'rubygems' |
2 |
| -require 'sinatra' |
3 |
| -require 'json' |
4 |
| -require 'time' |
5 |
| -require 'date' |
6 |
| -require 'rss/maker' |
7 |
| -require 'yaml' |
8 |
| -require 'tzinfo' |
9 |
| - |
10 |
| -$LOAD_PATH << '.' |
11 |
| -$LOAD_PATH << './lib' |
12 |
| - |
13 |
| -if ENV["KIBANA_CONFIG"] |
14 |
| - require ENV["KIBANA_CONFIG"] |
15 |
| -else |
16 |
| - require 'KibanaConfig' |
17 |
| -end |
18 |
| -Dir['./lib/*.rb'].each{ |f| require f } |
19 |
| -ruby_18 { require 'fastercsv' } |
20 |
| -ruby_19 { require 'csv' } |
21 |
| - |
22 |
| -configure do |
23 |
| - set :bind, defined?(KibanaConfig::KibanaHost) ? KibanaConfig::KibanaHost : '0.0.0.0' |
24 |
| - set :port, KibanaConfig::KibanaPort |
25 |
| - set :public_folder, Proc.new { File.join(root, "public") } |
26 |
| - enable :sessions |
27 |
| -end |
28 |
| - |
29 |
| -helpers do |
30 |
| - def link_to url_fragment, mode=:full_url |
31 |
| - case mode |
32 |
| - when :path_only |
33 |
| - base = request.script_name |
34 |
| - when :full_url |
35 |
| - if (request.scheme == 'http' && request.port == 80 || |
36 |
| - request.scheme == 'https' && request.port == 443) |
37 |
| - port = "" |
38 |
| - else |
39 |
| - port = ":#{request.port}" |
40 |
| - end |
41 |
| - base = "#{request.scheme}://#{request.host}#{port}#{request.script_name}" |
42 |
| - else |
43 |
| - raise "Unknown script_url mode #{mode}" |
44 |
| - end |
45 |
| - "#{base}#{url_fragment}" |
46 |
| - end |
47 |
| - |
48 |
| -end |
49 |
| - |
50 |
| -get '/' do |
51 |
| - if KibanaConfig::Allow_iframed |
52 |
| - headers "X-Frame-Options" => "allow","X-XSS-Protection" => "0" |
53 |
| - end |
54 |
| - send_file File.join(settings.public_folder, 'index.html') |
55 |
| -end |
56 |
| - |
57 |
| -get '/stream' do |
58 |
| - send_file File.join(settings.public_folder, 'stream.html') |
59 |
| -end |
60 |
| - |
61 |
| -# Returns |
62 |
| -get '/api/search/:hash/?:segment?' do |
63 |
| - segment = params[:segment].nil? ? 0 : params[:segment].to_i |
64 |
| - |
65 |
| - req = ClientRequest.new(params[:hash]) |
66 |
| - if KibanaConfig::Highlight_results |
67 |
| - query = HighlightedQuery.new(req.search,req.from,req.to,req.offset) |
68 |
| - else |
69 |
| - query = SortedQuery.new(req.search,req.from,req.to,req.offset) |
70 |
| - end |
71 |
| - indices = Kelastic.index_range(req.from,req.to) |
72 |
| - result = KelasticMulti.new(query,indices) |
73 |
| - |
74 |
| - # Not sure this is required. This should be able to be handled without |
75 |
| - # server communication |
76 |
| - result.response['kibana']['time'] = { |
77 |
| - "from" => req.from.iso8601, "to" => req.to.iso8601} |
78 |
| - result.response['kibana']['default_fields'] = KibanaConfig::Default_fields |
79 |
| - # Enable clickable URL links |
80 |
| - result.response['kibana']['clickable_urls'] = KibanaConfig::Clickable_URLs |
81 |
| - |
82 |
| - JSON.generate(result.response) |
83 |
| -end |
84 |
| - |
85 |
| -get '/api/graph/:mode/:interval/:hash/?:segment?' do |
86 |
| - segment = params[:segment].nil? ? 0 : params[:segment].to_i |
87 |
| - |
88 |
| - req = ClientRequest.new(params[:hash]) |
89 |
| - case params[:mode] |
90 |
| - when "count" |
91 |
| - query = DateHistogram.new( |
92 |
| - req.search,req.from,req.to,params[:interval].to_i) |
93 |
| - when "mean" |
94 |
| - query = StatsHistogram.new( |
95 |
| - req.search,req.from,req.to,req.analyze,params[:interval].to_i) |
96 |
| - end |
97 |
| - indices = Kelastic.index_range(req.from,req.to) |
98 |
| - result = KelasticSegment.new(query,indices,segment) |
99 |
| - |
100 |
| - JSON.generate(result.response) |
101 |
| -end |
102 |
| - |
103 |
| -get '/api/id/:id/:index' do |
104 |
| - ## TODO: Make this verify that the index matches the smart index pattern. |
105 |
| - id = params[:id] |
106 |
| - index = "#{params[:index]}" |
107 |
| - query = IDQuery.new(id) |
108 |
| - result = Kelastic.new(query,index) |
109 |
| - JSON.generate(result.response) |
110 |
| -end |
111 |
| - |
112 |
| -get '/api/analyze/:field/trend/:hash' do |
113 |
| - limit = KibanaConfig::Analyze_limit |
114 |
| - show = KibanaConfig::Analyze_show |
115 |
| - req = ClientRequest.new(params[:hash]) |
116 |
| - |
117 |
| - query_end = SortedQuery.new( |
118 |
| - req.search,req.from,req.to,0,limit,'@timestamp','desc') |
119 |
| - indices_end = Kelastic.index_range(req.from,req.to) |
120 |
| - result_end = KelasticMulti.new(query_end,indices_end) |
121 |
| - |
122 |
| - # Oh snaps. too few results for full limit analysis, rerun with less |
123 |
| - if (result_end.response['hits']['hits'].length < limit) |
124 |
| - limit = (result_end.response['hits']['hits'].length / 2).to_i |
125 |
| - query_end = SortedQuery.new( |
126 |
| - req.search,req.from,req.to,0,limit,'@timestamp','desc') |
127 |
| - indices_end = Kelastic.index_range(req.from,req.to) |
128 |
| - result_end = KelasticMulti.new(query_end,indices_end) |
129 |
| - end |
130 |
| - |
131 |
| - fields = Array.new |
132 |
| - fields = params[:field].split(',,') |
133 |
| - count_end = KelasticResponse.count_field(result_end.response,fields) |
134 |
| - |
135 |
| - query_begin = SortedQuery.new( |
136 |
| - req.search,req.from,req.to,0,limit,'@timestamp','asc') |
137 |
| - indices_begin = Kelastic.index_range(req.from,req.to).reverse |
138 |
| - result_begin = KelasticMulti.new(query_begin,indices_begin) |
139 |
| - count_begin = KelasticResponse.count_field(result_begin.response,fields) |
140 |
| - |
141 |
| - |
142 |
| - |
143 |
| - # Not sure this is required. This should be able to be handled without |
144 |
| - # server communication |
145 |
| - result_end.response['kibana']['time'] = { |
146 |
| - "from" => req.from.iso8601, "to" => req.to.iso8601} |
147 |
| - |
148 |
| - final = Array.new(0) |
149 |
| - count = result_end.response['hits']['hits'].length |
150 |
| - count_end.each do |key, value| |
151 |
| - first = count_begin[key].nil? ? 0 : count_begin[key]; |
152 |
| - final << { |
153 |
| - :id => key, |
154 |
| - :count => value, |
155 |
| - :start => first, |
156 |
| - :trend => (((value.to_f / count) - (first.to_f / count)) * 100).to_f |
157 |
| - } |
158 |
| - end |
159 |
| - final = final.sort_by{ |hsh| hsh[:trend].abs }.reverse |
160 |
| - |
161 |
| - result_end.response['hits']['count'] = result_end.response['hits']['hits'].length |
162 |
| - result_end.response['hits']['hits'] = final[0..(show - 1)] |
163 |
| - JSON.generate(result_end.response) |
164 |
| -end |
165 |
| - |
166 |
| -get '/api/analyze/:field/terms/:hash' do |
167 |
| - limit = KibanaConfig::Analyze_show |
168 |
| - req = ClientRequest.new(params[:hash]) |
169 |
| - fields = Array.new |
170 |
| - fields = params[:field].split(',,') |
171 |
| - query = TermsFacet.new(req.search,req.from,req.to,fields) |
172 |
| - indices = Kelastic.index_range( |
173 |
| - req.from,req.to,KibanaConfig::Facet_index_limit) |
174 |
| - result = KelasticMultiFlat.new(query,indices) |
175 |
| - |
176 |
| - # Not sure this is required. This should be able to be handled without |
177 |
| - # server communication |
178 |
| - result.response['kibana']['time'] = { |
179 |
| - "from" => req.from.iso8601, "to" => req.to.iso8601} |
| 1 | +$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "lib"))) |
180 | 2 |
|
181 |
| - JSON.generate(result.response) |
182 |
| -end |
183 |
| - |
184 |
| -get '/api/analyze/:field/score/:hash' do |
185 |
| - limit = KibanaConfig::Analyze_limit |
186 |
| - show = KibanaConfig::Analyze_show |
187 |
| - req = ClientRequest.new(params[:hash]) |
188 |
| - query = SortedQuery.new(req.search,req.from,req.to,0,limit) |
189 |
| - indices = Kelastic.index_range(req.from,req.to) |
190 |
| - result = KelasticMulti.new(query,indices) |
191 |
| - fields = Array.new |
192 |
| - fields = params[:field].split(',,') |
193 |
| - count = KelasticResponse.count_field(result.response,fields) |
194 |
| - |
195 |
| - # Not sure this is required. This should be able to be handled without |
196 |
| - # server communication |
197 |
| - result.response['kibana']['time'] = { |
198 |
| - "from" => req.from.iso8601, "to" => req.to.iso8601} |
199 |
| - |
200 |
| - final = Array.new(0) |
201 |
| - count.each do |key, value| |
202 |
| - final << { |
203 |
| - :id => key, |
204 |
| - :count => value, |
205 |
| - } |
206 |
| - end |
207 |
| - final = final.sort_by{ |hsh| hsh[:count].abs }.reverse |
208 |
| - |
209 |
| - result.response['hits']['count'] = result.response['hits']['hits'].length |
210 |
| - result.response['hits']['hits'] = final[0..(show - 1)] |
211 |
| - JSON.generate(result.response) |
212 |
| -end |
213 |
| - |
214 |
| -get '/api/analyze/:field/mean/:hash' do |
215 |
| - req = ClientRequest.new(params[:hash]) |
216 |
| - query = StatsFacet.new(req.search,req.from,req.to,params[:field]) |
217 |
| - indices = Kelastic.index_range(req.from,req.to,KibanaConfig::Facet_index_limit) |
218 |
| - type = Kelastic.field_type(indices.first,params[:field]) |
219 |
| - if ['long','integer','double','float'].include? type |
220 |
| - result = KelasticMultiFlat.new(query,indices) |
221 |
| - |
222 |
| - # Not sure this is required. This should be able to be handled without |
223 |
| - # server communication |
224 |
| - result.response['kibana']['time'] = { |
225 |
| - "from" => req.from.iso8601, "to" => req.to.iso8601} |
226 |
| - |
227 |
| - JSON.generate(result.response) |
228 |
| - else |
229 |
| - JSON.generate({"error" => "Statistics not supported for type: #{type}"}) |
230 |
| - end |
231 |
| -end |
232 |
| - |
233 |
| -get '/api/stream/:hash/?:from?' do |
234 |
| - # This is delayed by 10 seconds to account for indexing time and a small time |
235 |
| - # difference between us and the ES server. |
236 |
| - delay = 10 |
237 |
| - |
238 |
| - # Calculate 'from' and 'to' based on last event in stream. |
239 |
| - from = params[:from].nil? ? |
240 |
| - Time.now - (10 + delay) : Time.parse("#{params[:from]}+0:00") |
241 |
| - |
242 |
| - # ES's range filter is inclusive. delay-1 should give us the correct window. |
243 |
| - # Maybe? |
244 |
| - to = Time.now - (delay) |
245 |
| - |
246 |
| - # Build and execute |
247 |
| - req = ClientRequest.new(params[:hash]) |
248 |
| - query = SortedQuery.new(req.search,from,to,0,30) |
249 |
| - indices = Kelastic.index_range(from,to) |
250 |
| - result = KelasticMulti.new(query,indices) |
251 |
| - output = JSON.generate(result.response) |
252 |
| - |
253 |
| - if result.response['hits']['total'] > 0 |
254 |
| - JSON.generate(result.response) |
255 |
| - end |
256 |
| -end |
257 |
| - |
258 |
| -get '/rss/:hash/?:count?' do |
259 |
| - # TODO: Make the count number above/below functional w/ hard limit setting |
260 |
| - count = KibanaConfig::Rss_show |
261 |
| - # count = params[:count].nil? ? 30 : params[:count].to_i |
262 |
| - span = (60 * 60 * 24) |
263 |
| - from = Time.now - span |
264 |
| - to = Time.now |
265 |
| - |
266 |
| - req = ClientRequest.new(params[:hash]) |
267 |
| - query = SortedQuery.new(req.search,from,to,0,count) |
268 |
| - indices = Kelastic.index_range(from,to) |
269 |
| - result = KelasticMulti.new(query,indices) |
270 |
| - flat = KelasticResponse.flatten_response(result.response,req.fields) |
271 |
| - |
272 |
| - headers "Content-Type" => "application/rss+xml", |
273 |
| - "charset" => "utf-8", |
274 |
| - "Content-Disposition" => "inline; " + |
275 |
| - "filename=kibana_rss_#{Time.now.to_i}.xml" |
276 |
| - |
277 |
| - content = RSS::Maker.make('2.0') do |m| |
278 |
| - m.channel.title = "Kibana #{req.search}" |
279 |
| - m.channel.link = "www.example.com" |
280 |
| - m.channel.description = |
281 |
| - "A event search for: #{req.search}. |
282 |
| - With title fields: #{req.fields.join(', ')} " |
283 |
| - m.items.do_sort = true |
284 |
| - |
285 |
| - result.response['hits']['hits'].each do |hit| |
286 |
| - i = m.items.new_item |
287 |
| - hash = IdRequest.new(hit['_id'],hit['_index']).hash |
288 |
| - i.title = KelasticResponse.flatten_hit(hit,req.fields).join(', ') |
289 |
| - i.date = Time.iso8601(KelasticResponse.get_field_value(hit,'@timestamp')) |
290 |
| - i.link = link_to("/##{hash}") |
291 |
| - i.description = "<pre>#{hit.to_yaml}</pre>" |
292 |
| - end |
293 |
| - end |
294 |
| - content.to_s |
295 |
| -end |
296 |
| - |
297 |
| -get '/export/:hash/?:count?' do |
298 |
| - |
299 |
| - count = KibanaConfig::Export_show |
300 |
| - # TODO: Make the count number above/below functional w/ hard limit setting |
301 |
| - # count = params[:count].nil? ? 20000 : params[:count].to_i |
302 |
| - sep = KibanaConfig::Export_delimiter |
303 |
| - |
304 |
| - req = ClientRequest.new(params[:hash]) |
305 |
| - query = SortedQuery.new(req.search,req.from,req.to,0,count) |
306 |
| - indices = Kelastic.index_range(req.from,req.to) |
307 |
| - result = KelasticMulti.new(query,indices) |
308 |
| - flat = KelasticResponse.flatten_response(result.response,req.fields) |
309 |
| - |
310 |
| - headers "Content-Type" => "application/octet-stream", |
311 |
| - "Content-Disposition" => "attachment;filename=Kibana_#{Time.now.to_i}.csv" |
312 |
| - |
313 |
| - |
314 |
| - if RUBY_VERSION < "1.9" |
315 |
| - FasterCSV.generate({:col_sep => sep}) do |file| |
316 |
| - file << req.fields |
317 |
| - flat.each { |row| file << row } |
318 |
| - end |
319 |
| - else |
320 |
| - CSV.generate({:col_sep => sep}) do |file| |
321 |
| - file << req.fields |
322 |
| - flat.each { |row| file << row } |
323 |
| - end |
324 |
| - end |
325 |
| - |
326 |
| -end |
327 |
| - |
328 |
| -# Transient URL Routes |
329 |
| -# |
330 |
| -# Access route |
331 |
| -# Extract the _64hash from the KtransientURL hash table and redirects |
332 |
| -get '/turl/:id' do |
333 |
| - b64hash = KtransientURL[params[:id]] |
334 |
| - |
335 |
| - redirect to("/index.html##{b64hash}") unless b64hash.nil? |
336 |
| - "sorry! #{params[:id]} does not match any entry in Kibana's transient" + |
337 |
| - " url table" |
338 |
| -end |
339 |
| - |
340 |
| -# Adds _64hash to hash table and returns and ID |
341 |
| -get '/turl/save/:hash' do |
342 |
| - "#{KtransientURL << params[:hash]}" |
343 |
| -end |
| 3 | +require 'rubygems' |
| 4 | +require 'kibana' |
344 | 5 |
|
345 |
| -get '/js/timezone.js' do |
346 |
| - erb :timezone |
347 |
| -end |
| 6 | +KibanaApp.run! |
0 commit comments