From 0e45201123fc8289f0339f33f5c27e7b72a9a94d Mon Sep 17 00:00:00 2001 From: Osama Sayegh Date: Mon, 13 Jan 2020 07:16:23 +0300 Subject: [PATCH] FEATURE: Solve All button for messages grouped by pattern (#105) --- assets/javascript/client-app.js | 35 ++++--- client-app/app/components/message-info.js | 41 +++++++- client-app/app/models/group.js | 7 +- client-app/app/models/message-collection.js | 4 +- client-app/app/models/message.js | 41 ++++---- client-app/app/templates/index.hbs | 1 + lib/logster/base_store.rb | 5 +- lib/logster/message.rb | 11 +-- lib/logster/middleware/viewer.rb | 33 +++++-- lib/logster/redis_store.rb | 2 +- test/logster/middleware/test_viewer.rb | 100 ++++++++++++++++++++ test/logster/test_redis_store.rb | 4 +- 12 files changed, 218 insertions(+), 66 deletions(-) diff --git a/assets/javascript/client-app.js b/assets/javascript/client-app.js index 384b316e..13b9f19f 100644 --- a/assets/javascript/client-app.js +++ b/assets/javascript/client-app.js @@ -15,8 +15,8 @@ return f&&(i=r({repo:f.url,path:c,filename:d,lineNumber:p})),i},GithubURLForApp: if(!s())return o var l=n.default.get("directories").filter(function(t){return a(e,t.path)}).sortBy("path.length").reverse()[0] if(l){var u,c,d,p=i(l.path),f=e.substring(p.length),m="",h=-1!==f.indexOf("/"),v=h?/(.+\/)(.+):(\d+)(:.*)/:/(.+):(\d+)(:.*)/ -if(h){var g=f.match(v)||[],b=(0,t.default)(g,5) -m=b[1],u=b[2],c=b[3],d=b[4]}else{var y=f.match(v)||[],E=(0,t.default)(y,4) +if(h){var b=f.match(v)||[],g=(0,t.default)(b,5) +m=g[1],u=g[2],c=g[3],d=g[4]}else{var y=f.match(v)||[],E=(0,t.default)(y,4) u=E[1],c=E[2],d=E[3]}if(u&&c&&d){var x=l.main_app?this.commitSha:null o=r({repo:l.url,path:m,filename:u,lineNumber:c,commitSha:x})}}return o},findGithubURL:function(e,t){return a(e,n.default.get("gems_dir"))?this.GithubURLForGem(t):this.GithubURLForApp(e)},commitSha:Ember.computed("env",function(){var e=null return Array.isArray(this.env)?e=this.env.map(function(e){return e.application_version}).filter(function(e){return e})[0]:this.env&&(e=this.env.application_version),e||n.default.get("application_version")}),lines:Ember.computed("backtrace","commitSha",function(){var e=this @@ -28,10 +28,10 @@ var a=Ember.Component.extend({didUpdateAttrs:function(){this.set("expanded",null if(this.isEnvArray){var a=Em.$.extend({},this.currentEnv) return(n.default.get("env_expandable_keys")||[]).forEach(function(t){if(a.hasOwnProperty(t)&&!Array.isArray(a[t])){var n=[a[t]] e.message.env.forEach(function(e){e[t]&&-1===n.indexOf(e[t])&&n.push(e[t])}),a[t]=n.length>1?n:n[0]}}),(0,t.buildHashString)(a,!1,this.expanded||[])}return(0,t.buildHashString)(this.get("message.env"))}),click:function(e){var t=Em.$(e.target),a=t.attr("data-key");-1!==(n.default.get("env_expandable_keys")||[]).indexOf(a)&&t.hasClass("expand-list")&&(e.preventDefault(),this.expanded?this.expanded.pushObject(a):this.set("expanded",[a]))}}) -e.default=a}),define("client-app/components/message-info",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 -var t=Ember.Component.extend({buttons:Ember.computed("currentMessage.{canSolve,protected}",function(){var e=this.get("currentMessage.canSolve"),t=this.get("currentMessage.protected"),n=[] -return!t&&e&&n.push({klass:"solve",action:"solve",icon:"check-square-o",label:"Solve",danger:!0}),t?n.push({klass:"unprotect",action:"unprotect",icon:"unlock",label:"Unprotect"}):n.push({klass:"remove",action:"remove",icon:"trash-o",label:"Remove",danger:!0},{klass:"protect",action:"protect",icon:"lock",label:"Protect"}),n}),actions:{tabChanged:function(e){this.onTabChange&&this.onTabChange(e)},protect:function(){this.get("currentMessage").protect()},unprotect:function(){this.get("currentMessage").unprotect()},remove:function(){this.removeMessage(this.get("currentMessage"))},solve:function(){this.solveMessage(this.get("currentMessage"))},share:function(){window.location.pathname=this.get("currentMessage.shareUrl")}}}) -e.default=t}),define("client-app/components/message-row",["exports"],function(e){var t,n +e.default=a}),define("client-app/components/message-info",["exports","client-app/lib/preload"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 +var n=Ember.Component.extend({showSolveAllButton:Ember.computed.bool("currentRow.group"),buttons:Ember.computed("currentMessage.protected","showSolveButton",function(){var e=this.get("currentMessage.protected"),t=[] +return!e&&this.showSolveButton&&t.push({klass:"solve",action:"solve",icon:"check-square-o",label:"Solve",danger:!0}),this.showSolveAllButton&&t.push({klass:"solve-all",action:"solveAll",icon:"check-square-o",label:"Solve All",danger:!0}),e?t.push({klass:"unprotect",action:"unprotect",icon:"unlock",label:"Unprotect"}):t.push({klass:"remove",action:"remove",icon:"trash-o",label:"Remove",danger:!0},{klass:"protect",action:"protect",icon:"lock",label:"Protect"}),t}),showSolveButton:Ember.computed("showSolveAllButton","currentMessage.{canSolve,env}",function(){return!this.showSolveAllButton&&(this.currentMessage.env?this.currentMessage.canSolve:!!t.default.get("application_version"))}),actions:{tabChanged:function(e){this.onTabChange&&this.onTabChange(e)},protect:function(){this.get("currentMessage").protect()},unprotect:function(){this.get("currentMessage").unprotect()},remove:function(){this.removeMessage(this.get("currentMessage"))},solve:function(){this.solveMessage(this.get("currentMessage"))},solveAll:function(){this.currentRow.solveAll()},share:function(){window.location.pathname=this.get("currentMessage.shareUrl")}}}) +e.default=n}),define("client-app/components/message-row",["exports"],function(e){var t,n Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 var a=Ember.Component.extend({tagName:"div",classNameBindings:["model.rowClass",":message-row","model.selected:selected"],click:function(){this.selectRow()},willInsertElement:function(){if(!t){var e=Em.$("#top-panel"),a=e.scrollTop(),s=e.height(),i=e[0].scrollHeight n=i-20"+r.join("\n")+""} var a,s,i,r={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"} function o(e){return String(e).replace(/[&<>"'\/]/g,function(e){return r[e]})}function l(e,t){return(t=t||{}).headers=t.headers||{},t.headers["X-SILENCE-LOGGER"]=!0,Em.$.ajax(n.default.get("rootPath")+e,t)}function u(){return void 0!==a?document[a]:!document.hasFocus}function c(e){var t=moment(e),n=moment() return t.diff(n.startOf("day"))>0?t.format("h:mm a"):t.diff(n.startOf("week"))>0?t.format("dd h:mm a"):t.diff(n.startOf("year"))>0?t.format("D MMM h:mm a"):t.format("D MMM YY")}function d(e){var t=[] -return e.forEach(function(e){null===e?t.push("null"):"[object Array]"===Object.prototype.toString.call(e)?t.push(d(e)):t.push(o(e.toString()))}),"["+t.join(", ")+"]"}}),define("client-app/models/group",["exports","client-app/models/message"],function(e,t){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 -var n=Ember.Object.extend({selected:!1,showCount:!0,key:Ember.computed.reads("regex"),displayMessage:Ember.computed.reads("messages.firstObject.message"),init:function(){this._super.apply(this,arguments) +return e.forEach(function(e){null===e?t.push("null"):"[object Array]"===Object.prototype.toString.call(e)?t.push(d(e)):t.push(o(e.toString()))}),"["+t.join(", ")+"]"}}),define("client-app/models/group",["exports","client-app/models/message","client-app/lib/utilities"],function(e,t,n){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 +var a=Ember.Object.extend({selected:!1,showCount:!0,key:Ember.computed.reads("regex"),displayMessage:Ember.computed.reads("messages.firstObject.message"),init:function(){this._super.apply(this,arguments) var e=this.messages.map(function(e){return t.default.create(e)}) -this.set("messages",e)},glyph:Ember.computed(function(){return""})}) -e.default=n}),define("client-app/models/message-collection",["exports","client-app/lib/utilities","client-app/models/message","client-app/models/group"],function(e,t,n,a){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 +this.set("messages",e)},glyph:Ember.computed(function(){return""}),solveAll:function(){return(0,n.ajax)("/solve-group",{type:"POST",data:{regex:this.regex}})}}) +e.default=a}),define("client-app/models/message-collection",["exports","client-app/lib/utilities","client-app/models/message","client-app/models/group"],function(e,t,n,a){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 var s=[0,1,2,3,4,5],i=Ember.Object.extend({total:0,rows:null,currentRow:null,currentTab:null,currentEnvPosition:0,currentGroupedMessagesPosition:0,init:function(){this._super.apply(this,arguments),this.setProperties({filter:s,search:"",rows:Ember.A()})},currentMessage:Ember.computed("currentRow","currentGroupedMessagesPosition",function(){var e=this.currentRow,t=this.currentGroupedMessagesPosition return e&&e.group?e.messages[t]:e}),solve:function(e){var t=this e.solve().then(function(){t.reload()})},selectRow:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=this.currentRow n&&n.set("selected",!1),e.set("selected",!0) var a=t.messageIndex||0,s=a===this.currentGroupedMessagesPosition -this.setProperties({currentRow:e,loadingEnv:!1,currentGroupedMessagesPosition:a,currentEnvPosition:0}),s&&this.notifyPropertyChange("currentGroupedMessagesPosition"),this.fetchEnv()},tabChanged:function(e){this.setProperties({currentTab:e,loadingEnv:!1}),this.fetchEnv()},groupedMessageChanged:function(e){this.setProperties({currentGroupedMessagesPosition:e,currentEnvPosition:0}),this.fetchEnv()},envChanged:function(e){this.set("currentEnvPosition",e),this.fetchEnv()},fetchEnv:function(){var e=this,n=this.currentMessage -if(n&&!n.env&&"env"===this.currentTab)return this.set("loadingEnv",!0),(0,t.ajax)("/fetch-env/".concat(n.key,".json")).then(function(e){return n.set("env",e)}).always(function(){return e.set("loadingEnv",!1)})},findEquivalentMessageIndex:function(e){var t=0 +this.setProperties({currentRow:e,loadingEnv:!1,currentGroupedMessagesPosition:a,currentEnvPosition:0}),s&&this.notifyPropertyChange("currentGroupedMessagesPosition"),this.fetchEnv()},tabChanged:function(e){this.setProperties({currentTab:e,loadingEnv:!1}),this.fetchEnv()},groupedMessageChanged:function(e){this.setProperties({currentGroupedMessagesPosition:e,currentEnvPosition:0}),this.fetchEnv()},envChanged:function(e){this.set("currentEnvPosition",e),this.fetchEnv()},fetchEnv:function(){var e=this,t=this.currentMessage +if(t&&!t.env&&"env"===this.currentTab)return this.set("loadingEnv",!0),t.fetchEnv().always(function(){return e.set("loadingEnv",!1)})},findEquivalentMessageIndex:function(e){var t=0 return e&&e.group&&this.currentRow&&this.currentRow.group&&e.key===this.currentRow.key&&(t=e.messages.mapBy("key").indexOf(this.currentMessage.key),t=Math.max(0,t)),t},updateSelectedRow:function(){var e=this.get("currentRow.key") if(e&&this.rows){var t=this.rows.find(function(t){return t.key===e}) if(t){var n=this.findEquivalentMessageIndex(t) @@ -173,10 +173,9 @@ if(e&&e.length>2&&"/"===e[0]){var t=e.match(/\/(.*)\/(.*)/) if(t&&3===t.length)try{return new RegExp(t[1],t[2])}catch(n){}}}),toObjects:function(e){return e.map(function(e){return e.group?a.default.create(e):n.default.create(e)})}}) e.default=i}) define("client-app/models/message",["exports","client-app/lib/utilities","client-app/lib/preload"],function(e,t,n){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 -var a=Em.Object.extend({MAX_LEN:200,expand:function(){this.set("expanded",!0)},solve:function(){return(0,t.ajax)("/solve/"+this.get("key"),{type:"PUT"})},destroy:function(){return(0,t.ajax)("/message/"+this.get("key"),{type:"DELETE"})},protect:function(){return this.set("protected",!0),(0,t.ajax)("/protect/"+this.get("key"),{type:"PUT"})},unprotect:function(){return this.set("protected",!1),(0,t.ajax)("/unprotect/"+this.get("key"),{type:"DELETE"})},showCount:Ember.computed("count",function(){return this.get("count")>1}),hasMore:Ember.computed("message","expanded",function(){var e=this.get("message") -return!this.get("expanded")&&e.length>this.MAX_LEN}),shareUrl:Ember.computed("key",function(){return n.default.get("rootPath")+"/show/"+this.get("key")}),displayMessage:Ember.computed("message","expanded",function(){var e=this.get("message") -return!this.get("expanded")&&e.length>this.MAX_LEN&&(e=e.substr(0,this.MAX_LEN)),e}),updateFromObject:function(e){this.set("count",e.get("count"))},canSolve:Ember.computed("env.{application_version,length}",function(){var e=this.get("backtrace"),t=this.get("env") -return(Array.isArray(t)?t.map(function(e){return e.application_version}).compact().join(""):t&&t.application_version)&&e&&e.length>0}),rowClass:Ember.computed("severity",function(){switch(this.get("severity")){case 0:return"debug" +var a=Em.Object.extend({MAX_LEN:200,fetchEnv:function(){var e=this +return(0,t.ajax)("/fetch-env/".concat(this.key,".json")).then(function(t){return e.set("env",t)})},expand:function(){this.set("expanded",!0)},solve:function(){return(0,t.ajax)("/solve/".concat(this.key),{type:"PUT"})},destroy:function(){return(0,t.ajax)("/message/".concat(this.key),{type:"DELETE"})},protect:function(){return this.set("protected",!0),(0,t.ajax)("/protect/".concat(this.key),{type:"PUT"})},unprotect:function(){return this.set("protected",!1),(0,t.ajax)("/unprotect/".concat(this.key),{type:"DELETE"})},showCount:Ember.computed("count",function(){return this.count>1}),hasMore:Ember.computed("message","expanded",function(){return!this.expanded&&this.message.length>this.MAX_LEN}),shareUrl:Ember.computed("key",function(){return"".concat(n.default.get("rootPath"),"/show/").concat(this.key)}),displayMessage:Ember.computed("message","expanded",function(){var e=this.message +return!this.expanded&&this.message.length>this.MAX_LEN&&(e=this.message.substr(0,this.MAX_LEN)),e}),updateFromObject:function(e){this.set("count",e.get("count"))},canSolve:Ember.computed("env.{application_version,length}",function(){return(Array.isArray(this.env)?this.env.map(function(e){return e.application_version}).compact().join(""):this.env&&this.env.application_version)&&this.backtrace&&this.backtrace.length>0}),rowClass:Ember.computed("severity",function(){switch(this.get("severity")){case 0:return"debug" case 1:return"info" case 2:return"warn" case 3:return"error" @@ -227,10 +226,10 @@ var t=Ember.HTMLBars.template({id:"zCP0V00P",block:'{"symbols":["tab","&default" e.default=t}),define("client-app/templates/components/time-formatter",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 var t=Ember.HTMLBars.template({id:"sp53cTcH",block:'{"symbols":[],"statements":[[1,[21,"time"],false],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"client-app/templates/components/time-formatter.hbs"}}) e.default=t}),define("client-app/templates/index",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 -var t=Ember.HTMLBars.template({id:"ZfZsvZU6",block:'{"symbols":["row"],"statements":[[7,"div"],[11,"id","top-panel"],[9],[0,"\\n "],[7,"div"],[11,"id","log-table"],[9],[0,"\\n"],[4,"if",[[23,["model","moreBefore"]]],null,{"statements":[[0," "],[7,"div"],[11,"class","show-more"],[9],[0,"\\n"],[4,"if",[[23,["model","hideCountInLoadMore"]]],null,{"statements":[[0," Load more\\n"]],"parameters":[]},{"statements":[[0," Select to see "],[1,[23,["model","totalBefore"]],false],[0," more\\n"]],"parameters":[]}],[0," "],[3,"action",[[22,0,[]],"showMoreBefore"]],[10],[0,"\\n"]],"parameters":[]},null],[4,"each",[[23,["model","rows"]]],null,{"statements":[[0," "],[1,[27,"message-row",null,[["model","selectRow"],[[22,1,[]],[27,"action",[[22,0,[]],"selectRowAction",[22,1,[]]],null]]]],false],[0,"\\n"]],"parameters":[1]},null],[0," "],[10],[0,"\\n"],[10],[0,"\\n"],[7,"div"],[11,"id","bottom-panel"],[12,"class",[28,[[27,"if",[[23,["model","currentRow","group"]],"group-view"],null]]]],[9],[0,"\\n"],[4,"if",[[23,["model","currentRow","group"]]],null,{"statements":[[0," "],[1,[27,"page-nav",null,[["list","position","extraClasses","navigate"],[[23,["model","currentRow","messages"]],[23,["model","currentGroupedMessagesPosition"]],"group-nav",[27,"action",[[22,0,[]],"groupedMessageChangedAction"],null]]]],false],[0,"\\n"]],"parameters":[]},null],[0," "],[1,[27,"message-info",null,[["currentMessage","loadingEnv","removeMessage","solveMessage","onTabChange","envChangedAction","currentEnvPosition","actionsInMenu"],[[23,["model","currentMessage"]],[23,["model","loadingEnv"]],[27,"action",[[22,0,[]],"removeMessage"],null],[27,"action",[[22,0,[]],"solveMessage"],null],[27,"action",[[22,0,[]],"tabChangedAction"],null],[27,"action",[[22,0,[]],"envChangedAction"],null],[23,["model","currentEnvPosition"]],[23,["actionsInMenu"]]]]],false],[0,"\\n\\n "],[7,"div"],[11,"class","action-panel"],[9],[0,"\\n "],[7,"div"],[11,"class","severity-filters"],[9],[0,"\\n "],[7,"div"],[11,"class","more-wrapping"],[9],[0,"\\n "],[7,"label"],[11,"class","debug"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showDebug"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showDebug"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Debug"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"label"],[11,"class","info"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showInfo"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showInfo"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Info"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"label"],[11,"class","warn"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showWarn"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showWarn"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"i"],[11,"class","fa fa-exclamation-circle warning"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Warning"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"label"],[11,"class","error"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showErr"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showErr"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"i"],[11,"class","fa fa-times-circle error"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Error"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"label"],[11,"class","fatal"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showFatal"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showFatal"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"i"],[11,"class","fa fa-times-circle fatal"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Fatal"],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"div"],[11,"class","search-clear-all"],[9],[0,"\\n "],[7,"input"],[11,"class","search"],[11,"placeholder","Search"],[12,"onkeyup",[27,"action",[[22,0,[]],"updateSearch"],[["value"],["target.value"]]]],[11,"type","text"],[9],[10],[0,"\\n "],[7,"div"],[11,"class","footer-btns"],[9],[0,"\\n"],[4,"if",[[23,["showSettings"]]],null,{"statements":[[4,"link-to",["settings"],[["class"],["settings btn no-text"]],{"statements":[[0," "],[7,"i"],[11,"class","fa fa-cog"],[9],[10],[0,"\\n"]],"parameters":[]},null]],"parameters":[]},null],[0," "],[7,"button"],[11,"class","clear btn danger"],[9],[7,"i"],[11,"class","fa fa-trash-o"],[9],[10],[7,"span"],[9],[0,"Clear logs"],[10],[3,"action",[[22,0,[]],"clear"]],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n"],[10],[0,"\\n"],[1,[21,"panel-resizer"],false],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"client-app/templates/index.hbs"}}) +var t=Ember.HTMLBars.template({id:"+KQ93N7I",block:'{"symbols":["row"],"statements":[[7,"div"],[11,"id","top-panel"],[9],[0,"\\n "],[7,"div"],[11,"id","log-table"],[9],[0,"\\n"],[4,"if",[[23,["model","moreBefore"]]],null,{"statements":[[0," "],[7,"div"],[11,"class","show-more"],[9],[0,"\\n"],[4,"if",[[23,["model","hideCountInLoadMore"]]],null,{"statements":[[0," Load more\\n"]],"parameters":[]},{"statements":[[0," Select to see "],[1,[23,["model","totalBefore"]],false],[0," more\\n"]],"parameters":[]}],[0," "],[3,"action",[[22,0,[]],"showMoreBefore"]],[10],[0,"\\n"]],"parameters":[]},null],[4,"each",[[23,["model","rows"]]],null,{"statements":[[0," "],[1,[27,"message-row",null,[["model","selectRow"],[[22,1,[]],[27,"action",[[22,0,[]],"selectRowAction",[22,1,[]]],null]]]],false],[0,"\\n"]],"parameters":[1]},null],[0," "],[10],[0,"\\n"],[10],[0,"\\n"],[7,"div"],[11,"id","bottom-panel"],[12,"class",[28,[[27,"if",[[23,["model","currentRow","group"]],"group-view"],null]]]],[9],[0,"\\n"],[4,"if",[[23,["model","currentRow","group"]]],null,{"statements":[[0," "],[1,[27,"page-nav",null,[["list","position","extraClasses","navigate"],[[23,["model","currentRow","messages"]],[23,["model","currentGroupedMessagesPosition"]],"group-nav",[27,"action",[[22,0,[]],"groupedMessageChangedAction"],null]]]],false],[0,"\\n"]],"parameters":[]},null],[0," "],[1,[27,"message-info",null,[["currentMessage","currentRow","loadingEnv","removeMessage","solveMessage","onTabChange","envChangedAction","currentEnvPosition","actionsInMenu"],[[23,["model","currentMessage"]],[23,["model","currentRow"]],[23,["model","loadingEnv"]],[27,"action",[[22,0,[]],"removeMessage"],null],[27,"action",[[22,0,[]],"solveMessage"],null],[27,"action",[[22,0,[]],"tabChangedAction"],null],[27,"action",[[22,0,[]],"envChangedAction"],null],[23,["model","currentEnvPosition"]],[23,["actionsInMenu"]]]]],false],[0,"\\n\\n "],[7,"div"],[11,"class","action-panel"],[9],[0,"\\n "],[7,"div"],[11,"class","severity-filters"],[9],[0,"\\n "],[7,"div"],[11,"class","more-wrapping"],[9],[0,"\\n "],[7,"label"],[11,"class","debug"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showDebug"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showDebug"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Debug"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"label"],[11,"class","info"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showInfo"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showInfo"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Info"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"label"],[11,"class","warn"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showWarn"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showWarn"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"i"],[11,"class","fa fa-exclamation-circle warning"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Warning"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"label"],[11,"class","error"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showErr"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showErr"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"i"],[11,"class","fa fa-times-circle error"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Error"],[10],[0,"\\n "],[10],[0,"\\n "],[7,"label"],[11,"class","fatal"],[9],[0,"\\n "],[7,"input"],[12,"checked",[21,"showFatal"]],[12,"onchange",[27,"action",[[22,0,[]],[27,"action",[[22,0,[]],"updateFilter","showFatal"],null]],null]],[11,"type","checkbox"],[9],[10],[0,"\\n "],[7,"i"],[11,"class","fa fa-times-circle fatal"],[9],[10],[0,"\\n "],[7,"span"],[9],[0,"Fatal"],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[7,"div"],[11,"class","search-clear-all"],[9],[0,"\\n "],[7,"input"],[11,"class","search"],[11,"placeholder","Search"],[12,"onkeyup",[27,"action",[[22,0,[]],"updateSearch"],[["value"],["target.value"]]]],[11,"type","text"],[9],[10],[0,"\\n "],[7,"div"],[11,"class","footer-btns"],[9],[0,"\\n"],[4,"if",[[23,["showSettings"]]],null,{"statements":[[4,"link-to",["settings"],[["class"],["settings btn no-text"]],{"statements":[[0," "],[7,"i"],[11,"class","fa fa-cog"],[9],[10],[0,"\\n"]],"parameters":[]},null]],"parameters":[]},null],[0," "],[7,"button"],[11,"class","clear btn danger"],[9],[7,"i"],[11,"class","fa fa-trash-o"],[9],[10],[7,"span"],[9],[0,"Clear logs"],[10],[3,"action",[[22,0,[]],"clear"]],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n "],[10],[0,"\\n"],[10],[0,"\\n"],[1,[21,"panel-resizer"],false],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"client-app/templates/index.hbs"}}) e.default=t}),define("client-app/templates/settings",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 var t=Ember.HTMLBars.template({id:"VCEsnuWV",block:'{"symbols":[],"statements":[[7,"div"],[11,"class","settings-page"],[9],[0,"\\n "],[4,"link-to",["index"],null,{"statements":[[0,"Home"]],"parameters":[]},null],[0,"\\n "],[7,"div"],[11,"class","settings-header"],[9],[0,"\\n "],[7,"h1"],[11,"class","header-title"],[9],[0,"Settings"],[10],[0,"\\n "],[7,"img"],[11,"class","header-logo"],[12,"src",[27,"logster-url",["images/icon_144x144.png"],null]],[9],[10],[0,"\\n "],[10],[0,"\\n\\n "],[7,"div"],[11,"class","settings-section suppression-patterns"],[9],[0,"\\n "],[7,"h2"],[11,"class","section-title"],[9],[0,"Suppression Patterns"],[10],[0,"\\n "],[7,"div"],[9],[0,"New messages that match these Regular Expression patterns will be suppressed. Checking Apply retroactively will remove all existing messages that match the patterns."],[10],[0,"\\n\\n"],[4,"if",[[23,["showCodedSuppression"]]],null,{"statements":[[0," "],[7,"h3"],[11,"class","subsection-title"],[9],[0,"Hard-coded patterns:"],[10],[0,"\\n "],[7,"div"],[11,"class","tip"],[9],[0,"These patterns can\'t be removed via the UI because they are commited to the source code of your app."],[10],[0,"\\n "],[1,[27,"patterns-list",null,[["patterns","mutable"],[[23,["codedSuppression"]],false]]],false],[0,"\\n"]],"parameters":[]},null],[0,"\\n "],[7,"h3"],[11,"class","subsection-title"],[9],[0,"Custom patterns:"],[10],[0,"\\n "],[1,[27,"patterns-list",null,[["patterns","key","applyRetroactivelyCheckbox","mutable"],[[23,["customSuppression"]],"suppression",true,true]]],false],[0,"\\n "],[10],[0,"\\n "],[7,"div"],[11,"class","settings-section grouping-patterns"],[9],[0,"\\n "],[7,"h2"],[11,"class","section-title"],[9],[0,"Grouping Patterns"],[10],[0,"\\n "],[7,"div"],[9],[0,"Add a Regular Expression pattern to group all new and existing messages into a single row when viewing the logs."],[10],[0,"\\n\\n "],[1,[27,"patterns-list",null,[["patterns","key","mutable"],[[23,["grouping"]],"grouping",true]]],false],[0,"\\n "],[10],[0,"\\n"],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"client-app/templates/settings.hbs"}}) e.default=t}),define("client-app/templates/show",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0 var t=Ember.HTMLBars.template({id:"Z9BtSx7U",block:'{"symbols":[],"statements":[[4,"link-to",["index"],null,{"statements":[[0,"Recent"]],"parameters":[]},null],[0,"\\n"],[7,"div"],[11,"id","bottom-panel"],[11,"class","full"],[9],[0,"\\n "],[1,[27,"message-info",null,[["currentMessage","showTitle","envChangedAction","currentEnvPosition","actionsInMenu"],[[23,["model"]],"true",[27,"action",[[22,0,[]],"envChanged"],null],[23,["envPosition"]],false]]],false],[0,"\\n"],[10],[0,"\\n"]],"hasEval":false}',meta:{moduleName:"client-app/templates/show.hbs"}}) e.default=t}),define("client-app/config/environment",[],function(){try{var e="client-app/config/environment",t=document.querySelector('meta[name="'+e+'"]').getAttribute("content"),n={default:JSON.parse(decodeURIComponent(t))} -return Object.defineProperty(n,"__esModule",{value:!0}),n}catch(a){throw new Error('Could not read config from meta tag with name "'+e+'".')}}),runningTests||require("client-app/app").default.create({name:"client-app",version:"0.0.0+39c1cae6"}) +return Object.defineProperty(n,"__esModule",{value:!0}),n}catch(a){throw new Error('Could not read config from meta tag with name "'+e+'".')}}),runningTests||require("client-app/app").default.create({name:"client-app",version:"0.0.0+eed389ce"}) diff --git a/client-app/app/components/message-info.js b/client-app/app/components/message-info.js index 960207e1..d438dba4 100644 --- a/client-app/app/components/message-info.js +++ b/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", @@ -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( { @@ -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) { @@ -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"); } diff --git a/client-app/app/models/group.js b/client-app/app/models/group.js index dd34979c..e73f99d9 100644 --- a/client-app/app/models/group.js +++ b/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, @@ -16,5 +17,9 @@ export default EmberObject.extend({ glyph: computed(function() { return ""; - }) + }), + + solveAll() { + return ajax("/solve-group", { type: "POST", data: { regex: this.regex } }); + } }); diff --git a/client-app/app/models/message-collection.js b/client-app/app/models/message-collection.js index 32b6fd95..3cf66341 100644 --- a/client-app/app/models/message-collection.js +++ b/client-app/app/models/message-collection.js @@ -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)); } }, diff --git a/client-app/app/models/message.js b/client-app/app/models/message.js index 956db212..7ea7cc0a 100644 --- a/client-app/app/models/message.js +++ b/client-app/app/models/message.js @@ -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; }), @@ -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() { diff --git a/client-app/app/templates/index.hbs b/client-app/app/templates/index.hbs index 4c977d9b..441c0909 100644 --- a/client-app/app/templates/index.hbs +++ b/client-app/app/templates/index.hbs @@ -24,6 +24,7 @@ {{/if}} {{message-info currentMessage=model.currentMessage + currentRow=model.currentRow loadingEnv=model.loadingEnv removeMessage=(action "removeMessage") solveMessage=(action "solveMessage") diff --git a/lib/logster/base_store.rb b/lib/logster/base_store.rb index 1b059b27..971e1d63 100644 --- a/lib/logster/base_store.rb +++ b/lib/logster/base_store.rb @@ -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) @@ -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 diff --git a/lib/logster/message.rb b/lib/logster/message.rb index e5306652..6992d873 100644 --- a/lib/logster/message.rb +++ b/lib/logster/message.rb @@ -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 @@ -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) diff --git a/lib/logster/middleware/viewer.rb b/lib/logster/middleware/viewer.rb index 010bf637..944944d6 100644 --- a/lib/logster/middleware/viewer.rb +++ b/lib/logster/middleware/viewer.rb @@ -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 @@ -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"]] @@ -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| @@ -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 @@ -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)]] @@ -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) @@ -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 @@ -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 diff --git a/lib/logster/redis_store.rb b/lib/logster/redis_store.rb index 5993ee0d..f6b1ff94 100644 --- a/lib/logster/redis_store.rb +++ b/lib/logster/redis_store.rb @@ -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 diff --git a/test/logster/middleware/test_viewer.rb b/test/logster/middleware/test_viewer.rb index 3f1d3b13..a92a1f59 100644 --- a/test/logster/middleware/test_viewer.rb +++ b/test/logster/middleware/test_viewer.rb @@ -370,4 +370,104 @@ def test_fetch_env_returns_404_when_invalid_key response = request.get("/logsie/fetch-env/123456abc.json") assert_equal(404, response.status) end + + def test_solve_group_api_requires_post_request + Logster.config.enable_custom_patterns_via_ui = true + Logster::GroupingPattern.new(/gotta be post/).save + msg = Logster.store.report( + Logger::WARN, + '', + 'gotta be post 22', + env: { "application_version" => "abc" }, + backtrace: "aa" + ) + latest = Logster.store.latest + assert_equal(1, latest.size) + assert_equal(msg.key, latest.first["messages"].first.key) + %i[get head options].each do |m| + response = request.public_send(m, "/logsie/solve-group", params: { regex: "/gotta be post/" }) + assert_equal(405, response.status) + assert_equal("POST", response.headers["Allow"]) + end + latest = Logster.store.latest + assert_equal(1, latest.size) + assert_equal(msg.key, latest.first["messages"].first.key) + ensure + Logster.config.enable_custom_patterns_via_ui = false + end + + def test_solve_group_returns_404_when_pattern_doesnt_exist + Logster.config.enable_custom_patterns_via_ui = true + Logster::GroupingPattern.new(/some pattern/).save + msg = Logster.store.report( + Logger::WARN, + '', + 'some pattern 22', + env: { "application_version" => "abc" }, + backtrace: "aa" + ) + latest = Logster.store.latest + assert_equal(1, latest.size) + assert_equal(msg.key, latest.first["messages"].first.key) + response = request.post("/logsie/solve-group", params: { regex: "/i dont exist/" }) + assert_equal(404, response.status) + latest = Logster.store.latest + assert_equal(1, latest.size) + assert_equal(msg.key, latest.first["messages"].first.key) + ensure + Logster.config.enable_custom_patterns_via_ui = false + end + + def test_solving_grouped_messages + Logster.config.enable_custom_patterns_via_ui = true + backtrace = "a b c d" + Logster::GroupingPattern.new(/test pattern/).save + msg1 = Logster.store.report(Logger::WARN, '', 'test pattern 1', backtrace: backtrace) + msg2 = Logster.store.report( + Logger::WARN, + '', + 'test pattern 2', + env: { "application_version" => "abc" }, + backtrace: backtrace + ) + msg3 = Logster.store.report( + Logger::WARN, + '', + 'test pattern 3', + env: [{ "application_version" => "def" }, { "application_version" => "ghi" }], + backtrace: backtrace + ) + group = Logster.store.find_pattern_groups { |p| p == /test pattern/ }.first + assert_equal([msg3, msg2, msg1].map(&:key), group.messages_keys) + + latest = Logster.store.latest + assert_equal(1, latest.size) + assert_equal([msg1, msg2, msg3].map(&:key).sort, latest.first["messages"].map(&:key).sort) + + response = request.post("/logsie/solve-group", params: { regex: "/test pattern/" }) + group = Logster.store.find_pattern_groups { |p| p == /test pattern/ }.first + assert_equal([msg1.key], group.messages_keys) + assert_equal(200, response.status) + + latest = Logster.store.latest + # msg1 remains cause it doesn't have application_version + assert_equal([msg1.key], latest.first["messages"].map(&:key)) + assert_equal(1, latest.size) + + msg4 = Logster.store.report(Logger::WARN, '', 'test pattern 4', backtrace: backtrace) + %w[abc def ghi].each do |version| + Logster.store.report( + Logger::WARN, + '', + 'test pattern 5', + env: { "application_version" => version }, + backtrace: backtrace + ) + end + latest = Logster.store.latest + assert_equal([msg1.key, msg4.key].sort, latest.first["messages"].map(&:key).sort) + assert_equal(1, latest.size) + ensure + Logster.config.enable_custom_patterns_via_ui = false + end end diff --git a/test/logster/test_redis_store.rb b/test/logster/test_redis_store.rb index 7ec76a8c..70f3e738 100644 --- a/test/logster/test_redis_store.rb +++ b/test/logster/test_redis_store.rb @@ -1036,13 +1036,13 @@ def test_latest_doesnt_include_rows_that_are_removed_from_grouping_patterns_due_ def config_reset(configs) defaults = {} - configs.each do |k,v| + configs.each do |k, v| defaults[k] = Logster.config.public_send(k) Logster.config.public_send("#{k}=", v) end yield ensure - defaults.each do |k,v| + defaults.each do |k, v| Logster.config.public_send("#{k}=", v) end end