logster 2.1.1 → 2.1.2

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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -19
  3. data/.rubocop.yml +1 -1
  4. data/.travis.yml +16 -16
  5. data/CHANGELOG.md +172 -169
  6. data/Gemfile +4 -4
  7. data/Guardfile +8 -8
  8. data/LICENSE.txt +22 -22
  9. data/README.md +99 -99
  10. data/Rakefile +21 -21
  11. data/assets/fonts/FontAwesome.otf +0 -0
  12. data/assets/fonts/fontawesome-webfont.eot +0 -0
  13. data/assets/fonts/fontawesome-webfont.svg +639 -639
  14. data/assets/fonts/fontawesome-webfont.ttf +0 -0
  15. data/assets/fonts/fontawesome-webfont.woff +0 -0
  16. data/assets/fonts/fontawesome-webfont.woff2 +0 -0
  17. data/assets/images/Icon-144_rounded.png +0 -0
  18. data/assets/images/Icon-144_square.png +0 -0
  19. data/assets/images/icon_144x144.png +0 -0
  20. data/assets/images/icon_64x64.png +0 -0
  21. data/assets/javascript/client-app.js +106 -100
  22. data/assets/stylesheets/client-app.css +1 -1
  23. data/build_client_app.sh +0 -0
  24. data/client-app/.editorconfig +20 -20
  25. data/client-app/.ember-cli +9 -9
  26. data/client-app/.eslintignore +19 -19
  27. data/client-app/.eslintrc.js +46 -46
  28. data/client-app/.gitignore +23 -23
  29. data/client-app/.travis.yml +27 -27
  30. data/client-app/.watchmanconfig +3 -3
  31. data/client-app/README.md +57 -57
  32. data/client-app/app/app.js +0 -0
  33. data/client-app/app/components/actions-menu.js +43 -37
  34. data/client-app/app/components/env-tab.js +80 -44
  35. data/client-app/app/components/message-info.js +0 -0
  36. data/client-app/app/components/message-row.js +0 -0
  37. data/client-app/app/components/panel-resizer.js +0 -0
  38. data/client-app/app/components/tab-contents.js +27 -27
  39. data/client-app/app/components/tabbed-section.js +0 -0
  40. data/client-app/app/components/time-formatter.js +0 -0
  41. data/client-app/app/components/update-time.js +0 -0
  42. data/client-app/app/controllers/index.js +0 -0
  43. data/client-app/app/controllers/show.js +0 -0
  44. data/client-app/app/index.html +29 -29
  45. data/client-app/app/initializers/app-init.js +67 -72
  46. data/client-app/app/lib/preload.js +20 -14
  47. data/client-app/app/lib/utilities.js +149 -140
  48. data/client-app/app/models/message-collection.js +0 -0
  49. data/client-app/app/models/message.js +100 -100
  50. data/client-app/app/resolver.js +0 -0
  51. data/client-app/app/router.js +0 -0
  52. data/client-app/app/routes/index.js +0 -0
  53. data/client-app/app/routes/show.js +0 -0
  54. data/client-app/app/styles/app.css +527 -521
  55. data/client-app/app/templates/application.hbs +2 -2
  56. data/client-app/app/templates/components/actions-menu.hbs +12 -12
  57. data/client-app/app/templates/components/env-tab.hbs +10 -10
  58. data/client-app/app/templates/components/message-info.hbs +41 -41
  59. data/client-app/app/templates/components/message-row.hbs +15 -15
  60. data/client-app/app/templates/components/panel-resizer.hbs +3 -3
  61. data/client-app/app/templates/components/tabbed-section.hbs +10 -10
  62. data/client-app/app/templates/components/time-formatter.hbs +1 -1
  63. data/client-app/app/templates/index.hbs +58 -58
  64. data/client-app/app/templates/show.hbs +7 -7
  65. data/client-app/config/environment.js +51 -51
  66. data/client-app/config/optional-features.json +3 -3
  67. data/client-app/config/targets.js +18 -18
  68. data/client-app/ember-cli-build.js +29 -29
  69. data/client-app/package-lock.json +11365 -11365
  70. data/client-app/package.json +56 -56
  71. data/client-app/testem.js +25 -25
  72. data/client-app/tests/index.html +34 -34
  73. data/client-app/tests/integration/components/env-tab-test.js +123 -73
  74. data/client-app/tests/integration/components/message-info-test.js +111 -26
  75. data/client-app/tests/test-helper.js +8 -8
  76. data/client-app/tests/unit/controllers/index-test.js +12 -12
  77. data/client-app/tests/unit/controllers/show-test.js +12 -12
  78. data/client-app/tests/unit/initializers/app-init-test.js +31 -31
  79. data/client-app/tests/unit/routes/index-test.js +11 -11
  80. data/client-app/tests/unit/routes/show-test.js +11 -11
  81. data/lib/examples/sidekiq_logster_reporter.rb +21 -21
  82. data/lib/logster.rb +54 -54
  83. data/lib/logster/base_store.rb +141 -141
  84. data/lib/logster/configuration.rb +26 -25
  85. data/lib/logster/defer_logger.rb +14 -14
  86. data/lib/logster/ignore_pattern.rb +65 -65
  87. data/lib/logster/logger.rb +113 -113
  88. data/lib/logster/message.rb +212 -212
  89. data/lib/logster/middleware/debug_exceptions.rb +26 -26
  90. data/lib/logster/middleware/reporter.rb +55 -55
  91. data/lib/logster/middleware/viewer.rb +222 -221
  92. data/lib/logster/rails/railtie.rb +63 -63
  93. data/lib/logster/redis_store.rb +566 -566
  94. data/lib/logster/scheduler.rb +54 -54
  95. data/lib/logster/version.rb +3 -3
  96. data/lib/logster/web.rb +14 -14
  97. data/logster.gemspec +35 -35
  98. data/test/examples/test_sidekiq_reporter_example.rb +46 -46
  99. data/test/fake_data/Gemfile +4 -4
  100. data/test/fake_data/generate.rb +10 -10
  101. data/test/logster/middleware/test_reporter.rb +19 -19
  102. data/test/logster/middleware/test_viewer.rb +96 -96
  103. data/test/logster/test_base_store.rb +147 -147
  104. data/test/logster/test_defer_logger.rb +34 -34
  105. data/test/logster/test_ignore_pattern.rb +41 -41
  106. data/test/logster/test_logger.rb +86 -86
  107. data/test/logster/test_message.rb +119 -119
  108. data/test/logster/test_redis_rate_limiter.rb +230 -230
  109. data/test/logster/test_redis_store.rb +720 -720
  110. data/test/test_helper.rb +38 -38
  111. data/vendor/assets/javascripts/logster.js.erb +39 -39
  112. metadata +1 -10
  113. data/client-app/app/components/tab-link.js +0 -5
  114. data/client-app/tests/integration/components/actions-menu-test.js +0 -26
  115. data/client-app/tests/integration/components/message-row-test.js +0 -26
  116. data/client-app/tests/integration/components/panel-resizer-test.js +0 -26
  117. data/client-app/tests/integration/components/tab-contents-test.js +0 -26
  118. data/client-app/tests/integration/components/tab-link-test.js +0 -26
  119. data/client-app/tests/integration/components/tabbed-section-test.js +0 -26
  120. data/client-app/tests/integration/components/time-formatter-test.js +0 -26
  121. data/client-app/tests/integration/components/update-time-test.js +0 -26
@@ -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,55 +1,55 @@
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
- path = env[PATH_INFO]
17
- script_name = env[SCRIPT_NAME]
18
-
19
- if script_name && script_name.length > 0
20
- path = script_name + path
21
- end
22
-
23
- if path == @error_path
24
- Logster.config.current_context.call(env) do
25
- report_js_error(env)
26
- end
27
- return [200, {}, ["OK"]]
28
- end
29
-
30
- @app.call(env)
31
- ensure
32
- Thread.current[Logster::Logger::LOGSTER_ENV] = nil
33
- end
34
-
35
- def report_js_error(env)
36
- req = Rack::Request.new(env)
37
- params = req.params
38
-
39
- message = params["message"] || ""
40
- message << "\nUrl: " << params["url"] if params["url"]
41
- message << "\nLine: " << params["line"] if params["line"]
42
- message << "\nColumn: " << params["column"] if params["column"]
43
- message << "\nWindow Location: " << params["window_location"] if params["window_location"]
44
-
45
- backtrace = params["stacktrace"] || ""
46
- Logster.store.report(::Logger::Severity::WARN,
47
- "javascript",
48
- message,
49
- backtrace: backtrace,
50
- env: env)
51
- end
52
-
53
- end
54
- end
55
- 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
+ path = env[PATH_INFO]
17
+ script_name = env[SCRIPT_NAME]
18
+
19
+ if script_name && script_name.length > 0
20
+ path = script_name + path
21
+ end
22
+
23
+ if path == @error_path
24
+ Logster.config.current_context.call(env) do
25
+ report_js_error(env)
26
+ end
27
+ return [200, {}, ["OK"]]
28
+ end
29
+
30
+ @app.call(env)
31
+ ensure
32
+ Thread.current[Logster::Logger::LOGSTER_ENV] = nil
33
+ end
34
+
35
+ def report_js_error(env)
36
+ req = Rack::Request.new(env)
37
+ params = req.params
38
+
39
+ message = params["message"] || ""
40
+ message << "\nUrl: " << params["url"] if params["url"]
41
+ message << "\nLine: " << params["line"] if params["line"]
42
+ message << "\nColumn: " << params["column"] if params["column"]
43
+ message << "\nWindow Location: " << params["window_location"] if params["window_location"]
44
+
45
+ backtrace = params["stacktrace"] || ""
46
+ Logster.store.report(::Logger::Severity::WARN,
47
+ "javascript",
48
+ message,
49
+ backtrace: backtrace,
50
+ env: env)
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -1,221 +1,222 @@
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) || 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
- <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes">
207
- #{css("vendor.css")}
208
- #{css("client-app.css")}
209
- #{script("vendor.js")}
210
- <meta id="preloaded-data" data-root-path="#{@logs_path}" data-preloaded="#{to_json_and_escape(preload)}">
211
- <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" />
212
- </head>
213
- <body>
214
- #{script("client-app.js")}
215
- </body>
216
- </html>
217
- HTML
218
- end
219
- end
220
- end
221
- 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) || 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
+ preload.merge!(env_expandable_keys: Logster.config.env_expandable_keys)
198
+ <<~HTML
199
+ <!doctype html>
200
+ <html>
201
+ <head>
202
+ <link rel="shortcut icon" href="#{@logs_path}/images/icon_64x64.png">
203
+ <link rel="apple-touch-icon" href="#{@logs_path}/images/icon_144x144.png" />
204
+ <title>#{Logster.config.web_title || "Logs"}</title>
205
+ <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
206
+ <link href='//fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
207
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes">
208
+ #{css("vendor.css")}
209
+ #{css("client-app.css")}
210
+ #{script("vendor.js")}
211
+ <meta id="preloaded-data" data-root-path="#{@logs_path}" data-preloaded="#{to_json_and_escape(preload)}">
212
+ <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" />
213
+ </head>
214
+ <body>
215
+ #{script("client-app.js")}
216
+ </body>
217
+ </html>
218
+ HTML
219
+ end
220
+ end
221
+ end
222
+ end