Skip to content

Commit

Permalink
FEATURE: Solve All button for messages grouped by pattern (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
OsamaSayegh committed Jan 13, 2020
1 parent eed389c commit 0e45201
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 66 deletions.
35 changes: 17 additions & 18 deletions assets/javascript/client-app.js

Large diffs are not rendered by default.

41 changes: 38 additions & 3 deletions client-app/app/components/message-info.js
@@ -1,13 +1,16 @@
import Component from "@ember/component";
import { computed } from "@ember/object";
import Preload from "client-app/lib/preload";
import { bool } from "@ember/object/computed";

export default Component.extend({
buttons: computed("currentMessage.{canSolve,protected}", function() {
const canSolve = this.get("currentMessage.canSolve");
showSolveAllButton: bool("currentRow.group"),

buttons: computed("currentMessage.protected", "showSolveButton", function() {
const protect = this.get("currentMessage.protected");
const buttons = [];

if (!protect && canSolve) {
if (!protect && this.showSolveButton) {
buttons.push({
klass: "solve",
action: "solve",
Expand All @@ -17,6 +20,16 @@ export default Component.extend({
});
}

if (this.showSolveAllButton) {
buttons.push({
klass: "solve-all",
action: "solveAll",
icon: "check-square-o",
label: "Solve All",
danger: true
});
}

if (!protect) {
buttons.push(
{
Expand Down Expand Up @@ -45,6 +58,20 @@ export default Component.extend({
return buttons;
}),

showSolveButton: computed(
"showSolveAllButton",
"currentMessage.{canSolve,env}",
function() {
if (this.showSolveAllButton) return false;
// env isn't loaded until you switch to the env tab
// so if we don't have env we show the button if
// application_version is provided in the config
return this.currentMessage.env
? this.currentMessage.canSolve
: !!Preload.get("application_version");
}
),

actions: {
tabChanged(newTab) {
if (this.onTabChange) {
Expand All @@ -55,15 +82,23 @@ export default Component.extend({
protect() {
this.get("currentMessage").protect();
},

unprotect() {
this.get("currentMessage").unprotect();
},

remove() {
this.removeMessage(this.get("currentMessage"));
},

solve() {
this.solveMessage(this.get("currentMessage"));
},

solveAll() {
this.currentRow.solveAll();
},

share() {
window.location.pathname = this.get("currentMessage.shareUrl");
}
Expand Down
7 changes: 6 additions & 1 deletion client-app/app/models/group.js
@@ -1,6 +1,7 @@
import Message from "client-app/models/message";
import { default as EmberObject, computed } from "@ember/object";
import { reads } from "@ember/object/computed";
import { ajax } from "client-app/lib/utilities";

export default EmberObject.extend({
selected: false,
Expand All @@ -16,5 +17,9 @@ export default EmberObject.extend({

glyph: computed(function() {
return "<i class='fa fa-clone group'></i>";
})
}),

solveAll() {
return ajax("/solve-group", { type: "POST", data: { regex: this.regex } });
}
});
4 changes: 1 addition & 3 deletions client-app/app/models/message-collection.js
Expand Up @@ -90,9 +90,7 @@ export default EmberObject.extend({
const message = this.currentMessage;
if (message && !message.env && this.currentTab === "env") {
this.set("loadingEnv", true);
return ajax(`/fetch-env/${message.key}.json`)
.then(env => message.set("env", env))
.always(() => this.set("loadingEnv", false));
return message.fetchEnv().always(() => this.set("loadingEnv", false));
}
},

Expand Down
41 changes: 21 additions & 20 deletions client-app/app/models/message.js
Expand Up @@ -5,48 +5,51 @@ import { computed } from "@ember/object";
export default Em.Object.extend({
MAX_LEN: 200,

fetchEnv() {
return ajax(`/fetch-env/${this.key}.json`).then(env =>
this.set("env", env)
);
},

expand() {
this.set("expanded", true);
},

solve() {
return ajax("/solve/" + this.get("key"), { type: "PUT" });
return ajax(`/solve/${this.key}`, { type: "PUT" });
},

destroy() {
return ajax("/message/" + this.get("key"), { type: "DELETE" });
return ajax(`/message/${this.key}`, { type: "DELETE" });
},

protect() {
this.set("protected", true);
return ajax("/protect/" + this.get("key"), { type: "PUT" });
return ajax(`/protect/${this.key}`, { type: "PUT" });
},

unprotect() {
this.set("protected", false);
return ajax("/unprotect/" + this.get("key"), { type: "DELETE" });
return ajax(`/unprotect/${this.key}`, { type: "DELETE" });
},

showCount: computed("count", function() {
return this.get("count") > 1;
return this.count > 1;
}),

hasMore: computed("message", "expanded", function() {
const message = this.get("message");
const expanded = this.get("expanded");

return !expanded && message.length > this.MAX_LEN;
return !this.expanded && this.message.length > this.MAX_LEN;
}),

shareUrl: computed("key", function() {
return Preload.get("rootPath") + "/show/" + this.get("key");
return `${Preload.get("rootPath")}/show/${this.key}`;
}),

displayMessage: computed("message", "expanded", function() {
let message = this.get("message");
const expanded = this.get("expanded");
let message = this.message;

if (!expanded && message.length > this.MAX_LEN) {
message = message.substr(0, this.MAX_LEN);
if (!this.expanded && this.message.length > this.MAX_LEN) {
message = this.message.substr(0, this.MAX_LEN);
}
return message;
}),
Expand All @@ -57,15 +60,13 @@ export default Em.Object.extend({
},

canSolve: computed("env.{application_version,length}", function() {
const backtrace = this.get("backtrace");
const env = this.get("env");
const appVersion = Array.isArray(env)
? env
const appVersion = Array.isArray(this.env)
? this.env
.map(e => e.application_version)
.compact()
.join("")
: env && env.application_version;
return appVersion && backtrace && backtrace.length > 0;
: this.env && this.env.application_version;
return appVersion && this.backtrace && this.backtrace.length > 0;
}),

rowClass: computed("severity", function() {
Expand Down
1 change: 1 addition & 0 deletions client-app/app/templates/index.hbs
Expand Up @@ -24,6 +24,7 @@
{{/if}}
{{message-info
currentMessage=model.currentMessage
currentRow=model.currentRow
loadingEnv=model.loadingEnv
removeMessage=(action "removeMessage")
solveMessage=(action "solveMessage")
Expand Down
5 changes: 3 additions & 2 deletions lib/logster/base_store.rb
Expand Up @@ -201,6 +201,7 @@ def report(severity, progname, msg, opts = {})

message.drop_redundant_envs(Logster.config.max_env_count_per_message)
message.apply_env_size_limit(Logster.config.max_env_bytes)
saved = true
if similar
similar.merge_similar_message(message)
replace_and_bump(similar)
Expand All @@ -210,13 +211,13 @@ def report(severity, progname, msg, opts = {})
Logster.config.maximum_message_size_bytes,
gems_dir: Logster.config.gems_dir
)
save message
saved = save(message)
message
end

message = similar || message

if Logster.config.enable_custom_patterns_via_ui || allow_custom_patterns
if (Logster.config.enable_custom_patterns_via_ui || allow_custom_patterns) && saved
grouping_patterns = @patterns_cache.fetch(Logster::GroupingPattern::CACHE_KEY) do
Logster::GroupingPattern.find_all(store: self)
end
Expand Down
11 changes: 5 additions & 6 deletions lib/logster/message.rb
Expand Up @@ -132,12 +132,11 @@ def solved_keys
if Array === env
versions = env.map { |single_env| single_env["application_version"] }
else
versions = env["application_version"]
versions = [env["application_version"]]
end
versions.compact!

if versions && backtrace && backtrace.length > 0
versions = [versions] if String === versions

if backtrace && backtrace.length > 0
versions.map do |version|
Digest::SHA1.hexdigest "#{version} #{backtrace}"
end
Expand Down Expand Up @@ -286,10 +285,10 @@ def truncate_env(env, limit)
if JSON.fast_generate(env).bytesize > limit
sizes = {}
braces = '{}'.bytesize
env.each do |k,v|
env.each do |k, v|
sizes[k] = JSON.fast_generate(k => v).bytesize - braces
end
sorted = env.keys.sort { |a,b| sizes[a] <=> sizes[b] }
sorted = env.keys.sort { |a, b| sizes[a] <=> sizes[b] }

kept_keys = []
if env.key?(:time)
Expand Down
33 changes: 23 additions & 10 deletions lib/logster/middleware/viewer.rb
Expand Up @@ -38,7 +38,7 @@ def call(env)

elsif resource =~ /\/message\/([0-9a-f]+)$/
if env[REQUEST_METHOD] != "DELETE"
return method_not_allowed("DELETE is needed for /clear")
return method_not_allowed("DELETE")
end

key = $1
Expand Down Expand Up @@ -87,7 +87,7 @@ def call(env)

elsif resource =~ /\/clear$/
if env[REQUEST_METHOD] != "POST"
return method_not_allowed("POST is needed for /clear")
return method_not_allowed("POST")
end
Logster.store.clear
return [200, {}, ["Messages cleared"]]
Expand Down Expand Up @@ -139,12 +139,12 @@ def call(env)

set_name = $1
req = Rack::Request.new(env)
return method_not_allowed if req.request_method == "GET"
return method_not_allowed(%w[POST PUT DELETE]) if req.request_method == "GET"

update_patterns(set_name, req)
elsif resource == "/reset-count.json"
req = Rack::Request.new(env)
return method_not_allowed("PUT is needed for this endpoint") if req.request_method != "PUT"
return method_not_allowed("PUT") if req.request_method != "PUT"
pattern = nil
if [true, "true"].include?(req.params["hard"])
pattern = Logster.store.ignore.find do |patt|
Expand All @@ -170,6 +170,16 @@ def call(env)
else
not_found
end
elsif resource == '/solve-group'
return not_allowed unless Logster.config.enable_custom_patterns_via_ui
req = Rack::Request.new(env)
return method_not_allowed("POST") if req.request_method != "POST"
group = Logster.store.find_pattern_groups do |patt|
patt.inspect == req.params["regex"]
end.first
return not_found("No such pattern group exists") if !group
group.messages_keys.each { |k| Logster.store.solve(k) }
return [200, {}, []]
else
not_found
end
Expand Down Expand Up @@ -243,7 +253,7 @@ def update_patterns(set_name, req)
when "DELETE"
record.destroy
else
return method_not_allowed("Allowed methods: POST, PUT or DELETE")
return method_not_allowed(%w[POST PUT DELETE])
end

[200, { "Content-Type" => "application/json" }, [JSON.generate(pattern: record.to_s)]]
Expand Down Expand Up @@ -277,8 +287,11 @@ def not_allowed(message = "Not allowed")
[403, {}, [message]]
end

def method_not_allowed(message = "Method not allowed")
[405, {}, [message]]
def method_not_allowed(allowed_methods)
if Array === allowed_methods
allowed_methods = allowed_methods.join(", ")
end
[405, { "Allow" => allowed_methods }, []]
end

def parse_regex(string)
Expand Down Expand Up @@ -327,8 +340,7 @@ def preload_backtrace_data
end
{
gems_data: gems_data,
directories: Logster.config.project_directories,
application_version: Logster.config.application_version
directories: Logster.config.project_directories
}
end

Expand All @@ -337,7 +349,8 @@ def body(preload)
root_url += "/" if root_url[-1] != "/"
preload.merge!(
env_expandable_keys: Logster.config.env_expandable_keys,
patterns_enabled: Logster.config.enable_custom_patterns_via_ui
patterns_enabled: Logster.config.enable_custom_patterns_via_ui,
application_version: Logster.config.application_version
)
backtrace_links_enabled = Logster.config.enable_backtrace_links
gems_dir = Logster.config.gems_dir
Expand Down
2 changes: 1 addition & 1 deletion lib/logster/redis_store.rb
Expand Up @@ -22,7 +22,7 @@ def initialize(redis = nil)
def save(message)
if keys = message.solved_keys
keys.each do |solved|
return true if @redis.hget(solved_key, solved)
return false if @redis.hget(solved_key, solved)
end
end

Expand Down

0 comments on commit 0e45201

Please sign in to comment.