From bbdb5046233d202d874548b3dd0376bbba98d011 Mon Sep 17 00:00:00 2001 From: Gustavo Caso Date: Thu, 23 Nov 2023 10:29:45 +0100 Subject: [PATCH] Compute apmStats in the server. - Handle communicating with the trace-agent in the status package - Update the GUI to rende the apmStats using the informatoin from the server rather than using AJAX --- cmd/agent/gui/views/private/js/apm.js | 52 ---------------- cmd/agent/gui/views/private/js/ejs.min.js | 1 - cmd/agent/gui/views/private/js/javascript.js | 24 +------- .../gui/views/templates/generalStatus.tmpl | 59 ++++++++++++++++++- cmd/agent/gui/views/templates/index.tmpl | 1 - cmd/agent/subcommands/status/command.go | 34 ----------- pkg/status/apm/apm.go | 59 +++++++++++++++++++ pkg/status/status.go | 2 + 8 files changed, 118 insertions(+), 114 deletions(-) delete mode 100644 cmd/agent/gui/views/private/js/apm.js delete mode 100644 cmd/agent/gui/views/private/js/ejs.min.js create mode 100644 pkg/status/apm/apm.go diff --git a/cmd/agent/gui/views/private/js/apm.js b/cmd/agent/gui/views/private/js/apm.js deleted file mode 100644 index 6bf7a59d93ac3..0000000000000 --- a/cmd/agent/gui/views/private/js/apm.js +++ /dev/null @@ -1,52 +0,0 @@ -// apmTemplate defines the template which will be displayed on the APM section of the status page. -var apmTemplate = '' + -' Status: Running
' + -' Pid: <%= pid %>
' + -' Uptime: <%= uptime %> seconds
' + -' Mem alloc: <%= memstats.Alloc %> bytes
' + -' Hostname: <%= config.Hostname %>
' + -' Receiver: <%= config.ReceiverHost %>:<%= config.ReceiverPort %>
' + -' Endpoints:' + -' ' + -' <% config.Endpoints.forEach(function(e) { %>' + -' <%= e.Host %>
' + -' <% }); %>' + -'
' + -' Receiver (previous minute)' + -' ' + -' <% if (!Array.isArray(receiver) || receiver.length == 0) { %>' + -' No traces received in the previous minute.
' + -' <% } else { %>' + -' <% receiver.forEach(function(ts, i) { %>' + -' From <% if (typeof ts.Lang != "undefined") { %>' + -' <%= ts.Lang %> <%= ts.LangVersion %> (<%= ts.Interpreter %>), client <%= ts.TracerVersion %>' + -' <% } else { %>' + -' unknown clients' + -' <% } %>' + -' ' + -' Traces received: <%= ts.TracesReceived %> (<%= ts.TracesBytes %> bytes)
' + -' Spans received: <%= ts.SpansReceived %>' + -' <% if (typeof ts.WarnString != "undefined") { %>' + -'
WARNING: <%= ts.WarnString %>
' + -' <% } %>' + -'
' + -' <% }); %>' + -' <% } %>' + -'' + -' <% if (typeof ratebyservice != "undefined" && ratebyservice != null) { %>' + -' <% Object.entries(ratebyservice).forEach(function(prop) { %>' + -' <% if (prop[0] == "service:,env:") { %>' + -' Default priority sampling rate: <%= prop[1].toFixed(2)*100 %>%' + -' <% } else { %>' + -' Priority sampling rate for \'<%= prop[0] %>\': <% prop[1].toFixed(2)*100 %>%' + -' <% } %>' + -' <% }); %>' + -' <% } %>' + -'
' + -' Writer (previous minute)' + -' ' + -' Traces: <%= trace_writer.Payloads %> payloads, <%= trace_writer.Traces %> traces, <%= trace_writer.Events %> events, <%= trace_writer.Bytes %> bytes
' + -' <% if (trace_writer.Errors > 0.0) { %>WARNING: Traces API errors (1 min): <%= trace_writer.Errors %><% } %>' + -' Stats: <%= stats_writer.Payloads %> payloads, <%= stats_writer.StatsBuckets %> stats buckets, <%= stats_writer.Bytes %> bytes
' + -' <% if (stats_writer.Errors > 0.0) { %>WARNING: Stats API errors (1 min): <%= stats_writer.Errors %><% } %>' + -'
'; diff --git a/cmd/agent/gui/views/private/js/ejs.min.js b/cmd/agent/gui/views/private/js/ejs.min.js deleted file mode 100644 index 9e86b9697acc5..0000000000000 --- a/cmd/agent/gui/views/private/js/ejs.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ejs=f()}})(function(){var define,module,exports;return function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o1;if(options.cache){if(!filename){throw new Error("cache option requires a filename")}func=exports.cache.get(filename);if(func){return func}if(!hasTemplate){template=fileLoader(filename).toString().replace(_BOM,"")}}else if(!hasTemplate){if(!filename){throw new Error("Internal EJS error: no file name or template "+"provided")}template=fileLoader(filename).toString().replace(_BOM,"")}func=exports.compile(template,options);if(options.cache){exports.cache.set(filename,func)}return func}function tryHandleCache(options,data,cb){var result;if(!cb){if(typeof exports.promiseImpl=="function"){return new exports.promiseImpl(function(resolve,reject){try{result=handleCache(options)(data);resolve(result)}catch(err){reject(err)}})}else{throw new Error("Please provide a callback function")}}else{try{result=handleCache(options)(data)}catch(err){return cb(err)}cb(null,result)}}function fileLoader(filePath){return exports.fileLoader(filePath)}function includeFile(path,options){var opts=utils.shallowCopy({},options);opts.filename=getIncludePath(path,opts);return handleCache(opts)}function rethrow(err,str,flnm,lineno,esc){var lines=str.split("\n");var start=Math.max(lineno-3,0);var end=Math.min(lines.length,lineno+3);var filename=esc(flnm);var context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" >> ":" ")+curr+"| "+line}).join("\n");err.path=filename;err.message=(filename||"ejs")+":"+lineno+"\n"+context+"\n\n"+err.message;throw err}function stripSemi(str){return str.replace(/;(\s*$)/,"$1")}exports.compile=function compile(template,opts){var templ;if(opts&&opts.scope){if(!scopeOptionWarned){console.warn("`scope` option is deprecated and will be removed in EJS 3");scopeOptionWarned=true}if(!opts.context){opts.context=opts.scope}delete opts.scope}templ=new Template(template,opts);return templ.compile()};exports.render=function(template,d,o){var data=d||{};var opts=o||{};if(arguments.length==2){utils.shallowCopyFromList(opts,data,_OPTS_PASSABLE_WITH_DATA)}return handleCache(opts,template)(data)};exports.renderFile=function(){var args=Array.prototype.slice.call(arguments);var filename=args.shift();var cb;var opts={filename:filename};var data;var viewOpts;if(typeof arguments[arguments.length-1]=="function"){cb=args.pop()}if(args.length){data=args.shift();if(args.length){utils.shallowCopy(opts,args.pop())}else{if(data.settings){if(data.settings.views){opts.views=data.settings.views}if(data.settings["view cache"]){opts.cache=true}viewOpts=data.settings["view options"];if(viewOpts){utils.shallowCopy(opts,viewOpts)}}utils.shallowCopyFromList(opts,data,_OPTS_PASSABLE_WITH_DATA_EXPRESS)}opts.filename=filename}else{data={}}return tryHandleCache(opts,data,cb)};exports.Template=Template;exports.clearCache=function(){exports.cache.reset()};function Template(text,opts){opts=opts||{};var options={};this.templateText=text;this.mode=null;this.truncate=false;this.currentLine=1;this.source="";options.client=opts.client||false;options.escapeFunction=opts.escape||opts.escapeFunction||utils.escapeXML;options.compileDebug=opts.compileDebug!==false;options.debug=!!opts.debug;options.filename=opts.filename;options.openDelimiter=opts.openDelimiter||exports.openDelimiter||_DEFAULT_OPEN_DELIMITER;options.closeDelimiter=opts.closeDelimiter||exports.closeDelimiter||_DEFAULT_CLOSE_DELIMITER;options.delimiter=opts.delimiter||exports.delimiter||_DEFAULT_DELIMITER;options.strict=opts.strict||false;options.context=opts.context;options.cache=opts.cache||false;options.rmWhitespace=opts.rmWhitespace;options.root=opts.root;options.outputFunctionName=opts.outputFunctionName;options.localsName=opts.localsName||exports.localsName||_DEFAULT_LOCALS_NAME;options.views=opts.views;options.async=opts.async;options.destructuredLocals=opts.destructuredLocals;options.legacyInclude=typeof opts.legacyInclude!="undefined"?!!opts.legacyInclude:true;if(options.strict){options._with=false}else{options._with=typeof opts._with!="undefined"?opts._with:true}this.opts=options;this.regex=this.createRegex()}Template.modes={EVAL:"eval",ESCAPED:"escaped",RAW:"raw",COMMENT:"comment",LITERAL:"literal"};Template.prototype={createRegex:function(){var str=_REGEX_STRING;var delim=utils.escapeRegExpChars(this.opts.delimiter);var open=utils.escapeRegExpChars(this.opts.openDelimiter);var close=utils.escapeRegExpChars(this.opts.closeDelimiter);str=str.replace(/%/g,delim).replace(//g,close);return new RegExp(str)},compile:function(){var src;var fn;var opts=this.opts;var prepended="";var appended="";var escapeFn=opts.escapeFunction;var ctor;if(!this.source){this.generateSource();prepended+=' var __output = "";\n'+" function __append(s) { if (s !== undefined && s !== null) __output += s }\n";if(opts.outputFunctionName){prepended+=" var "+opts.outputFunctionName+" = __append;"+"\n"}if(opts.destructuredLocals&&opts.destructuredLocals.length){var destructuring=" var __locals = ("+opts.localsName+" || {}),\n";for(var i=0;i0){destructuring+=",\n "}destructuring+=name+" = __locals."+name}prepended+=destructuring+";\n"}if(opts._with!==false){prepended+=" with ("+opts.localsName+" || {}) {"+"\n";appended+=" }"+"\n"}appended+=" return __output;"+"\n";this.source=prepended+this.source+appended}if(opts.compileDebug){src="var __line = 1"+"\n"+" , __lines = "+JSON.stringify(this.templateText)+"\n"+" , __filename = "+(opts.filename?JSON.stringify(opts.filename):"undefined")+";"+"\n"+"try {"+"\n"+this.source+"} catch (e) {"+"\n"+" rethrow(e, __lines, __filename, __line, escapeFn);"+"\n"+"}"+"\n"}else{src=this.source}if(opts.client){src="escapeFn = escapeFn || "+escapeFn.toString()+";"+"\n"+src;if(opts.compileDebug){src="rethrow = rethrow || "+rethrow.toString()+";"+"\n"+src}}if(opts.strict){src='"use strict";\n'+src}if(opts.debug){console.log(src)}if(opts.compileDebug&&opts.filename){src=src+"\n"+"//# sourceURL="+opts.filename+"\n"}try{if(opts.async){try{ctor=new Function("return (async function(){}).constructor;")()}catch(e){if(e instanceof SyntaxError){throw new Error("This environment does not support async/await")}else{throw e}}}else{ctor=Function}fn=new ctor(opts.localsName+", escapeFn, include, rethrow",src)}catch(e){if(e instanceof SyntaxError){if(opts.filename){e.message+=" in "+opts.filename}e.message+=" while compiling ejs\n\n";e.message+="If the above error is not helpful, you may want to try EJS-Lint:\n";e.message+="https://github.com/RyanZim/EJS-Lint";if(!opts.async){e.message+="\n";e.message+="Or, if you meant to create an async function, pass `async: true` as an option."}}throw e}var returnedFn=opts.client?fn:function anonymous(data){var include=function(path,includeData){var d=utils.shallowCopy({},data);if(includeData){d=utils.shallowCopy(d,includeData)}return includeFile(path,opts)(d)};return fn.apply(opts.context,[data||{},escapeFn,include,rethrow])};if(opts.filename&&typeof Object.defineProperty==="function"){var filename=opts.filename;var basename=path.basename(filename,path.extname(filename));try{Object.defineProperty(returnedFn,"name",{value:basename,writable:false,enumerable:false,configurable:true})}catch(e){}}return returnedFn},generateSource:function(){var opts=this.opts;if(opts.rmWhitespace){this.templateText=this.templateText.replace(/[\r\n]+/g,"\n").replace(/^\s+|\s+$/gm,"")}this.templateText=this.templateText.replace(/[ \t]*<%_/gm,"<%_").replace(/_%>[ \t]*/gm,"_%>");var self=this;var matches=this.parseTemplateText();var d=this.opts.delimiter;var o=this.opts.openDelimiter;var c=this.opts.closeDelimiter;if(matches&&matches.length){matches.forEach(function(line,index){var closing;if(line.indexOf(o+d)===0&&line.indexOf(o+d+d)!==0){closing=matches[index+2];if(!(closing==d+c||closing=="-"+d+c||closing=="_"+d+c)){throw new Error('Could not find matching close tag for "'+line+'".')}}self.scanLine(line)})}},parseTemplateText:function(){var str=this.templateText;var pat=this.regex;var result=pat.exec(str);var arr=[];var firstPos;while(result){firstPos=result.index;if(firstPos!==0){arr.push(str.substring(0,firstPos));str=str.slice(firstPos)}arr.push(result[0]);str=str.slice(result[0].length);result=pat.exec(str)}if(str){arr.push(str)}return arr},_addOutput:function(line){if(this.truncate){line=line.replace(/^(?:\r\n|\r|\n)/,"");this.truncate=false}if(!line){return line}line=line.replace(/\\/g,"\\\\");line=line.replace(/\n/g,"\\n");line=line.replace(/\r/g,"\\r");line=line.replace(/"/g,'\\"');this.source+=' ; __append("'+line+'")'+"\n"},scanLine:function(line){var self=this;var d=this.opts.delimiter;var o=this.opts.openDelimiter;var c=this.opts.closeDelimiter;var newLineCount=0;newLineCount=line.split("\n").length-1;switch(line){case o+d:case o+d+"_":this.mode=Template.modes.EVAL;break;case o+d+"=":this.mode=Template.modes.ESCAPED;break;case o+d+"-":this.mode=Template.modes.RAW;break;case o+d+"#":this.mode=Template.modes.COMMENT;break;case o+d+d:this.mode=Template.modes.LITERAL;this.source+=' ; __append("'+line.replace(o+d+d,o+d)+'")'+"\n";break;case d+d+c:this.mode=Template.modes.LITERAL;this.source+=' ; __append("'+line.replace(d+d+c,d+c)+'")'+"\n";break;case d+c:case"-"+d+c:case"_"+d+c:if(this.mode==Template.modes.LITERAL){this._addOutput(line)}this.mode=null;this.truncate=line.indexOf("-")===0||line.indexOf("_")===0;break;default:if(this.mode){switch(this.mode){case Template.modes.EVAL:case Template.modes.ESCAPED:case Template.modes.RAW:if(line.lastIndexOf("//")>line.lastIndexOf("\n")){line+="\n"}}switch(this.mode){case Template.modes.EVAL:this.source+=" ; "+line+"\n";break;case Template.modes.ESCAPED:this.source+=" ; __append(escapeFn("+stripSemi(line)+"))"+"\n";break;case Template.modes.RAW:this.source+=" ; __append("+stripSemi(line)+")"+"\n";break;case Template.modes.COMMENT:break;case Template.modes.LITERAL:this._addOutput(line);break}}else{this._addOutput(line)}}if(self.opts.compileDebug&&newLineCount){this.currentLine+=newLineCount;this.source+=" ; __line = "+this.currentLine+"\n"}}};exports.escapeXML=utils.escapeXML;exports.__express=exports.renderFile;exports.VERSION=_VERSION_STRING;exports.name=_NAME;if(typeof window!="undefined"){window.ejs=exports}},{"../package.json":6,"./utils":2,fs:3,path:4}],2:[function(require,module,exports){"use strict";var regExpChars=/[|\\{}()[\]^$+*?.]/g;exports.escapeRegExpChars=function(string){if(!string){return""}return String(string).replace(regExpChars,"\\$&")};var _ENCODE_HTML_RULES={"&":"&","<":"<",">":">",'"':""","'":"'"};var _MATCH_HTML=/[&<>'"]/g;function encode_char(c){return _ENCODE_HTML_RULES[c]||c}var escapeFuncStr="var _ENCODE_HTML_RULES = {\n"+' "&": "&"\n'+' , "<": "<"\n'+' , ">": ">"\n'+' , \'"\': """\n'+' , "\'": "'"\n'+" }\n"+" , _MATCH_HTML = /[&<>'\"]/g;\n"+"function encode_char(c) {\n"+" return _ENCODE_HTML_RULES[c] || c;\n"+"};\n";exports.escapeXML=function(markup){return markup==undefined?"":String(markup).replace(_MATCH_HTML,encode_char)};exports.escapeXML.toString=function(){return Function.prototype.toString.call(this)+";\n"+escapeFuncStr};exports.shallowCopy=function(to,from){from=from||{};for(var p in from){to[p]=from[p]}return to};exports.shallowCopyFromList=function(to,from,list){for(var i=0;i=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;var splitPath=function(filename){return splitPathRe.exec(filename).slice(1)};exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:process.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.isAbsolute=function(path){return path.charAt(0)==="/"};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=="string"){throw new TypeError("Arguments to path.join must be strings")}return p}).join("/"))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i1){for(var i=1;i (http://fleegix.org)",license:"Apache-2.0",main:"./lib/ejs.js",repository:{type:"git",url:"git://github.com/mde/ejs.git"},bugs:"https://github.com/mde/ejs/issues",homepage:"https://github.com/mde/ejs",dependencies:{},devDependencies:{browserify:"^13.1.1",eslint:"^4.14.0","git-directory-deploy":"^1.5.1",jake:"^10.3.1",jsdoc:"^3.4.0","lru-cache":"^4.0.1",mocha:"^5.0.5","uglify-js":"^3.3.16"},engines:{node:">=0.10.0"},scripts:{test:"mocha",postinstall:"node --harmony ./postinstall.js"}}},{}]},{},[1])(1)}); diff --git a/cmd/agent/gui/views/private/js/javascript.js b/cmd/agent/gui/views/private/js/javascript.js index 66d800a586e34..d8c6c9eb25ed6 100644 --- a/cmd/agent/gui/views/private/js/javascript.js +++ b/cmd/agent/gui/views/private/js/javascript.js @@ -124,7 +124,7 @@ function checkStatus() { if (checkStatus.uptime > last_ts) { $("#restart_status").hide() } - checkStatus.uptime = last_ts + checkStatus.uptime = last_ts },function() { $("#agent_status").html("Not connected
to Agent"); $("#agent_status").css({ @@ -153,28 +153,6 @@ function loadStatus(page) { sendMessage("agent/status/" + page, "", "post", function(data, status, xhr){ $("#" + page + "_status").html(DOMPurify.sanitize(data)); - - // Get the trace-agent status - sendMessage("agent/getConfig/apm_config.receiver_port", "", "GET", - function(data, status, xhr) { - var apmPort = data["apm_config.debug.port"]; - if (apmPort == null) { - apmPort = "5012"; - } - var url = "http://127.0.0.1:"+apmPort+"/debug/vars" - $.ajax({ - url: url, - type: "GET", - success: function(data) { - $("#apmStats > .stat_data").html(ejs.render(apmTemplate, data)); - }, - error: function() { - $("#apmStats > .stat_data").text("Status: Not running or not on localhost."); - } - }) - }, function() { - $("#apmStats > .stat_data").html("Could not obtain trace-agent port from API."); - }) },function(){ $("#" + page + "_status").html("An error occurred."); }); diff --git a/cmd/agent/gui/views/templates/generalStatus.tmpl b/cmd/agent/gui/views/templates/generalStatus.tmpl index fd993865c4cdc..d60bcc18466cd 100644 --- a/cmd/agent/gui/views/templates/generalStatus.tmpl +++ b/cmd/agent/gui/views/templates/generalStatus.tmpl @@ -478,9 +478,61 @@ -
+
APM - Loading... + + {{- with .apmStats -}} + {{- if .error }} + Not running or unreachable on localhost:{{.port}}
+ Error: {{.error}}
+ {{- else}} + Status: Running
+ Pid: {{.pid}}
+ Uptime: {{.uptime}} seconds
+ Mem alloc: {{humanize .memstats.Alloc}} bytes
+ Hostname: {{.config.Hostname}}
+ Receiver: {{.config.ReceiverHost}}:{{.config.ReceiverPort}}
+ Endpoints: + + {{- range $i, $e := .config.Endpoints}} + {{ $e.Host }}
+ {{- end }} +
+ Receiver (previous minute) + + {{- if eq (len .receiver) 0}} + No traces received in the previous minute.
+ {{ else }} + {{ range $i, $ts := .receiver }} + From {{if $ts.Lang}}{{ $ts.Lang }} {{ $ts.LangVersion }} ({{ $ts.Interpreter }}), client {{ $ts.TracerVersion }}{{else}}unknown clients{{end}} + + Traces received: {{ $ts.TracesReceived }} ({{ humanize $ts.TracesBytes }} bytes)
+ Spans received: {{ $ts.SpansReceived }} + {{ with $ts.WarnString }} +
WARNING: {{ . }}
+ {{ end }} +
+ {{ end }} + {{ end }} + + {{range $key, $value := .ratebyservice_filtered -}} + {{- if eq $key "service:,env:" -}} + Default priority sampling rate: {{percent $value}}% + {{- else}} + Priority sampling rate for '{{ $key }}': {{percent $value}}% + {{- end}} + {{- end }} +
+ Writer (previous minute) + + Traces: {{.trace_writer.Payloads}} payloads, {{.trace_writer.Traces}} traces, {{.trace_writer.Events}} events, {{humanize .trace_writer.Bytes}} bytes
+ {{- if gt .trace_writer.Errors 0.0}}WARNING: Traces API errors (1 min): {{.trace_writer.Errors}}{{end}} + Stats: {{.stats_writer.Payloads}} payloads, {{.stats_writer.StatsBuckets}} stats buckets, {{humanize .stats_writer.Bytes}} bytes
+ {{- if gt .stats_writer.Errors 0.0}}WARNING: Stats API errors (1 min): {{.stats_writer.Errors}}{{end}} +
+ {{- end }} + {{ end }} +
@@ -532,7 +584,8 @@ {{- end }} {{ end }} -
+
+
diff --git a/cmd/agent/gui/views/templates/index.tmpl b/cmd/agent/gui/views/templates/index.tmpl index 78723c4b4e7b4..daed6e5788e09 100644 --- a/cmd/agent/gui/views/templates/index.tmpl +++ b/cmd/agent/gui/views/templates/index.tmpl @@ -8,7 +8,6 @@ - diff --git a/cmd/agent/subcommands/status/command.go b/cmd/agent/subcommands/status/command.go index 88b30ee873dc1..a86a5d61e0076 100644 --- a/cmd/agent/subcommands/status/command.go +++ b/cmd/agent/subcommands/status/command.go @@ -11,10 +11,8 @@ import ( "encoding/json" "errors" "fmt" - "net/http" "net/url" "os" - "time" "go.uber.org/fx" @@ -162,14 +160,6 @@ func requestStatus(config config.Component, cliParams *cliParams) error { if err != nil { return err } - // attach trace-agent status, if obtainable - temp := make(map[string]interface{}) - if err := json.Unmarshal(r, &temp); err == nil { - temp["apmStats"] = getAPMStatus(config) - if newr, err := json.Marshal(temp); err == nil { - r = newr - } - } // The rendering is done in the client so that the agent has less work to do if cliParams.prettyPrintJSON { @@ -195,30 +185,6 @@ func requestStatus(config config.Component, cliParams *cliParams) error { return nil } -// getAPMStatus returns a set of key/value pairs summarizing the status of the trace-agent. -// If the status can not be obtained for any reason, the returned map will contain an "error" -// key with an explanation. -func getAPMStatus(config config.Component) map[string]interface{} { - port := config.GetInt("apm_config.debug.port") - url := fmt.Sprintf("http://localhost:%d/debug/vars", port) - resp, err := (&http.Client{Timeout: 2 * time.Second}).Get(url) - if err != nil { - return map[string]interface{}{ - "port": port, - "error": err.Error(), - } - } - defer resp.Body.Close() - status := make(map[string]interface{}) - if err := json.NewDecoder(resp.Body).Decode(&status); err != nil { - return map[string]interface{}{ - "port": port, - "error": err.Error(), - } - } - return status -} - func componentStatusCmd(log log.Component, config config.Component, cliParams *cliParams) error { if len(cliParams.args) != 1 { return fmt.Errorf("a component name must be specified") diff --git a/pkg/status/apm/apm.go b/pkg/status/apm/apm.go new file mode 100644 index 0000000000000..e5576ded12793 --- /dev/null +++ b/pkg/status/apm/apm.go @@ -0,0 +1,59 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package apm fetch information about the apm agent. +// This will, in time, be migrated to the apm agent component. +package apm + +import ( + "encoding/json" + "fmt" + "net/http" + "sync" + + apiutil "github.com/DataDog/datadog-agent/pkg/api/util" + "github.com/DataDog/datadog-agent/pkg/config" +) + +// httpClients should be reused instead of created as needed. They keep cached TCP connections +// that may leak otherwise +var ( + httpClient *http.Client + clientInitOnce sync.Once +) + +func client() *http.Client { + clientInitOnce.Do(func() { + httpClient = apiutil.GetClient(false) + }) + + return httpClient +} + +// GetAPMStatus returns a set of key/value pairs summarizing the status of the trace-agent. +// If the status can not be obtained for any reason, the returned map will contain an "error" +// key with an explanation. +func GetAPMStatus() map[string]interface{} { + port := config.Datadog.GetInt("apm_config.debug.port") + + c := client() + url := fmt.Sprintf("http://localhost:%d/debug/vars", port) + resp, err := apiutil.DoGet(c, url, apiutil.CloseConnection) + if err != nil { + return map[string]interface{}{ + "port": port, + "error": err.Error(), + } + } + + status := make(map[string]interface{}) + if err := json.Unmarshal(resp, &status); err != nil { + return map[string]interface{}{ + "port": port, + "error": err.Error(), + } + } + return status +} diff --git a/pkg/status/status.go b/pkg/status/status.go index 21696dc0ac489..a3f86b0c6cabb 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -30,6 +30,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/config/utils" logsStatus "github.com/DataDog/datadog-agent/pkg/logs/status" "github.com/DataDog/datadog-agent/pkg/snmp/traps" + "github.com/DataDog/datadog-agent/pkg/status/apm" "github.com/DataDog/datadog-agent/pkg/status/collector" "github.com/DataDog/datadog-agent/pkg/status/jmx" "github.com/DataDog/datadog-agent/pkg/status/otlp" @@ -82,6 +83,7 @@ func GetStatus(verbose bool, invAgent inventoryagent.Component) (map[string]inte } stats["processAgentStatus"] = GetProcessAgentStatus() + stats["apmStats"] = apm.GetAPMStatus() if !config.Datadog.GetBool("no_proxy_nonexact_match") { stats["TransportWarnings"] = httputils.GetNumberOfWarnings() > 0