logster 0.8.3 → 0.8.4.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.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/assets/javascript/app.js +75 -27
- data/assets/javascript/components/message-info.handlebars +5 -1
- data/assets/javascript/templates/message.handlebars +5 -1
- data/assets/stylesheets/app.css +24 -4
- data/lib/logster.rb +6 -1
- data/lib/logster/base_store.rb +34 -2
- data/lib/logster/configuration.rb +18 -1
- data/lib/logster/message.rb +70 -0
- data/lib/logster/middleware/reporter.rb +1 -1
- data/lib/logster/middleware/viewer.rb +1 -1
- data/lib/logster/rails/railtie.rb +2 -2
- data/lib/logster/redis_store.rb +28 -3
- data/lib/logster/version.rb +1 -1
- data/logster.gemspec +1 -1
- data/vendor/assets/javascripts/logster.js.erb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 357588b2742ffeef436fe0367522604fccf12668
|
4
|
+
data.tar.gz: 694af6857f570a5ba4a7ceb670407a9a296f29f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14d5ff717da5d8b4c22759846ec5ec71e16a848844762f5417e1769b29de629dfe972c38631b8dad2e31ca357ada9dea387ce63e18c7983027a6dc83a6a9c6cf
|
7
|
+
data.tar.gz: 9453d1b7016dcf744e7d6d79e8ef0eb6b9a2b0df97d6f159ac46fcc2747359762e0a3bdfce2318d55b1b3a29b6b9e4dc6b30850777b0d92ac1faac8f39dda101
|
data/README.md
CHANGED
@@ -25,6 +25,14 @@ constraints lambda { |req| req.session["admin"] } do
|
|
25
25
|
end
|
26
26
|
```
|
27
27
|
|
28
|
+
By default, logster will only run in development and production environments.
|
29
|
+
|
30
|
+
To run logster in other environments, in `config/application.rb`
|
31
|
+
|
32
|
+
```
|
33
|
+
Logster.set_environments([:development, :staging, :production])
|
34
|
+
```
|
35
|
+
|
28
36
|
### Note
|
29
37
|
If you are seeing error
|
30
38
|
'No such middleware to insert before: ActionDispatch::DebugExceptions' after installing logster,
|
@@ -62,7 +70,7 @@ Logster UI is built using [Ember.js](http://emberjs.com/)
|
|
62
70
|
|
63
71
|
## Contributing
|
64
72
|
|
65
|
-
1. Fork it ( https://github.com/
|
73
|
+
1. Fork it ( https://github.com/discourse/logster/fork )
|
66
74
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
67
75
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
68
76
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/assets/javascript/app.js
CHANGED
@@ -50,6 +50,49 @@ App.Router.map(function() {
|
|
50
50
|
this.route("show", { path: "/show/:id" });
|
51
51
|
});
|
52
52
|
|
53
|
+
function buildArrayString(array) {
|
54
|
+
var buffer = [];
|
55
|
+
_.each(array, function(v) {
|
56
|
+
if (v === null) {
|
57
|
+
buffer.push('null');
|
58
|
+
} else if (Object.prototype.toString.call(v) === '[object Array]') {
|
59
|
+
buffer.push(buildArrayString(v));
|
60
|
+
} else {
|
61
|
+
buffer.push(v.toString());
|
62
|
+
}
|
63
|
+
});
|
64
|
+
return '[' + buffer.join(', ') + ']';
|
65
|
+
}
|
66
|
+
|
67
|
+
function buildHashString(hash, indent) {
|
68
|
+
if (!hash) return '';
|
69
|
+
if (!indent) indent = "";
|
70
|
+
|
71
|
+
var buffer = [],
|
72
|
+
hashes = [];
|
73
|
+
_.each(hash, function(v, k) {
|
74
|
+
if (v === null) {
|
75
|
+
buffer.push('null');
|
76
|
+
} else if (Object.prototype.toString.call(v) === '[object Array]') {
|
77
|
+
buffer.push(k + ": " + buildArrayString(v));
|
78
|
+
} else if (typeof v === "object") {
|
79
|
+
hashes.push(k);
|
80
|
+
} else {
|
81
|
+
buffer.push(k + ": " + v);
|
82
|
+
}
|
83
|
+
});
|
84
|
+
|
85
|
+
if (_.size(hashes) > 0) {
|
86
|
+
_.each(hashes, function(k1) {
|
87
|
+
var v = hash[k1];
|
88
|
+
buffer.push("");
|
89
|
+
buffer.push(k1 + ":");
|
90
|
+
buffer.push(buildHashString(v, indent + " "));
|
91
|
+
});
|
92
|
+
}
|
93
|
+
return indent + buffer.join("\n" + indent);
|
94
|
+
}
|
95
|
+
|
53
96
|
App.Message = Ember.Object.extend({
|
54
97
|
|
55
98
|
MAX_LEN: 200,
|
@@ -67,6 +110,10 @@ App.Message = Ember.Object.extend({
|
|
67
110
|
return App.ajax("/unprotect/" + this.get('key'), { type: "DELETE" });
|
68
111
|
},
|
69
112
|
|
113
|
+
showCount: function() {
|
114
|
+
return this.get('count') > 1;
|
115
|
+
}.property('count'),
|
116
|
+
|
70
117
|
hasMore: function() {
|
71
118
|
var message = this.get("message");
|
72
119
|
var expanded = this.get("expanded");
|
@@ -92,32 +139,13 @@ App.Message = Ember.Object.extend({
|
|
92
139
|
return message;
|
93
140
|
}.property("message", "expanded"),
|
94
141
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
hashes = [];
|
100
|
-
_.each(env, function(v, k) {
|
101
|
-
if (typeof v === "object") {
|
102
|
-
hashes.push(k);
|
103
|
-
} else {
|
104
|
-
buffer.push(k + ": " + v);
|
105
|
-
}
|
106
|
-
});
|
107
|
-
|
108
|
-
if (_.size(hashes) > 0) {
|
109
|
-
_.each(hashes, function(k1) {
|
110
|
-
v1 = env[k1];
|
111
|
-
buffer.push("");
|
112
|
-
buffer.push(k1 + ":");
|
113
|
-
_.each(v1, function(v2, k2) {
|
114
|
-
buffer.push(" " + k2 + ": " + v2);
|
115
|
-
})
|
116
|
-
});
|
117
|
-
}
|
118
|
-
return buffer.join("\n");
|
119
|
-
}
|
142
|
+
updateFromObject: function(other) {
|
143
|
+
// XXX Only updatable property is count right now
|
144
|
+
this.set('count', other.get('count'));
|
145
|
+
},
|
120
146
|
|
147
|
+
envDebug: function() {
|
148
|
+
return buildHashString(this.get('env'));
|
121
149
|
}.property("env"),
|
122
150
|
|
123
151
|
rowClass: function() {
|
@@ -154,6 +182,7 @@ App.Message = Ember.Object.extend({
|
|
154
182
|
App.MessageCollection = Em.Object.extend({
|
155
183
|
|
156
184
|
messages: Em.A(),
|
185
|
+
currentMessage: null,
|
157
186
|
total: 0,
|
158
187
|
|
159
188
|
load: function(opts) {
|
@@ -190,6 +219,18 @@ App.MessageCollection = Em.Object.extend({
|
|
190
219
|
if (opts.before) {
|
191
220
|
messages.unshiftObjects(newRows);
|
192
221
|
} else {
|
222
|
+
newRows.forEach(function(nmsg) {
|
223
|
+
messages.forEach(function(emsg, idx) {
|
224
|
+
if (emsg.key == nmsg.key) {
|
225
|
+
messages.removeObject(emsg);
|
226
|
+
if (self.get('currentMessage') === emsg) {
|
227
|
+
// TODO would updateFromJson() work here?
|
228
|
+
self.set('currentMessage', nmsg);
|
229
|
+
nmsg.set('selected', emsg.get('selected'));
|
230
|
+
}
|
231
|
+
}
|
232
|
+
});
|
233
|
+
});
|
193
234
|
messages.addObjects(newRows);
|
194
235
|
}
|
195
236
|
}
|
@@ -293,11 +334,18 @@ App.ShowRoute = Em.Route.extend({
|
|
293
334
|
});
|
294
335
|
|
295
336
|
App.IndexController = Em.Controller.extend({
|
337
|
+
|
338
|
+
currentMessage: Em.computed.alias('model.currentMessage'),
|
339
|
+
|
296
340
|
actions: {
|
297
341
|
expandMessage: function(message){
|
298
342
|
message.expand();
|
299
343
|
},
|
300
344
|
|
345
|
+
selectMessage: function(message) {
|
346
|
+
this.set('currentMessage', message);
|
347
|
+
},
|
348
|
+
|
301
349
|
showMoreBefore: function(){
|
302
350
|
this.get('model').showMoreBefore();
|
303
351
|
},
|
@@ -468,9 +516,9 @@ App.MessageView = Em.View.extend({
|
|
468
516
|
|
469
517
|
classNameBindings: ["context.rowClass", ":message-row", "context.selected:selected"],
|
470
518
|
|
471
|
-
click: function(){
|
519
|
+
click: function() {
|
472
520
|
var old = this.get("controller.currentMessage");
|
473
|
-
if(old){
|
521
|
+
if (old) {
|
474
522
|
old.set("selected",false);
|
475
523
|
}
|
476
524
|
this.set("context.selected", true);
|
@@ -2,7 +2,11 @@
|
|
2
2
|
{{#tabbed-section}}
|
3
3
|
{{#tab-contents name="info" hint="show info" currentMessage=currentMessage}}
|
4
4
|
{{#if showTitle}}
|
5
|
-
<h3>Message
|
5
|
+
<h3>Message
|
6
|
+
{{#if currentMessage.showCount}}
|
7
|
+
({{currentMessage.count}} copies reported)
|
8
|
+
{{/if}}
|
9
|
+
</h3>
|
6
10
|
{{/if}}
|
7
11
|
<pre>{{currentMessage.message}}</pre>
|
8
12
|
{{/tab-contents}}
|
data/assets/stylesheets/app.css
CHANGED
@@ -36,7 +36,7 @@ td.time {
|
|
36
36
|
vertical-align: top;
|
37
37
|
}
|
38
38
|
.message {
|
39
|
-
font-family: Consolas,
|
39
|
+
font-family: Consolas, Monaco, Ubuntu Mono, monospace;
|
40
40
|
font-size: 13px;
|
41
41
|
overflow: hidden;
|
42
42
|
white-space: nowrap;
|
@@ -45,7 +45,7 @@ td.time {
|
|
45
45
|
}
|
46
46
|
|
47
47
|
th.severity{
|
48
|
-
width:
|
48
|
+
width: 35px;
|
49
49
|
}
|
50
50
|
|
51
51
|
th.time{
|
@@ -55,18 +55,38 @@ th.time{
|
|
55
55
|
.fatal{
|
56
56
|
color: #E00;
|
57
57
|
}
|
58
|
+
.fatal .count {
|
59
|
+
background-color: #E00;
|
60
|
+
}
|
58
61
|
|
59
62
|
.error {
|
60
63
|
color: #9F6000;
|
61
64
|
}
|
65
|
+
.error .count {
|
66
|
+
background-color: #9F6000;
|
67
|
+
}
|
62
68
|
|
63
|
-
.warn{
|
69
|
+
.warn {
|
64
70
|
color: #66000C;
|
65
71
|
}
|
72
|
+
.warn .count {
|
73
|
+
background-color: #66000C;
|
74
|
+
}
|
66
75
|
|
67
|
-
.debug{
|
76
|
+
.debug {
|
68
77
|
color: #777;
|
69
78
|
}
|
79
|
+
.debug .count {
|
80
|
+
background-color: #666;
|
81
|
+
}
|
82
|
+
|
83
|
+
.count {
|
84
|
+
color: #FFF;
|
85
|
+
border-radius: 3px;
|
86
|
+
padding: 0 4px;
|
87
|
+
float: right;
|
88
|
+
margin-right: 6px;
|
89
|
+
}
|
70
90
|
|
71
91
|
.action-panel .search input {
|
72
92
|
border: 1px solid #DDD;
|
data/lib/logster.rb
CHANGED
@@ -40,9 +40,14 @@ module Logster
|
|
40
40
|
logster_env = Logster::Message.populate_from_env(env)
|
41
41
|
logster_env[key] = value
|
42
42
|
end
|
43
|
+
|
44
|
+
def self.set_environments(envs)
|
45
|
+
@config.environments = envs
|
46
|
+
end
|
43
47
|
end
|
44
48
|
|
45
|
-
|
49
|
+
# check logster/configuration.rb for config options
|
50
|
+
# Logster.config.environments << :staging
|
46
51
|
|
47
52
|
if defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i >= 3
|
48
53
|
require 'logster/rails/railtie'
|
data/lib/logster/base_store.rb
CHANGED
@@ -10,30 +10,48 @@ module Logster
|
|
10
10
|
@skip_empty = true
|
11
11
|
end
|
12
12
|
|
13
|
+
# Save a new message at the front of the latest list.
|
13
14
|
def save(message)
|
14
15
|
not_implemented
|
15
16
|
end
|
16
17
|
|
18
|
+
# Modify the saved message to the given one (identified by message.key) and bump it to the top of the latest list
|
19
|
+
def replace_and_bump(message)
|
20
|
+
not_implemented
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check if another message with the same grouping_key is already stored.
|
24
|
+
# Returns the similar message's message.key
|
25
|
+
def similar_key(message)
|
26
|
+
not_implemented
|
27
|
+
end
|
28
|
+
|
29
|
+
# The number of messages currently stored.
|
17
30
|
def count
|
18
31
|
not_implemented
|
19
32
|
end
|
20
33
|
|
34
|
+
# Delete all unprotected messages in the store.
|
21
35
|
def clear
|
22
36
|
not_implemented
|
23
37
|
end
|
24
38
|
|
39
|
+
# Delete all messages, including protected messages.
|
25
40
|
def clear_all
|
26
41
|
not_implemented
|
27
42
|
end
|
28
43
|
|
44
|
+
# Get a message by its message_key
|
29
45
|
def get(message_key)
|
30
46
|
not_implemented
|
31
47
|
end
|
32
48
|
|
49
|
+
# Mark a message as protected; i.e. it is not deleted by the #clear method
|
33
50
|
def protect(message_key)
|
34
51
|
not_implemented
|
35
52
|
end
|
36
53
|
|
54
|
+
# Clear the protected mark for a message.
|
37
55
|
def unprotect(message_key)
|
38
56
|
not_implemented
|
39
57
|
end
|
@@ -65,9 +83,23 @@ module Logster
|
|
65
83
|
|
66
84
|
return if ignore && ignore.any? { |pattern| message =~ pattern}
|
67
85
|
|
68
|
-
|
86
|
+
similar = nil
|
69
87
|
|
70
|
-
|
88
|
+
if Logster.config.allow_grouping
|
89
|
+
key = self.similar_key(message)
|
90
|
+
similar = get key if key
|
91
|
+
end
|
92
|
+
|
93
|
+
if similar
|
94
|
+
similar.count += 1
|
95
|
+
similar.merge_similar_message(message)
|
96
|
+
|
97
|
+
replace_and_bump similar
|
98
|
+
similar
|
99
|
+
else
|
100
|
+
save message
|
101
|
+
message
|
102
|
+
end
|
71
103
|
end
|
72
104
|
|
73
105
|
private
|
@@ -1,5 +1,22 @@
|
|
1
1
|
module Logster
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :current_context, :allow_grouping, :environments
|
4
|
+
attr_writer :subdirectory
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
# lambda |env,block|
|
8
|
+
@current_context = lambda{ |_, &block| block.call }
|
9
|
+
@environments = [:development, :production]
|
10
|
+
@subdirectory = nil
|
11
|
+
|
12
|
+
@allow_grouping = false
|
13
|
+
if defined?(::Rails) && Rails.env.production?
|
14
|
+
@allow_grouping = true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def subdirectory
|
19
|
+
@subdirectory || '/logs'
|
20
|
+
end
|
4
21
|
end
|
5
22
|
end
|
data/lib/logster/message.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
1
3
|
module Logster
|
4
|
+
|
5
|
+
MAX_GROUPING_LENGTH = 50
|
6
|
+
|
2
7
|
class Message
|
3
8
|
LOGSTER_ENV = "_logster_env".freeze
|
4
9
|
ALLOWED_ENV = %w{
|
@@ -69,6 +74,26 @@ module Logster
|
|
69
74
|
@env = Message.populate_from_env(env)
|
70
75
|
end
|
71
76
|
|
77
|
+
# in its own method so it can be overridden
|
78
|
+
def grouping_hash
|
79
|
+
return { message: self.message, severity: self.severity, backtrace: self.backtrace }
|
80
|
+
end
|
81
|
+
|
82
|
+
# todo - memoize?
|
83
|
+
def grouping_key
|
84
|
+
Digest::SHA1.hexdigest JSON.fast_generate grouping_hash
|
85
|
+
end
|
86
|
+
|
87
|
+
def is_similar?(other)
|
88
|
+
self.grouping_key == other.grouping_key
|
89
|
+
end
|
90
|
+
|
91
|
+
def merge_similar_message(other)
|
92
|
+
other_env = JSON.load JSON.fast_generate other.env
|
93
|
+
other_env.keys.each do |env_key|
|
94
|
+
self.env[env_key] = Message.env_merge_helper(self.env[env_key], other_env[env_key])
|
95
|
+
end
|
96
|
+
end
|
72
97
|
|
73
98
|
def self.populate_from_env(env)
|
74
99
|
env[LOGSTER_ENV] ||= begin
|
@@ -82,6 +107,8 @@ module Logster
|
|
82
107
|
request.params.each do |k,v|
|
83
108
|
if k.include? "password"
|
84
109
|
params[k] = "[redacted]"
|
110
|
+
elsif Array === v
|
111
|
+
params[k] = v[0..20]
|
85
112
|
else
|
86
113
|
params[k] = v && v[0..100]
|
87
114
|
end
|
@@ -121,5 +148,48 @@ module Logster
|
|
121
148
|
def get_timestamp
|
122
149
|
(Time.new.to_f * 1000).to_i
|
123
150
|
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def self.env_merge_helper(self_value, other_value)
|
155
|
+
other_value = other_value.to_s if Symbol === other_value
|
156
|
+
|
157
|
+
if (Hash === self_value || self_value.nil?) && (Hash === other_value || other_value.nil?) && (!self_value.nil? || !other_value.nil?)
|
158
|
+
# one or both is a hash but not neither -> recurse on the keys
|
159
|
+
self_value = {} unless self_value
|
160
|
+
other_value = {} unless other_value
|
161
|
+
shared_keys = self_value.keys | (other_value.keys rescue [])
|
162
|
+
shared_keys.each do |key|
|
163
|
+
self_value[key] = env_merge_helper(self_value[key], other_value[key])
|
164
|
+
end
|
165
|
+
self_value
|
166
|
+
elsif self_value.is_a?(Array) && !other_value.is_a?(Array)
|
167
|
+
# Already have grouped data, so append to array (it's actually a set)
|
168
|
+
self_value << other_value unless self_value.include?(other_value) || self_value.length >= Logster::MAX_GROUPING_LENGTH
|
169
|
+
self_value
|
170
|
+
elsif !self_value.is_a?(Array)
|
171
|
+
if self_value == other_value
|
172
|
+
self_value
|
173
|
+
else
|
174
|
+
[self_value, other_value]
|
175
|
+
end
|
176
|
+
else
|
177
|
+
# They're both arrays.
|
178
|
+
# Three cases:
|
179
|
+
# self = [1,2,3] and other = [1,2,4] -> make into array of array
|
180
|
+
# self = [] and other = [1,2,4] -> make into array of array
|
181
|
+
# self = [[1,2,3], [1,2,5]] and other = [1,2,4] -> append to array
|
182
|
+
if self_value.length > 0 && self_value[0].is_a?(Array)
|
183
|
+
self_value << other_value unless self_value.include?(other_value) || self_value.length >= Logster::MAX_GROUPING_LENGTH
|
184
|
+
self_value
|
185
|
+
else
|
186
|
+
if self_value == other_value
|
187
|
+
self_value
|
188
|
+
else
|
189
|
+
[self_value, other_value]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
124
194
|
end
|
125
195
|
end
|
@@ -11,7 +11,7 @@ module Logster
|
|
11
11
|
def initialize(app)
|
12
12
|
@app = app
|
13
13
|
|
14
|
-
@logs_path = Logster.config.subdirectory
|
14
|
+
@logs_path = Logster.config.subdirectory
|
15
15
|
@path_regex = Regexp.new("^(#{@logs_path}$)|^(#{@logs_path}(/.*))$")
|
16
16
|
@store = Logster.store or raise ArgumentError.new("store")
|
17
17
|
|
@@ -5,7 +5,7 @@ module Logster::Rails
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def self.set_logger(config)
|
8
|
-
return unless
|
8
|
+
return unless Logster.config.environments.include?(Rails.env.to_sym)
|
9
9
|
|
10
10
|
require 'logster/middleware/debug_exceptions'
|
11
11
|
require 'logster/middleware/reporter'
|
@@ -22,7 +22,7 @@ module Logster::Rails
|
|
22
22
|
|
23
23
|
|
24
24
|
def self.initialize!(app)
|
25
|
-
return unless
|
25
|
+
return unless Logster.config.environments.include?(Rails.env.to_sym)
|
26
26
|
|
27
27
|
if Logster::Logger === Rails.logger
|
28
28
|
app.middleware.insert_before ActionDispatch::ShowExceptions, Logster::Middleware::Reporter
|
data/lib/logster/redis_store.rb
CHANGED
@@ -13,21 +13,41 @@ module Logster
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def save(message)
|
16
|
-
#
|
16
|
+
# TODO this whole method should be atomic lol, but it hasn't bitten me yet
|
17
17
|
@redis.multi do
|
18
18
|
@redis.hset(hash_key, message.key, message.to_json)
|
19
|
+
@redis.hset(grouping_key, message.grouping_key, message.key)
|
19
20
|
@redis.rpush(list_key, message.key)
|
20
21
|
end
|
21
22
|
|
22
|
-
# TODO make it atomic
|
23
23
|
if @redis.llen(list_key) > max_backlog
|
24
24
|
removed_key = @redis.lpop(list_key)
|
25
25
|
if removed_key && !@redis.sismember(protected_key, removed_key)
|
26
|
-
|
26
|
+
rmsg = get removed_key
|
27
|
+
@redis.hdel(hash_key, rmsg.key)
|
28
|
+
@redis.hdel(grouping_key, rmsg.grouping_key)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
33
|
+
def replace_and_bump(message)
|
34
|
+
# TODO make it atomic
|
35
|
+
exists = @redis.hexists(hash_key, message.key)
|
36
|
+
return false unless exists
|
37
|
+
|
38
|
+
@redis.multi do
|
39
|
+
@redis.hset(hash_key, message.key, message.to_json)
|
40
|
+
@redis.lrem(list_key, -1, message.key)
|
41
|
+
@redis.rpush(list_key, message.key)
|
42
|
+
end
|
43
|
+
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def similar_key(message)
|
48
|
+
@redis.hget(grouping_key, message.grouping_key)
|
49
|
+
end
|
50
|
+
|
31
51
|
def count
|
32
52
|
@redis.llen(list_key)
|
33
53
|
end
|
@@ -108,6 +128,7 @@ module Logster
|
|
108
128
|
@redis.del(list_key)
|
109
129
|
@redis.del(protected_key)
|
110
130
|
@redis.del(hash_key)
|
131
|
+
@redis.del(grouping_key)
|
111
132
|
end
|
112
133
|
|
113
134
|
def get(message_key)
|
@@ -233,5 +254,9 @@ module Logster
|
|
233
254
|
def protected_key
|
234
255
|
@saved_key ||= "__LOGSTER__SAVED"
|
235
256
|
end
|
257
|
+
|
258
|
+
def grouping_key
|
259
|
+
@grouping_key ||= "__LOGSTER__GMAP"
|
260
|
+
end
|
236
261
|
end
|
237
262
|
end
|
data/lib/logster/version.rb
CHANGED
data/logster.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["sam.saffron@gmail.com"]
|
11
11
|
spec.summary = %q{UI for viewing logs in Rack}
|
12
12
|
spec.description = %q{UI for viewing logs in Rack}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/discourse/logster"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject{|f| f.start_with?("bower_components") || f.start_with?("website") || f.start_with?("bin") }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.4.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- UI for viewing logs in Rack
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06
|
11
|
+
date: 2015-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -148,7 +148,7 @@ files:
|
|
148
148
|
- test/logster/test_redis_store.rb
|
149
149
|
- test/test_helper.rb
|
150
150
|
- vendor/assets/javascripts/logster.js.erb
|
151
|
-
homepage:
|
151
|
+
homepage: https://github.com/discourse/logster
|
152
152
|
licenses:
|
153
153
|
- MIT
|
154
154
|
metadata: {}
|
@@ -163,9 +163,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
163
163
|
version: '0'
|
164
164
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
165
|
requirements:
|
166
|
-
- - "
|
166
|
+
- - ">"
|
167
167
|
- !ruby/object:Gem::Version
|
168
|
-
version:
|
168
|
+
version: 1.3.1
|
169
169
|
requirements: []
|
170
170
|
rubyforge_project:
|
171
171
|
rubygems_version: 2.4.5
|