Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Solve All button for messages grouped by pattern #105

Merged
merged 1 commit into from Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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