logster 2.1.2 → 2.2.0

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +21 -19
  3. data/.rubocop.yml +1 -1
  4. data/.travis.yml +16 -16
  5. data/CHANGELOG.md +224 -172
  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 +115 -106
  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 -43
  34. data/client-app/app/components/env-tab.js +80 -80
  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/patterns-list.js +109 -0
  39. data/client-app/app/components/tab-contents.js +27 -27
  40. data/client-app/app/components/tabbed-section.js +0 -0
  41. data/client-app/app/components/time-formatter.js +0 -0
  42. data/client-app/app/components/update-time.js +0 -0
  43. data/client-app/app/controllers/index.js +22 -6
  44. data/client-app/app/controllers/show.js +0 -0
  45. data/client-app/app/helpers/logster-url.js +12 -0
  46. data/client-app/app/helpers/or.js +7 -0
  47. data/client-app/app/index.html +30 -29
  48. data/client-app/app/initializers/app-init.js +67 -67
  49. data/client-app/app/lib/preload.js +20 -20
  50. data/client-app/app/lib/utilities.js +150 -149
  51. data/client-app/app/models/message-collection.js +0 -0
  52. data/client-app/app/models/message.js +100 -100
  53. data/client-app/app/models/pattern-item.js +25 -0
  54. data/client-app/app/resolver.js +0 -0
  55. data/client-app/app/router.js +1 -0
  56. data/client-app/app/routes/index.js +2 -9
  57. data/client-app/app/routes/settings.js +15 -0
  58. data/client-app/app/routes/show.js +0 -0
  59. data/client-app/app/styles/app.css +633 -527
  60. data/client-app/app/templates/application.hbs +2 -2
  61. data/client-app/app/templates/components/actions-menu.hbs +12 -12
  62. data/client-app/app/templates/components/env-tab.hbs +10 -10
  63. data/client-app/app/templates/components/message-info.hbs +41 -41
  64. data/client-app/app/templates/components/message-row.hbs +15 -15
  65. data/client-app/app/templates/components/panel-resizer.hbs +3 -3
  66. data/client-app/app/templates/components/patterns-list.hbs +25 -0
  67. data/client-app/app/templates/components/tabbed-section.hbs +10 -10
  68. data/client-app/app/templates/components/time-formatter.hbs +1 -1
  69. data/client-app/app/templates/index.hbs +65 -58
  70. data/client-app/app/templates/settings.hbs +26 -0
  71. data/client-app/app/templates/show.hbs +7 -7
  72. data/client-app/config/environment.js +51 -51
  73. data/client-app/config/optional-features.json +3 -3
  74. data/client-app/config/targets.js +18 -18
  75. data/client-app/ember-cli-build.js +29 -29
  76. data/client-app/package-lock.json +11357 -11365
  77. data/client-app/package.json +57 -56
  78. data/client-app/public/assets/images/icon_144x144.png +0 -0
  79. data/client-app/public/assets/images/icon_64x64.png +0 -0
  80. data/client-app/testem.js +25 -25
  81. data/client-app/tests/index.html +34 -34
  82. data/client-app/tests/integration/components/env-tab-test.js +134 -123
  83. data/client-app/tests/integration/components/message-info-test.js +111 -111
  84. data/client-app/tests/integration/components/patterns-list-test.js +56 -0
  85. data/client-app/tests/test-helper.js +8 -8
  86. data/client-app/tests/unit/controllers/index-test.js +12 -12
  87. data/client-app/tests/unit/controllers/show-test.js +12 -12
  88. data/client-app/tests/unit/initializers/app-init-test.js +31 -31
  89. data/client-app/tests/unit/routes/index-test.js +11 -11
  90. data/client-app/tests/unit/routes/show-test.js +11 -11
  91. data/lib/examples/sidekiq_logster_reporter.rb +21 -21
  92. data/lib/logster.rb +59 -54
  93. data/lib/logster/base_store.rb +169 -141
  94. data/lib/logster/cache.rb +20 -0
  95. data/lib/logster/configuration.rb +27 -26
  96. data/lib/logster/defer_logger.rb +14 -14
  97. data/lib/logster/ignore_pattern.rb +65 -65
  98. data/lib/logster/logger.rb +113 -113
  99. data/lib/logster/message.rb +212 -212
  100. data/lib/logster/middleware/debug_exceptions.rb +26 -26
  101. data/lib/logster/middleware/reporter.rb +55 -55
  102. data/lib/logster/middleware/viewer.rb +297 -222
  103. data/lib/logster/pattern.rb +95 -0
  104. data/lib/logster/rails/railtie.rb +63 -63
  105. data/lib/logster/redis_store.rb +584 -566
  106. data/lib/logster/scheduler.rb +54 -54
  107. data/lib/logster/suppression_pattern.rb +17 -0
  108. data/lib/logster/version.rb +3 -3
  109. data/lib/logster/web.rb +14 -14
  110. data/logster.gemspec +35 -35
  111. data/test/examples/test_sidekiq_reporter_example.rb +46 -46
  112. data/test/fake_data/Gemfile +4 -4
  113. data/test/fake_data/generate.rb +10 -10
  114. data/test/logster/middleware/test_reporter.rb +19 -19
  115. data/test/logster/middleware/test_viewer.rb +267 -96
  116. data/test/logster/test_base_store.rb +147 -147
  117. data/test/logster/test_cache.rb +38 -0
  118. data/test/logster/test_defer_logger.rb +34 -34
  119. data/test/logster/test_ignore_pattern.rb +41 -41
  120. data/test/logster/test_logger.rb +100 -86
  121. data/test/logster/test_message.rb +119 -119
  122. data/test/logster/test_pattern.rb +152 -0
  123. data/test/logster/test_redis_rate_limiter.rb +230 -230
  124. data/test/logster/test_redis_store.rb +689 -720
  125. data/test/test_helper.rb +38 -38
  126. data/vendor/assets/javascripts/logster.js.erb +39 -39
  127. metadata +24 -7
@@ -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,222 +1,297 @@
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
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 method_not_allowed("DELETE is needed 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 method_not_allowed("POST is needed 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 =~ /\/settings(\.json)?$/
110
+ json = $1 == ".json"
111
+ if json
112
+ coded_patterns = Logster.store.ignore&.map(&:inspect) || []
113
+ custom_patterns = Logster::SuppressionPattern.find_all(raw: true)
114
+ [200, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.generate(coded_patterns: coded_patterns, custom_patterns: custom_patterns)]]
115
+ else
116
+ [200, { "Content-Type" => "text/html; charset=utf-8" }, [body(preload_json)]]
117
+ end
118
+ elsif resource =~ /\/patterns\/([a-zA-Z0-9_]+)\.json$/
119
+ unless Logster.config.enable_custom_patterns_via_ui
120
+ 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")
121
+ end
122
+
123
+ set_name = $1
124
+ req = Rack::Request.new(env)
125
+ return method_not_allowed if req.request_method == "GET"
126
+
127
+ update_patterns(set_name, req)
128
+ elsif resource == "/"
129
+ [200, { "Content-Type" => "text/html; charset=utf-8" }, [body(preload_json)]]
130
+ else
131
+ not_found
132
+ end
133
+ else
134
+ @app.call(env)
135
+ end
136
+ end
137
+
138
+ protected
139
+
140
+ def serve_file(env, path)
141
+ env[PATH_INFO] = path
142
+ # accl redirect is going to be trouble, ensure its bypassed
143
+ env['sendfile.type'] = ''
144
+ @fileserver.call(env)
145
+ end
146
+
147
+ def serve_messages(req)
148
+ params = req.params
149
+
150
+ opts = {
151
+ before: params["before"],
152
+ after: params["after"]
153
+ }
154
+
155
+ if (filter = params["filter"])
156
+ filter = filter.split("_").map { |s| s.to_i }
157
+ opts[:severity] = filter
158
+ end
159
+
160
+ if search = params["search"]
161
+ search = (parse_regex(search) || search) if params["regex_search"] == "true"
162
+ opts[:search] = search
163
+ end
164
+
165
+ payload = {
166
+ messages: @store.latest(opts),
167
+ total: @store.count,
168
+ search: params['search'] || '',
169
+ filter: filter || '',
170
+ }
171
+
172
+ json = JSON.generate(payload)
173
+ [200, { "Content-Type" => "application/json" }, [json]]
174
+ end
175
+
176
+ def update_patterns(set_name, req)
177
+ klass = get_class(set_name)
178
+ return not_found("Unknown set name") unless klass
179
+
180
+ request_method = req.request_method
181
+ pattern = req.params["pattern"]
182
+
183
+ record = request_method == "POST" ? klass.new(pattern) : klass.find(pattern)
184
+ return not_found unless record
185
+
186
+ case request_method
187
+ when "POST"
188
+ record.save
189
+ when "PUT"
190
+ record.modify(req.params["new_pattern"])
191
+ when "DELETE"
192
+ record.destroy
193
+ else
194
+ return method_not_allowed("Allowed methods: POST, PUT or DELETE")
195
+ end
196
+
197
+ [200, { "Content-Type" => "application/json" }, [JSON.generate(pattern: record.to_s)]]
198
+ rescue => err
199
+ error_message = err.message
200
+
201
+ unless Logster::Pattern::PatternError === err # likely a bug, give us the backtrace
202
+ error_message += "\n\n#{err.backtrace.join("\n")}"
203
+ return [500, {}, [error_message]]
204
+ end
205
+
206
+ [400, {}, [error_message]]
207
+ end
208
+
209
+ def get_class(set_name)
210
+ case set_name
211
+ when "suppression"
212
+ Logster::SuppressionPattern
213
+ else
214
+ nil
215
+ end
216
+ end
217
+
218
+ def not_found(message = "Not found")
219
+ [404, {}, [message]]
220
+ end
221
+
222
+ def not_allowed(message = "Not allowed")
223
+ [403, {}, [message]]
224
+ end
225
+
226
+ def method_not_allowed(message = "Method not allowed")
227
+ [405, {}, [message]]
228
+ end
229
+
230
+ def parse_regex(string)
231
+ if string =~ /\/(.+)\/(.*)/
232
+ s = $1
233
+ flags = Regexp::IGNORECASE if $2 && $2.include?("i")
234
+ Regexp.new(s, flags) rescue nil
235
+ end
236
+ end
237
+
238
+ def resolve_path(path)
239
+ if path =~ @path_regex
240
+ $3 || "/"
241
+ end
242
+ end
243
+
244
+ def preload_json(extra = {})
245
+ values = {}
246
+ values.merge!(extra)
247
+ end
248
+
249
+ def css(name, attrs = {})
250
+ attrs = attrs.map do |k, v|
251
+ "#{k}='#{v}'"
252
+ end.join(" ")
253
+
254
+ "<link rel='stylesheet' type='text/css' href='#{@logs_path}/stylesheets/#{name}' #{attrs}>"
255
+ end
256
+
257
+ def script(prod, dev = nil)
258
+ name = ENV['DEBUG_JS'] == "1" && dev ? dev : prod
259
+ "<script src='#{@logs_path}/javascript/#{name}'></script>"
260
+ end
261
+
262
+ def to_json_and_escape(payload)
263
+ Rack::Utils.escape_html(JSON.fast_generate(payload))
264
+ end
265
+
266
+ def body(preload)
267
+ root_url = @logs_path
268
+ root_url += "/" if root_url[-1] != "/"
269
+ preload.merge!(
270
+ env_expandable_keys: Logster.config.env_expandable_keys,
271
+ patterns_enabled: Logster.config.enable_custom_patterns_via_ui
272
+ )
273
+ <<~HTML
274
+ <!doctype html>
275
+ <html>
276
+ <head>
277
+ <link rel="shortcut icon" href="#{@logs_path}/images/icon_64x64.png">
278
+ <link rel="apple-touch-icon" href="#{@logs_path}/images/icon_144x144.png" />
279
+ <title>#{Logster.config.web_title || "Logs"}</title>
280
+ <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
281
+ <link href='//fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
282
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes">
283
+ #{css("vendor.css")}
284
+ #{css("client-app.css")}
285
+ #{script("vendor.js")}
286
+ <meta id="preloaded-data" data-root-path="#{@logs_path}" data-preloaded="#{to_json_and_escape(preload)}">
287
+ <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" />
288
+ </head>
289
+ <body>
290
+ #{script("client-app.js")}
291
+ </body>
292
+ </html>
293
+ HTML
294
+ end
295
+ end
296
+ end
297
+ end