logster 1.3.4 → 1.4.0.pre

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.
@@ -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