class Logster::Middleware::Viewer
Constants
- PATH_INFO
- REQUEST_METHOD
- SCRIPT_NAME
Public Class Methods
new(app)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 13 def initialize(app) @app = app @logs_path = Logster.config.subdirectory @path_regex = Regexp.new("^(#{@logs_path}$)|^(#{@logs_path}(/.*))$") (@store = Logster.store) || raise(ArgumentError.new("store")) @assets_path = File.expand_path("../../../../assets", __FILE__) @fileserver = Rack::File.new(@assets_path) end
Public Instance Methods
call(env)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 24 def call(env) path = env[PATH_INFO] script_name = env[SCRIPT_NAME] if script_name && script_name.length > 0 path = script_name + path end if resource = resolve_path(path) if resource =~ /\.ico$|\.js$|\.png|\.handlebars$|\.css$|\.woff$|\.ttf$|\.woff2$|\.svg$|\.otf$|\.eot$/ serve_file(env, resource) elsif resource.start_with?("/messages.json") serve_messages(Rack::Request.new(env)) elsif resource =~ /\/message\/([0-9a-f]+)$/ if env[REQUEST_METHOD] != "DELETE" return method_not_allowed("DELETE") end key = $1 message = Logster.store.get(key) unless message return [404, {}, ["Message not found"]] end Logster.store.delete(message) [301, { "Location" => "#{@logs_path}/" }, []] elsif resource =~ /\/(un)?protect\/([0-9a-f]+)$/ off = $1 == "un" key = $2 message = Logster.store.get(key) unless message return [404, {}, ["Message not found"]] end if off if Logster.store.unprotect(key) [301, { "Location" => "#{@logs_path}/show/#{key}?protected=false" }, []] else [500, {}, ["Failed"]] end else if Logster.store.protect(key) [301, { "Location" => "#{@logs_path}/show/#{key}?protected=true" }, []] else [500, {}, ["Failed"]] end end elsif resource =~ /\/solve\/([0-9a-f]+)$/ key = $1 message = Logster.store.get(key) unless message return [404, {}, ["Message not found"]] end Logster.store.solve(key) [301, { "Location" => "#{@logs_path}" }, []] elsif resource =~ /\/clear$/ if env[REQUEST_METHOD] != "POST" return method_not_allowed("POST") end Logster.store.clear [200, {}, ["Messages cleared"]] elsif resource =~ /\/show\/([0-9a-f]+)(\.json)?$/ key = $1 json = $2 == ".json" message = Logster.store.get(key) unless message return [404, {}, ["Message not found"]] end if json [200, { "Content-Type" => "application/json; charset=utf-8" }, [message.to_json]] else preload = { "/show/#{key}" => message } [200, { "Content-Type" => "text/html; charset=utf-8" }, [body(preload)]] end elsif resource =~ /\/settings(\.json)?$/ json = $1 == ".json" if json ignore_count = Logster.store.get_all_ignore_count suppression = [] Logster.store.ignore&.each do |pattern| string_pattern = Regexp === pattern ? pattern.inspect : pattern.to_s count = ignore_count[string_pattern] || 0 suppression << { value: string_pattern, count: count, hard: true } end Logster::SuppressionPattern.find_all(raw: true).each do |pattern| count = ignore_count[pattern] || 0 suppression << { value: pattern, count: count } end grouping = Logster::GroupingPattern.find_all(raw: true).map do |pattern| { value: pattern } end [200, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.generate(suppression: suppression, grouping: grouping)]] else [200, { "Content-Type" => "text/html; charset=utf-8" }, [body]] end elsif resource =~ /\/patterns\/([a-zA-Z0-9_]+)\.json$/ unless Logster.config.enable_custom_patterns_via_ui return not_allowed("Custom patterns via the UI is disabled. You can enable it by committing this line to your app source code:\nLogster.config.enable_custom_patterns_via_ui = true") end set_name = $1 req = Rack::Request.new(env) return method_not_allowed(%w[POST PUT DELETE]) if req.request_method == "GET" update_patterns(set_name, req) elsif resource == "/reset-count.json" req = Rack::Request.new(env) return method_not_allowed("PUT") if req.request_method != "PUT" pattern = nil if [true, "true"].include?(req.params["hard"]) pattern = Logster.store.ignore.find do |patt| str = Regexp === patt ? patt.inspect : patt.to_s str == req.params["pattern"] end else pattern = Logster::SuppressionPattern.find_all(raw: true).find do |patt| patt == req.params["pattern"] end end return not_found("Pattern not found") unless pattern pattern = Regexp === pattern ? pattern.inspect : pattern.to_s Logster.store.remove_ignore_count(pattern) [200, {}, ["OK"]] elsif resource == "/" [200, { "Content-Type" => "text/html; charset=utf-8" }, [body]] elsif resource =~ /\/fetch-env\/([0-9a-f]+)\.json$/ key = $1 env = Logster.store.get_env(key) if env [200, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.generate(env)]] else not_found end elsif resource == '/solve-group' return not_allowed unless Logster.config.enable_custom_patterns_via_ui req = Rack::Request.new(env) return method_not_allowed("POST") if req.request_method != "POST" group = Logster.store.find_pattern_groups do |patt| patt.inspect == req.params["regex"] end.first return not_found("No such pattern group exists") if !group group.messages_keys.each { |k| Logster.store.solve(k) } [200, {}, []] elsif resource == '/development-preload.json' && ENV["LOGSTER_ENV"] == "development" [200, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.generate(preloaded_data)]] else not_found end else @app.call(env) end end
Protected Instance Methods
body(preload = {})
click to toggle source
# File lib/logster/middleware/viewer.rb, line 361 def body(preload = {}) preload = preloaded_data.merge(preload) root_url = @logs_path root_url += "/" if root_url[-1] != "/" <<~HTML <!doctype html> <html> <head> <link rel="shortcut icon" href="#{@logs_path}/images/icon_64x64.png"> <link rel="apple-touch-icon" href="#{@logs_path}/images/icon_144x144.png" /> <title>#{Logster.config.web_title || "Logs"}</title> <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'> <link href='//fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'> <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes"> #{css("vendor.css")} #{css("client-app.css")} #{script("vendor.js")} <meta id="preloaded-data" data-root-path="#{@logs_path}" data-preloaded="#{to_json_and_escape(preload)}"> <meta name="client-app/config/environment" content="%7B%22modulePrefix%22%3A%22client-app%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22#{root_url}%22%2C%22locationType%22%3A%22history%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_DEFAULT_ASYNC_OBSERVERS%22%3Atrue%2C%22_JQUERY_INTEGRATION%22%3Afalse%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22name%22%3A%22client-app%22%2C%22version%22%3A%220.0.0%2B7a424002%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D" /> </head> <body> #{script("client-app.js")} </body> </html> HTML end
css(name, attrs = {})
click to toggle source
# File lib/logster/middleware/viewer.rb, line 313 def css(name, attrs = {}) attrs = attrs.map do |k, v| "#{k}='#{v}'" end.join(" ") "<link rel='stylesheet' type='text/css' href='#{@logs_path}/stylesheets/#{name}' #{attrs}>" end
get_class(set_name)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 273 def get_class(set_name) case set_name when "suppression" Logster::SuppressionPattern when "grouping" Logster::GroupingPattern else nil end end
method_not_allowed(allowed_methods)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 292 def method_not_allowed(allowed_methods) if Array === allowed_methods allowed_methods = allowed_methods.join(", ") end [405, { "Allow" => allowed_methods }, []] end
not_allowed(message = "Not allowed")
click to toggle source
# File lib/logster/middleware/viewer.rb, line 288 def not_allowed(message = "Not allowed") [403, {}, [message]] end
not_found(message = "Not found")
click to toggle source
# File lib/logster/middleware/viewer.rb, line 284 def not_found(message = "Not found") [404, {}, [message]] end
parse_regex(string)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 299 def parse_regex(string) if string =~ /\/(.+)\/(.*)/ s = $1 flags = Regexp::IGNORECASE if $2 && $2.include?("i") Regexp.new(s, flags) rescue nil end end
preload_backtrace_data()
click to toggle source
# File lib/logster/middleware/viewer.rb, line 330 def preload_backtrace_data gems_data = [] Gem::Specification.find_all do |gem| url = gem.metadata["source_code_uri"] || gem.homepage if url && url.match(/^https?:\/\/github.com\//) gems_data << { name: gem.name, url: url } end end { gems_data: gems_data, directories: Logster.config.project_directories } end
preloaded_data()
click to toggle source
# File lib/logster/middleware/viewer.rb, line 344 def preloaded_data preload = { env_expandable_keys: Logster.config.env_expandable_keys, patterns_enabled: Logster.config.enable_custom_patterns_via_ui, application_version: Logster.config.application_version } backtrace_links_enabled = Logster.config.enable_backtrace_links gems_dir = Logster.config.gems_dir gems_dir += "/" if gems_dir[-1] != "/" preload.merge!(gems_dir: gems_dir, backtrace_links_enabled: backtrace_links_enabled) if backtrace_links_enabled preload.merge!(preload_backtrace_data) end preload end
resolve_path(path)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 307 def resolve_path(path) if path =~ @path_regex $3 || "/" end end
script(prod, dev = nil)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 321 def script(prod, dev = nil) name = ENV['DEBUG_JS'] == "1" && dev ? dev : prod "<script src='#{@logs_path}/javascript/#{name}'></script>" end
serve_file(env, path)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 195 def serve_file(env, path) env[PATH_INFO] = path # accl redirect is going to be trouble, ensure its bypassed env['sendfile.type'] = '' @fileserver.call(env) end
serve_messages(req)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 202 def serve_messages(req) params = req.params opts = { before: params["before"], after: params["after"] } if (filter = params["filter"]) filter = filter.split("_").map { |s| s.to_i } opts[:severity] = filter end if search = params["search"] search = (parse_regex(search) || search) if params["regex_search"] == "true" opts[:search] = search end search = opts[:search] if params["known_groups"] opts[:known_groups] = params["known_groups"] end opts[:with_env] = (String === search && search.size > 0) || Regexp === search payload = { messages: @store.latest(opts), total: @store.count, search: params['search'] || '', filter: filter || '', } json = JSON.generate(payload) [200, { "Content-Type" => "application/json" }, [json]] end
to_json_and_escape(payload)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 326 def to_json_and_escape(payload) Rack::Utils.escape_html(JSON.fast_generate(payload)) end
update_patterns(set_name, req)
click to toggle source
# File lib/logster/middleware/viewer.rb, line 236 def update_patterns(set_name, req) klass = get_class(set_name) return not_found("Unknown set name") unless klass request_method = req.request_method pattern = req.params["pattern"] record = request_method == "POST" ? klass.new(pattern) : klass.find(pattern) return not_found unless record case request_method when "POST" args = {} if Logster::SuppressionPattern === record && [true, "true"].include?(req.params["retroactive"]) args[:retroactive] = true end record.save(args) when "PUT" record.modify(req.params["new_pattern"]) when "DELETE" record.destroy else return method_not_allowed(%w[POST PUT DELETE]) end [200, { "Content-Type" => "application/json" }, [JSON.generate(pattern: record.to_s)]] rescue => err error_message = err.message unless Logster::Pattern::PatternError === err # likely a bug, give us the backtrace error_message += "\n\n#{err.backtrace.join("\n")}" return [500, {}, [error_message]] end [400, {}, [error_message]] end