logster 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +18 -18
  3. data/.travis.yml +15 -15
  4. data/CHANGELOG.md +137 -130
  5. data/Gemfile +4 -4
  6. data/Guardfile +8 -8
  7. data/LICENSE.txt +22 -22
  8. data/README.md +99 -99
  9. data/Rakefile +24 -24
  10. data/assets/fonts/FontAwesome.otf +0 -0
  11. data/assets/fonts/fontawesome-webfont.eot +0 -0
  12. data/assets/fonts/fontawesome-webfont.svg +639 -639
  13. data/assets/fonts/fontawesome-webfont.ttf +0 -0
  14. data/assets/fonts/fontawesome-webfont.woff +0 -0
  15. data/assets/fonts/fontawesome-webfont.woff2 +0 -0
  16. data/assets/images/Icon-144_rounded.png +0 -0
  17. data/assets/images/Icon-144_square.png +0 -0
  18. data/assets/images/icon_144x144.png +0 -0
  19. data/assets/images/icon_64x64.png +0 -0
  20. data/assets/javascript/client-app.js +81 -81
  21. data/assets/javascript/vendor.js +5302 -5302
  22. data/assets/stylesheets/client-app.css +0 -0
  23. data/assets/stylesheets/vendor.css +3 -3
  24. data/build_client_app.sh +12 -12
  25. data/client-app/.editorconfig +20 -20
  26. data/client-app/.ember-cli +9 -9
  27. data/client-app/.eslintignore +19 -19
  28. data/client-app/.eslintrc.js +46 -46
  29. data/client-app/.gitignore +23 -23
  30. data/client-app/.travis.yml +27 -27
  31. data/client-app/.watchmanconfig +3 -3
  32. data/client-app/README.md +57 -57
  33. data/client-app/app/app.js +14 -14
  34. data/client-app/app/components/message-info.js +18 -18
  35. data/client-app/app/components/message-row.js +45 -45
  36. data/client-app/app/components/panel-resizer.js +75 -75
  37. data/client-app/app/components/tab-contents.js +27 -27
  38. data/client-app/app/components/tab-link.js +5 -5
  39. data/client-app/app/components/tabbed-section.js +32 -32
  40. data/client-app/app/components/time-formatter.js +25 -25
  41. data/client-app/app/components/update-time.js +21 -21
  42. data/client-app/app/controllers/index.js +83 -83
  43. data/client-app/app/controllers/show.js +13 -13
  44. data/client-app/app/index.html +29 -29
  45. data/client-app/app/initializers/app-init.js +55 -55
  46. data/client-app/app/lib/preload.js +14 -14
  47. data/client-app/app/lib/utilities.js +140 -140
  48. data/client-app/app/models/message-collection.js +158 -158
  49. data/client-app/app/models/message.js +99 -99
  50. data/client-app/app/resolver.js +3 -3
  51. data/client-app/app/router.js +14 -14
  52. data/client-app/app/routes/index.js +53 -53
  53. data/client-app/app/routes/show.js +14 -14
  54. data/client-app/app/styles/app.css +387 -387
  55. data/client-app/app/templates/application.hbs +2 -2
  56. data/client-app/app/templates/components/message-info.hbs +44 -44
  57. data/client-app/app/templates/components/message-row.hbs +17 -17
  58. data/client-app/app/templates/components/tabbed-section.hbs +10 -10
  59. data/client-app/app/templates/components/time-formatter.hbs +1 -1
  60. data/client-app/app/templates/index.hbs +57 -57
  61. data/client-app/app/templates/show.hbs +4 -4
  62. data/client-app/config/environment.js +51 -51
  63. data/client-app/config/optional-features.json +3 -3
  64. data/client-app/config/targets.js +18 -18
  65. data/client-app/ember-cli-build.js +29 -29
  66. data/client-app/package-lock.json +11365 -11365
  67. data/client-app/package.json +56 -56
  68. data/client-app/testem.js +25 -25
  69. data/client-app/tests/index.html +34 -34
  70. data/client-app/tests/integration/components/message-info-test.js +26 -26
  71. data/client-app/tests/integration/components/message-row-test.js +26 -26
  72. data/client-app/tests/integration/components/panel-resizer-test.js +26 -26
  73. data/client-app/tests/integration/components/tab-contents-test.js +26 -26
  74. data/client-app/tests/integration/components/tab-link-test.js +26 -26
  75. data/client-app/tests/integration/components/tabbed-section-test.js +26 -26
  76. data/client-app/tests/integration/components/time-formatter-test.js +26 -26
  77. data/client-app/tests/integration/components/update-time-test.js +26 -26
  78. data/client-app/tests/test-helper.js +8 -8
  79. data/client-app/tests/unit/controllers/index-test.js +12 -12
  80. data/client-app/tests/unit/controllers/show-test.js +12 -12
  81. data/client-app/tests/unit/initializers/app-init-test.js +31 -31
  82. data/client-app/tests/unit/routes/index-test.js +11 -11
  83. data/client-app/tests/unit/routes/show-test.js +11 -11
  84. data/lib/examples/sidekiq_logster_reporter.rb +21 -21
  85. data/lib/logster.rb +54 -54
  86. data/lib/logster/base_store.rb +130 -130
  87. data/lib/logster/configuration.rb +25 -25
  88. data/lib/logster/ignore_pattern.rb +65 -65
  89. data/lib/logster/logger.rb +108 -102
  90. data/lib/logster/message.rb +227 -227
  91. data/lib/logster/middleware/debug_exceptions.rb +26 -26
  92. data/lib/logster/middleware/reporter.rb +56 -56
  93. data/lib/logster/middleware/viewer.rb +220 -220
  94. data/lib/logster/rails/railtie.rb +58 -58
  95. data/lib/logster/redis_store.rb +481 -481
  96. data/lib/logster/version.rb +3 -3
  97. data/lib/logster/web.rb +14 -14
  98. data/logster.gemspec +34 -34
  99. data/test/examples/test_sidekiq_reporter_example.rb +46 -46
  100. data/test/fake_data/Gemfile +4 -4
  101. data/test/fake_data/generate.rb +10 -10
  102. data/test/logster/middleware/test_reporter.rb +21 -21
  103. data/test/logster/middleware/test_viewer.rb +96 -96
  104. data/test/logster/test_base_store.rb +147 -147
  105. data/test/logster/test_ignore_pattern.rb +41 -41
  106. data/test/logster/test_logger.rb +80 -74
  107. data/test/logster/test_message.rb +34 -34
  108. data/test/logster/test_redis_rate_limiter.rb +230 -230
  109. data/test/logster/test_redis_store.rb +427 -427
  110. data/test/test_helper.rb +38 -38
  111. data/vendor/assets/javascripts/logster.js.erb +39 -39
  112. metadata +3 -3
@@ -1,26 +1,26 @@
1
- class Logster::Middleware::DebugExceptions < ActionDispatch::DebugExceptions
2
- private
3
-
4
- def log_error(request_or_env, wrapper)
5
- env =
6
- if Rails::VERSION::MAJOR > 4
7
- request_or_env.env
8
- else
9
- request_or_env
10
- end
11
-
12
- exception = wrapper.exception
13
-
14
- Logster.config.current_context.call(env) do
15
- location = exception.backtrace[0]
16
- exception_string = exception.to_s
17
-
18
- Logster.logger.add_with_opts(::Logger::Severity::FATAL,
19
- exception.class.to_s << " (" << exception_string << ")\n#{location}",
20
- "web-exception",
21
- backtrace: exception.backtrace.join("\n"),
22
- env: env)
23
- end
24
-
25
- end
26
- end
1
+ class Logster::Middleware::DebugExceptions < ActionDispatch::DebugExceptions
2
+ private
3
+
4
+ def log_error(request_or_env, wrapper)
5
+ env =
6
+ if Rails::VERSION::MAJOR > 4
7
+ request_or_env.env
8
+ else
9
+ request_or_env
10
+ end
11
+
12
+ exception = wrapper.exception
13
+
14
+ Logster.config.current_context.call(env) do
15
+ location = exception.backtrace[0]
16
+ exception_string = exception.to_s
17
+
18
+ Logster.logger.add_with_opts(::Logger::Severity::FATAL,
19
+ exception.class.to_s << " (" << exception_string << ")\n#{location}",
20
+ "web-exception",
21
+ backtrace: exception.backtrace.join("\n"),
22
+ env: env)
23
+ end
24
+
25
+ end
26
+ end
@@ -1,56 +1,56 @@
1
- module Logster
2
- module Middleware
3
- class Reporter
4
-
5
- PATH_INFO = "PATH_INFO".freeze
6
- SCRIPT_NAME = "SCRIPT_NAME".freeze
7
-
8
- def initialize(app, config={})
9
- @app = app
10
- @error_path = Logster.config.subdirectory + '/report_js_error'
11
- end
12
-
13
- def call(env)
14
- Thread.current[Logster::Logger::LOGSTER_ENV] = env
15
-
16
-
17
- path = env[PATH_INFO]
18
- script_name = env[SCRIPT_NAME]
19
-
20
- if script_name && script_name.length > 0
21
- path = script_name + path
22
- end
23
-
24
- if path == @error_path
25
- Logster.config.current_context.call(env) do
26
- report_js_error(env)
27
- end
28
- return [200,{},["OK"]]
29
- end
30
-
31
- @app.call(env)
32
- ensure
33
- Thread.current[Logster::Logger::LOGSTER_ENV] = nil
34
- end
35
-
36
- def report_js_error(env)
37
- req = Rack::Request.new(env)
38
- params = req.params
39
-
40
- message = params["message"] || ""
41
- message << "\nUrl: " << params["url"] if params["url"]
42
- message << "\nLine: " << params["line"] if params["line"]
43
- message << "\nColumn: " << params["column"] if params["column"]
44
- message << "\nWindow Location: " << params["window_location"] if params["window_location"]
45
-
46
- backtrace = params["stacktrace"] || ""
47
- Logster.store.report(::Logger::Severity::WARN,
48
- "javascript",
49
- message,
50
- backtrace: backtrace,
51
- env: env)
52
- end
53
-
54
- end
55
- end
56
- end
1
+ module Logster
2
+ module Middleware
3
+ class Reporter
4
+
5
+ PATH_INFO = "PATH_INFO".freeze
6
+ SCRIPT_NAME = "SCRIPT_NAME".freeze
7
+
8
+ def initialize(app, config={})
9
+ @app = app
10
+ @error_path = Logster.config.subdirectory + '/report_js_error'
11
+ end
12
+
13
+ def call(env)
14
+ Thread.current[Logster::Logger::LOGSTER_ENV] = env
15
+
16
+
17
+ path = env[PATH_INFO]
18
+ script_name = env[SCRIPT_NAME]
19
+
20
+ if script_name && script_name.length > 0
21
+ path = script_name + path
22
+ end
23
+
24
+ if path == @error_path
25
+ Logster.config.current_context.call(env) do
26
+ report_js_error(env)
27
+ end
28
+ return [200,{},["OK"]]
29
+ end
30
+
31
+ @app.call(env)
32
+ ensure
33
+ Thread.current[Logster::Logger::LOGSTER_ENV] = nil
34
+ end
35
+
36
+ def report_js_error(env)
37
+ req = Rack::Request.new(env)
38
+ params = req.params
39
+
40
+ message = params["message"] || ""
41
+ message << "\nUrl: " << params["url"] if params["url"]
42
+ message << "\nLine: " << params["line"] if params["line"]
43
+ message << "\nColumn: " << params["column"] if params["column"]
44
+ message << "\nWindow Location: " << params["window_location"] if params["window_location"]
45
+
46
+ backtrace = params["stacktrace"] || ""
47
+ Logster.store.report(::Logger::Severity::WARN,
48
+ "javascript",
49
+ message,
50
+ backtrace: backtrace,
51
+ env: env)
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -1,220 +1,220 @@
1
- require 'json'
2
-
3
- module Logster
4
- module Middleware
5
- class Viewer
6
-
7
- PATH_INFO = "PATH_INFO".freeze
8
- SCRIPT_NAME = "SCRIPT_NAME".freeze
9
- REQUEST_METHOD = "REQUEST_METHOD".freeze
10
-
11
- def initialize(app)
12
- @app = app
13
-
14
- @logs_path = Logster.config.subdirectory
15
- @path_regex = Regexp.new("^(#{@logs_path}$)|^(#{@logs_path}(/.*))$")
16
- @store = Logster.store or raise ArgumentError.new("store")
17
-
18
- @assets_path = File.expand_path("../../../../assets", __FILE__)
19
- @fileserver = Rack::File.new(@assets_path)
20
- end
21
-
22
- def call(env)
23
- path = env[PATH_INFO]
24
- script_name = env[SCRIPT_NAME]
25
-
26
- if script_name && script_name.length > 0
27
- path = script_name + path
28
- end
29
-
30
- if resource = resolve_path(path)
31
- if resource =~ /\.ico$|\.js$|\.png|\.handlebars$|\.css$|\.woff$|\.ttf$|\.woff2$|\.svg$|\.otf$|\.eot$/
32
- serve_file(env, resource)
33
-
34
- elsif resource.start_with?("/messages.json")
35
- serve_messages(Rack::Request.new(env))
36
-
37
- elsif resource =~ /\/message\/([0-9a-f]+)$/
38
- if env[REQUEST_METHOD] != "DELETE"
39
- return [405, {}, ["GET not allowed for /clear"]]
40
- end
41
-
42
- key = $1
43
- message = Logster.store.get(key)
44
- unless message
45
- return [404, {}, ["Message not found"]]
46
- end
47
-
48
- Logster.store.delete(message)
49
- return [301, {"Location" => "#{@logs_path}/"}, []]
50
-
51
- elsif resource =~ /\/(un)?protect\/([0-9a-f]+)$/
52
- off = $1 == "un"
53
- key = $2
54
-
55
- message = Logster.store.get(key)
56
- unless message
57
- return [404, {}, ["Message not found"]]
58
- end
59
-
60
- if off
61
- if Logster.store.unprotect(key)
62
- return [301, {"Location" => "#{@logs_path}/show/#{key}?protected=false"}, []]
63
- else
64
- return [500, {}, ["Failed"]]
65
- end
66
- else
67
- if Logster.store.protect(key)
68
- return [301, {"Location" => "#{@logs_path}/show/#{key}?protected=true"}, []]
69
- else
70
- return [500, {}, ["Failed"]]
71
- end
72
- end
73
-
74
- elsif resource =~ /\/solve\/([0-9a-f]+)$/
75
- key = $1
76
-
77
- message = Logster.store.get(key)
78
- unless message
79
- return [404, {}, ["Message not found"]]
80
- end
81
-
82
- Logster.store.solve(key)
83
-
84
- return [301, {"Location" => "#{@logs_path}"}, []]
85
-
86
- elsif resource =~ /\/clear$/
87
- if env[REQUEST_METHOD] != "POST"
88
- return [405, {}, ["GET not allowed for /clear"]]
89
- end
90
- Logster.store.clear
91
- return [200, {}, ["Messages cleared"]]
92
-
93
- elsif resource =~ /\/show\/([0-9a-f]+)(\.json)?$/
94
- key = $1
95
- json = $2 == ".json"
96
-
97
- message = Logster.store.get(key)
98
- unless message
99
- return [404, {}, ["Message not found"]]
100
- end
101
-
102
- if json
103
- [200, {"Content-Type" => "application/json; charset=utf-8"}, [message.to_json]]
104
- else
105
- preload = preload_json({"/show/#{key}" => message})
106
- [200, {"Content-Type" => "text/html; charset=utf-8"}, [body(preload)]]
107
- end
108
-
109
- elsif resource == "/"
110
- [200, {"Content-Type" => "text/html; charset=utf-8"}, [body(preload_json)]]
111
-
112
- else
113
- [404, {}, ["Not found"]]
114
- end
115
- else
116
- @app.call(env)
117
- end
118
- end
119
-
120
- protected
121
-
122
- def serve_file(env, path)
123
- env[PATH_INFO] = path
124
- # accl redirect is going to be trouble, ensure its bypassed
125
- env['sendfile.type'] = ''
126
- @fileserver.call(env)
127
- end
128
-
129
- def serve_messages(req)
130
- params = req.params
131
-
132
- opts = {
133
- before: params["before"],
134
- after: params["after"]
135
- }
136
-
137
- if(filter = params["filter"])
138
- filter = filter.split("_").map{|s| s.to_i}
139
- opts[:severity] = filter
140
- end
141
-
142
- if search = params["search"]
143
- search = (parse_regex(search) || search) if params["regex_search"] == "true"
144
- opts[:search] = search
145
- end
146
-
147
- payload = {
148
- messages: @store.latest(opts),
149
- total: @store.count,
150
- search: params['search'] || '',
151
- filter: filter || '',
152
- }
153
-
154
- json = JSON.generate(payload)
155
- [200, {"Content-Type" => "application/json"}, [json]]
156
- end
157
-
158
- def parse_regex(string)
159
- if string =~ /\/(.+)\/(.*)/
160
- s = $1
161
- flags = Regexp::IGNORECASE if $2 && $2.include?("i")
162
- Regexp.new(s, flags) rescue nil
163
- end
164
- end
165
-
166
- def resolve_path(path)
167
- if path =~ @path_regex
168
- $3 || "/"
169
- end
170
- end
171
-
172
- def preload_json(extra={})
173
- values = {}
174
- values.merge!(extra)
175
- end
176
-
177
- def css(name, attrs={})
178
- attrs = attrs.map do |k,v|
179
- "#{k}='#{v}'"
180
- end.join(" ")
181
-
182
- "<link rel='stylesheet' type='text/css' href='#{@logs_path}/stylesheets/#{name}' #{attrs}>"
183
- end
184
-
185
- def script(prod, dev=nil)
186
- name = ENV['DEBUG_JS'] == "1" && dev ? dev : prod
187
- "<script src='#{@logs_path}/javascript/#{name}'></script>"
188
- end
189
-
190
- def to_json_and_escape(payload)
191
- Rack::Utils.escape_html(JSON.fast_generate(payload))
192
- end
193
-
194
- def body(preload)
195
- root_url = @logs_path
196
- root_url += "/" if root_url[-1] != "/"
197
- <<~HTML
198
- <!doctype html>
199
- <html>
200
- <head>
201
- <link rel="shortcut icon" href="#{@logs_path}/images/icon_64x64.png">
202
- <link rel="apple-touch-icon" href="#{@logs_path}/images/icon_144x144.png" />
203
- <title>#{Logster.config.web_title || "Logs"}</title>
204
- <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
205
- <link href='//fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
206
- #{css("vendor.css")}
207
- #{css("client-app.css")}
208
- #{script("vendor.js")}
209
- <meta id="preloaded-data" data-root-path="#{@logs_path}" data-preloaded="#{to_json_and_escape(preload)}">
210
- <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%7D%2C%22APP%22%3A%7B%22name%22%3A%22client-app%22%2C%22version%22%3A%220.0.0+8c60a18b%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D" />
211
- </head>
212
- <body>
213
- #{script("client-app.js")}
214
- </body>
215
- </html>
216
- HTML
217
- end
218
- end
219
- end
220
- end
1
+ require 'json'
2
+
3
+ module Logster
4
+ module Middleware
5
+ class Viewer
6
+
7
+ PATH_INFO = "PATH_INFO".freeze
8
+ SCRIPT_NAME = "SCRIPT_NAME".freeze
9
+ REQUEST_METHOD = "REQUEST_METHOD".freeze
10
+
11
+ def initialize(app)
12
+ @app = app
13
+
14
+ @logs_path = Logster.config.subdirectory
15
+ @path_regex = Regexp.new("^(#{@logs_path}$)|^(#{@logs_path}(/.*))$")
16
+ @store = Logster.store or raise ArgumentError.new("store")
17
+
18
+ @assets_path = File.expand_path("../../../../assets", __FILE__)
19
+ @fileserver = Rack::File.new(@assets_path)
20
+ end
21
+
22
+ def call(env)
23
+ path = env[PATH_INFO]
24
+ script_name = env[SCRIPT_NAME]
25
+
26
+ if script_name && script_name.length > 0
27
+ path = script_name + path
28
+ end
29
+
30
+ if resource = resolve_path(path)
31
+ if resource =~ /\.ico$|\.js$|\.png|\.handlebars$|\.css$|\.woff$|\.ttf$|\.woff2$|\.svg$|\.otf$|\.eot$/
32
+ serve_file(env, resource)
33
+
34
+ elsif resource.start_with?("/messages.json")
35
+ serve_messages(Rack::Request.new(env))
36
+
37
+ elsif resource =~ /\/message\/([0-9a-f]+)$/
38
+ if env[REQUEST_METHOD] != "DELETE"
39
+ return [405, {}, ["GET not allowed for /clear"]]
40
+ end
41
+
42
+ key = $1
43
+ message = Logster.store.get(key)
44
+ unless message
45
+ return [404, {}, ["Message not found"]]
46
+ end
47
+
48
+ Logster.store.delete(message)
49
+ return [301, {"Location" => "#{@logs_path}/"}, []]
50
+
51
+ elsif resource =~ /\/(un)?protect\/([0-9a-f]+)$/
52
+ off = $1 == "un"
53
+ key = $2
54
+
55
+ message = Logster.store.get(key)
56
+ unless message
57
+ return [404, {}, ["Message not found"]]
58
+ end
59
+
60
+ if off
61
+ if Logster.store.unprotect(key)
62
+ return [301, {"Location" => "#{@logs_path}/show/#{key}?protected=false"}, []]
63
+ else
64
+ return [500, {}, ["Failed"]]
65
+ end
66
+ else
67
+ if Logster.store.protect(key)
68
+ return [301, {"Location" => "#{@logs_path}/show/#{key}?protected=true"}, []]
69
+ else
70
+ return [500, {}, ["Failed"]]
71
+ end
72
+ end
73
+
74
+ elsif resource =~ /\/solve\/([0-9a-f]+)$/
75
+ key = $1
76
+
77
+ message = Logster.store.get(key)
78
+ unless message
79
+ return [404, {}, ["Message not found"]]
80
+ end
81
+
82
+ Logster.store.solve(key)
83
+
84
+ return [301, {"Location" => "#{@logs_path}"}, []]
85
+
86
+ elsif resource =~ /\/clear$/
87
+ if env[REQUEST_METHOD] != "POST"
88
+ return [405, {}, ["GET not allowed for /clear"]]
89
+ end
90
+ Logster.store.clear
91
+ return [200, {}, ["Messages cleared"]]
92
+
93
+ elsif resource =~ /\/show\/([0-9a-f]+)(\.json)?$/
94
+ key = $1
95
+ json = $2 == ".json"
96
+
97
+ message = Logster.store.get(key)
98
+ unless message
99
+ return [404, {}, ["Message not found"]]
100
+ end
101
+
102
+ if json
103
+ [200, {"Content-Type" => "application/json; charset=utf-8"}, [message.to_json]]
104
+ else
105
+ preload = preload_json({"/show/#{key}" => message})
106
+ [200, {"Content-Type" => "text/html; charset=utf-8"}, [body(preload)]]
107
+ end
108
+
109
+ elsif resource == "/"
110
+ [200, {"Content-Type" => "text/html; charset=utf-8"}, [body(preload_json)]]
111
+
112
+ else
113
+ [404, {}, ["Not found"]]
114
+ end
115
+ else
116
+ @app.call(env)
117
+ end
118
+ end
119
+
120
+ protected
121
+
122
+ def serve_file(env, path)
123
+ env[PATH_INFO] = path
124
+ # accl redirect is going to be trouble, ensure its bypassed
125
+ env['sendfile.type'] = ''
126
+ @fileserver.call(env)
127
+ end
128
+
129
+ def serve_messages(req)
130
+ params = req.params
131
+
132
+ opts = {
133
+ before: params["before"],
134
+ after: params["after"]
135
+ }
136
+
137
+ if(filter = params["filter"])
138
+ filter = filter.split("_").map{|s| s.to_i}
139
+ opts[:severity] = filter
140
+ end
141
+
142
+ if search = params["search"]
143
+ search = (parse_regex(search) || search) if params["regex_search"] == "true"
144
+ opts[:search] = search
145
+ end
146
+
147
+ payload = {
148
+ messages: @store.latest(opts),
149
+ total: @store.count,
150
+ search: params['search'] || '',
151
+ filter: filter || '',
152
+ }
153
+
154
+ json = JSON.generate(payload)
155
+ [200, {"Content-Type" => "application/json"}, [json]]
156
+ end
157
+
158
+ def parse_regex(string)
159
+ if string =~ /\/(.+)\/(.*)/
160
+ s = $1
161
+ flags = Regexp::IGNORECASE if $2 && $2.include?("i")
162
+ Regexp.new(s, flags) rescue nil
163
+ end
164
+ end
165
+
166
+ def resolve_path(path)
167
+ if path =~ @path_regex
168
+ $3 || "/"
169
+ end
170
+ end
171
+
172
+ def preload_json(extra={})
173
+ values = {}
174
+ values.merge!(extra)
175
+ end
176
+
177
+ def css(name, attrs={})
178
+ attrs = attrs.map do |k,v|
179
+ "#{k}='#{v}'"
180
+ end.join(" ")
181
+
182
+ "<link rel='stylesheet' type='text/css' href='#{@logs_path}/stylesheets/#{name}' #{attrs}>"
183
+ end
184
+
185
+ def script(prod, dev=nil)
186
+ name = ENV['DEBUG_JS'] == "1" && dev ? dev : prod
187
+ "<script src='#{@logs_path}/javascript/#{name}'></script>"
188
+ end
189
+
190
+ def to_json_and_escape(payload)
191
+ Rack::Utils.escape_html(JSON.fast_generate(payload))
192
+ end
193
+
194
+ def body(preload)
195
+ root_url = @logs_path
196
+ root_url += "/" if root_url[-1] != "/"
197
+ <<~HTML
198
+ <!doctype html>
199
+ <html>
200
+ <head>
201
+ <link rel="shortcut icon" href="#{@logs_path}/images/icon_64x64.png">
202
+ <link rel="apple-touch-icon" href="#{@logs_path}/images/icon_144x144.png" />
203
+ <title>#{Logster.config.web_title || "Logs"}</title>
204
+ <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
205
+ <link href='//fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
206
+ #{css("vendor.css")}
207
+ #{css("client-app.css")}
208
+ #{script("vendor.js")}
209
+ <meta id="preloaded-data" data-root-path="#{@logs_path}" data-preloaded="#{to_json_and_escape(preload)}">
210
+ <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%7D%2C%22APP%22%3A%7B%22name%22%3A%22client-app%22%2C%22version%22%3A%220.0.0+8c60a18b%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D" />
211
+ </head>
212
+ <body>
213
+ #{script("client-app.js")}
214
+ </body>
215
+ </html>
216
+ HTML
217
+ end
218
+ end
219
+ end
220
+ end