aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Topolnjak <ivantopo@gmail.com>2019-02-04 23:59:54 +0100
committerIvan Topolnjak <ivantopo@gmail.com>2019-02-04 23:59:54 +0100
commit7e9cb172cb7ead8e5579ffb1f0b0ba3ffef90605 (patch)
tree184174241e960180a06d1b4af982d9630f427a3c
parent11fc4117be9ef93f3471ed22de596a6bd8088eb9 (diff)
downloadKamon-7e9cb172cb7ead8e5579ffb1f0b0ba3ffef90605.tar.gz
Kamon-7e9cb172cb7ead8e5579ffb1f0b0ba3ffef90605.tar.bz2
Kamon-7e9cb172cb7ead8e5579ffb1f0b0ba3ffef90605.zip
include environment data in the status page, plust skeleton for instrumentation
-rw-r--r--kamon-core/src/main/resources/status/css/app.8ec53011.css1
-rw-r--r--kamon-core/src/main/resources/status/css/app.dbe764fe.css1
-rw-r--r--kamon-core/src/main/resources/status/index.html2
-rw-r--r--kamon-core/src/main/resources/status/js/about.5273d30c.js1
-rw-r--r--kamon-core/src/main/resources/status/js/app.218647d9.js1
-rw-r--r--kamon-core/src/main/resources/status/js/app.5d48f1c1.js1
-rw-r--r--kamon-core/src/main/scala/kamon/Kamon.scala4
-rw-r--r--kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala31
-rw-r--r--kamon-core/src/main/scala/kamon/status/Status.scala90
-rw-r--r--kamon-core/src/main/scala/kamon/status/StatusPageServer.scala10
-rw-r--r--kamon-status/src/api/StatusApi.ts34
-rw-r--r--kamon-status/src/components/MetricList.vue20
-rw-r--r--kamon-status/src/components/ModuleStatus.vue4
-rw-r--r--kamon-status/src/styles/main.scss20
-rw-r--r--kamon-status/src/views/Overview.vue70
15 files changed, 252 insertions, 38 deletions
diff --git a/kamon-core/src/main/resources/status/css/app.8ec53011.css b/kamon-core/src/main/resources/status/css/app.8ec53011.css
deleted file mode 100644
index 5eae8dc1..00000000
--- a/kamon-core/src/main/resources/status/css/app.8ec53011.css
+++ /dev/null
@@ -1 +0,0 @@
-.header{height:70px;background-color:#fff;-webkit-box-shadow:0 5px 9px 1px rgba(0,0,0,.1);box-shadow:0 5px 9px 1px rgba(0,0,0,.1)}.header .navigation{line-height:70px}.header .navigation .navigation-link,.header .navigation a{display:inline-block;padding:0 .5rem;text-transform:uppercase;text-decoration:none;color:#b3b3b3}.header .navigation .navigation-link:hover,.header .navigation a:hover{color:#888}.outer[data-v-1f523759]{background-color:#fff;-webkit-box-shadow:0 2px 9px 1px rgba(0,0,0,.1);box-shadow:0 2px 9px 1px rgba(0,0,0,.1)}.outer .heading[data-v-1f523759]{font-size:.9rem;color:#a5a5a5}.outer .message[data-v-1f523759]{color:#868686;font-weight:600;font-size:1.5rem}.outer .caption[data-v-1f523759]{color:#a5a5a5}.outer .critical[data-v-1f523759]{color:#ff6e6b}hr[data-v-1f523759]{margin:1px;border-color:#f3f3f3}body{background-color:#f7f7f7;font-size:16px;color:#616161;font-family:Open Sans,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1,h2,h3{font-weight:300} \ No newline at end of file
diff --git a/kamon-core/src/main/resources/status/css/app.dbe764fe.css b/kamon-core/src/main/resources/status/css/app.dbe764fe.css
new file mode 100644
index 00000000..3db8d313
--- /dev/null
+++ b/kamon-core/src/main/resources/status/css/app.dbe764fe.css
@@ -0,0 +1 @@
+.header{height:70px;background-color:#fff;-webkit-box-shadow:0 5px 9px 1px rgba(0,0,0,.1);box-shadow:0 5px 9px 1px rgba(0,0,0,.1)}.header .navigation{line-height:70px}.header .navigation .navigation-link,.header .navigation a{display:inline-block;padding:0 .5rem;text-transform:uppercase;text-decoration:none;color:#b3b3b3}.header .navigation .navigation-link:hover,.header .navigation a:hover{color:#888}.outer[data-v-836357a0]{background-color:#fff;-webkit-box-shadow:0 2px 9px 1px rgba(0,0,0,.1);box-shadow:0 2px 9px 1px rgba(0,0,0,.1)}hr[data-v-836357a0]{margin:1px;border-color:#f3f3f3}.status-indicator[data-v-63f6fd9e]{font-size:.9rem;border-radius:.3rem;color:#fff;margin:.5rem 1rem;padding:.1rem 1rem;background-color:#ccc}.critical[data-v-63f6fd9e]{background-color:#ff6e6b}.healthy[data-v-63f6fd9e]{background-color:#6ada87}hr[data-v-63f6fd9e]{margin:1px;border-color:#f3f3f3}.search-box input[data-v-5342e993]{color:#676767;height:2.5rem;border:none;border-radius:.3rem;background-color:#efefef}.search-box input[data-v-5342e993]:focus{outline:none}.search-box[data-v-5342e993] ::-webkit-input-placeholder{color:#929292}.search-box[data-v-5342e993] ::-ms-input-placeholder{color:#929292}.search-box[data-v-5342e993] ::placeholder{color:#929292}.search-box .search-stats[data-v-5342e993]{position:absolute;line-height:2.5rem;right:0;padding-right:1rem}body{background-color:#f7f7f7;font-size:14px;color:#929292;font-family:Open Sans,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1,h2,h3{font-weight:300}.text-label{color:#b3b3b3}.tag-container{margin:0 -.3rem}.tag{display:inline-block;background-color:#f4f4f4;margin:.3rem;padding:.1rem .5rem;border-radius:.2rem}.tag,.tag-value{overflow-wrap:anywhere}.tag-value{color:#676767} \ No newline at end of file
diff --git a/kamon-core/src/main/resources/status/index.html b/kamon-core/src/main/resources/status/index.html
index 9cfaa09c..b3795496 100644
--- a/kamon-core/src/main/resources/status/index.html
+++ b/kamon-core/src/main/resources/status/index.html
@@ -1 +1 @@
-<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=apple-touch-icon sizes=76x76 href=/favicon/apple-touch-icon.png><link rel=icon type=image/png sizes=32x32 href=/favicon/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/favicon/favicon-16x16.png><link rel=manifest href=/favicon/site.webmanifest><link rel=mask-icon href=/favicon/safari-pinned-tab.svg color=#ffffff><link rel="shortcut icon" href=/favicon/favicon.ico><meta name=msapplication-TileColor content=#ffffff><meta name=msapplication-config content=/favicon/browserconfig.xml><meta name=theme-color content=#3bd265><link rel=stylesheet href=https://use.fontawesome.com/releases/v5.7.0/css/all.css integrity=sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ crossorigin=anonymous><title>Kamon Status</title><link href=/js/about.5273d30c.js rel=prefetch><link href=/css/app.8ec53011.css rel=preload as=style><link href=/css/chunk-vendors.2da46af1.css rel=preload as=style><link href=/js/app.218647d9.js rel=preload as=script><link href=/js/chunk-vendors.b54318de.js rel=preload as=script><link href=/css/chunk-vendors.2da46af1.css rel=stylesheet><link href=/css/app.8ec53011.css rel=stylesheet></head><body><noscript><strong>We're sorry but kamon-status doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.b54318de.js></script><script src=/js/app.218647d9.js></script></body></html> \ No newline at end of file
+<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=apple-touch-icon sizes=76x76 href=/favicon/apple-touch-icon.png><link rel=icon type=image/png sizes=32x32 href=/favicon/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/favicon/favicon-16x16.png><link rel=manifest href=/favicon/site.webmanifest><link rel=mask-icon href=/favicon/safari-pinned-tab.svg color=#ffffff><link rel="shortcut icon" href=/favicon/favicon.ico><meta name=msapplication-TileColor content=#ffffff><meta name=msapplication-config content=/favicon/browserconfig.xml><meta name=theme-color content=#3bd265><link rel=stylesheet href=https://use.fontawesome.com/releases/v5.7.0/css/all.css integrity=sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ crossorigin=anonymous><title>Kamon Status</title><link href=/css/app.dbe764fe.css rel=preload as=style><link href=/css/chunk-vendors.2da46af1.css rel=preload as=style><link href=/js/app.5d48f1c1.js rel=preload as=script><link href=/js/chunk-vendors.b54318de.js rel=preload as=script><link href=/css/chunk-vendors.2da46af1.css rel=stylesheet><link href=/css/app.dbe764fe.css rel=stylesheet></head><body><noscript><strong>We're sorry but kamon-status doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.b54318de.js></script><script src=/js/app.5d48f1c1.js></script></body></html> \ No newline at end of file
diff --git a/kamon-core/src/main/resources/status/js/about.5273d30c.js b/kamon-core/src/main/resources/status/js/about.5273d30c.js
deleted file mode 100644
index c13c6e2e..00000000
--- a/kamon-core/src/main/resources/status/js/about.5273d30c.js
+++ /dev/null
@@ -1 +0,0 @@
-(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about"],{f820:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},s=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"about"},[n("h1",[t._v("This is an about page")])])}],u=n("2877"),i={},o=Object(u["a"])(i,a,s,!1,null,null,null);o.options.__file="About.vue";e["default"]=o.exports}}]); \ No newline at end of file
diff --git a/kamon-core/src/main/resources/status/js/app.218647d9.js b/kamon-core/src/main/resources/status/js/app.218647d9.js
deleted file mode 100644
index 5ef0a92a..00000000
--- a/kamon-core/src/main/resources/status/js/app.218647d9.js
+++ /dev/null
@@ -1 +0,0 @@
-(function(t){function n(n){for(var r,o,i=n[0],u=n[1],c=n[2],l=0,p=[];l<i.length;l++)o=i[l],a[o]&&p.push(a[o][0]),a[o]=0;for(r in u)Object.prototype.hasOwnProperty.call(u,r)&&(t[r]=u[r]);f&&f(n);while(p.length)p.shift()();return s.push.apply(s,c||[]),e()}function e(){for(var t,n=0;n<s.length;n++){for(var e=s[n],r=!0,o=1;o<e.length;o++){var u=e[o];0!==a[u]&&(r=!1)}r&&(s.splice(n--,1),t=i(i.s=e[0]))}return t}var r={},a={app:0},s=[];function o(t){return i.p+"js/"+({about:"about"}[t]||t)+"."+{about:"5273d30c"}[t]+".js"}function i(n){if(r[n])return r[n].exports;var e=r[n]={i:n,l:!1,exports:{}};return t[n].call(e.exports,e,e.exports,i),e.l=!0,e.exports}i.e=function(t){var n=[],e=a[t];if(0!==e)if(e)n.push(e[2]);else{var r=new Promise(function(n,r){e=a[t]=[n,r]});n.push(e[2]=r);var s,u=document.createElement("script");u.charset="utf-8",u.timeout=120,i.nc&&u.setAttribute("nonce",i.nc),u.src=o(t),s=function(n){u.onerror=u.onload=null,clearTimeout(c);var e=a[t];if(0!==e){if(e){var r=n&&("load"===n.type?"missing":n.type),s=n&&n.target&&n.target.src,o=new Error("Loading chunk "+t+" failed.\n("+r+": "+s+")");o.type=r,o.request=s,e[1](o)}a[t]=void 0}};var c=setTimeout(function(){s({type:"timeout",target:u})},12e4);u.onerror=u.onload=s,document.head.appendChild(u)}return Promise.all(n)},i.m=t,i.c=r,i.d=function(t,n,e){i.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:e})},i.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,n){if(1&n&&(t=i(t)),8&n)return t;if(4&n&&"object"===typeof t&&t&&t.__esModule)return t;var e=Object.create(null);if(i.r(e),Object.defineProperty(e,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var r in t)i.d(e,r,function(n){return t[n]}.bind(null,r));return e},i.n=function(t){var n=t&&t.__esModule?function(){return t["default"]}:function(){return t};return i.d(n,"a",n),n},i.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},i.p="/",i.oe=function(t){throw console.error(t),t};var u=window["webpackJsonp"]=window["webpackJsonp"]||[],c=u.push.bind(u);u.push=n,u=u.slice();for(var l=0;l<u.length;l++)n(u[l]);var f=c;s.push([0,"chunk-vendors"]),e()})({0:function(t,n,e){t.exports=e("cd49")},"3a3a":function(t,n,e){"use strict";var r=e("504a"),a=e.n(r);a.a},"504a":function(t,n,e){},"5c0b":function(t,n,e){"use strict";var r=e("5e27"),a=e.n(r);a.a},"5e27":function(t,n,e){},"9b19":function(t,n,e){t.exports=e.p+"img/logo.c6a2dbca.svg"},cd49:function(t,n,e){"use strict";e.r(n);var r=e("2b0e"),a=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{attrs:{id:"app"}},[e("div",{staticClass:"header w-100 mb-5 sticky-top"},[e("div",{staticClass:"container h-100"},[e("div",{staticClass:"row h-100 justify-content-between"},[t._m(0),e("div",{staticClass:"col-auto navigation"},[e("router-link",{attrs:{to:"/"}},[e("div",{staticClass:"navigation-link"},[t._v("\n Overview\n ")])]),e("router-link",{attrs:{to:"/"}},[e("div",{staticClass:"navigation-link"},[t._v("\n Metrics\n ")])])],1)])])]),e("router-view")],1)},s=[function(){var t=this,n=t.$createElement,r=t._self._c||n;return r("div",{staticClass:"col-auto h-100"},[r("img",{staticClass:"py-3 h-100 img-fluid",attrs:{src:e("9b19"),alt:""}})])}],o=(e("5c0b"),e("2877")),i={},u=Object(o["a"])(i,a,s,!1,null,null,null);u.options.__file="App.vue";var c,l=u.exports,f=e("8c4f"),p=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"container"},[e("div",{staticClass:"row"},[t._m(0),e("div",{staticClass:"col-6 col-md-4 p-2"},[e("status-card",{attrs:{statusInfo:t.instrumentationStatus}})],1),e("div",{staticClass:"col-6 col-md-4 p-2"},[e("status-card",{attrs:{statusInfo:t.reporterStatus}})],1),e("div",{staticClass:"col-6 col-md-4 p-2"},[e("status-card",{attrs:{statusInfo:t.metricsStatus}})],1)])])},d=[function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"col-12 pb-3"},[e("h2",[t._v("Overview")])])}],v=e("9ab4"),m=e("60a3"),h=e("1b15"),g=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"outer"},[e("div",{staticClass:"py-2 px-3 heading text-uppercase"},[t._v("\n "+t._s(t.statusInfo.heading)+"\n ")]),e("hr"),e("div",{staticClass:"px-3 py-2"},[e("div",{staticClass:"message",class:t.messageStatusClass},[t._v("\n "+t._s(t.statusInfo.message)+"\n ")]),e("div",{staticClass:"caption"},[t._v("\n "+t._s(t.statusInfo.caption)+"\n ")])])])},b=[];(function(t){t[t["Healthy"]=0]="Healthy",t[t["Critical"]=1]="Critical",t[t["Unknown"]=2]="Unknown"})(c||(c={}));var y=function(t){function n(){return null!==t&&t.apply(this,arguments)||this}return v["b"](n,t),Object.defineProperty(n.prototype,"messageStatusClass",{get:function(){return null!=this.statusInfo&&this.statusInfo.status!=c.Unknown?this.statusInfo.status==c.Healthy?["healthy"]:["critical"]:[]},enumerable:!0,configurable:!0}),v["a"]([Object(m["b"])()],n.prototype,"statusInfo",void 0),n=v["a"]([m["a"]],n),n}(m["c"]),w=y,_=w,C=(e("3a3a"),Object(o["a"])(_,g,b,!1,null,"1f523759",null));C.options.__file="StatusCard.vue";var O,j=C.exports,S=e("bc3a"),k=e.n(S);(function(t){t["Combined"]="combined",t["Metric"]="metric",t["Span"]="span",t["Plain"]="plain",t["Unknown"]="unknown"})(O||(O={}));var x=function(){function t(){}return t.configStatus=function(){return k.a.get("/status/config").then(function(t){var n=JSON.parse(t.data.config);return{version:t.data.version,environment:t.data.environment,config:n}})},t.moduleRegistryStatus=function(){return k.a.get("/status/modules").then(function(t){return t.data})},t.metricRegistryStatus=function(){return k.a.get("/status/metrics").then(function(t){return t.data})},t}(),P=function(t){function n(){var n=null!==t&&t.apply(this,arguments)||this;return n.config=h["none"],n.moduleRegistry=h["none"],n.metricsRegistry=h["none"],n}return v["b"](n,t),Object.defineProperty(n.prototype,"reporterStatus",{get:function(){var t={heading:"Reporters",message:"Unknown",status:c.Unknown},n=function(t){return[O.Combined,O.Span,O.Metric].indexOf(t.kind)>0};return this.moduleRegistry.forEach(function(e){var r=e.modules.filter(n);r.length>0&&(t.status=c.Healthy,t.message=r.length+" Active")}),t},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"metricsStatus",{get:function(){var t={heading:"Metrics",message:"Unknown",status:c.Unknown};return this.metricsRegistry.forEach(function(n){t.message=n.metrics.length+" Metrics"}),t},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"instrumentationStatus",{get:function(){return{heading:"Instrumentation",message:"Unknown",status:c.Unknown}},enumerable:!0,configurable:!0}),n.prototype.mounted=function(){this.refreshData()},n.prototype.refreshData=function(){var t=this;x.configStatus().then(function(n){t.config=Object(h["some"])(n)}),x.metricRegistryStatus().then(function(n){t.metricsRegistry=Object(h["some"])(n)}),x.moduleRegistryStatus().then(function(n){t.moduleRegistry=Object(h["some"])(n)})},n=v["a"]([Object(m["a"])({components:{"status-card":j}})],n),n}(m["c"]),I=P,R=I,U=Object(o["a"])(R,p,d,!1,null,null,null);U.options.__file="Overview.vue";var E=U.exports;r["default"].use(f["a"]);var M=new f["a"]({routes:[{path:"/",name:"overview",component:E},{path:"/about",name:"about",component:function(){return e.e("about").then(e.bind(null,"f820"))}}]});e("ab8b"),e("fb98");r["default"].config.productionTip=!1,new r["default"]({router:M,render:function(t){return t(l)}}).$mount("#app")},fb98:function(t,n,e){}}); \ No newline at end of file
diff --git a/kamon-core/src/main/resources/status/js/app.5d48f1c1.js b/kamon-core/src/main/resources/status/js/app.5d48f1c1.js
new file mode 100644
index 00000000..f6d4ec9c
--- /dev/null
+++ b/kamon-core/src/main/resources/status/js/app.5d48f1c1.js
@@ -0,0 +1 @@
+(function(t){function e(e){for(var r,i,o=e[0],c=e[1],u=e[2],p=0,f=[];p<o.length;p++)i=o[p],s[i]&&f.push(s[i][0]),s[i]=0;for(r in c)Object.prototype.hasOwnProperty.call(c,r)&&(t[r]=c[r]);l&&l(e);while(f.length)f.shift()();return a.push.apply(a,u||[]),n()}function n(){for(var t,e=0;e<a.length;e++){for(var n=a[e],r=!0,o=1;o<n.length;o++){var c=n[o];0!==s[c]&&(r=!1)}r&&(a.splice(e--,1),t=i(i.s=n[0]))}return t}var r={},s={app:0},a=[];function i(e){if(r[e])return r[e].exports;var n=r[e]={i:e,l:!1,exports:{}};return t[e].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.m=t,i.c=r,i.d=function(t,e,n){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},i.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)i.d(n,r,function(e){return t[e]}.bind(null,r));return n},i.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="/";var o=window["webpackJsonp"]=window["webpackJsonp"]||[],c=o.push.bind(o);o.push=e,o=o.slice();for(var u=0;u<o.length;u++)e(o[u]);var l=c;a.push([0,"chunk-vendors"]),n()})({0:function(t,e,n){t.exports=n("cd49")},"363f":function(t,e,n){},"4fc8":function(t,e,n){"use strict";var r=n("7b4e"),s=n.n(r);s.a},"5c0b":function(t,e,n){"use strict";var r=n("5e27"),s=n.n(r);s.a},"5e27":function(t,e,n){},"7b4e":function(t,e,n){},"9b19":function(t,e,n){t.exports=n.p+"img/logo.c6a2dbca.svg"},ab9c:function(t,e,n){"use strict";var r=n("d8a1"),s=n.n(r);s.a},b9e3:function(t,e,n){"use strict";var r=n("363f"),s=n.n(r);s.a},cd49:function(t,e,n){"use strict";n.r(e);var r=n("2b0e"),s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{attrs:{id:"app"}},[t._m(0),n("router-view")],1)},a=[function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"header w-100 mb-1 sticky-top"},[r("div",{staticClass:"container h-100"},[r("div",{staticClass:"row h-100 justify-content-between"},[r("div",{staticClass:"col-auto h-100"},[r("img",{staticClass:"py-3 h-100 img-fluid",attrs:{src:n("9b19"),alt:""}})])])])])}],i=(n("5c0b"),n("2877")),o={},c=Object(i["a"])(o,s,a,!1,null,null,null);c.options.__file="App.vue";var u=c.exports,l=n("8c4f"),p=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"container"},[n("div",{staticClass:"row"},[t._m(0),n("div",{staticClass:"col-12"},[n("card",[n("div",{staticClass:"row py-2 no-gutters"},[n("div",{staticClass:"col-12 col-md-3 py-2 px-3"},[n("div",{staticClass:"text-uppercase text-label pb-1"},[t._v("Instrumentation")]),n("h5",[t._v(t._s(t.instrumentationStatusMessage))])]),n("div",{staticClass:"col-12 col-md-3 py-2 px-3"},[n("div",{staticClass:"text-uppercase text-label pb-1"},[t._v("Reporters")]),n("h5",[t._v(t._s(t.activeReporters.length)+" Started")])]),n("div",{staticClass:"col-12 col-md-3 py-2 px-3"},[n("div",{staticClass:"text-uppercase text-label pb-1"},[t._v("Metrics")]),n("h5",[t._v(t._s(t.metricsStatusMessage))])])])])],1),t._m(1),n("div",{staticClass:"col-12"},[n("card",[n("div",{staticClass:"row py-2 no-gutters"},[n("div",{staticClass:"col-auto py-2 px-3"},[n("div",{staticClass:"text-uppercase text-label pb-1"},[t._v("Service")]),n("h6",[t._v(t._s(t.service))])]),n("div",{staticClass:"col-auto py-2 px-3"},[n("div",{staticClass:"text-uppercase text-label pb-1"},[t._v("Host")]),n("h6",[t._v(t._s(t.host))])]),n("div",{staticClass:"col-auto py-2 px-3"},[n("div",{staticClass:"text-uppercase text-label pb-1"},[t._v("instance")]),n("h6",[t._v(t._s(t.instance))])]),n("div",{staticClass:"col-12 col-md-3 py-2 px-3"},[n("div",{staticClass:"text-uppercase text-label pb-1"},[t._v("tags")]),Object.keys(t.environmentTags).length>0?n("div",{staticClass:"tag-container"},t._l(Object.keys(t.environmentTags),function(e){return n("span",{key:e,staticClass:"tag"},[t._v("\n "+t._s(e)+"="),n("span",{staticClass:"tag-value"},[t._v(t._s(t.environmentTags[e]))])])}),0):n("div",[n("h6",[t._v("None")])])])])])],1),t._m(2),t._l(t.reporterModules,function(t){return n("div",{key:t.name,staticClass:"col-12 py-1"},[n("module-status",{attrs:{moduleStatus:t}})],1)}),t.plainModules.length>0?n("div",{staticClass:"col-12 pt-4 pb-2"},[n("h2",[t._v("Modules")])]):t._e(),t._l(t.plainModules,function(t){return n("div",{key:t.name,staticClass:"col-12 py-1"},[n("module-status",{attrs:{moduleStatus:t}})],1)}),t.metrics.length>0?n("div",{staticClass:"col-12 pt-4 pb-2"},[n("h2",[t._v("Metrics")])]):t._e(),n("div",{staticClass:"col-12 mb-5"},[n("metric-list",{attrs:{metrics:t.metrics}})],1)],2)])},f=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"col-12 pt-4 pb-2"},[n("h3",[t._v("Status")])])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"col-12 pt-4 pb-2"},[n("h3",[t._v("Environment")])])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"col-12 pt-4 pb-2"},[n("h3",[t._v("Reporters")])])}],d=n("9ab4"),v=n("60a3"),m=n("1b15"),b=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("card",[n("div",{staticClass:"row"},[n("div",{staticClass:"col-12"},[n("div",{staticClass:"py-2 px-3 text-uppercase text-label"},[t._v("\n "+t._s(t.moduleStatus.kind)+"\n ")]),n("hr")]),n("div",{staticClass:"col"},[n("div",{staticClass:"px-3 py-3"},[n("h5",{staticClass:"mb-0"},[t._v(t._s(t.moduleStatus.name))]),n("div",{staticClass:"text-label"},[t._v("\n "+t._s(t.moduleStatus.description)+"\n ")])])]),n("div",{staticClass:"col-auto"},[n("div",{staticClass:"status-indicator text-center",class:t.runStatus.class},[t._v(t._s(t.runStatus.message))]),n("div",{staticClass:"status-indicator text-center"},[t._v(t._s(t.discoveryStatus.message))])])])])},g=[],h=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"outer py-1"},[t._t("default")],2)},y=[],_=(n("4fc8"),{}),C=Object(i["a"])(_,h,y,!1,null,"836357a0",null);C.options.__file="Card.vue";var O=C.exports,x=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return d["b"](e,t),Object.defineProperty(e.prototype,"discoveryStatus",{get:function(){return this.moduleStatus.isProgrammaticallyRegistered?{message:"manual",class:""}:{message:"automatic",class:""}},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"runStatus",{get:function(){return this.moduleStatus.isStarted?{message:"started",class:"healthy"}:{message:"disabled",class:"critical"}},enumerable:!0,configurable:!0}),d["a"]([Object(v["b"])()],e.prototype,"moduleStatus",void 0),e=d["a"]([Object(v["a"])({components:{card:O}})],e),e}(v["c"]),j=x,S=j,w=(n("b9e3"),Object(i["a"])(S,b,g,!1,null,"63f6fd9e",null));w.options.__file="ModuleStatus.vue";var P=w.exports,M=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"row no-gutters"},[n("div",{staticClass:"col-12"},[n("div",{staticClass:"search-box mb-3"},[n("input",{directives:[{name:"model",rawName:"v-model",value:t.filterPattern,expression:"filterPattern"}],staticClass:"w-100 px-3 py-2",attrs:{type:"text",placeholder:"filter"},domProps:{value:t.filterPattern},on:{input:function(e){e.target.composing||(t.filterPattern=e.target.value)}}}),n("span",{staticClass:"search-stats"},[t._v(t._s(t.searchStats))])])]),n("div",{staticClass:"col-12"},[t.matchedMetrics.length>0?n("card",t._l(t.matchedMetrics,function(e,r){return n("div",{key:e.search,staticClass:"row no-gutters"},[n("div",{staticClass:"col-12 px-3 pt-1 pb-3"},[n("div",{staticClass:"text-uppercase text-label"},[t._v(t._s(e.type))]),n("h5",[t._v(t._s(e.name))]),n("div",{staticClass:"tag-container"},t._l(Object.keys(e.tags),function(r){return n("span",{key:r,staticClass:"tag"},[t._v("\n "+t._s(r)+"="),n("span",{staticClass:"tag-value"},[t._v(t._s(e.tags[r]))])])}),0)]),r<t.matchedMetrics.length-1?n("hr",{staticClass:"w-100"}):t._e()])}),0):t._e()],1)])},k=[],R=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.filterPattern="",e}return d["b"](e,t),Object.defineProperty(e.prototype,"totalMetrics",{get:function(){return this.metrics.length},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"filterRegex",{get:function(){return new RegExp(this.filterPattern)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"searchStats",{get:function(){return this.filterPattern.length>0?this.matchedMetrics.length+" matched":this.totalMetrics+" metrics"},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"matchedMetrics",{get:function(){var t=this;return this.filterPattern.length>0?this.metrics.filter(function(e){return null!=e.search.match(t.filterRegex)}):this.metrics},enumerable:!0,configurable:!0}),d["a"]([Object(v["b"])()],e.prototype,"metrics",void 0),e=d["a"]([Object(v["a"])({components:{card:O}})],e),e}(v["c"]),E=R,$=E,T=(n("ab9c"),Object(i["a"])($,M,k,!1,null,"5342e993",null));T.options.__file="MetricList.vue";var U,A=T.exports,J=n("bc3a"),N=n.n(J);(function(t){t["Combined"]="combined",t["Metric"]="metric",t["Span"]="span",t["Plain"]="plain",t["Unknown"]="unknown"})(U||(U={}));var D=function(){function t(){}return t.settings=function(){return N.a.get("/status/settings").then(function(t){var e=JSON.parse(t.data.config);return{version:t.data.version,environment:t.data.environment,config:e}})},t.moduleRegistryStatus=function(){return N.a.get("/status/modules").then(function(t){return t.data})},t.metricRegistryStatus=function(){return N.a.get("/status/metrics").then(function(t){var e=t.data,n=function(t,e){return t+":"+e+" "};return e.metrics.forEach(function(t){"RangeSampler"===t.type&&(t.type="Range Sampler");var e="";Object.keys(t.tags).forEach(function(r){e+=n(r,t.tags[r])}),t.search=n("name",t.name.toLowerCase())+n("type",t.type.toLowerCase())+e}),e})},t.instrumentationStatus=function(){return N.a.get("/status/instrumentation").then(function(t){var e={isActive:t.data.isActive,modules:{},errors:{}},n=t.data.modules;Object.keys(n).forEach(function(t){e.modules[t]=JSON.parse(n[t])});var r=t.data.errors;return Object.keys(r).forEach(function(t){e.errors[t]=JSON.parse(r[t])}),e})},t}(),L=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.settings=m["none"],e.moduleRegistry=m["none"],e.metricsRegistry=m["none"],e.instrumentation=m["none"],e}return d["b"](e,t),Object.defineProperty(e.prototype,"reporterModules",{get:function(){var t=this;return this.moduleRegistry.map(function(e){return e.modules.filter(t.isReporter)}).getOrElse([])},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"activeReporters",{get:function(){return this.reporterModules.filter(this.isStarted)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"plainModules",{get:function(){var t=this;return this.moduleRegistry.map(function(e){return e.modules.filter(function(e){return!t.isReporter(e)})}).getOrElse([])},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"trackedMetrics",{get:function(){return this.metricsRegistry.map(function(t){return t.metrics.length})},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"instrumentationStatusMessage",{get:function(){return this.instrumentation.map(function(t){return t.isActive?"Active":"Disabled"}).getOrElse("Unknown")},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"metricsStatusMessage",{get:function(){return this.trackedMetrics.map(function(t){return t+" Tracked"}).getOrElse("Unknown")},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"metrics",{get:function(){return this.metricsRegistry.map(function(t){return t.metrics}).getOrElse([])},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"instance",{get:function(){return this.settings.map(function(t){return t.environment.instance}).getOrElse("Unknown")},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"host",{get:function(){return this.settings.map(function(t){return t.environment.host}).getOrElse("Unknown")},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"service",{get:function(){return this.settings.map(function(t){return t.environment.service}).getOrElse("Unknown")},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"environmentTags",{get:function(){return this.settings.map(function(t){return t.environment.tags}).getOrElse({})},enumerable:!0,configurable:!0}),e.prototype.mounted=function(){this.refreshData()},e.prototype.refreshData=function(){var t=this;D.settings().then(function(e){t.settings=Object(m["some"])(e)}),D.metricRegistryStatus().then(function(e){t.metricsRegistry=Object(m["some"])(e)}),D.moduleRegistryStatus().then(function(e){t.moduleRegistry=Object(m["some"])(e)}),D.instrumentationStatus().then(function(e){t.instrumentation=Object(m["some"])(e)})},e.prototype.isReporter=function(t){return[U.Combined,U.Span,U.Metric].indexOf(t.kind)>0},e.prototype.isStarted=function(t){return t.isStarted},e=d["a"]([Object(v["a"])({components:{card:O,"module-status":P,"metric-list":A}})],e),e}(v["c"]),H=L,I=H,q=Object(i["a"])(I,p,f,!1,null,null,null);q.options.__file="Overview.vue";var z=q.exports;r["default"].use(l["a"]);var B=new l["a"]({routes:[{path:"/",name:"overview",component:z}]});n("ab8b"),n("fb98");r["default"].config.productionTip=!1,new r["default"]({router:B,render:function(t){return t(u)}}).$mount("#app")},d8a1:function(t,e,n){},fb98:function(t,e,n){}}); \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala
index 50c3e69d..284c7553 100644
--- a/kamon-core/src/main/scala/kamon/Kamon.scala
+++ b/kamon-core/src/main/scala/kamon/Kamon.scala
@@ -52,6 +52,10 @@ object QuickTest extends App {
| class = kamon.MyCustomMetricDude
| }
|}
+ |
+ |kamon.environment.tags {
+ | one = test
+ |}
""".stripMargin
val newConfig = ConfigFactory.parseString(manualConfig).withFallback(Kamon.config())
diff --git a/kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala b/kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala
index 5a3f22dc..370ab467 100644
--- a/kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala
+++ b/kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala
@@ -6,6 +6,8 @@ import java.lang.{StringBuilder => JavaStringBuilder}
import com.typesafe.config.ConfigRenderOptions
import kamon.module.Module
+import scala.collection.JavaConverters.{iterableAsScalaIterableConverter, mapAsScalaMapConverter}
+
trait JsonMarshalling[T] {
@@ -96,4 +98,33 @@ object JsonMarshalling {
.done()
}
}
+
+ implicit object InstrumentationStatusJsonMarshalling extends JsonMarshalling[Status.Instrumentation] {
+ override def toJson(instance: Status.Instrumentation, builder: JavaStringBuilder): Unit = {
+ val instrumentationObject = JsonWriter.on(builder)
+ .`object`()
+ .value("isActive", instance.isIActive)
+ .`object`("modules")
+
+ instance.modules.asScala.foreach {
+ case (moduleName, moduleDescription) => instrumentationObject.value(moduleName, moduleDescription)
+ }
+
+ instrumentationObject
+ .end() // end modules
+ .`object`("errors")
+
+ instance.errors.asScala.foreach {
+ case (moduleName, errors) =>
+ instrumentationObject.array(moduleName)
+ errors.asScala.foreach(t => instrumentationObject.value(t.toString))
+ instrumentationObject.end()
+ }
+
+ instrumentationObject
+ .end() // errors
+ .end() // object
+ .done()
+ }
+ }
} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/status/Status.scala b/kamon-core/src/main/scala/kamon/status/Status.scala
index dc059277..2a52b95f 100644
--- a/kamon-core/src/main/scala/kamon/status/Status.scala
+++ b/kamon-core/src/main/scala/kamon/status/Status.scala
@@ -6,23 +6,50 @@ import kamon.metric.MetricRegistry
import kamon.{Configuration, Environment, Kamon}
import kamon.module.ModuleRegistry
import kamon.module.Module.{Kind => ModuleKind}
+import java.util.{Collections, Map => JavaMap, List => JavaList}
/**
- * Allows accessing of component's status APIs without exposing any other internal API from those components.
+ * Exposes Kamon components' status information. This is meant to be used for informational and debugging purposes.
*/
class Status(_moduleRegistry: ModuleRegistry, _metricRegistry: MetricRegistry, configuration: Configuration) {
+ /**
+ * Settings currently used by Kamon.
+ */
def settings(): Status.Settings =
Status.Settings(BuildInfo.version, Kamon.environment, configuration.config())
/**
- * Information about what modules have been detected in the classpath and their current status.
+ * Status of the module registry. Describes what modules have been detected in the classpath and their current
+ * statuses.
*/
def moduleRegistry(): Status.ModuleRegistry =
_moduleRegistry.status()
+ /**
+ * Status of the metric registry. Describes all metrics currently tracked by Kamon.
+ */
def metricRegistry(): Status.MetricRegistry =
_metricRegistry.status()
+
+
+ /**
+ * PRIVATE API.
+ *
+ * Status of instrumentation modules that have been detected and/or loaded into the current JVM. This
+ * API is not meant to be used by the general public.
+ *
+ * Read the [[Status.Instrumentation]] companion object's docs for more information.
+ */
+ private[kamon] def instrumentation(): Status.Instrumentation = {
+ import Status.Instrumentation._
+
+ Status.Instrumentation(
+ isActive(),
+ modules(),
+ errors()
+ )
+ }
}
@@ -57,4 +84,63 @@ object Status {
tags: Map[String, String],
instrumentType: InstrumentType
)
+
+
+ /**
+ * Status of the instrumentation modules. This data is completely untyped and not expected to be used anywhere
+ * outside Kamon.
+ */
+ private[kamon] case class Instrumentation(
+ isIActive: Boolean,
+ modules: JavaMap[String, String],
+ errors: JavaMap[String, JavaList[Throwable]]
+ )
+
+
+ /**
+ * This object works as a bridge between Kamon and Kanela to gather information about instrumentation modules. When
+ * instrumentation is enabled, it should replace the implementation of the members of this object and return proper
+ * information.
+ *
+ * This data is only exposed directly to the status page API because it lacks any sort of type safety. We might
+ * change this in the future and provide proper types for all instrumentation modules' info.
+ */
+ private[kamon] object Instrumentation {
+
+ /**
+ * Whether instrumentation is active or not. When Kanela is present it will replace this method to return true.
+ */
+ def isActive(): Boolean =
+ false
+
+ /**
+ * List all instrumentation modules known and their current status. The result map contains the module name as keys
+ * and a JSON representation of the module status as values. The expected structure in the JSON representations is
+ * as follows:
+ *
+ * {
+ * 'description': 'A explicative module description',
+ * 'isEnabled': true | false,
+ * 'isActive': true | false
+ * }
+ *
+ * The "isEnabled" flag tells whether the module is able to instrument classes or not. By default, all modules are
+ * able to instrument classes but some modules might be shipped in a disabled state or forced to be disabled via
+ * configuration.
+ *
+ * The "isActive" flag tells whether the modules has already applied instrumentation to any of its target classes.
+ *
+ */
+ def modules(): JavaMap[String, String] =
+ Collections.emptyMap()
+
+
+ /**
+ * List all errors that might have happened during the instrumentation initialization. The resulting map contains
+ * a list of modules and any exceptions thrown by them during initialization. If not exceptions are thrown the map
+ * will always be empty.
+ */
+ def errors(): JavaMap[String, JavaList[Throwable]] =
+ Collections.emptyMap()
+ }
}
diff --git a/kamon-core/src/main/scala/kamon/status/StatusPageServer.scala b/kamon-core/src/main/scala/kamon/status/StatusPageServer.scala
index 35273f39..2784b87a 100644
--- a/kamon-core/src/main/scala/kamon/status/StatusPageServer.scala
+++ b/kamon-core/src/main/scala/kamon/status/StatusPageServer.scala
@@ -17,10 +17,11 @@ class StatusPageServer(hostname: String, port: Int, resourceLoader: ClassLoader,
// Serve the current status data on Json.
session.getUri() match {
- case "/status/settings" => json(status.settings())
- case "/status/modules" => json(status.moduleRegistry())
- case "/status/metrics" => json(status.metricRegistry())
- case _ => NotFound
+ case "/status/settings" => json(status.settings())
+ case "/status/modules" => json(status.moduleRegistry())
+ case "/status/metrics" => json(status.metricRegistry())
+ case "/status/instrumentation" => json(status.instrumentation())
+ case _ => NotFound
}
} else {
@@ -43,6 +44,7 @@ class StatusPageServer(hostname: String, port: Int, resourceLoader: ClassLoader,
case "css" => "text/css"
case "js" => "application/javascript"
case "ico" => "image/x-icon"
+ case "svg" => "image/svg+xml"
case "html" => "text/html"
case _ => "text/plain"
}
diff --git a/kamon-status/src/api/StatusApi.ts b/kamon-status/src/api/StatusApi.ts
index d00761e5..25d525d2 100644
--- a/kamon-status/src/api/StatusApi.ts
+++ b/kamon-status/src/api/StatusApi.ts
@@ -44,6 +44,18 @@ export interface MetricRegistry {
metrics: Metric[]
}
+export interface InstrumentationModule {
+ description: string
+ isEnabled: boolean
+ isActive: boolean
+}
+
+export interface Instrumentation {
+ isActive: boolean
+ modules: { [key: string]: InstrumentationModule }
+ errors: { [key: string]: string[]}
+}
+
export class StatusApi {
@@ -91,4 +103,26 @@ export class StatusApi {
return metricRegistry
})
}
+
+ public static instrumentationStatus(): Promise<Instrumentation> {
+ return axios.get('/status/instrumentation').then(response => {
+ const instrumentation: Instrumentation = {
+ isActive: response.data.isActive as boolean,
+ modules: {},
+ errors: {}
+ }
+
+ const rawModules = response.data.modules
+ Object.keys(rawModules).forEach(key => {
+ instrumentation.modules[key] = JSON.parse(rawModules[key])
+ })
+
+ const rawErrors = response.data.errors
+ Object.keys(rawErrors).forEach(key => {
+ instrumentation.errors[key] = JSON.parse(rawErrors[key])
+ })
+
+ return instrumentation
+ })
+ }
}
diff --git a/kamon-status/src/components/MetricList.vue b/kamon-status/src/components/MetricList.vue
index 758252e7..b9b6a92a 100644
--- a/kamon-status/src/components/MetricList.vue
+++ b/kamon-status/src/components/MetricList.vue
@@ -12,7 +12,7 @@
<div class="row no-gutters" v-for="(metric, index) in matchedMetrics" :key="metric.search">
<div class="col-12 px-3 pt-1 pb-3">
<div class="text-uppercase text-label">{{ metric.type }}</div>
- <h4>{{ metric.name }}</h4>
+ <h5>{{ metric.name }}</h5>
<div class="tag-container">
<span class="tag" v-for="tag in Object.keys(metric.tags)" :key="tag">
{{ tag }}=<span class="tag-value">{{ metric.tags[tag] }}</span>
@@ -75,7 +75,7 @@ export default class MetricList extends Vue {
height: 2.5rem;
border: none;
border-radius: 0.3rem;
- background-color: #e8e8e8;
+ background-color: #efefef;
&:focus {
outline: none;
@@ -93,20 +93,4 @@ export default class MetricList extends Vue {
padding-right: 1rem;
}
}
-
-.tag-container {
- margin: 0rem -0.3rem;
-}
-
-.tag {
- background-color: #f4f4f4;
- margin: 0.3rem;
- padding: 0.1rem 0.5rem;
- border-radius: 0.2rem;
-}
-
-.tag-value {
- color: #676767;
-}
-
</style>
diff --git a/kamon-status/src/components/ModuleStatus.vue b/kamon-status/src/components/ModuleStatus.vue
index a92cf46f..fff3373e 100644
--- a/kamon-status/src/components/ModuleStatus.vue
+++ b/kamon-status/src/components/ModuleStatus.vue
@@ -8,8 +8,8 @@
<hr>
</div>
<div class="col">
- <div class="px-3 py-2">
- <h4>{{ moduleStatus.name }}</h4>
+ <div class="px-3 py-3">
+ <h5 class="mb-0">{{ moduleStatus.name }}</h5>
<div class="text-label">
{{ moduleStatus.description }}
</div>
diff --git a/kamon-status/src/styles/main.scss b/kamon-status/src/styles/main.scss
index cde62b37..271da8af 100644
--- a/kamon-status/src/styles/main.scss
+++ b/kamon-status/src/styles/main.scss
@@ -13,4 +13,22 @@ h1, h2, h3 {
.text-label {
color: #b3b3b3;
-} \ No newline at end of file
+}
+
+.tag-container {
+ margin: 0rem -0.3rem;
+}
+
+.tag {
+ display: inline-block;
+ overflow-wrap: anywhere;
+ background-color: #f4f4f4;
+ margin: 0.3rem;
+ padding: 0.1rem 0.5rem;
+ border-radius: 0.2rem;
+}
+
+.tag-value {
+ overflow-wrap: anywhere;
+ color: #676767;
+}
diff --git a/kamon-status/src/views/Overview.vue b/kamon-status/src/views/Overview.vue
index 2d6e8dd7..75b39eb0 100644
--- a/kamon-status/src/views/Overview.vue
+++ b/kamon-status/src/views/Overview.vue
@@ -8,15 +8,15 @@
<card>
<div class="row py-2 no-gutters">
<div class="col-12 col-md-3 py-2 px-3">
- <div class="text-uppercase text-label pb-2">Instrumentation</div>
- <h5>Active</h5>
+ <div class="text-uppercase text-label pb-1">Instrumentation</div>
+ <h5>{{instrumentationStatusMessage}}</h5>
</div>
<div class="col-12 col-md-3 py-2 px-3">
- <div class="text-uppercase text-label pb-2">Reporters</div>
+ <div class="text-uppercase text-label pb-1">Reporters</div>
<h5>{{ activeReporters.length }} Started</h5>
</div>
<div class="col-12 col-md-3 py-2 px-3">
- <div class="text-uppercase text-label pb-2">Metrics</div>
+ <div class="text-uppercase text-label pb-1">Metrics</div>
<h5>{{metricsStatusMessage}}</h5>
</div>
</div>
@@ -24,6 +24,39 @@
</div>
<div class="col-12 pt-4 pb-2">
+ <h3>Environment</h3>
+ </div>
+ <div class="col-12">
+ <card>
+ <div class="row py-2 no-gutters">
+ <div class="col-auto py-2 px-3">
+ <div class="text-uppercase text-label pb-1">Service</div>
+ <h6>{{ service }}</h6>
+ </div>
+ <div class="col-auto py-2 px-3">
+ <div class="text-uppercase text-label pb-1">Host</div>
+ <h6>{{ host }}</h6>
+ </div>
+ <div class="col-auto py-2 px-3">
+ <div class="text-uppercase text-label pb-1">instance</div>
+ <h6>{{instance}}</h6>
+ </div>
+ <div class="col-12 col-md-3 py-2 px-3">
+ <div class="text-uppercase text-label pb-1">tags</div>
+ <div class="tag-container" v-if="Object.keys(environmentTags).length > 0">
+ <span class="tag" v-for="tag in Object.keys(environmentTags)" :key="tag">
+ {{ tag }}=<span class="tag-value">{{ environmentTags[tag] }}</span>
+ </span>
+ </div>
+ <div v-else>
+ <h6>None</h6>
+ </div>
+ </div>
+ </div>
+ </card>
+ </div>
+
+ <div class="col-12 pt-4 pb-2">
<h3>Reporters</h3>
</div>
<div class="col-12 py-1" v-for="reporter in reporterModules" :key="reporter.name">
@@ -53,7 +86,7 @@ import {Option, none, some} from 'ts-option'
import ModuleStatus from '../components/ModuleStatus.vue'
import Card from '../components/Card.vue'
import MetricList from '../components/MetricList.vue'
-import {StatusApi, Settings, ModuleRegistry, ModuleKind, MetricRegistry, Module, Metric} from '../api/StatusApi'
+import {StatusApi, Settings, ModuleRegistry, ModuleKind, MetricRegistry, Module, Metric, Instrumentation} from '../api/StatusApi'
@Component({
components: {
@@ -66,6 +99,7 @@ export default class Overview extends Vue {
private settings: Option<Settings> = none
private moduleRegistry: Option<ModuleRegistry> = none
private metricsRegistry: Option<MetricRegistry> = none
+ private instrumentation: Option<Instrumentation> = none
get reporterModules(): Module[] {
return this.moduleRegistry
@@ -87,6 +121,10 @@ export default class Overview extends Vue {
return this.metricsRegistry.map(metricRegistry => metricRegistry.metrics.length)
}
+ get instrumentationStatusMessage(): string {
+ return this.instrumentation.map(i => (i.isActive ? 'Active' : 'Disabled') as string).getOrElse('Unknown')
+ }
+
get metricsStatusMessage(): string {
return this.trackedMetrics.map(mc => mc + ' Tracked').getOrElse('Unknown')
}
@@ -97,6 +135,23 @@ export default class Overview extends Vue {
.getOrElse([])
}
+ get instance(): string {
+ return this.settings.map(s => s.environment.instance).getOrElse('Unknown')
+ }
+
+ get host(): string {
+ return this.settings.map(s => s.environment.host).getOrElse('Unknown')
+ }
+
+ get service(): string {
+ return this.settings.map(s => s.environment.service).getOrElse('Unknown')
+ }
+
+ get environmentTags(): { [key: string]: string } {
+ return this.settings.map(s => s.environment.tags).getOrElse({})
+ }
+
+
public mounted() {
this.refreshData()
@@ -106,11 +161,12 @@ export default class Overview extends Vue {
StatusApi.settings().then(settings => { this.settings = some(settings) })
StatusApi.metricRegistryStatus().then(metricsRegistry => { this.metricsRegistry = some(metricsRegistry) })
StatusApi.moduleRegistryStatus().then(moduleRegistry => {this.moduleRegistry = some(moduleRegistry) })
+ StatusApi.instrumentationStatus().then(instrumentation => {this.instrumentation = some(instrumentation) })
}
private isReporter(module: Module): boolean {
- return [ModuleKind.Combined, ModuleKind.Span, ModuleKind.Metric].indexOf(module.kind) > 0
- }
+ return [ModuleKind.Combined, ModuleKind.Span, ModuleKind.Metric].indexOf(module.kind) > 0
+ }
private isStarted(module: Module): boolean {
return module.isStarted