logster 1.3.4 → 1.4.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ {{#if isEnvArray}}
2
+ <div class="nav-controls">
3
+ <button disabled={{disableBackButtons}} {{action "bigJump" "back"}} class="btn nav-btn no-text"><i class="fa fa-fast-backward"></i></button>
4
+ <button disabled={{disableBackButtons}} {{action "takeStep" "back"}} class="btn nav-btn no-text"><i class="fa fa-backward"></i></button>
5
+ <span class="env-number">{{current}}/{{message.env.length}}</span>
6
+ <button disabled={{disableForwardButtons}} {{action "takeStep" "front"}} class="btn nav-btn no-text"><i class="fa fa-forward"></i></button>
7
+ <button disabled={{disableForwardButtons}} {{action "bigJump" "front"}} class="btn nav-btn no-text"><i class="fa fa-fast-forward"></i></button>
8
+ </div>
9
+ {{/if}}
10
+ {{{html}}}
@@ -21,7 +21,7 @@
21
21
  {{#if showTitle}}
22
22
  <h3>Env</h3>
23
23
  {{/if}}
24
- {{{currentMessage.envTable}}}
24
+ {{env-tab message=currentMessage}}
25
25
  {{/tab-contents}}
26
26
  {{/if}}
27
27
  {{/tabbed-section}}
@@ -1,17 +1,15 @@
1
- <td class="count">
1
+ <div class="count">
2
2
  {{#if model.showCount}}
3
- <span>{{model.count}}</span>
3
+ {{model.count}}
4
4
  {{/if}}
5
- </td>
6
- <td class="severity">{{{model.glyph}}}</td>
7
- <td class="message-body">
8
- <div class="message">
9
- {{model.displayMessage}}
10
- </div>
11
- </td>
12
- <td class='protected'>
5
+ </div>
6
+ <div class="severity">{{{model.glyph}}}</div>
7
+ <div class="message-body">
8
+ {{model.displayMessage}}
9
+ </div>
10
+ <div class='protected'>
13
11
  {{#if model.protected}}
14
12
  <i title="message is protected, clearing will not remove it" class='fa fa-lock'></i>
15
13
  {{/if}}
16
- </td>
17
- <td class="time">{{time-formatter timestamp=model.timestamp}}</td>
14
+ </div>
15
+ <div class="time">{{time-formatter timestamp=model.timestamp}}</div>
@@ -1,25 +1,18 @@
1
1
  <div id='top-panel'>
2
- <table id='log-table'>
3
- <thead>
4
- <tr>
5
- <th class="count"></th>
6
- <th class="severity"></th>
7
- <th class="info"></th>
8
- <th class="protected"></th>
9
- <th class="time"></th>
10
- </tr>
11
- </thead>
12
- <tbody>
2
+ <div id="log-table">
13
3
  {{#if model.moreBefore}}
14
- <tr {{action "showMoreBefore"}} class="show-more">
15
- <td colspan=5>select to see {{model.totalBefore}} more</td>
16
- </tr>
4
+ <div {{action "showMoreBefore"}} class="show-more">
5
+ {{#if model.hideCountInLoadMore}}
6
+ Load more
7
+ {{else}}
8
+ Select to see {{model.totalBefore}} more
9
+ {{/if}}
10
+ </div>
17
11
  {{/if}}
18
12
  {{#each model.messages as |message|}}
19
13
  {{message-row model=message selectedMessage=(action "selectMessage")}}
20
14
  {{/each}}
21
- </tbody>
22
- </table>
15
+ </div>
23
16
  </div>
24
17
  <div id="bottom-panel">
25
18
  {{message-info
@@ -0,0 +1,73 @@
1
+ import { module, test } from 'qunit';
2
+ import { setupRenderingTest } from 'ember-qunit';
3
+ import { render, find, findAll, click } from '@ember/test-helpers';
4
+ import hbs from 'htmlbars-inline-precompile';
5
+ import Message from "client-app/models/message";
6
+
7
+ const message = Message.create({
8
+ env: [
9
+ { a: "aa", b: "bb" },
10
+ { c: "cc", d: "dd" }
11
+ ]
12
+ })
13
+
14
+ const message2 = Message.create({
15
+ env: { e: "ee", f: "ff" }
16
+ });
17
+
18
+ function reduceToContent(node) {
19
+ return Array.from(node.childNodes).reduce((ac, cr) => `${ac.textContent}: ${cr.textContent}`);
20
+ }
21
+
22
+ module('Integration | Component | env-tab', function(hooks) {
23
+ setupRenderingTest(hooks);
24
+
25
+ test('it renders', async function(assert) {
26
+ this.set("message", message);
27
+ await render(hbs`{{env-tab message=message}}`);
28
+
29
+ assert.equal(find(".env-number").textContent, "1/2", "shows the current over the total number of env objects");
30
+ let trs = findAll("tr");
31
+ assert.equal(trs.length, 2);
32
+ assert.equal(reduceToContent(trs[0]), 'a: aa', "has the right content");
33
+ assert.equal(reduceToContent(trs[1]), 'b: bb', "has the right content");
34
+
35
+ const buttons = findAll("button.nav-btn");
36
+ // at first page, you can't go back
37
+ assert.ok(buttons[0].disabled, "back buttons are disabled");
38
+ assert.ok(buttons[1].disabled, "back buttons are disabled");
39
+
40
+ assert.notOk(buttons[2].disabled, "forward buttons are not disabled");
41
+ assert.notOk(buttons[3].disabled, "forward buttons are not disabled");
42
+
43
+ this.set("message", message2);
44
+ assert.dom("button").doesNotExist("doesn't show buttons for non-array env");
45
+
46
+ trs = findAll("tr");
47
+ assert.equal(trs.length, 2);
48
+ assert.equal(reduceToContent(trs[0]), 'e: ee', "has the right content");
49
+ assert.equal(reduceToContent(trs[1]), 'f: ff', "has the right content");
50
+ });
51
+
52
+ test('it works correctly', async function(assert) {
53
+ this.set("message", message);
54
+ await render(hbs`{{env-tab message=message}}`);
55
+
56
+ const buttons = findAll("button.nav-btn");
57
+ await click(buttons[2]);
58
+
59
+ assert.equal(find(".env-number").textContent, "2/2", "shows the current over the total number of env objects");
60
+
61
+ const trs = findAll("tr");
62
+ assert.equal(trs.length, 2);
63
+ assert.equal(reduceToContent(trs[0]), 'c: cc', "has the right content");
64
+ assert.equal(reduceToContent(trs[1]), 'd: dd', "has the right content");
65
+
66
+ // at last page, you can't go forward but you can go back
67
+ assert.notOk(buttons[0].disabled, "back buttons are not disabled");
68
+ assert.notOk(buttons[1].disabled, "back buttons are not disabled");
69
+
70
+ assert.ok(buttons[2].disabled, "forward buttons are disabled");
71
+ assert.ok(buttons[3].disabled, "forward buttons are disabled");
72
+ });
73
+ });
@@ -1,4 +1,3 @@
1
-
2
1
  module Logster
3
2
  class BaseStore
4
3
 
@@ -79,12 +78,11 @@ module Logster
79
78
  return if (!msg || (String === msg && msg.empty?)) && skip_empty
80
79
  return if level && severity < level
81
80
 
82
- message = Logster::Message.new(severity, progname, msg, opts[:timestamp])
81
+ message = Logster::Message.new(severity, progname, msg, opts[:timestamp], count: opts[:count])
83
82
 
84
83
  env = opts[:env] || {}
85
84
  backtrace = opts[:backtrace]
86
-
87
- if env[:backtrace]
85
+ if Hash === env && env[:backtrace]
88
86
  # Special - passing backtrace through env
89
87
  backtrace = env.delete(:backtrace)
90
88
  end
@@ -110,7 +108,6 @@ module Logster
110
108
  end
111
109
 
112
110
  if similar
113
- similar.count += 1
114
111
  similar.merge_similar_message(message)
115
112
 
116
113
  replace_and_bump similar
@@ -35,7 +35,7 @@ module Logster
35
35
  when Regexp
36
36
  message.to_s =~ pattern
37
37
  when String
38
- message.to_s.downcase =~ Regexp.new(pattern.downcase, Regexp::IGNORECASE)
38
+ message.to_s =~ Regexp.new(pattern, Regexp::IGNORECASE)
39
39
  when Hash
40
40
  if Hash === message
41
41
  compare_hash(message, pattern)
@@ -57,7 +57,7 @@ module Logster
57
57
  (ol && ol[Thread.current.object_id]) || @level
58
58
  end
59
59
 
60
- def add_with_opts(severity, message, progname, opts=nil, &block)
60
+ def add_with_opts(severity, message, progname=progname(), opts=nil, &block)
61
61
  if severity < level
62
62
  return true
63
63
  end
@@ -23,14 +23,14 @@ module Logster
23
23
 
24
24
  attr_accessor :timestamp, :severity, :progname, :message, :key, :backtrace, :count, :env, :protected, :first_timestamp
25
25
 
26
- def initialize(severity, progname, message, timestamp = nil, key = nil)
26
+ def initialize(severity, progname, message, timestamp = nil, key = nil, count: 1)
27
27
  @timestamp = timestamp || get_timestamp
28
28
  @severity = severity
29
29
  @progname = progname
30
30
  @message = message
31
31
  @key = key || SecureRandom.hex
32
32
  @backtrace = nil
33
- @count = 1
33
+ @count = count || 1
34
34
  @protected = false
35
35
  @first_timestamp = nil
36
36
  end
@@ -80,7 +80,14 @@ module Logster
80
80
 
81
81
  def populate_from_env(env)
82
82
  env ||= {}
83
- @env = Message.populate_from_env(self.class.default_env.merge env)
83
+ if Array === env
84
+ env = env.map do |single_env|
85
+ self.class.default_env.merge(single_env)
86
+ end
87
+ else
88
+ env = self.class.default_env.merge(env)
89
+ end
90
+ @env = Message.populate_from_env(env)
84
91
  end
85
92
 
86
93
  def self.default_env
@@ -104,8 +111,13 @@ module Logster
104
111
 
105
112
  # todo - memoize?
106
113
  def solved_keys
107
- if (versions=env["application_version"]) &&
108
- (backtrace && backtrace.length > 0)
114
+ if Array === env
115
+ versions = env.map { |single_env| single_env["application_version"] }
116
+ else
117
+ versions = env["application_version"]
118
+ end
119
+
120
+ if versions && backtrace && backtrace.length > 0
109
121
  versions = [versions] if String === versions
110
122
 
111
123
  versions.map do |version|
@@ -121,35 +133,49 @@ module Logster
121
133
  def merge_similar_message(other)
122
134
  self.first_timestamp ||= self.timestamp
123
135
  self.timestamp = [self.timestamp,other.timestamp].max
136
+ self.count += other.count || 1
137
+
124
138
  other_env = JSON.load JSON.fast_generate other.env
125
- other_env.keys.each do |env_key|
126
- self.env[env_key] = Message.env_merge_helper(self.env[env_key], other_env[env_key])
139
+ if Array === self.env
140
+ Array === other_env ? self.env.concat(other_env) : self.env << other_env
141
+ else
142
+ Array === other_env ? self.env = [self.env, *other_env] : self.env = [self.env, other_env]
127
143
  end
128
144
  end
129
145
 
130
146
  def self.populate_from_env(env)
147
+ if Array === env
148
+ env.map do |single_env|
149
+ self.populate_env_helper(single_env)
150
+ end
151
+ else
152
+ self.populate_env_helper(env)
153
+ end
154
+ end
155
+
156
+ def self.populate_env_helper(env)
131
157
  env[LOGSTER_ENV] ||= begin
132
- unless env.include? "rack.input"
133
- # Not a web request
134
- return env
135
- end
136
- scrubbed = default_env
137
- request = Rack::Request.new(env)
138
- params = {}
139
- request.params.each do |k,v|
140
- if k.include? "password"
141
- params[k] = "[redacted]"
142
- elsif Array === v
143
- params[k] = v[0..20]
144
- else
145
- params[k] = v && v[0..100]
146
- end
158
+ unless env.include? "rack.input"
159
+ # Not a web request
160
+ return env
161
+ end
162
+ scrubbed = default_env
163
+ request = Rack::Request.new(env)
164
+ params = {}
165
+ request.params.each do |k,v|
166
+ if k.include? "password"
167
+ params[k] = "[redacted]"
168
+ elsif Array === v
169
+ params[k] = v[0..20]
170
+ else
171
+ params[k] = v && v[0..100]
147
172
  end
148
- scrubbed["params"] = params if params.length > 0
149
- ALLOWED_ENV.map{ |k|
150
- scrubbed[k] = env[k] if env[k]
151
- }
152
- scrubbed
173
+ end
174
+ scrubbed["params"] = params if params.length > 0
175
+ ALLOWED_ENV.map{ |k|
176
+ scrubbed[k] = env[k] if env[k]
177
+ }
178
+ scrubbed
153
179
  end
154
180
  end
155
181
 
@@ -423,18 +423,69 @@ module Logster
423
423
  [start, finish]
424
424
  end
425
425
 
426
+ def get_search(search)
427
+ exclude = false
428
+ if String === search && search[0] == "-"
429
+ exclude = true
430
+ search = search.sub("-", "")
431
+ end
432
+ [search, exclude]
433
+ end
434
+
426
435
  def filter_search(row, search)
436
+ search, exclude = get_search(search)
427
437
  return row unless row && search
428
438
 
429
- if Regexp === search
430
- row if row.message =~ search
431
- elsif search[0] == "-"
432
- exclude = search.sub('-', '')
433
- row unless row.message.include?(exclude)
434
- elsif row.message.include?(search)
435
- row
439
+ if exclude
440
+ row if !(row =~ search) && filter_env!(row, search, exclude)
441
+ else
442
+ row if row =~ search || filter_env!(row, search)
443
+ end
444
+ end
445
+
446
+ def filter_env!(message, search, exclude = false)
447
+ if Array === message.env
448
+ array_env_matches?(message, search, exclude)
449
+ else
450
+ if exclude
451
+ !env_matches?(message.env, search)
452
+ else
453
+ env_matches?(message.env, search)
454
+ end
455
+ end
456
+ end
457
+
458
+ def env_matches?(env, search)
459
+ return false unless env && search
460
+
461
+ env.values.any? do |value|
462
+ if Hash === value
463
+ env_matches?(value, search)
464
+ else
465
+ case search
466
+ when Regexp
467
+ value.to_s =~ search
468
+ when String
469
+ value.to_s =~ Regexp.new(search, Regexp::IGNORECASE)
470
+ else
471
+ false
472
+ end
473
+ end
436
474
  end
475
+ end
437
476
 
477
+ def array_env_matches?(message, search, exclude)
478
+ matches = message.env.select do |env|
479
+ if exclude
480
+ !env_matches?(env, search)
481
+ else
482
+ env_matches?(env, search)
483
+ end
484
+ end
485
+ return false if matches.empty?
486
+ message.env = matches
487
+ message.count = matches.size
488
+ true
438
489
  end
439
490
 
440
491
  def check_rate_limits(severity)
@@ -1,3 +1,3 @@
1
1
  module Logster
2
- VERSION = "1.3.4"
2
+ VERSION = "1.4.0.pre"
3
3
  end
@@ -18,6 +18,43 @@ class TestMessage < MiniTest::Test
18
18
 
19
19
  assert_equal(20, msg1.timestamp)
20
20
  assert_equal(10, msg1.first_timestamp)
21
+
22
+ assert Array === msg1.env
23
+ assert_equal(msg1.env.size, 2)
24
+ assert({ "a" => "1", "b" => "2" } <= msg1.env[0])
25
+ assert({ "a" => "2", "c" => "3" } <= msg1.env[1])
26
+ end
27
+
28
+ def test_merge_messages_both_with_array_envs
29
+ msg1 = Logster::Message.new(0, '', 'test', 10)
30
+ msg1.env = [{ a: "aa", b: "bb" }, { c: "cc", d: "dd" }]
31
+
32
+ msg2 = Logster::Message.new(0, '', 'test', 20)
33
+ msg2.env = [{ e: "ee", f: "ff" }, { g: "gg", h: "hh" }]
34
+
35
+ msg1.merge_similar_message(msg2)
36
+ msg1 = Logster::Message.from_json(msg1.to_json)
37
+
38
+ # new env should be an array, but it should never have
39
+ # another array of envs within itself (hence flatten(1))
40
+ assert_equal(msg1.env.size, 4)
41
+ assert_equal(msg1.env.map(&:keys).flatten(1), %w{a b c d e f g h})
42
+ assert_equal(msg1.env.map(&:values).flatten(1), %w{aa bb cc dd ee ff gg hh})
43
+ end
44
+
45
+ def test_merge_messages_one_with_array_envs
46
+ msg1 = Logster::Message.new(0, '', 'test', 10)
47
+ msg1.env = [{ a: "aa", b: "bb" }, { c: "cc", d: "dd" }]
48
+
49
+ msg2 = Logster::Message.new(0, '', 'test', 20)
50
+ msg2.env = { e: "ee", f: "ff" }
51
+
52
+ msg1.merge_similar_message(msg2)
53
+ msg1 = Logster::Message.from_json(msg1.to_json)
54
+
55
+ assert_equal(msg1.env.size, 3)
56
+ assert_equal(msg1.env.map(&:keys).flatten(1), %w{a b c d e f})
57
+ assert_equal(msg1.env.map(&:values).flatten(1), %w{aa bb cc dd ee ff})
21
58
  end
22
59
 
23
60
  def test_adds_application_version
@@ -31,4 +68,26 @@ class TestMessage < MiniTest::Test
31
68
  Logster.config.application_version = nil
32
69
  end
33
70
 
71
+ def test_merging_sums_count_for_both_messages
72
+ msg1 = Logster::Message.new(0, '', 'test', 10, count: 15)
73
+ msg2 = Logster::Message.new(0, '', 'test', 20, count: 13)
74
+ msg2.env = {}
75
+
76
+ assert_equal(msg1.grouping_key, msg2.grouping_key)
77
+
78
+ msg1.merge_similar_message(msg2)
79
+ msg1 = Logster::Message.from_json(msg1.to_json)
80
+ assert_equal(msg1.count, 15 + 13)
81
+ end
82
+
83
+ def test_populate_from_env_works_on_array
84
+ msg = Logster::Message.new(0, '', 'test', 10)
85
+ hash = { "custom_key" => "key" }
86
+ msg.populate_from_env([hash])
87
+
88
+ msg = Logster::Message.from_json(msg.to_json)
89
+ assert Array === msg.env
90
+ assert_equal(msg.env.size, 1)
91
+ assert hash <= msg.env[0]
92
+ end
34
93
  end