From b404ddab20f2f6e833d58ff808b2477a00581546 Mon Sep 17 00:00:00 2001 From: shakuzen Date: Fri, 8 Dec 2023 05:33:28 +0000 Subject: [PATCH] deploy: ef16b69edb390fbbf9353855fd3350c4dca7d5a9 --- asset-manifest.json | 6 +++--- index.html | 2 +- .../js/{main.5d59054d.chunk.js => main.38af4cc0.chunk.js} | 4 ++-- ...ain.5d59054d.chunk.js.map => main.38af4cc0.chunk.js.map} | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename static/js/{main.5d59054d.chunk.js => main.38af4cc0.chunk.js} (68%) rename static/js/{main.5d59054d.chunk.js.map => main.38af4cc0.chunk.js.map} (71%) diff --git a/asset-manifest.json b/asset-manifest.json index 74e39c9..1c01178 100644 --- a/asset-manifest.json +++ b/asset-manifest.json @@ -1,8 +1,8 @@ { "files": { "main.css": "/static/css/main.6901d5e3.chunk.css", - "main.js": "/static/js/main.5d59054d.chunk.js", - "main.js.map": "/static/js/main.5d59054d.chunk.js.map", + "main.js": "/static/js/main.38af4cc0.chunk.js", + "main.js.map": "/static/js/main.38af4cc0.chunk.js.map", "runtime-main.js": "/static/js/runtime-main.a024820b.js", "runtime-main.js.map": "/static/js/runtime-main.a024820b.js.map", "static/css/2.1c8e5216.chunk.css": "/static/css/2.1c8e5216.chunk.css", @@ -55,6 +55,6 @@ "static/css/2.1c8e5216.chunk.css", "static/js/2.bcd91392.chunk.js", "static/css/main.6901d5e3.chunk.css", - "static/js/main.5d59054d.chunk.js" + "static/js/main.38af4cc0.chunk.js" ] } \ No newline at end of file diff --git a/index.html b/index.html index 7dfe820..59ce5da 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -Micrometer Application Observability
\ No newline at end of file +Micrometer Application Observability
\ No newline at end of file diff --git a/static/js/main.5d59054d.chunk.js b/static/js/main.38af4cc0.chunk.js similarity index 68% rename from static/js/main.5d59054d.chunk.js rename to static/js/main.38af4cc0.chunk.js index da0877c..e280ca0 100644 --- a/static/js/main.5d59054d.chunk.js +++ b/static/js/main.38af4cc0.chunk.js @@ -1,2 +1,2 @@ -(this["webpackJsonpmicrometer-docs"]=this["webpackJsonpmicrometer-docs"]||[]).push([[0],Array(28).concat([function(e,t,n){var r=n(46);r.registerLanguage("gradle",n(47)),r.registerLanguage("groovy",n(48)),r.registerLanguage("http",n(49)),r.registerLanguage("java",n(50)),r.registerLanguage("xml",n(51)),r.registerLanguage("yaml",n(52)),r.registerLanguage("json",n(53)),e.exports=r},,,,,,,,,function(e,t,n){},,,,,,function(e,t,n){},,,function(e,t,n){!function(e){"object"===typeof window&&window||"object"===typeof self&&self;(function(e){var t=[],n=Object.keys,r={},a={},i=/^(no-?highlight|plain|text)$/i,o=/\blang(?:uage)?-([\w-]+)\b/i,s=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,c="",l={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function u(e){return e.replace(/&/g,"&").replace(//g,">")}function d(e){return e.nodeName.toLowerCase()}function m(e,t){var n=e&&e.exec(t);return n&&0===n.index}function g(e){return i.test(e)}function p(e){var t,n,r,a,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",n=o.exec(i))return R(n[1])?n[1]:"no-highlight";for(t=0,r=(i=i.split(/\s+/)).length;t"}function l(e){i+=""}function m(e){("start"===e.event?c:l)(e.node)}for(;e.length||n.length;){var g=s();if(i+=u(r.substring(a,g[0].offset)),a=g[0].offset,g===e){o.reverse().forEach(l);do{m(g.splice(0,1)[0]),g=s()}while(g===e&&g.length&&g[0].offset===a);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),m(g.splice(0,1)[0])}return i+u(r.substr(a))}function y(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(t){return h(e,{variants:null},t)}))),e.cached_variants||e.endsWithParent&&[h(e)]||[e]}function v(e){function t(e){return e&&e.source||e}function r(n,r){return new RegExp(t(n),"m"+(e.case_insensitive?"i":"")+(r?"g":""))}function a(i,o){if(!i.compiled){if(i.compiled=!0,i.keywords=i.keywords||i.beginKeywords,i.keywords){var s={},c=function(t,n){e.case_insensitive&&(n=n.toLowerCase()),n.split(" ").forEach((function(e){var n=e.split("|");s[n[0]]=[t,n[1]?Number(n[1]):1]}))};"string"===typeof i.keywords?c("keyword",i.keywords):n(i.keywords).forEach((function(e){c(e,i.keywords[e])})),i.keywords=s}i.lexemesRe=r(i.lexemes||/\w+/,!0),o&&(i.beginKeywords&&(i.begin="\\b("+i.beginKeywords.split(" ").join("|")+")\\b"),i.begin||(i.begin=/\B|\b/),i.beginRe=r(i.begin),i.end||i.endsWithParent||(i.end=/\B|\b/),i.end&&(i.endRe=r(i.end)),i.terminator_end=t(i.end)||"",i.endsWithParent&&o.terminator_end&&(i.terminator_end+=(i.end?"|":"")+o.terminator_end)),i.illegal&&(i.illegalRe=r(i.illegal)),null==i.relevance&&(i.relevance=1),i.contains||(i.contains=[]),i.contains=Array.prototype.concat.apply([],i.contains.map((function(e){return y("self"===e?i:e)}))),i.contains.forEach((function(e){a(e,i)})),i.starts&&a(i.starts,o);var l=i.contains.map((function(e){return e.beginKeywords?"\\.?("+e.begin+")\\.?":e.begin})).concat([i.terminator_end,i.illegal]).map(t).filter(Boolean);i.terminators=l.length?r(l.join("|"),!0):{exec:function(){return null}}}}a(e)}function w(e,t,n,a){function i(e,t){var n,r;for(n=0,r=t.contains.length;n')+t+(n?"":c)}function p(){var e,t,n,r;if(!O.keywords)return u(I);for(r="",t=0,O.lexemesRe.lastIndex=0,n=O.lexemesRe.exec(I);n;)r+=u(I.substring(t,n.index)),(e=d(O,n))?(k+=e[1],r+=g(e[0],u(n[0]))):r+=u(n[0]),t=O.lexemesRe.lastIndex,n=O.lexemesRe.exec(I);return r+u(I.substr(t))}function h(){var e="string"===typeof O.subLanguage;if(e&&!r[O.subLanguage])return u(I);var t=e?w(O.subLanguage,I,!0,M[O.subLanguage]):T(I,O.subLanguage.length?O.subLanguage:void 0);return O.relevance>0&&(k+=t.relevance),e&&(M[O.subLanguage]=t.top),g(t.language,t.value,!1,!0)}function f(){C+=null!=O.subLanguage?h():p(),I=""}function b(e){C+=e.className?g(e.className,"",!0):"",O=Object.create(e,{parent:{value:O}})}function y(e,t){if(I+=e,null==t)return f(),0;var n=i(t,O);if(n)return n.skip?I+=t:(n.excludeBegin&&(I+=t),f(),n.returnBegin||n.excludeBegin||(I=t)),b(n,t),n.returnBegin?0:t.length;var r=o(O,t);if(r){var a=O;a.skip?I+=t:(a.returnEnd||a.excludeEnd||(I+=t),f(),a.excludeEnd&&(I=t));do{O.className&&(C+=c),O.skip||O.subLanguage||(k+=O.relevance),O=O.parent}while(O!==r.parent);return r.starts&&b(r.starts,""),a.returnEnd?0:t.length}if(s(t,O))throw new Error('Illegal lexeme "'+t+'" for mode "'+(O.className||"")+'"');return I+=t,t.length||1}var x=R(e);if(!x)throw new Error('Unknown language: "'+e+'"');v(x);var S,O=a||x,M={},C="";for(S=O;S!==x;S=S.parent)S.className&&(C=g(S.className,"",!0)+C);var I="",k=0;try{for(var E,A,j=0;O.terminators.lastIndex=j,E=O.terminators.exec(t);)A=y(t.substring(j,E.index),E[0]),j=E.index+A;for(y(t.substr(j)),S=O;S.parent;S=S.parent)S.className&&(C+=c);return{relevance:k,value:C,language:e,top:O}}catch(D){if(D.message&&-1!==D.message.indexOf("Illegal"))return{relevance:0,value:u(t)};throw D}}function T(e,t){t=t||l.languages||n(r);var a={relevance:0,value:u(e)},i=a;return t.filter(R).forEach((function(t){var n=w(t,e,!1);n.language=t,n.relevance>i.relevance&&(i=n),n.relevance>a.relevance&&(i=a,a=n)})),i.language&&(a.second_best=i),a}function x(e){return l.tabReplace||l.useBR?e.replace(s,(function(e,t){return l.useBR&&"\n"===e?"
":l.tabReplace?t.replace(/\t/g,l.tabReplace):""})):e}function S(e,t,n){var r=t?a[t]:n,i=[e.trim()];return e.match(/\bhljs\b/)||i.push("hljs"),-1===e.indexOf(r)&&i.push(r),i.join(" ").trim()}function O(e){var t,n,r,a,i,o=p(e);g(o)||(l.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):t=e,i=t.textContent,r=o?w(o,i,!0):T(i),(n=f(t)).length&&((a=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=r.value,r.value=b(n,f(a),i)),r.value=x(r.value),e.innerHTML=r.value,e.className=S(e.className,o,r.language),e.result={language:r.language,re:r.relevance},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.relevance}))}function M(e){l=h(l,e)}function C(){if(!C.called){C.called=!0;var e=document.querySelectorAll("pre code");t.forEach.call(e,O)}}function I(){addEventListener("DOMContentLoaded",C,!1),addEventListener("load",C,!1)}function k(t,n){var i=r[t]=n(e);i.aliases&&i.aliases.forEach((function(e){a[e]=t}))}function E(){return n(r)}function R(e){return e=(e||"").toLowerCase(),r[e]||r[a[e]]}e.highlight=w,e.highlightAuto=T,e.fixMarkup=x,e.highlightBlock=O,e.configure=M,e.initHighlighting=C,e.initHighlightingOnLoad=I,e.registerLanguage=k,e.listLanguages=E,e.getLanguage=R,e.inherit=h,e.IDENT_RE="[a-zA-Z]\\w*",e.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",e.NUMBER_RE="\\b\\d+(\\.\\d+)?",e.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BINARY_NUMBER_RE="\\b(0b[01]+)",e.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BACKSLASH_ESCAPE={begin:"\\\\[\\s\\S]",relevance:0},e.APOS_STRING_MODE={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},e.QUOTE_STRING_MODE={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},e.PHRASAL_WORDS_MODE={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.COMMENT=function(t,n,r){var a=e.inherit({className:"comment",begin:t,end:n,contains:[]},r||{});return a.contains.push(e.PHRASAL_WORDS_MODE),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|XXX):",relevance:0}),a},e.C_LINE_COMMENT_MODE=e.COMMENT("//","$"),e.C_BLOCK_COMMENT_MODE=e.COMMENT("/\\*","\\*/"),e.HASH_COMMENT_MODE=e.COMMENT("#","$"),e.NUMBER_MODE={className:"number",begin:e.NUMBER_RE,relevance:0},e.C_NUMBER_MODE={className:"number",begin:e.C_NUMBER_RE,relevance:0},e.BINARY_NUMBER_MODE={className:"number",begin:e.BINARY_NUMBER_RE,relevance:0},e.CSS_NUMBER_MODE={className:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},e.REGEXP_MODE={className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0,contains:[e.BACKSLASH_ESCAPE]}]},e.TITLE_MODE={className:"title",begin:e.IDENT_RE,relevance:0},e.UNDERSCORE_TITLE_MODE={className:"title",begin:e.UNDERSCORE_IDENT_RE,relevance:0},e.METHOD_GUARD={begin:"\\.\\s*"+e.UNDERSCORE_IDENT_RE,relevance:0}})(t)}()},function(e,t){e.exports=function(e){return{case_insensitive:!0,keywords:{keyword:"task project allprojects subprojects artifacts buildscript configurations dependencies repositories sourceSets description delete from into include exclude source classpath destinationDir includes options sourceCompatibility targetCompatibility group flatDir doLast doFirst flatten todir fromdir ant def abstract break case catch continue default do else extends final finally for if implements instanceof native new private protected public return static switch synchronized throw throws transient try volatile while strictfp package import false null super this true antlrtask checkstyle codenarc copy boolean byte char class double float int interface long short void compile runTime file fileTree abs any append asList asWritable call collect compareTo count div dump each eachByte eachFile eachLine every find findAll flatten getAt getErr getIn getOut getText grep immutable inject inspect intersect invokeMethods isCase join leftShift minus multiply newInputStream newOutputStream newPrintWriter newReader newWriter next plus pop power previous print println push putAt read readBytes readLines reverse reverseEach round size sort splitEachLine step subMap times toInteger toList tokenize upto waitForOrKill withPrintWriter withReader withStream withWriter withWriterAppend write writeLine"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,e.REGEXP_MODE]}}},function(e,t){e.exports=function(e){return{keywords:{literal:"true false null",keyword:"byte short char int long boolean float double void def as in assert trait super this abstract static volatile transient public private protected synchronized final class interface enum if else for while switch case break default continue throw throws try catch finally implements extends new import package return instanceof"},contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string",begin:'"""',end:'"""'},{className:"string",begin:"'''",end:"'''"},{className:"string",begin:"\\$/",end:"/\\$",relevance:10},e.APOS_STRING_MODE,{className:"regexp",begin:/~?\/[^\/\n]+\//,contains:[e.BACKSLASH_ESCAPE]},e.QUOTE_STRING_MODE,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},e.BINARY_NUMBER_MODE,{className:"class",beginKeywords:"class interface trait enum",end:"{",illegal:":",contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"},{className:"string",begin:/[^\?]{0}[A-Za-z0-9_$]+ *:/},{begin:/\?/,end:/\:/},{className:"symbol",begin:"^\\s*[A-Za-z0-9_$]+:",relevance:0}],illegal:/#|<\//}}},function(e,t){e.exports=function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],illegal:"\\S",contains:[{begin:"^"+t,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+t+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:t},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}},function(e,t){e.exports=function(e){var t="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",n={className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0};return{aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(<[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(\\s*,\\s*[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},n,{className:"meta",begin:"@[A-Za-z]+"}]}}},function(e,t){e.exports=function(e){var t={endsWithParent:!0,illegal:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[{begin:"\\[",end:"\\]"}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{begin:/<\?(php)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0}]},{className:"tag",begin:"|$)",end:">",keywords:{name:"style"},contains:[t],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:"|$)",end:">",keywords:{name:"script"},contains:[t],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["actionscript","javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},t]}]}}},function(e,t){e.exports=function(e){var t="true false yes no null",n="^[ \\-]*",r="[a-zA-Z_][\\w\\-]*",a={className:"attr",variants:[{begin:n+r+":"},{begin:'^[ \\-]*"'+r+'":'},{begin:"^[ \\-]*'"+r+"':"}]},i={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]};return{case_insensitive:!0,aliases:["yml","YAML","yaml"],contains:[a,{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>] *$",returnEnd:!0,contains:i.contains,end:a.variants[0].begin},{className:"type",begin:"!!"+e.UNDERSCORE_IDENT_RE},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"^ *-",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},e.C_NUMBER_MODE,i]}}},function(e,t){e.exports=function(e){var t={literal:"true false null"},n=[e.QUOTE_STRING_MODE,e.C_NUMBER_MODE],r={end:",",endsWithParent:!0,excludeEnd:!0,contains:n,keywords:t},a={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE],illegal:"\\n"},e.inherit(r,{begin:/:/})],illegal:"\\S"},i={begin:"\\[",end:"\\]",contains:[e.inherit(r)],illegal:"\\S"};return n.splice(n.length,0,a,i),{contains:n,keywords:t,illegal:"\\S"}}},function(e,t,n){},,,,,,,,,function(e,t,n){},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer contains a core library with the instrumentation SPI and an in-memory implementation that does not export data anywhere, a series of modules with implementations for various monitoring systems, and a test module.\n\nTo use Micrometer, add the dependency for your monitoring system.\n\nThe following example adds Prometheus in Gradle:\n\n[source,groovy]\n----\nimplementation 'io.micrometer:micrometer-registry-prometheus:latest.release'\n----\n\nThe following example adds Prometheus in Maven:\n\n[source,xml]\n----\n\n io.micrometer\n micrometer-registry-prometheus\n ${micrometer.version}\n\n----\n\nThrough Micrometer's composite meter registry (described in greater detail in link:/docs/concepts#_composite_registries[\"Concepts\"]), you may configure more than one registry implementation if you intend to publish metrics to more than one monitoring system.\n\nIf you have not decided on a monitoring system yet and want only to try out the instrumentation SPI, you can add a dependency on `micrometer-core` instead and configure the `SimpleMeterRegistry`.\n\n== Snapshots\n\nEvery successful https://app.circleci.com/pipelines/github/micrometer-metrics/micrometer[build] of Micrometer's `main` and maintenance branches (for example, `1.7.x`) results in the publication of a new snapshot version. You can use the latest snapshot by adding the Maven repository `https://repo.spring.io/snapshot` to your build and using the corresponding snapshot version -- for example, `1.8.0-SNAPSHOT`.\n\n== Milestones\n\nMilestone releases are made available for early testing purposes and are not intended for production use.\nMilestone releases are published to https://repo.spring.io/milestone.\nInclude that as a Maven repository in your build configuration to use milestone releases.\nMilestones are marked as \"`pre-releases`\" on GitHub, and the version has a suffix, such as `-M1` or `-RC1` (milestone 1 or release candidate 1, respectively).\n"},function(e,t,n){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Concepts\n:toc:\n:sectnums:\n:dimensional: true\n\n== Purpose\n\nMicrometer is a metrics instrumentation library for JVM-based applications. It provides a simple facade over the instrumentation clients for the most popular monitoring systems, letting you instrument your JVM-based application code without vendor lock-in. It is designed to add little to no overhead to your metrics collection activity while maximizing the portability of your metrics effort.\n\nStarting from Micrometer 1.10, Micrometer provides the link:../docs/observation[Observation API] and a plugin mechanism that allows you to add capabilities including the tracing features. You can read more about this in the link:../docs/tracing[Micrometer Tracing documentation].\n\nFor better understanding the differences among these different types of systems (Metrics, Distributed Tracing, and Logging) we recommend Adrian Cole\'s talk, titled https://www.dotconferences.com/2017/04/adrian-cole-observability-3-ways-logging-metrics-tracing[Observability\n3 Ways]. To learn more about Micrometer Observation API we recommend Tommy Ludwig\'s and Marcin Grzejszczak\'s talk, titled https://www.youtube.com/watch?v=fh3VbrPvAjg[Observability of Your Application].\n\n== Dependencies\n\nThe `micrometer-core` module aims to have minimal dependencies. It does not require any third-party (non-Micrometer) dependencies on the classpath at compile time for applications using Micrometer.\n\nUse of the link:#_pause_detection[pause detection] feature requires the https://github.com/LatencyUtils/LatencyUtils[LatencyUtils] dependency to be available on the classpath at runtime. If your application does not use the pause detection feature, you can exclude LatencyUtils from your runtime classpath.\n\nhttps://github.com/HdrHistogram/HdrHistogram[HdrHistogram] is needed on the classpath at runtime if you use link:#_histograms_and_percentiles[client-side percentiles]. If you are not using client-side percentiles, you may exclude HdrHistogram from your application\'s runtime classpath.\n\n== Supported Monitoring Systems\n\n:leveloffset: +1\n\nMicrometer contains a core module with an instrumentation https://en.wikipedia.org/wiki/Service_provider_interface[SPI], a set of modules containing implementations for various monitoring systems (each is called a registry), and a test kit. You need to understand three important characteristics of monitoring systems:\n\n* *Dimensionality*. Whether the system supports metric names to be enriched with tag key/value pairs. If a system is not _dimensional_, it is _hierarchical_, which means it supports only a flat metric name. When publishing metrics to hierarchical systems, Micrometer flattens the set of tag key/value pairs and adds them to the name.\n\n[cols=2*,options="header"]\n|===\n|Dimensional\n|Hierarchical\n\n|AppOptics, Atlas, Azure Monitor, Cloudwatch, Datadog, Datadog StatsD, Dynatrace, Elastic, Humio, Influx, KairosDB, New Relic, Prometheus, SignalFx, Sysdig StatsD, Telegraf StatsD, Wavefront\n|Graphite, Ganglia, JMX, Etsy StatsD\n|===\n\n\n* *<>*. In this context, we mean aggregation of a set of samples over a prescribed time interval. Some monitoring systems expect some types of discrete samples (such as counts) to be converted to a rate by the application prior to being published. Other systems expect cumulative values to always be sent. Still others have no opinion on it either way.\n\n[cols=2*,options="header"]\n|===\n|Client-side\n|Server-side\n\n|AppOptics, Atlas, Azure Monitor, Datadog, Dynatrace, Elastic, Graphite, Ganglia, Humio, Influx, JMX, Kairos, New Relic, all StatsD flavors, SignalFx\n|Prometheus, Wavefront footnote:[As of 1.2.0, Micrometer sends cumulative values to Wavefront.]\n|===\n\n* *Publishing*. Some systems expect to poll applications for metrics at their leisure, while others expect metrics to be pushed to them on a regular interval.\n\n[cols=2*,options="header"]\n|===\n|Client pushes\n|Server polls\n\n|AppOptics, Atlas, Azure Monitor, Datadog, Dynatrace, Elastic, Graphite, Ganglia, Humio, Influx, JMX, Kairos, New Relic, SignalFx, Wavefront\n|Prometheus, all StatsD flavors\n|===\n\nThere are other, more minor, variations in expectations from one monitoring system to another, such as their conception of base units of measurement (particularly time) and the canonical naming convention for metrics. Micrometer customizes your metrics to meet these demands on a per-registry basis.\n\n:leveloffset!:\n\n== Registry\n\n:leveloffset: +1\n\nA `Meter` is the interface for collecting a set of measurements (which we individually call metrics) about your application. Meters in Micrometer are created from and held in a `MeterRegistry`. Each supported monitoring system has an implementation of `MeterRegistry`. How a registry is created varies for each implementation.\n\nMicrometer includes a `SimpleMeterRegistry` that holds the latest value of each meter in memory and does not export the data anywhere. If you do not yet have a preferred monitoring system, you can get started playing with metrics by using the simple registry:\n\n====\n[source,java]\n----\nMeterRegistry registry = new SimpleMeterRegistry();\n----\n====\n\nNOTE: A `SimpleMeterRegistry` is autowired for you in Spring-based applications.\n\n== Composite Registries\n\nMicrometer provides a `CompositeMeterRegistry` to which you can add multiple registries, letting you publish metrics to more than one monitoring system simultaneously:\n\n====\n[source,java]\n----\nCompositeMeterRegistry composite = new CompositeMeterRegistry();\n\nCounter compositeCounter = composite.counter("counter");\ncompositeCounter.increment(); <1>\n\nSimpleMeterRegistry simple = new SimpleMeterRegistry();\ncomposite.add(simple); <2>\n\ncompositeCounter.increment(); <3>\n----\n\n1. Increments are NOOP\'d until there is a registry in the composite. The counter\'s count still yields 0 at this point.\n2. A counter named `counter` is registered to the simple registry.\n3. The simple registry counter is incremented, along with counters for any other registries in the composite.\n====\n\n== Global Registry\n\nMicrometer provides a static global registry `Metrics.globalRegistry` and a set of static builders for generating meters based on this registry (note that `globalRegistry` is a composite registry):\n\n====\n[source,java]\n----\nclass MyComponent {\n Counter featureCounter = Metrics.counter("feature", "region", "test"); <1>\n\n void feature() {\n featureCounter.increment();\n }\n\n void feature2(String type) {\n Metrics.counter("feature.2", "type", type).increment(); <2>\n }\n}\n\nclass MyApplication {\n void start() {\n // wire your monitoring system to global static state\n Metrics.addRegistry(new SimpleMeterRegistry()); <3>\n }\n}\n----\n\n1. Wherever possible (and especially where instrumentation performance is critical), store `Meter` instances in fields to avoid a lookup on their name or tags on each use.\n2. When tags need to be determined from local context, you have no choice but to construct or lookup the Meter inside your method body. The lookup cost is just a single hash lookup, so it is acceptable for most use cases.\n3. It is OK to add registries _after_ meters have been created with code like `Metrics.counter(...)`. These meters are added to each registry, as it is bound to the global composite.\n====\n\n:leveloffset!:\n\n== Meters\n\n:leveloffset: +1\n\nMicrometer supports a set of `Meter` primitives, including `Timer`, `Counter`, `Gauge`, `DistributionSummary`, `LongTaskTimer`, `FunctionCounter`, `FunctionTimer`, and `TimeGauge`. Different meter types result in a different number of time series metrics. For example, while there is a single metric that represents a `Gauge`, a `Timer` measures both the count of timed events and the total time of all timed events.\n\nA meter is uniquely identified by its name and dimensions. We use the terms, "`dimensions`" and "`tags,`" interchangeably, and the Micrometer interface is `Tag` simply because it is shorter. As a general rule, it should be possible to use the name as a pivot. Dimensions let a particular named metric be sliced to drill down and reason about the data. This means that, if only the name is selected, you can drill down by using other dimensions and reason about the value being shown.\n\n:leveloffset!:\n\n== Naming Meters\n\n:leveloffset: +1\n\nMicrometer employs a naming convention that separates lowercase words with a `.` (dot) character. Different monitoring systems have different recommendations regarding naming convention, and some naming conventions may be incompatible between one system and another. Each Micrometer implementation for a monitoring system comes with a naming convention that transforms lowercase dot notation names to the monitoring system\'s recommended naming convention. Additionally, this naming convention implementation removes special characters that are disallowed by the monitoring system from the metric names and tags. You can override the default naming convention for a registry by implementing `NamingConvention` and setting it on the registry:\n\n====\n[source,java]\n----\nregistry.config().namingConvention(myCustomNamingConvention);\n----\n====\n\nWith naming conventions in place, the following timer registered in Micrometer looks good natively in a wide variety of monitoring systems:\n\n====\n[source,java]\n----\nregistry.timer("http.server.requests");\n----\n====\n\n. Prometheus - `http_server_requests_duration_seconds`\n. Atlas - `httpServerRequests`\n. Graphite - `http.server.requests`\n. InfluxDB - `http_server_requests`\n\nBy adhering to Micrometer\'s lowercase dot notation convention, you guarantee the maximum degree of portability for your metric names across monitoring systems.\n\n== Tag Naming\n\nTIP: We recommend that you follow the same lowercase dot notation described for meter names when naming tags. Using this consistent naming convention for tags allows for better translation into the respective monitoring system\'s idiomatic naming schemes.\n\nSuppose we are trying to measure the number of http requests and the number of database calls.\n\n*Recommended Approach*\n\n====\n[source,java]\n----\nregistry.counter("database.calls", "db", "users")\nregistry.counter("http.requests", "uri", "/api/users")\n----\n====\n\nThis variant provides enough context so that, if only the name is selected, the value can be reasoned about and is at least potentially meaningful. For example if we select `database.calls`, we can see the total number of calls to all databases. Then we can group by or select by `db` to drill down further or perform comparative analysis on the contribution of calls to each database.\n\n*Bad Approach*\n\n====\n[source,java]\n----\nregistry.counter("calls",\n "class", "database",\n "db", "users");\n\nregistry.counter("calls",\n "class", "http",\n "uri", "/api/users");\n----\n====\n\nIn this approach, if we select `calls`, we get a value that is an aggregate of the number of calls to the database and to our API endpoint. This time series is not useful without further dimensional drill-down.\n\n== Common Tags\n\nYou can define common tags at the registry level and add them to every metric reported to the monitoring system. This is generally used for dimensional drill-down on the operating environment, such as host, instance, region, stack, and others:\n\n====\n[source,java]\n----\nregistry.config().commonTags("stack", "prod", "region", "us-east-1");\nregistry.config().commonTags(Arrays.asList(Tag.of("stack", "prod"), Tag.of("region", "us-east-1"))); // equivalently\n----\n====\n\nCalls to `commonTags` append additional common tags.\n\n[IMPORTANT]\n====\nCommon tags generally have to be added to the registry _before_ any (possibly autoconfigured) meter binders. Depending on your environment, there are different ways to achieve this.\n\nIf you use Spring Boot, you have two options:\n\n* Add your common tags with https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.customizing.common-tags[configuration properties]\n* If you need more flexibility (for example, you have to add common tags to a registry defined in a shared library), register a `MeterRegistryCustomizer` callback interface as a bean to add your common tags. See the\nhttps://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.getting-started[Spring Boot Reference Documentation] for more information.\n====\n\n== Tag Values\n\nTag values must be non-null.\n\nWARNING: Beware of the potential for tag values coming from user-supplied sources to blow up the cardinality of a metric. You should always carefully normalize and add bounds to user-supplied input. Sometimes, the cause is sneaky. Consider the URI tag for recording HTTP requests on service endpoints. If we do not constrain 404\'s to a value like NOT_FOUND, the dimensionality of the metric would grow with each resource that cannot be found.\n\n:leveloffset!:\n\n== Meter Filters\n\n:leveloffset: +1\n\nYou can configure each registry with meter filters, which give you greater control over how and when meters are registered and what kinds of statistics they emit. Meter filters serve three basic functions:\n\n1. **Deny** (or **Accept**) meters being registered.\n2. **Transform** meter IDs (for example, changing the name, adding or removing tags, and changing description or base units).\n3. **Configure** distribution statistics for some meter types.\n\nImplementations of `MeterFilter` are added to the registry programmatically:\n\n====\n[source, java]\n----\nregistry.config()\n .meterFilter(MeterFilter.ignoreTags("too.much.information"))\n .meterFilter(MeterFilter.denyNameStartsWith("jvm"));\n----\n====\n\nMeter filters are applied in order, and the results of transforming or configuring a meter are chained.\n\n== Deny or Accept Meters\n\nThe verbose form of an accept or deny filter is:\n\n====\n[source, java]\n----\nnew MeterFilter() {\n @Override\n public MeterFilterReply accept(Meter.Id id) {\n if(id.getName().contains("test")) {\n return MeterFilterReply.DENY;\n }\n return MeterFilterReply.NEUTRAL;\n }\n}\n----\n====\n\n`MeterFilterReply` has three possible states:\n\n* `DENY`: Do not let this meter be registered. When you try to register a meter against a registry and the filter returns `DENY`, the registry returns a NOOP version of that meter (for example, `NoopCounter` or `NoopTimer`). Your code can continue to interact with the NOOP meter, but anything recorded to it is discarded immediately with minimal overhead.\n* `NEUTRAL`: If no other meter filter has returned `DENY`, registration of meters proceeds normally.\n* `ACCEPT`: If a filter returns `ACCEPT`, the meter is immediately registered without interrogating the accept methods of any further filters.\n\n=== Convenience Methods\n\n`MeterFilter` provides several convenience static builders for deny and accept type filters:\n\n* `accept()`: Accept every meter, overriding the decisions of any filters that follow.\n* `accept(Predicate)`: Accept any meter matching the predicate.\n* `acceptNameStartsWith(String)`: Accept every meter with a matching prefix.\n* `deny()`: Deny every meter, overriding the decisions of any filters that follow.\n* `denyNameStartsWith(String)`: Deny every meter with a matching prefix. All `MeterBinder` implementations provided by Micrometer have names with common prefixes to allow for easy grouping visualization in UIs but also to make them easy to disable or enable as a group with a prefix. For example, you can deny all JVM metrics with `MeterFilter.denyNameStartsWith("jvm")`.\n* `deny(Predicate)`: Deny any meter that matches the predicate.\n* `maximumAllowableMetrics(int)`: Deny any meter after the registry has reached a certain number of meters.\n* `maximumAllowableTags(String meterNamePrefix, String tagKey, int maximumTagValues, MeterFilter onMaxReached)`: Places an upper bound on the number of tags produced by matching series.\n\n**Whitelisting** only a certain group of metrics is a particularly common case for monitoring systems that are _expensive_. This can be achieved with a static call:\n\n* `denyUnless(Predicate)`: Deny all meters that _do not_ match the predicate.\n\n=== Chaining Deny Accept Meters\n\nMeter filters are applied in the order in which they are configured on the registry, so it is possible to stack deny and accept filters to achieve more complex rules:\n\n====\n[source, java]\n----\nregistry.config()\n .meterFilter(MeterFilter.acceptNameStartsWith("http"))\n .meterFilter(MeterFilter.deny()); <1>\n----\n====\n\nThis achieves another form of whitelisting by stacking two filters together. Only `http` metrics are allowed to exist in this registry.\n\n== Transforming metrics\n\nThe following example shows a transform filter:\n\n====\n[source, java]\n----\nnew MeterFilter() {\n @Override\n public Meter.Id map(Meter.Id id) {\n if(id.getName().startsWith("test")) {\n return id.withName("extra." + id.getName()).withTag("extra.tag", "value");\n }\n return id;\n }\n}\n----\n====\n\nThis filter adds a name prefix and an additional tag conditionally to meters starting with a name of `test`.\n\n`MeterFilter` provides convenience builders for many common transformation cases:\n\n* `commonTags(Iterable)`: Adds a set of tags to all metrics. Adding common tags for application name, host, region, and others is a highly recommended practice.\n* `ignoreTags(String...)`: Drops matching tag keys from every meter. This is particularly useful when a tag provably comes to have\ntoo high cardinality and starts stressing your monitoring system or costing too much but you cannot change all the instrumentation points quickly.\n* `replaceTagValues(String tagKey, Function replacement, String... exceptions)`: Replace tag values according to the provided mapping for all matching tag keys. You can use this to reduce the total cardinality of a tag by mapping some portion of tag values to something else.\n* `renameTag(String meterNamePrefix, String fromTagKey, String toTagKey)`: Rename a tag key for every metric that begins with a given prefix.\n\n== Configuring Distribution Statistics\n\n`Timer` and `DistributionSummary` contain a set of optional distribution statistics (in addition to the basics of count, total, and max) that you can configure through filters. These distribution statistics include pre-computed percentiles, SLOs, and histograms.\n\n====\n[source, java]\n----\nnew MeterFilter() {\n @Override\n public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {\n if (id.getName().startsWith(prefix)) {\n return DistributionStatisticConfig.builder()\n .publishPercentiles(0.9, 0.95)\n .build()\n .merge(config);\n }\n return config;\n }\n};\n----\n====\n\nGenerally, you should create a new `DistributionStatisticConfig` with only the pieces you wish to configure and then `merge` it with the input configuration. This lets you drop down on registry-provided defaults for distribution statistics and to chain multiple filters together, each configuring some part of the distribution statistics (for example, you might want a 100ms SLO for all HTTP requests but only percentile histograms on a few critical endpoints).\n\n`MeterFilter` provides convenience builders for:\n\n* `maxExpected(Duration/long)`: Governs the upper bound of percentile histogram buckets shipped from a timer or summary.\n* `minExpected(Duration/long)`: Governs the lower bound of percentile histogram buckets shipped from a timer or summary.\n\nSpring Boot offers property-based filters for configuring SLOs, percentiles, and percentile histograms by name prefix.\n\n:leveloffset!:\n\n[[rate-aggregation]]\n== Rate Aggregation\n\n:leveloffset: +1\n\nMicrometer is aware of whether a particular monitoring system expects rate aggregation to happen client-side before metrics are published or ad-hoc as part of the query on the server. It accumulates metrics according to which style the monitoring system expects.\n\nNot all measurements are reported or best viewed as a rate. For example, gauge values and the active task count long task timers are not rates.\n\n== Server-side\n\nMonitoring systems that perform server-side rate math expect absolute values to be reported at each publishing interval. For example, the absolute count of all increments to a counter since the beginning of the application is sent on each publishing interval.\n\nSuppose we have a slightly positively biased random walk that chooses to increment a counter once every 10 milliseconds. If we view the raw counter value in a system like Prometheus, we see a step-wise monotonically increasing function (the width of the step is the interval at which Prometheus is polling or scraping for data).\n\nimage::'+n(66)+"[Absolute counter value]\n\nRepresenting a counter without rate aggregation over some time window is rarely useful, as the representation is a function of both the rapidity with which the counter is incremented and the longevity of the service. In the preceding example, the counter drops back to zero on service restart. The rate-aggregated graph would return back to a value around 55 as soon as the new instance (say on a production deployment) was in service.\n\nimage::"+n(67)+'[Rate-aggregated counter]\n\nIf you have achieved zero-downtime deployments (for example, through red-black deployments), you should be able to comfortably set _minimum_ alert thresholds on the rate-aggregated graph without service restarts causing dips in the counter value.\n\nIMPORTANT: For most production purposes, whether it be alerting, automated canary analysis, or other use cases, base your automation off of rate-aggregated data.\n\n== Client-side\n\nTwo other classes of monitoring system either:\n\n. Expect rate-aggregated data. Given the key insight that for most production purposes, we should be basing our decisions off of rates rather than absolute values, such systems benefit from having to do less math to satisfy queries.\n. Have relatively little or no math operations that would let us rate-aggregate data through our queries. For these systems, publishing pre-aggregated data is the only way to build meaningful representations.\n\nMicrometer efficiently maintains rate data by means of a step value that accumulates data for the current publishing interval. When the step value is polled (when publishing, for example), if the step value detects that the current interval has elapsed, it moves current data to "`previous`" state. This previous state is what is reported until the next time current data overwrites it. The following image shows the interaction of current and previous state, along with polling:\n\nimage::'+n(68)+'[Behavior of a step value,width=1200]\n\nThe value returned by the poll function is always _rate per second * interval_. If the step value shown in the preceding image represents the values of a counter, we could say that the counter saw "`0.3 increments per second`" in the first interval, which is reportable to the backend at any time during the second interval.\n\nMicrometer timers track at least a count and the total time as separate measurements. Suppose we configure publishing at 10-second intervals and we saw 20 requests that each took 100ms. Then, for the first interval:\n\n. `count` = 10 seconds * (20 requests / 10 seconds) = 20 requests\n. `totalTime` = 10 seconds * (20 * 100 ms / 10 seconds) = 2 seconds\n\nThe `count` statistic is meaningful by itself: It is a measure of _throughput_. `totalTime` represents the total latency of all requests in the interval. Additionally:\n\n`totalTime / count` = 2 seconds / 20 requests = 0.1 seconds / request = 100 ms / request\n\nThis is a useful measure of _average latency_. When the same idea is applied to the `totalAmount` and `count` emanating from distribution summaries, the measure is called a _distribution average_. Average latency is just the distribution average for a distribution summary measured in time (a timer). Some monitoring systems (such as Atlas) provide facilities for computing the distribution average from these statistics, and Micrometer includes `totalTime` and `count` as separate statistics. Others, (such as Datadog) do not have this kind of operation built-in, and Micrometer calculates the distribution average client-side and ships that.\n\nShipping the rate for the publishing interval is sufficient to reason about the rate over any time window greater than or equal to the publishing interval. In our example, if a service continues to receive 20 requests that each take 100ms for every 10 second interval in a given minute, we could say:\n\n. Micrometer reported "`20 requests`" for `count` on every 10 second interval. The monitoring system sums these six 10 second intervals and arrives at the conclusion that there are 120 requests / minute. Note that it is the monitoring system doing this summation, not Micrometer.\n. Micrometer reported "`2 seconds`" of `totalTime` on every 10 second interval. The monitoring system can sum all total time statistics over the minute to yield "`12 seconds`" of total time in the minute interval. Then, the average latency is as we expect: 12 seconds / 120 requests = 100 ms / request.\n\n:leveloffset!:\n\n== Counters\n\n:leveloffset: +1\n\nCounters report a single metric: a count. The `Counter` interface lets you increment by a fixed amount, which must be positive.\n\nTIP: Never count something you can time with a `Timer` or summarize with a `DistributionSummary`! Both `Timer` and `DistributionSummary` always publish a count of events in addition to other measurements.\n\nWhen building graphs and alerts off of counters, you should generally be most interested in measuring the rate at which some event occurs over a given time interval. Consider a simple queue. You could use counters to measure various things, such as the rate at which items are being inserted and removed.\n\nIt is tempting, at first, to conceive of visualizing absolute counts rather than a rate, but the absolute count is usually both a function of the rapidity with which something is used *and* the longevity of the application instance under instrumentation. Building dashboards and alerts of the rate of a counter per some interval of time disregards the longevity of the app, letting you see aberrant behavior long after the application has started.\n\nNOTE: Be sure to read through the timer section before jumping into using counters, as timers record a count of timed events as part of the suite of metrics that go into timing. For those pieces of code you intend to time, you do NOT need to add a separate counter.\n\nThe following code simulates a real counter whose rate exhibits some perturbation over a short time window:\n\n[source,java]\n----\nNormal rand = ...; // a random generator\n\nMeterRegistry registry = ...\nCounter counter = registry.counter("counter"); <1>\n\nFlux.interval(Duration.ofMillis(10))\n .doOnEach(d -> {\n if (rand.nextDouble() + 0.1 > 0) { <2>\n counter.increment(); <3>\n }\n })\n .blockLast();\n----\n<1> Most counters can be created off of the registry itself with a name and, optionally, a set of tags.\n<2> A slightly positively biased random walk.\n<3> This is how you interact with a counter. You could also call `counter.increment(n)` to increment by more than one in a single operation.\n\nA fluent builder for counters on the `Counter` interface itself provides access to less frequently used options, such as\nbase units and description. You can register the counter as the last step of its construction by calling `register`:\n\n[source, java]\n----\nCounter counter = Counter\n .builder("counter")\n .baseUnit("beans") // optional\n .description("a description of what this counter does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\n== Function-tracking Counters\n\nMicrometer also provides a more infrequently used counter pattern that tracks a monotonically increasing function (a function that stays the same or increases over time but never decreases). Some monitoring systems, such as Prometheus, push cumulative values for counters to the backend, but others publish the rate at which a counter is incrementing over the push interval. By employing this pattern, you let the Micrometer implementation for your monitoring system choose whether to rate-normalize the counter, and your counter remains portable across different types of monitoring systems.\n\n[source, java]\n-----\nCache cache = ...; // suppose we have a Guava cache with stats recording on\nregistry.more().counter("evictions", tags, cache, c -> c.stats().evictionCount()); <1>\n-----\n\n<1> `evictionCount()` is a monotonically increasing function that increments with every cache eviction from the beginning of its life.\n\nThe function-tracking counter, in concert with the monitoring system\'s rate normalizing functionality (whether this is an artifact of the query language or the way data is pushed to the system), adds a layer of richness on top of the cumulative value of the function itself. You can reason about the _rate_ at which the value is increasing, whether that rate is within an acceptable bound, is increasing or decreasing over time, and so on.\n\nWARNING: Micrometer cannot guarantee the monotonicity of the function for you. By using this signature, you are asserting its monotonicity based on what you know about its definition.\n\nA fluent builder for function counters on the `FunctionCounter` interface itself provides access to less frequently used options, such as base units and description. You can register the counter as the last step of its construction by calling `register(MeterRegistry)`.\n\n[source, java]\n----\nMyCounterState state = ...;\n\nFunctionCounter counter = FunctionCounter\n .builder("counter", state, state -> state.count())\n .baseUnit("beans") // optional\n .description("a description of what this counter does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\n:leveloffset!:\n\n== Gauges\n\n:leveloffset: +1\n\nA gauge is a handle to get the current value. Typical examples for gauges would be the size of a collection or map or number of threads in a running state.\n\nTIP: Gauges are useful for monitoring things with natural upper bounds. We do not recommend using a gauge to monitor things like request count, as they can grow without bound for the duration of an application instance\'s life.\n\nTIP: Never gauge something you can count with a `Counter`!\n\nMicrometer takes the stance that gauges should be sampled and not be set, so there is no information about what might have occurred between samples. Any intermediate values set on a gauge are lost by the time the gauge value is reported to a metrics backend, so there is little value in setting those intermediate values in the first place.\n\nThink of a `Gauge` as a "`heisen-gauge`": a meter that changes only when it is observed. Every other meter type accumulates intermediate counts toward the point where the data is sent to the metrics backend.\n\nThe `MeterRegistry` interface contains methods for building gauges to observe numeric values, functions, collections, and maps:\n\n[source, java]\n----\nList list = registry.gauge("listGauge", Collections.emptyList(), new ArrayList<>(), List::size); <1>\nList list2 = registry.gaugeCollectionSize("listSize2", Tags.empty(), new ArrayList<>()); <2>\nMap map = registry.gaugeMapSize("mapGauge", Tags.empty(), new HashMap<>());\n----\n<1> A slightly more common form of gauge is one that monitors some non-numeric object. The last argument establishes the function that is used to determine the value of the gauge when the gauge is observed.\n<2> A more convenient form of (1) for when you want to monitor collection size.\n\nAll of the different forms of creating a gauge maintain only a _weak reference_ to the object being observed, so as not to prevent garbage collection of the object.\n\n== Manually Incrementing or Decrementing a Gauge\n\nA gauge can be made to track any `java.lang.Number` subtype that is settable, such as `AtomicInteger` and `AtomicLong` found in `java.util.concurrent.atomic` and similar types, such as Guava\'s `AtomicDouble`:\n\n[source,java]\n----\n// maintain a reference to myGauge\nAtomicInteger myGauge = registry.gauge("numberGauge", new AtomicInteger(0));\n\n// ... elsewhere you can update the value it holds using the object reference\nmyGauge.set(27);\nmyGauge.set(11);\n----\n\nNote that, in this form, unlike other meter types, you do not get a reference to the `Gauge` when creating one. Rather, you get a reference to the thing being observed. This is because of the "`heisen-gauge`" principal: The gauge is self-sufficient once created, so you should never need to interact with it. This lets us give you back only the instrumented object, which allows for quick one-liners that both create the object to be observed and set up metrics around it.\n\nThis pattern should be less common than the `DoubleFunction` form. Remember that frequent setting of the observed `Number` results in a lot of intermediate values that never get published. Only the _instantaneous value_ of the gauge at publish time is ever sent to the monitoring system.\n\nWARNING: Attempting to construct a gauge with a primitive number or one of its `java.lang` object forms is always incorrect. These numbers are immutable. Thus, the gauge cannot ever be changed. Attempting to "`re-register`" the gauge with a different number does not work, as the registry maintains only one meter for each unique combination of name and tags.\n\n== Gauge Fluent Builder\n\nThe interface contains a fluent builder for gauges:\n\n[source, java]\n----\nGauge gauge = Gauge\n .builder("gauge", myObj, myObj::gaugeValue)\n .description("a description of what this gauge does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\nGenerally the returned `Gauge` instance is not useful except in testing, as the gauge is already set up to track a value automatically upon registration.\n\n== Why is My Gauge Reporting NaN or Disappearing?\n\nIt is your responsibility to hold a strong reference to the state object that you are measuring with a `Gauge`. Micrometer is careful to not create strong references to objects that would otherwise be garbage collected. Once the object being gauged is de-referenced and is garbage collected, Micrometer starts reporting a NaN or nothing for a gauge, depending on the registry implementation.\n\nIf you see your gauge reporting for a few minutes and then disappearing or reporting NaN, it almost certainly suggests that the underlying object being gauged has been garbage collected.\n\n== `TimeGauge`\n\n`TimeGauge` is a specialized gauge that tracks a time value, to be scaled to the base unit of time expected by each registry implementation.\n\n`TimeGauge` can be registered with `TimeUnit` as follows:\n\n[source, java]\n----\nAtomicInteger msTimeGauge = new AtomicInteger(4000);\nAtomicInteger usTimeGauge = new AtomicInteger(4000);\nTimeGauge.builder("my.gauge", msTimeGauge, TimeUnit.MILLISECONDS, AtomicInteger::get).register(registry);\nTimeGauge.builder("my.other.gauge", usTimeGauge, TimeUnit.MICROSECONDS, AtomicInteger::get).register(registry);\n----\n\nAnd for example, if the registry is Prometheus, they will be converted in seconds as follows:\n\n```\n# HELP my_gauge_seconds\n# TYPE my_gauge_seconds gauge\nmy_gauge_seconds 4.0\n# HELP my_other_gauge_seconds\n# TYPE my_other_gauge_seconds gauge\nmy_other_gauge_seconds 0.004\n```\n\n== Multi-gauge\n\nMicrometer supports one last special type of `Gauge`, called a `MultiGauge`, to help manage gauging a growing or shrinking list of criteria.\nThis feature lets you select a set of well-bounded but slightly changing set of criteria from something like an SQL query and report some metric for each row as a `Gauge`. The following example creates a `MultiGauge`:\n\n[source, java]\n----\n// SELECT count(*) from job group by status WHERE job = \'dirty\'\nMultiGauge statuses = MultiGauge.builder("statuses")\n .tag("job", "dirty")\n .description("The number of widgets in various statuses")\n .baseUnit("widgets")\n .register(registry);\n\n...\n\n// run this periodically whenever you re-run your query\nstatuses.register(\n resultSet.stream()\n .map(result -> Row.of(Tags.of("status", result.getAsString("status")), result.getAsInt("count")))\n .collect(toList())\n);\n----\n\n:leveloffset!:\n\n== Timers\n\n:leveloffset: +1\n\nTimers are intended for measuring short-duration latencies and the frequency of such events. All implementations of `Timer` report at least the total time and the count of events as separate time series. While you can use timers for other use cases, note that negative values are not supported, and recording many longer durations could cause overflow of the total time at `Long.MAX_VALUE` nanoseconds (292.3 years).\n\nAs an example, consider a graph showing request latency to a typical web server. The server can be expected to respond to many requests quickly, so the timer gets updated many times per second.\n\nThe appropriate base unit for timers varies by metrics backend, and for good reason. Micrometer is decidedly un-opinionated about this. However, because of the potential for confusion, Micrometer requires a `TimeUnit` when interacting with `Timer` implementations. Micrometer is aware of the preferences of each implementation and publishes your timing in the appropriate base unit based on the implementation. The following listing shows part of the `Timer` interface:\n\n[source,java]\n----\npublic interface Timer extends Meter {\n ...\n void record(long amount, TimeUnit unit);\n void record(Duration duration);\n double totalTime(TimeUnit unit);\n}\n----\n\nThe interface contains a fluent builder for timers:\n\n[source,java]\n----\nTimer timer = Timer\n .builder("my.timer")\n .description("a description of what this timer does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\nNOTE: The maximum statistical value for basic `Timer` implementations, such as `CumulativeTimer` and `StepTimer`, is a time window maximum (`TimeWindowMax`).\nIt means that its value is the maximum value during a time window.\nIf no new values are recorded for the time window length, the max is reset to 0 as a new time window starts.\nTime window size is the step size of the meter registry, unless expiry in `DistributionStatisticConfig` is explicitly set to other value.\nA time window maximum is used to capture maximum latency in a subsequent interval after heavy resource pressure triggers the latency and prevents metrics from being published.\nPercentiles are also time window percentiles (`TimeWindowPercentileHistogram`).\n\n== Recording Blocks of Code\n\nThe `Timer` interface exposes several convenience overloads for recording timings inline, including the following:\n\n[source,java]\n----\ntimer.record(() -> dontCareAboutReturnValue());\ntimer.recordCallable(() -> returnValue());\n\nRunnable r = timer.wrap(() -> dontCareAboutReturnValue()); <1>\nCallable c = timer.wrap(() -> returnValue());\n----\n<1> Wrap `Runnable` or `Callable` and return the instrumented version of it for use later.\n\nNOTE: A `Timer` is really a specialized distribution summary that is aware of how to scale durations to the base unit of time of each monitoring system and has an automatically\ndetermined base unit. In every case where you want to measure time, you should use a `Timer` rather than a `DistributionSummary`.\n\n== Storing Start State in `Timer.Sample`\n\nYou may also store start state in a sample instance that can be stopped later. The sample records a start time based on the registry\'s clock. After starting a sample, execute the code to be timed and finish the operation by calling `stop(Timer)` on the sample:\n\n[source, java]\n----\nTimer.Sample sample = Timer.start(registry);\n\n// do stuff\nResponse response = ...\n\nsample.stop(registry.timer("my.timer", "response", response.status()));\n----\n\nNote how we do not decide the timer to which to accumulate the sample until it is time to stop the sample. This lets us dynamically determine certain tags from the end state of the operation we are timing.\n\n== The `@Timed` annotation\n\nThe `micrometer-core` modules contains a `@Timed` annotation that frameworks can use to add timing support to either specific types of methods such as those serving web request endpoints or, more generally, to all methods.\n\nWARNING: Micrometer\'s Spring Boot configuration does _not_ recognize `@Timed` on arbitrary methods.\n\nAlso, an incubating AspectJ aspect is included in `micrometer-core`. You can use it in your application either through compile/load time AspectJ weaving or through framework facilities that interpret AspectJ aspects and proxy targeted methods in some other way, such as Spring AOP. Here is a sample Spring AOP configuration:\n\n[source,java]\n----\n@Configuration\npublic class TimedConfiguration {\n @Bean\n public TimedAspect timedAspect(MeterRegistry registry) {\n return new TimedAspect(registry);\n }\n}\n----\n\nApplying `TimedAspect` makes `@Timed` usable on any arbitrary method in an AspectJ proxied instance, as the following example shows:\n\n[source,java]\n----\n@Service\npublic class ExampleService {\n\n @Timed\n public void sync() {\n // @Timed will record the execution time of this method,\n // from the start and until it exits normally or exceptionally.\n ...\n }\n\n @Async\n @Timed\n public CompletableFuture async() {\n // @Timed will record the execution time of this method,\n // from the start and until the returned CompletableFuture\n // completes normally or exceptionally.\n return CompletableFuture.supplyAsync(...);\n }\n\n}\n----\n\n=== @MeterTag on Method Parameters\n\nTo support `@MeterTag` annotation on method parameters you need to configure the `@TimedAspect` to add the `MeterTagAnnotationHandler`.\n\n[source,java,subs=+attributes]\n-----\nValueResolver valueResolver = parameter -> "Value from myCustomTagValueResolver [" + parameter + "]";\n\n// Example of a ValueExpressionResolver that uses Spring Expression Language\nValueExpressionResolver valueExpressionResolver = new SpelValueExpressionResolver();\n\n\n// Setting the handler on the aspect\ntimedAspect.setMeterTagAnnotationHandler(\n new MeterTagAnnotationHandler(aClass -> valueResolver, aClass -> valueExpressionResolver));\n-----\n\nLet\'s assume that we have the following interface.\n\n[source,java,subs=+attributes]\n-----\ninterface MeterTagClassInterface {\n\n @Timed\n void getAnnotationForTagValueResolver(@MeterTag(key = "test", resolver = ValueResolver.class) String test);\n\n @Timed\n void getAnnotationForTagValueExpression(\n @MeterTag(key = "test", expression = "\'hello\' + \' characters\'") String test);\n\n @Timed\n void getAnnotationForArgumentToString(@MeterTag("test") Long param);\n\n}\n-----\n\nWhen its implementations would be called with different arguments (remember that the implementation needs to be annotated with `@Timed` annotation too) the following timers would be created:\n\n[source,java,subs=+attributes]\n-----\n// Example for returning on the parameter\nservice.getAnnotationForArgumentToString(15L);\n\nassertThat(registry.get("method.timed").tag("test", "15").timer().count()).isEqualTo(1);\n\n// Example for calling the provided on the parameter\nservice.getAnnotationForTagValueResolver("foo");\n\nassertThat(registry.get("method.timed")\n .tag("test", "Value from myCustomTagValueResolver [foo]")\n .timer()\n .count()).isEqualTo(1);\n\n// Example for calling the provided \nservice.getAnnotationForTagValueExpression("15L");\n\nassertThat(registry.get("method.timed").tag("test", "hello characters").timer().count()).isEqualTo(1);\n-----\n\n== Function-tracking Timers\n\nMicrometer also provides a more infrequently used timer pattern that tracks two monotonically increasing functions (a function that stays the same or increases over time but never decreases): a count function and a total time function. Some monitoring systems, such as Prometheus, push cumulative values for counters (which apply to both the count and total time functions in this case) to the backend, but others publish the rate at which a counter increments over the push interval. By employing this pattern, you let the Micrometer implementation for your monitoring system choose whether to rate normalize the timer, and your timer remains portable across different types of monitoring systems.\n\n[source, java]\n-----\nIMap cache = ...; // suppose we have a Hazelcast cache\nregistry.more().timer("cache.gets.latency", Tags.of("name", cache.getName()), cache,\n c -> c.getLocalMapStats().getGetOperationCount(), <1>\n c -> c.getLocalMapStats().getTotalGetLatency(),\n TimeUnit.NANOSECONDS <2>\n);\n-----\n\n<1> `getGetOperationCount()` is a monotonically increasing function incrementing with every cache get from the beginning of its life.\n<2> This represents the unit of time represented by `getTotalGetLatency()`. Each registry implementation specifies what its expected base unit of time is, and the total time reported will be scaled to this value.\n\nThe function-tracking timer, in concert with the monitoring system\'s rate normalizing functionality (whether this is an artifact of the query language or the way data is pushed to the system), adds a layer of richness to the cumulative value of the functions themselves. You can reason about the _rate_ of throughput and latency, whether that rate is within an acceptable bound, is increasing or decreasing over time, and so on.\n\nWARNING: Micrometer cannot guarantee the monotonicity of the count and total time functions for you. By using this signature, you are asserting their monotonicity based on what you know about their definitions.\n\nThere is also a fluent builder for function timers on the `FunctionTimer` interface itself, providing access to less frequently used options, such as base units and description. You can register the timer as the last step of its construction by calling `register(MeterRegistry)`:\n\n[source, java]\n----\nIMap cache = ...\n\nFunctionTimer.builder("cache.gets.latency", cache,\n c -> c.getLocalMapStats().getGetOperationCount(),\n c -> c.getLocalMapStats().getTotalGetLatency(),\n TimeUnit.NANOSECONDS)\n .tags("name", cache.getName())\n .description("Cache gets")\n .register(registry);\n----\n\n== Pause Detection\n\nMicrometer uses the `LatencyUtils` package to compensate for https://highscalability.com/blog/2015/10/5/your-load-generator-is-probably-lying-to-you-take-the-red-pi.html[coordinated omission] -- extra latency arising from system and VM pauses that skew your latency statistics downward. Distribution statistics, such as percentiles and SLO counts, are influenced by a pause detector implementation that adds additional latency here and there to compensate for pauses.\n\nMicrometer supports two pause detector implementations: a clock-drift based detector and a no-op detector. Before Micrometer 1.0.10/1.1.4/1.2.0, a clock-drift detector was configured by default to report as-accurate-as-possible metrics without further configuration. Since 1.0.10/1.1.4/1.2.0, the no-op detector is configured by default, but the clock-drift detector can be configured as shown in the next example.\n\nThe clock-drift based detector has a configurable sleep interval and pause threshold. CPU consumption is inversely proportional to `sleepInterval`, as is pause detection accuracy. 100ms for both values is a reasonable default to offer decent detection of long pause events while consuming a negligible amount of CPU time.\n\nYou can customize the pause detector as follows:\n\n[source,java]\n----\nregistry.config().pauseDetector(new ClockDriftPauseDetector(sleepInterval, pauseThreshold));\nregistry.config().pauseDetector(new NoPauseDetector());\n----\n\nIn the future, we may provide further detector implementations. Some pauses may be able to be inferred from GC logging in some circumstances, for example, without requiring a constant CPU load, however minimal. Also, a future JDK may provide direct access to pause events.\n\n== Memory Footprint Estimation\n\nTimers are the most memory-consuming meter, and their total footprint can vary dramatically, depending on which options you choose. The following table of memory consumption is based on the use of various features. These figures assume no tags and a ring buffer length of 3. Adding tags adds somewhat to the total, as does increasing the buffer length. Total storage can also vary somewhat depending on the registry implementation.\n\n* R = Ring buffer length. We assume the default of 3 in all examples. R is set with `Timer.Builder#distributionStatisticBufferLength`.\n* B = Total histogram buckets. Can be SLO boundaries or percentile histogram buckets. By default, timers are clamped to a minimum expected value of 1ms and a maximum expected value of 30 seconds, yielding 66 buckets for percentile histograms, when applicable.\n* I = Interval estimator for pause compensation. 1.7 kb.\n* M = Time-decaying max. 104 bytes.\n* Fb = Fixed boundary histogram. 8b * B * R.\n* Pp = Percentile precision. By default, it is 1. Generally in the range [0, 3]. Pp is set with `Timer.Builder#percentilePrecision`.\n* Hdr(Pp) = High dynamic range histogram.\n - When Pp = 0: 1.9kb * R + 0.8kb\n - When Pp = 1: 3.8kb * R + 1.1kb\n - When Pp = 2: 18.2kb * R + 4.7kb\n - When Pp = 3: 66kb * R + 33kb\n\n[width="80%",options="header"]\n|=========================================================\n|Pause detection |Client-side percentiles |Histogram and/or SLOs |Formula | Example\n\n|Yes |No |No |I + M| ~1.8kb\n|Yes |No |Yes |I + M + Fb|For default percentile histogram, ~7.7kb\n|Yes |Yes |Yes |I + M + Hdr(Pp)|For the addition of a 0.95 percentile with defaults otherwise, ~14.3kb\n|No |No |No |M| ~0.1kb\n|No |No |Yes |M + Fb|For default percentile histogram, ~6kb\n|No |Yes |Yes |M + Hdr(Pp)|For the addition of a 0.95 percentile with defaults otherwise, ~12.6kb\n|=========================================================\n\nNOTE: For Prometheus, specifically, R is _always_ equal to 1, regardless of how you attempt to configure it through `Timer.Builder`. This special case exists because Prometheus expects cumulative histogram data that never rolls over.\n\n:leveloffset!:\n\n== Distribution Summaries\n\n:leveloffset: +1\n\nA distribution summary tracks the distribution of events. It is similar to a timer structurally, but records values that do not represent a unit of time. For example, you could use a distribution summary to measure the payload sizes of requests hitting a server.\n\nThe following example creates a distribution summary:\n\n[source, java]\n----\nDistributionSummary summary = registry.summary("response.size");\n----\n\nThe interface contains a fluent builder for distribution summaries:\n\n[source, java]\n----\nDistributionSummary summary = DistributionSummary\n .builder("response.size")\n .description("a description of what this summary does") // optional\n .baseUnit("bytes") // optional <1>\n .tags("region", "test") // optional\n .scale(100) // optional <2>\n .register(registry);\n----\n\n<1> Add base units for maximum portability. Base units are part of the naming convention for some monitoring systems. Leaving it off and violating the naming convention has no adverse effect if you forget.\n<2> Optionally, you can provide a scaling factor by which each recorded sample is multiplied as it is recorded.\n\nNOTE: The maximum (which is named `max`) for basic `DistributionSummary` implementations, such as `CumulativeDistributionSummary` and `StepDistributionSummary`, is a time window maximum (`TimeWindowMax`).\nIt means that its value is the maximum value during a time window.\nIf no new values are recorded for the time window length, the maximum is reset to 0 as a new time window starts.\nTime window size is the step size of the meter registry, unless expiry in `DistributionStatisticConfig` is explicitly set to another value.\nA time window max is used to capture the maximum latency in a subsequent interval after heavy resource pressure triggers the latency and prevents metrics from being published.\nPercentiles are also time window percentiles (`TimeWindowPercentileHistogram`).\n\n== Scaling and Histograms\n\nMicrometer\'s preselected percentile histogram buckets are all integers from 1 to `Long.MAX_VALUE`. Currently, `minimumExpectedValue` and `maximumExpectedValue` serve to control the cardinality of the bucket set. If we try to detect that your min/max yields a small range and scale the preselected bucket domain to your summary\'s range, we do not have another lever to control bucket cardinality.\n\nInstead, if your summary\'s domain is more constrained, scale your summary\'s range by a fixed factor. The use case we have heard so far is for summaries of ratios whose domain is [0,1]. Given that scenario, we can use the following code to create values from 0 to 100:\n\n[source,java]\n----\nDistributionSummary.builder("my.ratio").scale(100).register(registry)\n----\n\nThis way, the ratio winds up in the range [0,100] and we can set `maximumExpectedValue` to 100. You can pair this with custom SLO boundaries if you care about particular ratios:\n\n[source,java]\n----\nDistributionSummary.builder("my.ratio")\n .scale(100)\n .serviceLevelObjectives(70, 80, 90)\n .register(registry)\n----\n\n\n== Memory Footprint Estimation\n\nThe total memory footprint of a distribution summary can vary dramatically, depending on which options you choose. The following table of memory consumption is based on the use of various features. These figures assume no tags and a ring buffer length of 3. Adding tags adds somewhat to the total, as does increasing the buffer length. Total storage can also vary somewhat depending on the registry implementation.\n\n* R = Ring buffer length. We assume the default of 3 in all examples. R is set with `DistributionSummary.Builder#distributionStatisticBufferLength`.\n* B = Total histogram buckets. It can be SLO boundaries or percentile histogram buckets. By default, summaries have NO minimum and maximum expected value, so we ship all 276 predetermined histogram buckets. You should always clamp distribution summaries with a `minimumExpectedValue` and `maximumExpectedValue` when you intend to ship percentile histograms.\n* M = Time-decaying max. 104 bytes.\n* Fb = Fixed boundary histogram. 8b * B * R.\n* Pp = Percentile precision. By default, it is 1. It is generally in the range of [0, 3]. Pp is set with `DistributionSummary.Builder#percentilePrecision`.\n* Hdr(Pp) = High dynamic range histogram.\n - When Pp = 0: 1.9kb * R + 0.8kb\n - When Pp = 1: 3.8kb * R + 1.1kb\n - When Pp = 2: 18.2kb * R + 4.7kb\n - When Pp = 3: 66kb * R + 33kb\n\n\n[width="80%",options="header"]\n|=========================================================\n|Client-side percentiles |Histogram and/or SLOs |Formula | Example\n\n|No |No |M| ~0.1kb\n|No |Yes |M + Fb|For percentile histogram clamped to 66 buckets, ~6kb\n|Yes |Yes |M + Hdr(Pp)|For the addition of a 0.95 percentile with defaults otherwise, ~12.6kb\n|=========================================================\n\nNOTE: For Prometheus, R is _always_ equal to 1, regardless of how you attempt to configure it through `DistributionSummary.Builder`. This special case exists for Prometheus because it expects cumulative histogram data that never rolls over.\n\n:leveloffset!:\n\n== Long Task Timers\n\n:leveloffset: +1\n\nThe long task timer is a special type of timer that lets you measure time while an event being measured is *still running*. A normal Timer only records the duration *after* the task is complete.\n\nLong task timers publish at least the following statistics:\n\n* Active task count\n* Total duration of active tasks\n* The maximum duration of active tasks\n\nUnlike a regular `Timer`, a long task timer does not publish statistics about completed tasks.\n\nConsider a background process to refresh metadata from a data store. For example, https://github.com/Netflix/edda[Edda] caches AWS resources, such as instances, volumes, auto-scaling groups, and others. Normally all data can be refreshed in a few minutes. If the AWS services have problems, it can take much longer. A long task timer can be used to track the active time for refreshing the metadata.\n\nFor example, in a Spring application, it is common for such long running processes to be implemented with `@Scheduled`. Micrometer provides a special `@Timed` annotation for instrumenting these processes with a long task timer:\n\n[source, java]\n----\n@Timed(value = "aws.scrape", longTask = true)\n@Scheduled(fixedDelay = 360000)\nvoid scrapeResources() {\n // find instances, volumes, auto-scaling groups, etc...\n}\n----\n\nIt is up to the application framework to make something happen with `@Timed`. If your framework of choice does not support it, you can still use the long task timer:\n\n[source, java]\n----\nLongTaskTimer scrapeTimer = registry.more().longTaskTimer("scrape");\nvoid scrapeResources() {\n scrapeTimer.record(() => {\n // find instances, volumes, auto-scaling groups, etc...\n });\n}\n----\n\nIf we wanted to alert when this process exceeds a threshold, with a long task timer, we receive that alert at the first reporting interval after we have exceeded the threshold. With a regular timer, we would not receive the alert until the first reporting interval after the process completed, over an hour later!\n\nThe interface contains a fluent builder for long task timers:\n\n[source, java]\n----\nLongTaskTimer longTaskTimer = LongTaskTimer\n .builder("long.task.timer")\n .description("a description of what this timer does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\n:leveloffset!:\n\n== Histograms and Percentiles\n\n:leveloffset: +1\n\nTimers and distribution summaries support collecting data to observe their percentile distributions. There are two main approaches to viewing percentiles:\n\n* *Percentile histograms*: Micrometer accumulates values to an underlying histogram and ships a predetermined set of buckets to the monitoring system. The monitoring system\'s query language is responsible for calculating percentiles off of this histogram. Currently, only Prometheus, Atlas, and Wavefront support histogram-based percentile approximations, through `histogram_quantile`, `:percentile`, and `hs()`, respectively. If you target Prometheus, Atlas, or Wavefront, prefer this approach, since you can aggregate the histograms across dimensions (by summing the values of the buckets across a set of dimensions) and derive an aggregable percentile from the histogram.\n* *Client-side percentiles*: Micrometer computes a percentile approximation for each meter ID (set of name and tags) and ships the percentile value to the monitoring system. This is not as flexible as a percentile histogram because it is not possible to aggregate percentile approximations across tags. Nevertheless, it provides some level of insight into percentile distributions for monitoring systems that do not support server-side percentile calculation based on a histogram.\n\nThe following example builds a timer with a histogram:\n\n[source,java]\n----\nTimer.builder("my.timer")\n .publishPercentiles(0.5, 0.95) // median and 95th percentile <1>\n .publishPercentileHistogram() // <2>\n .serviceLevelObjectives(Duration.ofMillis(100)) // <3>\n .minimumExpectedValue(Duration.ofMillis(1)) // <4>\n .maximumExpectedValue(Duration.ofSeconds(10))\n----\n\n<1> `publishPercentiles`: Used to publish percentile values computed in your application. These values are non-aggregable across dimensions.\n<2> `publishPercentileHistogram`: Used to publish a histogram suitable for computing aggregable (across dimensions) percentile approximations in Prometheus (by using `histogram_quantile`), Atlas (by using `:percentile`), and Wavefront (by using `hs()`). For Prometheus and Atlas, the buckets in the resulting histogram are preset by Micrometer based on a generator that has been determined empirically by Netflix to yield a reasonable error bound on most real world timers and distribution summaries. By default, the generator yields 276 buckets, but Micrometer includes only those that are within the range set by `minimumExpectedValue` and `maximumExpectedValue`, inclusive. Micrometer clamps timers by default to a range of 1 millisecond to 1 minute, yielding 73 histogram buckets per timer dimension. `publishPercentileHistogram` has no effect on systems that do not support aggregable percentile approximations. No histogram is shipped for these systems.\n<3> `serviceLevelObjectives`: Used to publish a cumulative histogram with buckets defined by your SLOs. When used in concert with `publishPercentileHistogram` on a monitoring system that supports aggregable percentiles, this setting adds additional buckets to the published histogram. When used on a system that does not support aggregable percentiles, this setting causes a histogram to be published with only these buckets.\n<4> `minimumExpectedValue`/`maximumExpectedValue`: Controls the number of buckets shipped by `publishPercentileHistogram` and controls the accuracy and memory footprint of the underlying HdrHistogram structure.\n\nSince shipping percentiles to the monitoring system generates additional time series, it is generally preferable to *not* configure them in core libraries that are included as dependencies in applications. Instead, applications can turn on this behavior for some set of timers and distribution summaries by using a meter filter.\n\nFor example, suppose we have a handful of timers in a common library. We have prefixed these timer names with `myservice`:\n\n[source,java]\n----\nregistry.timer("myservice.http.requests").record(..);\nregistry.timer("myservice.db.requests").record(..);\n----\n\nWe can turn on client-side percentiles for both timers by using a meter filter:\n\n[source,java]\n----\nregistry.config().meterFilter(\n new MeterFilter() {\n @Override\n public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {\n if(id.getName().startsWith("myservice")) {\n return DistributionStatisticConfig.builder()\n .percentiles(0.95)\n .build()\n .merge(config);\n }\n return config;\n }\n });\n----\n\n:leveloffset!:\n'},function(e,t,n){"use strict";n.r(t),t.default=n.p+"439c0eea1918ef143002cf62a9393e2a.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"594dce115710525d7771ebe3ab82db19.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"5cdb40b089820639454d90a5e4874aee.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer provides several binders for monitoring the JVM:\n\n[source, java]\n----\nnew ClassLoaderMetrics().bindTo(registry); <1>\nnew JvmMemoryMetrics().bindTo(registry); <2>\nnew JvmGcMetrics().bindTo(registry); <3>\nnew ProcessorMetrics().bindTo(registry); <4>\nnew JvmThreadMetrics().bindTo(registry); <5>\n----\n<1> Gauges loaded and unloaded classes.\n<2> Gauges buffer and memory pool utilization.\n<3> Gauges max and live data size, promotion and allocation rates, and times GC pauses (or concurrent phase time in the case of CMS).\n<4> Gauges current CPU total and load average.\n<5> Gauges thread peak, number of daemon threads, and live threads.\n\nMicrometer also provides a meter binder for `ExecutorService`. You can instrument your `ExecutorService` as follows:\n\n[source, java]\n----\nnew ExecutorServiceMetrics(executor, executorServiceName, tags).bindTo(registry);\n----\n\nMetrics created from the binder vary based on the type of `ExecutorService`.\n\nFor `ThreadPoolExecutor`, the following metrics are provided:\n\n* `executor.completed` (`FunctionCounter`): The approximate total number of tasks that have completed execution.\n* `executor.active` (`Gauge`): The approximate number of threads that are actively executing tasks.\n* `executor.queued` (`Gauge`): The approximate number of tasks that are queued for execution.\n* `executor.pool.size` (`Gauge`): The current number of threads in the pool.\n\nFor `ForkJoinPool`, the following metrics are provided:\n\n* `executor.steals` (`FunctionCounter`): Estimate of the total number of tasks stolen from one thread's work queue by\nanother. The reported value underestimates the actual total number of steals when the pool is not quiescent.\n* `executor.queued` (`Gauge`): An estimate of the total number of tasks currently held in queues by worker threads.\n* `executor.active` (`Gauge`): An estimate of the number of threads that are currently stealing or executing tasks.\n* `executor.running` (`Gauge`): An estimate of the number of worker threads that are not blocked but are waiting to join tasks or for other managed synchronization threads.\n"},function(e,t,n){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer supports binding metrics to a variety of different popular caching libraries. Each implementation supports basic features, such as cache hits versus misses, from which you can derive basic information about the cache hit ratio over a period of time. Micrometer uses a function tracking counter to monitor such things as hits and misses, giving you a notion not only of hits and misses over the total life of the cache (the basic metric exposed from Guava\'s `CacheStats`, for example) but hits and misses inside a given interval.\n\nTo demonstrate the features of cache monitoring, we start with a simple program that uses `reactor-netty` to read the entirety of Mary Shelley\'s _Frankenstein_ and put each word in the cache if it has not yet been seen:\n\n====\n[source,java]\n----\n// read all of Frankenstein\nHttpClient.create("www.gutenberg.org")\n .get("/cache/epub/84/pg84.txt")\n .flatMapMany(res -> res.addHandler(wordDecoder()).receive().asString())\n .delayElements(Duration.ofMillis(10)) // one word per 10 ms\n .filter(word -> !word.isEmpty())\n .doOnNext(word -> {\n if (cache.getIfPresent(word) == null)\n cache.put(word, 1);\n })\n .blockLast();\n----\n====\n\nThe following image shows the hits versus misses on a cache that has been tuned to hold a maximum of 10,000 entries:\n\n.Hits vs. misses, viewed in Prometheus\nimage::'+n(71)+"[Hits vs. misses,width=800]\n\n```\nrate(book_guava_requests_total[10s])\n```\n\nBy dividing the hits by the sum of all `get` operations (regardless of whether or not each one was a hit or a miss), we can arrive at a notion of the upper bound for the hit ratio for reading Frankenstein with only 10,000 words:\n\n.Hit ratio, viewed by Prometheus\nimage::"+n(72)+'[Hit ratio,width=800]\n\n```\nsum(rate(book_guava_requests_total{result="hit"}[1m])) / sum(rate(book_guava_requests_total[1m]))\n```\n\nIn a real-world scenario, we tune caches according to how we evaluate the tradeoff between storage and load efficiency. You could create an alert based on some upper bound for the rate at which misses occur or on a lower bound for the hit ratio. Setting an upper bound on miss ratio is better than a lower bound on hit ratio. For both ratios, an absence of any activity drops the value to 0.\nThe following image shows the miss ratio when it exceeds 10%:\n\n.Alerting when the miss ratio exceeds 10%\nimage::'+n(73)+"[Miss ratio (alerted),width=800]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"fd12bde9dc50409da244eb7ba80f6548.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"aa70057042488eef46660369434e99d4.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"3c191f6ce81fefe9b7d1980fc9d5b4fe.png"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer supports binding metrics to `OkHttpClient` through `EventListener`.\n\nYou can collect metrics from `OkHttpClient` by adding `OkHttpMetricsEventListener` as follows:\n\n[source,java]\n----\nOkHttpClient client = new OkHttpClient.Builder()\n .eventListener(OkHttpMetricsEventListener.builder(registry, "okhttp.requests")\n .tags(Tags.of("foo", "bar"))\n .build())\n .build();\n----\n\nNOTE: The `uri` tag is usually limited to URI patterns to mitigate tag cardinality explosion, but `OkHttpClient` does not\nprovide URI patterns. We provide `URI_PATTERN` header to support `uri` tag, or you can configure a URI mapper to provide\nyour own tag values for `uri` tag.\n\nTo configure a URI mapper, you can use `uriMapper()` as follows:\n\n[source,java]\n----\nOkHttpClient client = new OkHttpClient.Builder()\n .eventListener(OkHttpMetricsEventListener.builder(registry, "okhttp.requests")\n .uriMapper(req -> req.url().encodedPath())\n .tags(Tags.of("foo", "bar"))\n .build())\n .build();\n----\n\nWARNING: The sample might trigger tag cardinality explosion, as a URI path itself is being used for tag values.\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer supports binding metrics to `Jetty` through `Connection.Listener`.\n\nYou can collect metrics from `Jetty` by adding `JettyConnectionMetrics` as follows:\n\n[source,java]\n----\n Server server = new Server(0);\n Connector connector = new ServerConnector(server);\n connector.addBean(new JettyConnectionMetrics(registry, connector, Tags.of("foo", "bar"));\n server.setConnectors(new Connector[] { connector });\n----\n\nMicrometer also supports binding metrics to `Jersey` through `ApplicationEventListener`.\n\nYou can collect metrics from `Jersey` by adding `MetricsApplicationEventListener` as follows:\n\n[source,java]\n----\nResourceConfig resourceConfig = new ResourceConfig();\nresourceConfig.register(new MetricsApplicationEventListener(\n registry,\n new DefaultJerseyTagsProvider(),\n "http.server.requests",\n true));\nServletContainer servletContainer = new ServletContainer(resourceConfig);\n----\n'},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer supports binding metrics to `Netty`.\n\nYou can collect metrics from `ByteBuf` allocators and from `EventLoopGroup` instances.\nInstrumentation can be done once at startup, if resources are already known:\n\n[source,java,subs=+attributes]\n-----\n// Create or get an existing resources\nDefaultEventLoopGroup eventExecutors = new DefaultEventLoopGroup();\nUnpooledByteBufAllocator unpooledByteBufAllocator = new UnpooledByteBufAllocator(false);\n// Use binders to instrument them\nnew NettyEventExecutorMetrics(eventExecutors).bindTo(this.registry);\nnew NettyAllocatorMetrics(unpooledByteBufAllocator).bindTo(this.registry);\n-----\n\nNetty infrastructure can be configured in many ways, so you can also instrument lazily at runtime as resources are used.\nIf you do so, you must ensure that you will not bind metrics for the same resource multiple times as this can have runtime drawbacks:\n\n[source,java,subs=+attributes]\n-----\n@Override\nprotected void initChannel(SocketChannel channel) throws Exception {\n EventLoop eventLoop = channel.eventLoop();\n if (!isEventLoopInstrumented(eventLoop)) {\n new NettyEventExecutorMetrics(eventLoop).bindTo(this.meterRegistry);\n }\n ByteBufAllocator allocator = channel.alloc();\n if (!isAllocatorInstrumented(allocator) && allocator instanceof ByteBufAllocatorMetricProvider) {\n ByteBufAllocatorMetricProvider allocatorMetric = (ByteBufAllocatorMetricProvider) allocator;\n new NettyAllocatorMetrics(allocatorMetric).bindTo(this.meterRegistry);\n }\n}\n-----\n"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Passing through to Dropwizard\'s Console Reporter\n:toc:\n\nThis guide shows how to plug in less commonly used Dropwizard `Reporter` implementations -- in this case, the `ConsoleReporter`.\n\n[source,java]\n----\n @Bean\n public MetricRegistry dropwizardRegistry() {\n return new MetricRegistry();\n }\n\n @Bean\n public ConsoleReporter consoleReporter(MetricRegistry dropwizardRegistry) {\n ConsoleReporter reporter = ConsoleReporter.forRegistry(dropwizardRegistry)\n .convertRatesTo(TimeUnit.SECONDS)\n .convertDurationsTo(TimeUnit.MILLISECONDS)\n .build();\n reporter.start(1, TimeUnit.SECONDS);\n return reporter;\n }\n\n @Bean\n public MeterRegistry consoleLoggingRegistry(MetricRegistry dropwizardRegistry) {\n DropwizardConfig consoleConfig = new DropwizardConfig() {\n\n @Override\n public String prefix() {\n return "console";\n }\n\n @Override\n public String get(String key) {\n return null;\n }\n\n };\n\n return new DropwizardMeterRegistry(consoleConfig, dropwizardRegistry, HierarchicalNameMapper.DEFAULT, Clock.SYSTEM) {\n @Override\n protected Double nullGaugeValue() {\n return null;\n }\n };\n }\n----\n\nNote that, in a Spring environment, this registry is added to other implementations in a composite and is used for all metrics, both custom and\nauto-configured.\n\n[source,java]\n----\nclass MyComponent {\n private final MeterRegistry registry;\n\n public MyComponent(MeterRegistry registry) {\n this.registry = registry;\n }\n\n public void doSomeWork(String lowCardinalityInput) {\n registry.timer("my.latency", "input", lowCardinalityInput).record(() -> {\n // do work\n });\n }\n}\n----\n\nThe following listing shows typical output for this custom timer:\n\n[source,txt]\n----\n3/2/18 10:14:27 AM =============================================================\n\n-- Timers ----------------------------------------------------------------------\nmyLatency.lowCardinalityInput.INPUT\n count = 1\n mean rate = 1.02 calls/second\n 1-minute rate = 0.00 calls/second\n 5-minute rate = 0.00 calls/second\n 15-minute rate = 0.00 calls/second\n min = 100.00 milliseconds\n max = 100.00 milliseconds\n mean = 100.00 milliseconds\n stddev = 0.00 milliseconds\n median = 100.00 milliseconds\n 75% <= 100.00 milliseconds\n 95% <= 100.00 milliseconds\n 98% <= 100.00 milliseconds\n 99% <= 100.00 milliseconds\n 99.9% <= 100.00 milliseconds\n----\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= HttpSender with Resilience4j Retry\n\n`HttpSender` is an interface for HTTP clients that are used in meter registries for HTTP-based metrics publication. There\nare two implementations:\n\n* `HttpUrlConnectionSender`: `HttpURLConnection`-based `HttpSender`\n* `OkHttpSender`: OkHttp-based `HttpSender`\n\nThere is no out-of-the-box support for retry, but you can decorate it with Resilience4j retry, as follows:\n\n[source,java]\n----\n @Bean\n public DatadogMeterRegistry datadogMeterRegistry(DatadogConfig datadogConfig, Clock clock) {\n return DatadogMeterRegistry.builder(datadogConfig)\n .clock(clock)\n .httpClient(new RetryHttpClient())\n .build();\n }\n\n private static class RetryHttpClient extends HttpUrlConnectionSender {\n\n private final RetryConfig retryConfig = RetryConfig.custom()\n .maxAttempts(2)\n .waitDuration(Duration.ofSeconds(5))\n .build();\n\n private final Retry retry = Retry.of("datadog-metrics", this.retryConfig);\n\n @Override\n public Response send(Request request) {\n CheckedFunction0 retryableSupplier = Retry.decorateCheckedSupplier(\n this.retry,\n () -> super.send(request));\n return Try.of(retryableSupplier).get();\n }\n\n }\n----\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Custom Meter Registry\n\nMicrometer supports popular meter registries out of the box, so you should check those first.\nFor an existing meter registry, if you think that your requirements are generally useful, consider creating an issue or PR against the Micrometer issue tracker.\nFor a non-existent meter registry, if it is widely-used, consider creating an issue or PR for it.\n\nIf you need to create your own custom meter registry, you can create it by extending one of the base classes for meter registries: `MeterRegistry`, `PushMeterRegistry`, or `StepMeterRegistry`.\n\nThe most common way is to extend `StepMeterRegistry`.\nYou can create your own custom `StepMeterRegistry`.\n\nFirst, define your own meter registry configuration by extending `StepRegistryConfig`, as follows:\n\n[source,java]\n----\npublic interface CustomRegistryConfig extends StepRegistryConfig {\n\n CustomRegistryConfig DEFAULT = k -> null;\n\n @Override\n default String prefix() {\n return "custom";\n }\n\n}\n----\n\nSecond, define your own meter registry by extending `StepMeterRegistry`, as follows:\n\n[source,java]\n----\npublic class CustomMeterRegistry extends StepMeterRegistry {\n\n public CustomMeterRegistry(CustomRegistryConfig config, Clock clock) {\n super(config, clock);\n\n start(new NamedThreadFactory("custom-metrics-publisher"));\n }\n\n @Override\n protected void publish() {\n getMeters().stream().forEach(meter -> System.out.println("Publishing " + meter.getId()));\n }\n\n @Override\n protected TimeUnit getBaseTimeUnit() {\n return TimeUnit.MILLISECONDS;\n }\n\n}\n----\n\nFinally, create the meter registry configuration and the meter registry.\nIf you use Spring Boot, you can do so as follows:\n\n[source,java]\n----\n@Configuration\npublic class MetricsConfig {\n\n @Bean\n public CustomRegistryConfig customRegistryConfig() {\n return CustomRegistryConfig.DEFAULT;\n }\n\n @Bean\n public CustomMeterRegistry customMeterRegistry(CustomRegistryConfig customRegistryConfig, Clock clock) {\n return new CustomMeterRegistry(customRegistryConfig, clock);\n }\n\n}\n----\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer\'s open source support policy is defined as follows for different types of releases. Release versions follow a MAJOR.MINOR.PATCH convention, as defined in https://semver.org/[semantic versioning].\n\n* *Major release lines* (such as 1.x or 2.x) are supported with patch releases for a minimum of 3 years from the date the major release (such as `1.0.0` or `2.0.0`) was made available for download.\n* *Minor release lines* (such as 1.1.x or 1.2.x) are supported with patch releases for a minimum of 12 months from the date the minor release (such as `1.1.0` or `1.2.0`) was made available for download.\n* Any confirmed security vulnerabilities on supported release lines should result in a patch release to Maven Central.\n\nCommercial support that extends beyond the OSS support period described on this page is https://tanzu.vmware.com/spring-runtime[available from VMware].\n\nWe generally plan to release a new major or minor version every 6 months (in May and November).\n\n## Released versions\n\nThe following releases are actively maintained:\n\n.Supported minor releases\n[width="35%",options="header"]\n|===========\n| Minor release | OSS Support Until\n| 1.12.x | November 2024\n| 1.11.x | May 2024\n|===========\n\nThe following releases are out of OSS support:\n\n.Out of OSS support minor releases\n[width="35%",options="header"]\n|===========\n| Minor release | Final patch\n| 1.10.x | `1.10.13`\n| 1.9.x | `1.9.17`\n| 1.8.x | `1.8.13`\n| 1.7.x | `1.7.12`\n| 1.6.x | `1.6.13`\n| 1.5.x | `1.5.17`\n| 1.4.x | `1.4.2`\n| 1.3.x | `1.3.20`\n| 1.2.x | `1.2.2`\n| 1.1.x | `1.1.19`\n| 1.0.x | `1.0.11`\n|===========\n\n## Examples\n\nThe following examples demonstrate how the support policy applies:\n\n** Micrometer 1.0.0 was released in February 2018. At a minimum, support for the 1.x line extends through February 2021 (Major Releases statement). Practically, the Micrometer 1.x line is supported for at least as long as Spring Boot 2.x and major versions of other dependent web frameworks are supported.\n** Micrometer 1.1.0 was released in October 2018, minimally extending support through October 2019 (Minor Releases statement). Practically, the Micrometer 1.1.x line is supported for at least as long as the Spring Boot 2.1.x line is supported.\n** If Micrometer 1.2.x were to be released, support for the 1.x line would be extended another 12 months from the 1.2.x release date.\n** Patch releases, such as Micrometer 1.0.7, do not increase the support obligations for the 1.0.x release line.\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Observation\n:toc:\n:sectnums:\n:dimensional: true\n\n== Purpose\n\nMicrometer Observation is part of the https://github.com/micrometer-metrics/micrometer[Micrometer] project and contains the Observation API. The main idea behind it is that you\n\n> Instrument code once, and get multiple benefits out of it\n\n== Installing\n\nMicrometer comes with a Bill of Materials (BOM) which is a project that manages all the project versions for consistency.\n\nThe following example shows the required dependency for Micrometer Observation in Gradle:\n\n[source,groovy,subs=+attributes]\n----\nimplementation platform(\'io.micrometer:micrometer-bom:latest.release\')\nimplementation \'io.micrometer:micrometer-observation\'\n----\n\nThe following example shows the required dependency in Maven:\n\n[source,xml,subs=+attributes]\n----\n\n \n \n io.micrometer\n micrometer-bom\n ${micrometer.version}\n pom\n import\n \n \n\n\n\n \n io.micrometer\n micrometer-observation\n \n\n----\n\n== Introduction\n\n:leveloffset: +1\n\nFor any Observation to happen, you need to register `ObservationHandler` objects through an `ObservationRegistry`. An `ObservationHandler` reacts only to supported implementations of an `Observation.Context` and can create timers, spans, and logs by reacting to the lifecycle events of an Observation such as\n\n* `start` - Observation has been started. Happens when the `Observation#start()` method gets called.\n* `stop` - Observation has been stopped. Happens when the `Observation#stop()` method gets called.\n* `error` - An error occurred while observing. Happens when the `Observation#error(exception)` method gets called.\n* `event` - An event happened when observing. Happens when the `Observation#event(event)` method gets called.\n* `scope started` - Observation opens a Scope. The Scope must be closed when no longer used. Handlers can create thread local variables on start that are cleared upon closing of the scope. Happens when the `Observation#openScope()` method gets called.\n* `scope stopped` - Observation stops a Scope. Happens when the `Observation.Scope#close()` method gets called.\n\nWhenever any of these methods is called, an `ObservationHandler` method (such as `onStart(T extends Observation.Context ctx)`, `onStop(T extends Observation.Context ctx)`, and so on) is called. To pass state between the handler methods, you can use the `Observation.Context`.\n\nThis is how Observation state diagram looks like:\n\n[source]\n----\n Observation Observation\n Context Context\nCreated ----------\x3e Started ----------\x3e Stopped\n----\n\nThis is how Observation Scope state diagram looks like:\n\n[source]\n----\n Observation\n Context\nScope Started ----------\x3e Scope Finished\n----\n\nTo make it possible to debug production problems an Observation needs additional metadata such as key-value pairs (also known as tags). You can then query your metrics or distributed tracing backend by those tags to find the required data. Tags can be either of high or low cardinality.\n\nIMPORTANT: *High cardinality* means that a pair will have an unbounded number of possible values. An HTTP URL is a good\nexample of such a key value (e.g. `/foo/user1234`, `/foo/user2345` etc.). *Low cardinality* means that a key value will have a bounded number of possible values. A *templated* HTTP URL is a good example of such a key value (e.g. `/foo/{userId}`).\n\nTo separate Observation lifecycle operations from an Observation configuration (such as names and low and high cardinality tags), you can use the `ObservationConvention` that provides an easy way of overriding the default naming conventions.\n\nBelow you can find an example of using the Observation API.\n\n[source,java,subs=+attributes]\n-----\nstatic class Example {\n\n private final ObservationRegistry registry;\n\n Example(ObservationRegistry registry) {\n this.registry = registry;\n }\n\n void run() {\n Observation.createNotStarted("foo", registry)\n .lowCardinalityKeyValue("lowTag", "lowTagValue")\n .highCardinalityKeyValue("highTag", "highTagValue")\n .observe(() -> System.out.println("Hello"));\n }\n\n}\n-----\n\nTIP: Calling `observe(() -> ...)` leads to starting the Observation, putting it in scope, running the lambda, putting an error on the Observation if one took place, closing the scope and stopping the Observation.\n\n:leveloffset!:\n\n== ObservationHandler\n\n:leveloffset: +1\n\nA popular way to record them is storing the start state in a `Timer.Sample` instance and stopping it when the event has ended.\nRecording such measurements could look like this:\n\n[source,java,subs=+attributes]\n-----\nMeterRegistry registry = new SimpleMeterRegistry();\nTimer.Sample sample = Timer.start(registry);\ntry {\n // do some work here\n}\nfinally {\n sample.stop(Timer.builder("my.timer").register(registry));\n}\n-----\n\nIf you want to have more observation options (such as metrics and tracing out of the box plus anything else you will plug in) then you\'ll need to rewrite that code to use the `Observation` API.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nObservation.createNotStarted("my.operation", registry).observe(this::doSomeWorkHere);\n-----\n\nOne of the new features in Micrometer 1.10 is the ability to register "handlers" (`ObservationHandler`) that are notified about the lifecycle event of an observation (e.g.: you can run custom code when an observation is started/stopped).\nUsing this feature lets you add tracing capabilities to your existing metrics instrumentation (see: `DefaultTracingObservationHandler`). The implementation of these handlers does not need to be tracing related, it is completely up to you how you are going to implement them (e.g.: you can add logging capabilities) if you want.\n\n=== Observation.Context\n\nIn order to pass information between the instrumented code and the handler (or between handler methods, e.g.: `onStart` and `onStop`), you can utilize an `Observation.Context`. An `Observation.Context` is a `Map`-like container that can store values for you while your handler can access the data inside the context.\n\n=== ObservationHandler Example\n\nBased on this, we can implement a simple handler that lets the users know about its invocations by printing them out to `stdout`.\n\n[source,java,subs=+attributes]\n-----\nstatic class SimpleHandler implements ObservationHandler {\n\n @Override\n public void onStart(Observation.Context context) {\n System.out.println("START " + "data: " + context.get(String.class));\n }\n\n @Override\n public void onError(Observation.Context context) {\n System.out.println("ERROR " + "data: " + context.get(String.class) + ", error: " + context.getError());\n }\n\n @Override\n public void onEvent(Observation.Event event, Observation.Context context) {\n System.out.println("EVENT " + "event: " + event + " data: " + context.get(String.class));\n }\n\n @Override\n public void onStop(Observation.Context context) {\n System.out.println("STOP " + "data: " + context.get(String.class));\n }\n\n @Override\n public boolean supportsContext(Observation.Context handlerContext) {\n // you can decide if your handler should be invoked for this context object or\n // not\n return true;\n }\n\n}\n-----\n\nYou need to register the handler to the `ObservationRegistry`.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nregistry.observationConfig().observationHandler(new SimpleHandler());\n-----\n\nYou can use the `observe` method to instrument your codebase.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nObservation.Context context = new Observation.Context().put(String.class, "test");\n// using a context is optional, so you can call createNotStarted without it:\n// Observation.createNotStarted(name, registry)\nObservation.createNotStarted("my.operation", () -> context, registry).observe(this::doSomeWorkHere);\n-----\n\nYou can also take full control of the scoping mechanism.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nObservation.Context context = new Observation.Context().put(String.class, "test");\n// using a context is optional, so you can call start without it:\n// Observation.start(name, registry)\nObservation observation = Observation.start("my.operation", () -> context, registry);\ntry (Observation.Scope scope = observation.openScope()) {\n doSomeWorkHere();\n}\ncatch (Exception ex) {\n observation.error(ex); // and don\'t forget to handle exceptions\n throw ex;\n}\nfinally {\n observation.stop();\n}\n-----\n\n=== Signaling Errors and Arbitrary Events\n\nWhen **instrumenting** code we might want to signal that an error happened or signal that an arbitrary event happened. The observation API lets us do it via it `error` and `event` methods.\n\nOne use-case for signaling arbitrary event can be attaching annotations to `Span` for Distributed Tracing but you can also process them however you want in your own handler, e.g.: emit log events based on them.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nObservation observation = Observation.start("my.operation", registry);\ntry (Observation.Scope scope = observation.openScope()) {\n observation.event(Observation.Event.of("my.event", "look what happened"));\n doSomeWorkHere();\n}\ncatch (Exception exception) {\n observation.error(exception);\n throw exception;\n}\nfinally {\n observation.stop();\n}\n-----\n\n=== Observation.ObservationConvention Example\n\nWhen **instrumenting** code we want to provide sensible defaults for tags but also we want to allow users to easily change those defaults. An `ObservationConvention` interface is a description of what tags and name we should create for an `Observation.Context`. See the full usage example of an instrumentation together with overriding the default tags.\n\n[source,java,subs=+attributes]\n-----\n\n/**\n * A dedicated {@link Observation.Context} used for taxing.\n */\nclass TaxContext extends Observation.Context {\n\n private final String taxType;\n\n private final String userId;\n\n TaxContext(String taxType, String userId) {\n this.taxType = taxType;\n this.userId = userId;\n }\n\n String getTaxType() {\n return taxType;\n }\n\n String getUserId() {\n return userId;\n }\n\n}\n\n/**\n * An example of an {@link ObservationFilter} that will add the key-values to all\n * observations.\n */\nclass CloudObservationFilter implements ObservationFilter {\n\n @Override\n public Observation.Context map(Observation.Context context) {\n return context.addLowCardinalityKeyValue(KeyValue.of("cloud.zone", CloudUtils.getZone()))\n .addHighCardinalityKeyValue(KeyValue.of("cloud.instance.id", CloudUtils.getCloudInstanceId()));\n }\n\n}\n\n/**\n * An example of an {@link ObservationConvention} that renames the tax related\n * observations and adds cloud related tags to all contexts. When registered via the\n * `ObservationRegistry#observationConfig#observationConvention` will override the\n * default {@link TaxObservationConvention}. If the user provides a custom\n * implementation of the {@link TaxObservationConvention} and passes it to the\n * instrumentation, the custom implementation wins.\n *\n * In other words\n *\n * 1) Custom {@link ObservationConvention} has precedence 2) If no custom convention\n * was passed and there\'s a matching {@link GlobalObservationConvention} it will be\n * picked 3) If there\'s no custom, nor matching global convention, the default\n * {@link ObservationConvention} will be used\n *\n * If you need to add some key-values regardless of the used\n * {@link ObservationConvention} you should use an {@link ObservationFilter}.\n */\nclass GlobalTaxObservationConvention implements GlobalObservationConvention {\n\n // this will be applicable for all tax contexts - it will rename all the tax\n // contexts\n @Override\n public boolean supportsContext(Observation.Context context) {\n return context instanceof TaxContext;\n }\n\n @Override\n public String getName() {\n return "global.tax.calculate";\n }\n\n}\n\n// Interface for an ObservationConvention related to calculating Tax\ninterface TaxObservationConvention extends ObservationConvention {\n\n @Override\n default boolean supportsContext(Observation.Context context) {\n return context instanceof TaxContext;\n }\n\n}\n\n/**\n * Default convention of tags related to calculating tax. If no user one or global\n * convention will be provided then this one will be picked.\n */\nclass DefaultTaxObservationConvention implements TaxObservationConvention {\n\n @Override\n public KeyValues getLowCardinalityKeyValues(TaxContext context) {\n return KeyValues.of(TAX_TYPE.withValue(context.getTaxType()));\n }\n\n @Override\n public KeyValues getHighCardinalityKeyValues(TaxContext context) {\n return KeyValues.of(USER_ID.withValue(context.getUserId()));\n }\n\n @Override\n public String getName() {\n return "default.tax.name";\n }\n\n}\n\n/**\n * If micrometer-docs-generator is used, we will automatically generate documentation\n * for your observations. Check this URL\n * https://github.com/micrometer-metrics/micrometer-docs-generator#documentation for\n * setup example and read the {@link ObservationDocumentation} javadocs.\n */\nenum TaxObservationDocumentation implements ObservationDocumentation {\n\n CALCULATE {\n @Override\n public Class> getDefaultConvention() {\n return DefaultTaxObservationConvention.class;\n }\n\n @Override\n public String getContextualName() {\n return "calculate tax";\n }\n\n @Override\n public String getPrefix() {\n return "tax";\n }\n\n @Override\n public KeyName[] getLowCardinalityKeyNames() {\n return TaxLowCardinalityKeyNames.values();\n }\n\n @Override\n public KeyName[] getHighCardinalityKeyNames() {\n return TaxHighCardinalityKeyNames.values();\n }\n };\n\n enum TaxLowCardinalityKeyNames implements KeyName {\n\n TAX_TYPE {\n @Override\n public String asString() {\n return "tax.type";\n }\n }\n\n }\n\n enum TaxHighCardinalityKeyNames implements KeyName {\n\n USER_ID {\n @Override\n public String asString() {\n return "tax.user.id";\n }\n }\n\n }\n\n}\n\n/**\n * Our business logic that we want to observe.\n */\nclass TaxCalculator {\n\n private final ObservationRegistry observationRegistry;\n\n // If the user wants to override the default they can override this. Otherwise,\n // it will be {@code null}.\n @Nullable\n private final TaxObservationConvention observationConvention;\n\n TaxCalculator(ObservationRegistry observationRegistry,\n @Nullable TaxObservationConvention observationConvention) {\n this.observationRegistry = observationRegistry;\n this.observationConvention = observationConvention;\n }\n\n void calculateTax(String taxType, String userId) {\n // Create a new context\n TaxContext taxContext = new TaxContext(taxType, userId);\n // Create a new observation\n TaxObservationDocumentation.CALCULATE\n .observation(this.observationConvention, new DefaultTaxObservationConvention(), () -> taxContext,\n this.observationRegistry)\n // Run the actual logic you want to observe\n .observe(this::calculateInterest);\n }\n\n private void calculateInterest() {\n // do some work\n }\n\n}\n\n/**\n * Example of user changing the default conventions.\n */\nclass CustomTaxObservationConvention extends DefaultTaxObservationConvention {\n\n @Override\n public KeyValues getLowCardinalityKeyValues(TaxContext context) {\n return super.getLowCardinalityKeyValues(context)\n .and(KeyValue.of("additional.low.cardinality.tag", "value"));\n }\n\n @Override\n public KeyValues getHighCardinalityKeyValues(TaxContext context) {\n return KeyValues.of("this.would.override.the.default.high.cardinality.tags", "value");\n }\n\n @Override\n public String getName() {\n return "tax.calculate";\n }\n\n}\n\n/**\n * A utility class to set cloud related arguments.\n */\nstatic class CloudUtils {\n\n static String getZone() {\n return "...";\n }\n\n static String getCloudInstanceId() {\n return "...";\n }\n\n}\n-----\n\nBelow you can find an example of setting the whole code together.\n\n[source,java,subs=+attributes]\n-----\n// Registry setup\nObservationRegistry observationRegistry = ObservationRegistry.create();\n// add metrics\nSimpleMeterRegistry registry = new SimpleMeterRegistry();\nobservationRegistry.observationConfig().observationHandler(new DefaultMeterObservationHandler(registry));\nobservationRegistry.observationConfig().observationConvention(new GlobalTaxObservationConvention());\n// This will be applied to all observations\nobservationRegistry.observationConfig().observationFilter(new CloudObservationFilter());\n\n// In this case we\'re overriding the default convention by passing the custom one\nTaxCalculator taxCalculator = new TaxCalculator(observationRegistry, new CustomTaxObservationConvention());\n// run the logic you want to observe\ntaxCalculator.calculateTax("INCOME_TAX", "1234567890");\n-----\n\n=== Observation Predicates and Filters\n\nTo globally disable observations under given conditions you can use an `ObservationPredicate`. To mutate the `Observation.Context` you can use an `ObservationFilter`.\n\nTo set these just call `ObservationRegistry#observationConfig()#observationPredicate()` and `ObservationRegistry#observationConfig()#observationFilter()` methods respectively.\n\nBelow you can find an example of predicates and filters.\n\n[source,java,subs=+attributes]\n-----\n// Example using a metrics handler - we need a MeterRegistry\nMeterRegistry meterRegistry = new SimpleMeterRegistry();\n\n// Create an ObservationRegistry\nObservationRegistry registry = ObservationRegistry.create();\n// Add predicates and filter to the registry\nregistry.observationConfig()\n // ObservationPredicate can decide whether an observation should be\n // ignored or not\n .observationPredicate((observationName, context) -> {\n // Creates a noop observation if observation name is of given name\n if ("to.ignore".equals(observationName)) {\n // Will be ignored\n return false;\n }\n if (context instanceof MyContext) {\n // For the custom context will ignore a user with a given name\n return !"user to ignore".equals(((MyContext) context).getUsername());\n }\n // Will proceed for all other types of context\n return true;\n })\n // ObservationFilter can modify a context\n .observationFilter(context -> {\n // We\'re adding a low cardinality key to all contexts\n context.addLowCardinalityKeyValue(KeyValue.of("low.cardinality.key", "low cardinality value"));\n if (context instanceof MyContext) {\n // We\'re mutating a specific type of a context\n MyContext myContext = (MyContext) context;\n myContext.setUsername("some username");\n // We want to remove a high cardinality key value\n return myContext.removeHighCardinalityKeyValue("high.cardinality.key.to.ignore");\n }\n return context;\n })\n // Example of using metrics\n .observationHandler(new DefaultMeterObservationHandler(meterRegistry));\n\n// Observation will be ignored because of the name\nthen(Observation.start("to.ignore", () -> new MyContext("don\'t ignore"), registry)).isSameAs(Observation.NOOP);\n// Observation will be ignored because of the entries in MyContext\nthen(Observation.start("not.to.ignore", () -> new MyContext("user to ignore"), registry))\n .isSameAs(Observation.NOOP);\n\n// Observation will not be ignored...\nMyContext myContext = new MyContext("user not to ignore");\nmyContext.addHighCardinalityKeyValue(KeyValue.of("high.cardinality.key.to.ignore", "some value"));\nObservation.createNotStarted("not.to.ignore", () -> myContext, registry).observe(this::yourCodeToMeasure);\n// ...and will have the context mutated\nthen(myContext.getLowCardinalityKeyValue("low.cardinality.key").getValue()).isEqualTo("low cardinality value");\nthen(myContext.getUsername()).isEqualTo("some username");\nthen(myContext.getHighCardinalityKeyValues())\n .doesNotContain(KeyValue.of("high.cardinality.key.to.ignore", "some value"));\n-----\n\n=== Using Annotations With @Observed\n\nIf you have turned on Aspect Oriented Programming (e.g. via `org.aspectj:aspectjweaver`) you can use the `@Observed` annotation to create observations. You can put that annotation either on a method to observe it or a class to observe all methods in it. Let\'s look at the following example.\n\nHere you can see an `ObservedService` that has an annotation on a method.\n\n[source,java,subs=+attributes]\n-----\nstatic class ObservedService {\n\n @Observed(name = "test.call", contextualName = "test#call",\n lowCardinalityKeyValues = { "abc", "123", "test", "42" })\n void call() {\n System.out.println("call");\n }\n\n}\n-----\n\nThe following test asserts whether the proper observation gets created when a proxied `ObservedService` instance gets called.\n\n[source,java,subs=+attributes]\n-----\n// create a test registry\nTestObservationRegistry registry = TestObservationRegistry.create();\n// add a system out printing handler\nregistry.observationConfig().observationHandler(new ObservationTextPublisher());\n\n// create a proxy around the observed service\nAspectJProxyFactory pf = new AspectJProxyFactory(new ObservedService());\npf.addAspect(new ObservedAspect(registry));\n\n// make a call\nObservedService service = pf.getProxy();\nservice.call();\n\n// assert that observation has been properly created\nTestObservationRegistryAssert.assertThat(registry)\n .hasSingleObservationThat()\n .hasBeenStopped()\n .hasNameEqualTo("test.call")\n .hasContextualNameEqualTo("test#call")\n .hasLowCardinalityKeyValue("abc", "123")\n .hasLowCardinalityKeyValue("test", "42")\n .hasLowCardinalityKeyValue("class", ObservedService.class.getName())\n .hasLowCardinalityKeyValue("method", "call").doesNotHaveError();\n-----\n\n:leveloffset!:\n\n== Instrumenting\n\n:leveloffset: +1\n\nIn this section we will see some common examples of reusing existing Micrometer and Micrometer Tracing handlers and context types to do instrumentation.\n\nIMPORTANT: Before you decide to instrument a project yourself, please double-check whether it hasn\'t already been instrumented!\n\nTo better understand how you can do instrumentation we need to distinguish two concepts.\n\n- Context propagation\n- Creation of Observations\n\n*Context propagation* - we propagate existing context through threads or network. We\'re using the https://micrometer.io/docs/contextPropagation[Micrometer Context Propagation] library to define the context and to propagate it through threads. We\'re using dedicated `SenderContext` and `ReceiverContext` objects, together with Micrometer Tracing handlers, to create Observations that will propagate context over the wire.\n\n*Creation of Observations* - we want to wrap an operation in an Observation to get measurements. We need to know if there previously has been a parent Observation to maintain the parent-child relationship of Observations.\n\n[[instrumentation_of_thread_switching_components]]\n== Instrumentation of Thread Switching Components\n\nWe might want to create an Observation around a `Runnable` or `Callable` that we\'re submitting through an `Executor`. For that to work we need to know if in the parent thread there was an Observation that the new thread should continue, or for which a child Observation should be created.\n\nLet\'s look at the following example.\n\n[source,java,subs=+attributes]\n-----\n// Example of an Executor Service\nExecutorService executor = Executors.newCachedThreadPool();\n\n\n// This snippet shows an example of how to wrap in an observation code that would\n// be executed in a separate thread\n\n// Let\'s assume that we have a parent observation\nObservation parent = Observation.createNotStarted("parent", registry);\n// Observation is put in scope via the method\nFuture child = parent.observe(() -> {\n // [Thread 1] Current Observation is the same as \n then(registry.getCurrentObservation()).isSameAs(parent);\n // [Thread 1] We\'re wrapping the executor in a Context Propagating version.\n // comes from Context Propagation library\n return ContextExecutorService.wrap(executor).submit(() -> {\n // [Thread 2] Current Observation is same as - context got\n // propagated\n then(registry.getCurrentObservation()).isSameAs(parent);\n // Wraps the code that should be run in a separate thread in an\n // observation\n return Observation.createNotStarted("child", registry).observe(this::yourCodeToMeasure);\n });\n});\n-----\n\n[[instrumentation_of_reactive_libraries]]\n== Instrumentation of Reactive Libraries\n\nIn this section we\'ll discuss how to wrap Reactive libraries in Observations and how to use Reactor Context to safely propagate Observations between threads.\n\n[[instrumentation_of_reactive_libraries_after_reactor_3_5_3]]\n=== For Reactor 3.5.3 and After\n\nIn Reactor 3.5.3 release (through this https://github.com/reactor/reactor-core/pull/3335[PR]) an option to turn on automated context propagation was added. To use this, please ensure that you\'re using the following projects at minimum in the following versions:\n\n- Reactor https://github.com/reactor/reactor-core/releases/tag/v3.5.7[3.5.7]\n- Micrometer Context-Propagation https://github.com/micrometer-metrics/context-propagation/releases/tag/v1.0.3[1.0.3]\n- Micrometer https://github.com/micrometer-metrics/micrometer/releases/tag/v1.10.8[1.10.8]\n- Micrometer Tracing https://github.com/micrometer-metrics/tracing/releases/tag/v1.0.7[1.0.7]\n\nTo use the feature call the new Reactor\'s Hook method (e.g. in your `public static void main` method) like this\n\n[source,java,subs=+attributes]\n-----\nHooks.enableAutomaticContextPropagation();\n-----\n\nThis will automatically wrap Reactor internal mechanisms to propagate context between operators, threads etc. Usage of `tap` and `handle` or Context Propagation API is not required.\n\nLet\'s look at the following example.\n\n[source,java,subs=+attributes]\n-----\n// This snippet shows an example of how to use the new Hook API with Reactor\nHooks.enableAutomaticContextPropagation();\n// Starting from Micrometer 1.10.8 you need to set your registry on this singleton\n// instance of OTLA\nObservationThreadLocalAccessor.getInstance().setObservationRegistry(registry);\n\n// Let\'s assume that we have a parent observation\nObservation parent = Observation.start("parent", registry);\n// Now we put it in thread local\nparent.scoped(() -> {\n\n // Example of propagating whatever there was in thread local\n Integer block = Mono.just(1).publishOn(Schedulers.boundedElastic()).doOnNext(integer -> {\n log.info("Context Propagation happens - the observation gets propagated ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isSameAs(parent);\n })\n .flatMap(integer -> Mono.just(integer).map(monoInteger -> monoInteger + 1))\n .transformDeferredContextual((integerMono, contextView) -> integerMono.doOnNext(integer -> {\n log.info("Context Propagation happens - the observation gets propagated ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isSameAs(parent);\n }))\n // Let\'s assume that we\'re modifying the context\n .contextWrite(context -> context.put("foo", "bar"))\n // Since we are NOT part of the Reactive Chain (e.g. this is not a\n // WebFlux application)\n // you MUST call to capture all ThreadLocal values\n // and store them in a Reactor Context.\n // ----------------------\n // If you were part of the\n // Reactive Chain (e.g. returning Mono from endpoint)\n // there is NO NEED to call . If you need to propagate\n // your e.g. Observation\n // to the Publisher you just created (e.g. Mono or Flux) please\n // consider adding it\n // to the Reactor Context directly instead of opening an Observation\n // scope and calling (see example below).\n .contextCapture()\n .block();\n\n // We\'re still using as current observation\n then(registry.getCurrentObservation()).isSameAs(parent);\n\n then(block).isEqualTo(2);\n\n // Now, we want to create a child observation for a Reactor stream and put it\n // to Reactor Context\n // Automatically its parent will be observation since is in\n // Thread Local\n Observation child = Observation.start("child", registry);\n block = Mono.just(1).publishOn(Schedulers.boundedElastic()).doOnNext(integer -> {\n log.info(\n "Context Propagation happens - the observation from Reactor Context takes precedence over thread local observation ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isSameAs(child);\n })\n .flatMap(integer -> Mono.just(integer).map(monoInteger -> monoInteger + 1))\n .transformDeferredContextual((integerMono, contextView) -> integerMono.doOnNext(integer -> {\n log.info(\n "Context Propagation happens - the observation from Reactor Context takes precedence over thread local observation ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isSameAs(child);\n }))\n // Remember to stop the child Observation!\n .doFinally(signalType -> child.stop())\n // When using Reactor we ALWAYS search for\n // ObservationThreadLocalAccessor.KEY entry in the Reactor Context to\n // search for an Observation. You DON\'T have to use \n // because\n // you have manually provided the ThreadLocalAccessor key\n .contextWrite(context -> context.put(ObservationThreadLocalAccessor.KEY, child))\n .block();\n\n // We\'re back to having as current observation\n then(registry.getCurrentObservation()).isSameAs(parent);\n\n then(block).isEqualTo(2);\n});\n\n// There should be no remaining observation\nthen(registry.getCurrentObservation()).isNull();\n\n// We need to stop the parent\nparent.stop();\n-----\n\nIf performance of this approach is not satisfactory, please verify whether disabling the hook and explicitly using `handle` or `tap` operators improves the performance.\n\n[[instrumentation_of_reactive_libraries_before_reactor_3_5_3]]\n=== Before Reactor 3.5.3\n\nThe preferred way of propagating elements through the Flux using Reactor is not via ``ThreadLocal``s but through Reactor Context. Reactor however gives you two operators, `tap()` and `handle()` where, if https://micrometer.io/docs/contextPropagation[Micrometer Context Propagation] library is on the classpath, it will set thread local values for you.\n\nLet\'s look at the following example.\n\n[source,java,subs=+attributes]\n-----\n// This snippet shows an example of how to wrap code that is using Reactor\n\n// Let\'s assume that we have a parent observation\nObservation parent = Observation.start("parent", registry);\n\n// We want to create a child observation for a Reactor stream\nObservation child = Observation.start("child", registry)\n // There\'s no thread local entry, so we will pass parent observation\n // manually. If we put the Observation in scope we could then call\n // <.contextCapture()> method from Reactor to capture all thread locals\n // and store them in Reactor Context.\n .parentObservation(parent);\nInteger block = Mono.just(1)\n // Example of not propagating context by default\n .doOnNext(integer -> {\n log.info(\n "No context propagation happens by default in Reactor - there will be no Observation in thread local here ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isNull();\n })\n // Example of having entries in thread local for operator\n .tap(() -> new DefaultSignalListener() {\n @Override\n public void doFirst() throws Throwable {\n log.info("We\'re using tap() -> there will be Observation in thread local here ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isNotNull();\n }\n })\n .flatMap(integer -> Mono.just(integer).map(monoInteger -> monoInteger + 1))\n // Example of retrieving ThreadLocal entries via ReactorContext\n .transformDeferredContextual((integerMono, contextView) -> integerMono.doOnNext(integer -> {\n try (ContextSnapshot.Scope scope = ContextSnapshot.setAllThreadLocalsFrom(contextView)) {\n log.info(\n "We\'re retrieving thread locals from Reactor Context - there will be Observation in thread local here ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isNotNull();\n }\n }))\n // Example of having entries in thread local for operator\n .handle((BiConsumer>) (integer, synchronousSink) -> {\n log.info("We\'re using handle() -> There will be Observation in thread local here ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isNotNull();\n synchronousSink.next(integer);\n })\n // Remember to stop the child Observation!\n .doFinally(signalType -> child.stop())\n // When using Reactor we ALWAYS search for\n // ObservationThreadLocalAccessor.KEY entry in the Reactor Context to\n // search for an Observation\n .contextWrite(context -> context.put(ObservationThreadLocalAccessor.KEY, child))\n // If there were ThreadLocal entries that are using Micrometer Context\n // Propagation they would be caught here. All implementations of\n // will store their thread local entries under their\n // keys in Reactor Context\n .contextCapture()\n .block();\n\n// We didn\'t have any observations in thread local\nthen(registry.getCurrentObservation()).isNull();\n\n// We need to stop the parent\nparent.stop();\n\nthen(block).isEqualTo(2);\n-----\n\n[[instrumentation_of_http_communication]]\n== Instrumentation of HTTP Communication\n\nIn order to instrument an HTTP-based communication we need to use the `RequestReplySenderContext` and `RequestReplyReceiverContext` for the client and server side respectively.\n\nAs an example for the client side we will use a handler that instruments the HTTP request by adding a `foo:bar` header (if you have Micrometer Tracing on the classpath you could reuse the `PropagatingSenderTracingObservationHandler` and `PropagatingReceiverTracingObservationHandler` to propagate tracing context over the wire). Let\'s look at the example of such a handler.\n\n[source,java,subs=+attributes]\n-----\nstatic class HeaderPropagatingHandler implements ObservationHandler> {\n\n @Override\n public void onStart(SenderContext context) {\n context.getSetter().set(context.getCarrier(), "foo", "bar");\n }\n\n @Override\n public boolean supportsContext(Observation.Context context) {\n return context instanceof SenderContext;\n }\n\n}\n-----\n\nLet\'s look at the following of HTTP client side instrumentation that reuses the handler.\n\n[source,java,subs=+attributes]\n-----\n// This example can be combined with the idea of ObservationConvention to allow\n// users to easily customize the key values. Please read the rest of the\n// documentation on how to do it.\n\n// In Micrometer Tracing we would have predefined\n// PropagatingSenderTracingObservationHandler but for the sake of this demo we\n// create our own handler that puts "foo":"bar" headers into the request\nregistry.observationConfig().observationHandler(new HeaderPropagatingHandler());\n\n// We\'re using WireMock to stub the HTTP GET call to "/foo" with a response "OK"\nstubFor(get("/foo").willReturn(ok().withBody("OK")));\n\n// RequestReplySenderContext is a special type of context used for request-reply\n// communication. It requires to define what the Request type is and how we can\n// instrument it. It also needs to know what the Response type is\nRequestReplySenderContext context = new RequestReplySenderContext<>(\n (carrier, key, value) -> Objects.requireNonNull(carrier).addHeader(key, value));\n\n// We\'re instrumenting the Apache HTTPClient\ntry (CloseableHttpClient httpclient = HttpClients.createDefault()) {\n // The HttpGet is our carrier (we can mutate it to instrument the headers)\n HttpGet httpget = new HttpGet(info.getHttpBaseUrl() + "/foo");\n // We must set the carrier BEFORE we run \n context.setCarrier(httpget);\n // You can set the remote service address to provide more debugging\n // information\n context.setRemoteServiceAddress(info.getHttpBaseUrl());\n // Examples of setting key values from the request\n Observation observation = Observation.createNotStarted("http.client.requests", () -> context, registry)\n .contextualName("HTTP " + httpget.getMethod())\n .lowCardinalityKeyValue("http.url", info.getHttpBaseUrl() + "/{name}")\n .highCardinalityKeyValue("http.full-url", httpget.getRequestUri());\n observation.observeChecked(() -> {\n String response = httpclient.execute(httpget, classicHttpResponse -> {\n // We should set the response before we stop the observation\n context.setResponse(classicHttpResponse);\n // Example of setting key values from the response\n observation.highCardinalityKeyValue("http.content.length",\n String.valueOf(classicHttpResponse.getEntity().getContentLength()));\n return EntityUtils.toString(classicHttpResponse.getEntity());\n });\n\n then(response).isEqualTo("OK");\n });\n}\n\n// We want to be sure that we have successfully enriched the HTTP headers\nverify(getRequestedFor(urlEqualTo("/foo")).withHeader("foo", equalTo("bar")));\n-----\n\nAs an example for the server side we will use a handler that instruments the Observation by adding the `foo` low cardinality key with the value being the matched path from the HTTP request. Let\'s look at the example of such a handler.\n\n[source,java,subs=+attributes]\n-----\nstatic class HeaderReadingHandler implements ObservationHandler> {\n\n @Override\n public void onStart(ReceiverContext context) {\n String fooHeader = context.getGetter().get(context.getCarrier(), "foo");\n // We\'re setting the value of the header as a low cardinality key value\n context.addLowCardinalityKeyValue(KeyValue.of("foo", fooHeader));\n }\n\n @Override\n public boolean supportsContext(Observation.Context context) {\n return context instanceof ReceiverContext;\n }\n\n}\n-----\n\nLet\'s look at the following of HTTP server side instrumentation that reuses the handler.\n\n[source,java,subs=+attributes]\n-----\n// This example can be combined with the idea of ObservationConvention to allow\n// users to easily customize the key values. Please read the rest of the\n// documentation on how to do it.\n\n// In Micrometer Tracing we would have predefined\n// PropagatingReceiverTracingObservationHandler but for the sake of this demo we\n// create our own handler that will reuse the header from the request as a\n// low cardinality key value\nregistry.observationConfig().observationHandler(new HeaderReadingHandler());\n\ntry (Javalin javalin = Javalin.create().before("/hello/{name}", ctx -> {\n // We\'re creating the special RequestReplyReceiverContext that will reuse the\n // information from the HTTP headers\n RequestReplyReceiverContext receiverContext = new RequestReplyReceiverContext<>(\n Context::header);\n // Remember to set the carrier!!!\n receiverContext.setCarrier(ctx);\n String remoteServiceAddress = ctx.scheme() + "://" + ctx.host();\n receiverContext.setRemoteServiceAddress(remoteServiceAddress);\n // We\'re starting an Observation with the context\n Observation observation = Observation\n .createNotStarted("http.server.requests", () -> receiverContext, registry)\n .contextualName("HTTP " + ctx.method() + " " + ctx.matchedPath())\n .lowCardinalityKeyValue("http.url", remoteServiceAddress + ctx.matchedPath())\n .highCardinalityKeyValue("http.full-url", remoteServiceAddress + ctx.path())\n .lowCardinalityKeyValue("http.method", ctx.method())\n .start();\n // Let\'s be consistent and always set the Observation related objects under\n // the same key\n ctx.attribute(ObservationThreadLocalAccessor.KEY, observation);\n}).get("/hello/{name}", ctx -> {\n // We need to be thread-safe - we\'re not using ThreadLocals, we\'re retrieving\n // information from the attributes\n Observation observation = ctx.attribute(ObservationThreadLocalAccessor.KEY);\n observation.scoped(() -> {\n // If we need thread locals (e.g. MDC entries) we can use \n log.info("We\'re using scoped - Observation in thread local here [" + registry.getCurrentObservation()\n + "]");\n then(registry.getCurrentObservation()).isNotNull();\n });\n // We\'re returning body\n ctx.result("Hello World [" + observation.getContext().getLowCardinalityKeyValue("foo").getValue() + "]");\n}).after("/hello/{name}", ctx -> {\n // After sending the response we want to stop the Observation\n Observation observation = ctx.attribute(ObservationThreadLocalAccessor.KEY);\n observation.stop();\n}).start(0)) {\n // We\'re sending an HTTP request with a header. We\'re expecting that\n // it will be reused in the response\n String response = sendRequestToHelloEndpointWithHeader(javalin.port(), "foo", "bar");\n\n // The response must contain the value from the header\n then(response).isEqualTo("Hello World [bar]");\n}\n-----\n\n== Instrumentation of Messaging Communication\n\nTo instrument messaging components you should proceed in the same way as you would with <>, however instead of `RequestReplySenderContext` and `RequestReplyReceiverContext` you would use `SenderContext` and `ReceiverContext`. You can also set the `remoteServiceName` on a context to suggest the name of the broker (e.g. `kafka` or `rabbitmq`).\n\n:leveloffset!:\n\n== Testing\n\n:leveloffset: +1\n\nMicrometer Observation comes with `micrometer-observation-test` module that allows you to unit-test your Observations.\n\n== Installing\n\nThe following example shows the required dependency in Gradle (assuming that Micrometer BOM has been added):\n\n[source,groovy,subs=+attributes]\n-----\ntestImplementation \'io.micrometer:micrometer-observation-test\'\n-----\n\nThe following example shows the required dependency in Maven (assuming that Micrometer BOM has been added):\n\n[source,xml,subs=+attributes]\n-----\n\n io.micrometer\n micrometer-observation-test\n test\n\n-----\n\n== Running Observation Unit Tests\n\nLet\'s say that you have the following production code. It will create an observation with 2 tags (low and high cardinality) and then call `observe` that will start the observation, put it in scope, close the scope and stop the observation.\n\n[source,java,subs=+attributes]\n-----\nstatic class Example {\n\n private final ObservationRegistry registry;\n\n Example(ObservationRegistry registry) {\n this.registry = registry;\n }\n\n void run() {\n Observation.createNotStarted("foo", registry)\n .lowCardinalityKeyValue("lowTag", "lowTagValue")\n .highCardinalityKeyValue("highTag", "highTagValue")\n .observe(() -> System.out.println("Hello"));\n }\n\n}\n-----\n\nTo unit-test this code you can use the `TestObservationRegistry` class.\n\n[source,java,subs=+attributes]\n-----\n@Test\nvoid should_assert_your_observation() {\n // create a test registry in your tests\n TestObservationRegistry registry = TestObservationRegistry.create();\n\n // run your production code with the TestObservationRegistry\n new Example(registry).run();\n\n // check your observation\n TestObservationRegistryAssert.assertThat(registry)\n .doesNotHaveAnyRemainingCurrentObservation()\n .hasObservationWithNameEqualTo("foo")\n .that()\n .hasHighCardinalityKeyValue("highTag", "highTagValue")\n .hasLowCardinalityKeyValue("lowTag", "lowTagValue")\n .hasBeenStarted()\n .hasBeenStopped();\n}\n-----\n\n:leveloffset!:\n\n== Documentation Generation\n\n:leveloffset: +1\n\n== Automated Documentation Generation\n\nBy using the https://github.com/micrometer-metrics/micrometer-docs-generator[Micrometer Docs Generator] project and by implementing the `ObservationDocumentation`, `SpanDocumentation` or `MeterDocumentation` interfaces as an `enum` we can scan your sources and generate Asciidoctor documentation. This allows you to maintain the docuemntation for your observability instrumentation in code, and as long as you use the `enum` implementation in your instrumentation, it will ensure that your documentation stays in-sync with the instrumentation.\n\nBelow you can find an example of a Maven `pom.xml` with the Micrometer Docs Generator project.\n\n.pom.xml\n[source,xml,subs=+attributes]\n-----\n\n\n\t4.0.0\n\tcom.example\n\tmicrometer-docs-generator-example\n\tjar\n\tmicrometer-docs-generator-example\n\tmicrometer-docs-generator-example\n\t\n\t\t1.0.0\n\t\t${maven.multiModuleProjectDirectory}/folder-with-sources-to-scan/\n\t\t.*\n\t\t${maven.multiModuleProjectDirectory}/target/output-folder-with-adocs/\'\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\torg.codehaus.mojo\n\t\t\t\texec-maven-plugin\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tgenerate-docs\n\t\t\t\t\t\tprepare-package\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tjava\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tio.micrometer.docs.DocsGeneratorCommand\n\t\t\t\t\t\t\ttrue\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t${micrometer-docs-generator.inputPath}\n\t\t\t\t\t\t\t\t${micrometer-docs-generator.inclusionPattern}\n\t\t\t\t\t\t\t\t${micrometer-docs-generator.outputPath}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tio.micrometer\n\t\t\t\t\t\tmicrometer-docs-generator\n\t\t\t\t\t\t${micrometer-docs-generator.version}\n\t\t\t\t\t\tjar\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\n\t\n\t\t\n\t\t\tspring-snapshots\n\t\t\tSpring Snapshots\n\t\t\thttps://repo.spring.io/snapshot \x3c!-- For Snapshots --\x3e\n\t\t\t\n\t\t\t\ttrue\n\t\t\t\n\t\t\t\n\t\t\t\tfalse\n\t\t\t\n\t\t\n\t\t\n\t\t\tspring-milestones\n\t\t\tSpring Milestones\n\t\t\thttps://repo.spring.io/milestone \x3c!-- For Milestones --\x3e\n\t\t\t\n\t\t\t\tfalse\n\t\t\t\n\t\t\n\t\n\n-----\n\nBelow you can find an example of a Gradle `build.gradle` with the Micrometer Docs Generator project.\n\n.build.gradle\n[source,groovy,subs=+attributes]\n-----\nrepositories {\n\tmaven { url \'https://repo.spring.io/snapshot\' } // for snapshots\n\tmaven { url \'https://repo.spring.io/milestone\' } // for milestones\n\tmavenCentral() // for GA\n}\n\next {\n\tmicrometerDocsVersion="1.0.2"\n}\n\nconfigurations {\n\tadoc\n}\n\ndependencies {\n\tadoc "io.micrometer:micrometer-docs-generator:$micrometerDocsVersion"\n}\n\ntask generateObservabilityDocs(type: JavaExec) {\n\tmainClass = "io.micrometer.docs.DocsGeneratorCommand"\n\tclasspath configurations.adoc\n\t// input folder, inclusion pattern, output folder\n\targs project.rootDir.getAbsolutePath(), ".*", project.rootProject.buildDir.getAbsolutePath()\n}\n-----\n\nRunning these tasks would lead to generation of adoc files similar to these ones.\n\n._metrics.adoc\n[source,adoc,subs=+attributes]\n-----\n[[observability-metrics]]\n=== Observability - Metrics\n\nBelow you can find a list of all samples declared by this project.\n\n[[observability-metrics-task-runner-observation]]\n==== Task Runner Observation\n\n> Observation created when a task runner is executed.\n\n**Metric name** `spring.cloud.task.runner` (defined by convention class `org.springframework.cloud.task.configuration.observation.DefaultTaskObservationConvention`). **Type** `timer` and **base unit** `seconds`.\n\nFully qualified name of the enclosing class `org.springframework.cloud.task.configuration.observation.TaskDocumentedObservation`.\n\nIMPORTANT: All tags must be prefixed with `spring.cloud.task` prefix!\n\n.Low cardinality Keys\n|===\n|Name | Description\n|`spring.cloud.task.runner.bean-name`|Name of the bean that was executed by Spring Cloud Task.\n|===\n-----\n\n._spans.adoc\n[source,adoc,subs=+attributes]\n-----\n[[observability-spans]]\n=== Observability - Spans\n\nBelow you can find a list of all spans declared by this project.\n\n[[observability-spans-task-runner-observation]]\n==== Task Runner Observation Span\n\n> Observation created when a task runner is executed.\n\n**Span name** `spring.cloud.task.runner` (defined by convention class `org.springframework.cloud.task.configuration.observation.DefaultTaskObservationConvention`).\n\nFully qualified name of the enclosing class `org.springframework.cloud.task.configuration.observation.TaskDocumentedObservation`.\n\nIMPORTANT: All tags and event names must be prefixed with `spring.cloud.task` prefix!\n\n.Tag Keys\n|===\n|Name | Description\n|`spring.cloud.task.runner.bean-name`|Name of the bean that was executed by Spring Cloud Task.\n|===\n-----\n\n=== Options\n\nThe main entry class for the docs generation is `DocsGeneratorCommand` class.\nThis class takes following options.\n\n.Optional parameters\n[cols="1,1"]\n|===\n| `--metrics`\n| Generate metrics documentation.\n\n| `--spans`\n| Generate spans documentation.\n\n| `--conventions`\n| Generate observation conventions documentation.\n\n| `--metrics-template=`\n| Handlebars template file location. This can be a path in the classpath or file system. +\ne.g. `templates/metrics.adoc.hbs`, `/home/foo/bar.hbs`\n\n| `--spans-template=`\n| Handlebars template file location. This can be a path in the classpath or file system. +\ne.g. `templates/spans.adoc.hbs`, `/home/foo/bar.hbs`\n\n| `--conventions-template=`\n| Handlebars template file location. This can be a path in the classpath or file system. +\ne.g. `templates/conventions.adoc.hbs`, `/home/foo/bar.hbs`\n\n| `--metrics-output=`\n| Generated metrics doc file location. This can be an absolute path or relative path to the output directory. +\nDefault: `_metrics.adoc`\n\n| `--spans-output=`\n| Generated spans doc file location. This can be an absolute path or relative path to the output directory. +\nDefault: `_spans.adoc`\n| `--conventions-output=`\n| Generated observation convention doc file location. This can be an absolute path or relative path to the output directory. +\nDefault: `_conventions.adoc`\n|===\n\n:leveloffset!:\n\n== Existing Instrumentations\n\n:leveloffset: +1\n\nMicrometer Observation is used to instrument various projects. Below you can find a table of projects that are using Micrometer Observation to _"instrument once and have multiple benefits out of it"_.\n\n.External Project Instrumentations\n|===\n|Project Name |Link\n\n| Apache Camel | https://issues.apache.org/jira/browse/CAMEL-19023[Issue]\n| Apache CXF | https://github.com/apache/cxf/pull/1346#event-10091735987[PR]\n| Apache Dubbo | https://github.com/apache/dubbo/pull/11021[PR]\n| Apache HttpComponents | https://github.com/micrometer-metrics/micrometer/tree/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents[Repo]\n| Apache Skywalking | https://github.com/apache/skywalking-java/pull/401[PR], https://skywalking.apache.org/docs/skywalking-java/next/en/setup/service-agent/java-agent/application-toolkit-micrometer-1.10/[Docs]\n| Armeria | https://github.com/line/armeria/pull/4980[PR]\n| Appsmith | https://github.com/appsmithorg/appsmith/commit/5e46a2f4b7bf184aba03b4b93038edce8a615366[Commit]\n| gRPC | https://github.com/micrometer-metrics/micrometer/pull/3427[PR]\n| Halo | https://github.com/halo-dev/halo/commit/d192b8c956887e4701b94e3ed302fb88e4771583[Commit]\n| JDBC | https://github.com/jdbc-observations/datasource-micrometer[Repo]\n| JDK Http Client | https://github.com/micrometer-metrics/micrometer/blob/main/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/MicrometerHttpClient.java[Repo]\n| Jetty | https://github.com/micrometer-metrics/micrometer/pull/3416[PR]\n| Jersey | https://github.com/micrometer-metrics/micrometer/tree/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jersey/server[Repo]\n| JMS | https://github.com/micrometer-metrics/micrometer/blob/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jms/JmsInstrumentation.java[Repo]\n| Kotlin Coroutines | https://github.com/micrometer-metrics/micrometer/pull/3256[PR]\n| Lettuce | https://github.com/lettuce-io/lettuce-core/commit/6604fbe9e9cff476806c50716e17803e11d1e0ca[Commit]\n| Micronaut | https://github.com/micronaut-projects/micronaut-micrometer/issues/492[Issue]\n| OkHttp | https://github.com/micrometer-metrics/micrometer/tree/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3[Repo]\n| OpenFeign | https://github.com/OpenFeign/feign/pull/1760[PR]\n| RabbitMQ | https://github.com/rabbitmq/rabbitmq-java-client/issues/952[Issue]\n| RabbitMQ Stream | https://github.com/rabbitmq/rabbitmq-stream-java-client/pull/384[PR]\n| Resilience4j | https://github.com/resilience4j/resilience4j/pull/1698[PR]\n| R2DBC | https://github.com/r2dbc/r2dbc-proxy/issues/122[Issue]\n| Reactor | https://micrometer.io/docs/observation#instrumentation_of_reactive_libraries[Docs]\n| Reactor Netty | https://projectreactor.io/docs/netty/release/reference/index.html#_tracing_3[Docs]\n| Redisson | https://github.com/redisson/redisson/issues/4976[Issue],\nhttps://github.com/redisson/redisson/wiki/16.-Observability#162-tracing[Docs]\n| RSocket | https://github.com/rsocket/rsocket-java/tree/master/rsocket-micrometer/src/main/java/io/rsocket/micrometer/observation[Repo]\n| Spring Amqp | https://docs.spring.io/spring-amqp/docs/current/reference/html/index.html#observation[Docs]\n| Spring Batch | https://docs.spring.io/spring-batch/docs/current/reference/html/monitoring-and-metrics.html#tracing[Docs]\n| Spring Cloud Config | https://docs.spring.io/spring-cloud-config/docs/current/reference/html/#observability[Docs]\n| Spring Cloud CircuitBreaker | https://github.com/spring-cloud/spring-cloud-circuitbreaker/commit/4aa6883274a26b4c01b2c38e256d0b985978052e[Commit]\n| Spring Cloud Function | https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability[Repo]\n| Spring Cloud Gateway | https://github.com/spring-cloud/spring-cloud-gateway/tree/main/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/observation[Repo]\n| Spring Cloud OpenFeign | https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#micrometer-support[Docs]\n| Spring Cloud Task | https://docs.spring.io/spring-cloud-task/docs/current/reference/html/#enabling-observations-for-applicationrunner-and-commandlinerunner[Docs]\n| Spring Cloud Vault | https://github.com/spring-cloud/spring-cloud-vault/commit/1116f81971f16f9f9e42ad0994ee12a24404610e[Commit]\n| Spring Data Cassandra | https://docs.spring.io/spring-data/cassandra/docs/current/reference/html/#cassandra.observability[Docs]\n| Spring Data MongoDB | https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongodb.observability[Docs]\n| Spring Data Redis | https://docs.spring.io/spring-data-redis/docs/current/reference/html/#redis.observability[Docs]\n| Spring GraphQL | https://docs.spring.io/spring-graphql/docs/current/reference/html/#observability[Docs]\n| Spring Integration | https://docs.spring.io/spring-integration/reference/metrics.html#micrometer-observation[Docs]\n| Spring Kafka | https://docs.spring.io/spring-kafka/reference/html/#x30-obs[Docs]\n| Spring Security | https://docs.spring.io/spring-security/reference/reactive/integrations/observability.html[Docs]\n| Spring Modulith | https://docs.spring.io/spring-modulith/docs/current/reference/html/#observability[Docs]\n| Spring MVC | https://docs.spring.io/spring-framework/reference/integration/observability.html[Docs]\n| Spring Pulsar | https://docs.spring.io/spring-pulsar/docs/current/reference/html/#micrometer[Docs]\n| Spring WebFlux | https://docs.spring.io/spring-framework/reference/integration/observability.html[Docs]\n|===\n\nIf your project is instrumented using Micrometer Observation, and it\'s not listed in the table above, https://github.com/micrometer-metrics/micrometer-docs/edit/main/src/docs/observation/observation-projects.adoc[please file a PR] to our documentation! If you want to instrument your project and need our help just mention us in your issue - https://github.com/shakuzen/[@shakuzen], https://github.com/jonatan-ivanov/[@jonatan-ivanov], https://github.com/marcingrzejszczak/[@marcingrzejszczak].\n\n:leveloffset!:\n'},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Tracing support\n:toc:\n:sectnums:\n:dimensional: true\n\n== Purpose\n\nThe problem of tracing is not new.\nApplication developers have been creating ways to track the state of their applications for a long time.\nFor much of that time, developers had to create the necessary tracing framework themselves.\n\nIn 2016, the Spring Cloud team created a tracing library that could help a lot of developers.\nIt was called https://github.com/spring-cloud/spring-cloud-sleuth[Spring Cloud Sleuth].\nThe Spring team realized that tracing could be separated from Spring Cloud and created the Micrometer Tracing project, which is, essentially, a Spring-agnostic copy of Spring Cloud Sleuth.\nMicrometer Tracing had its 1.0.0 GA release in November 2022 and has been getting steadily better ever since.\n\nhttps://github.com/micrometer-metrics/tracing[Micrometer Tracing] provides a simple facade for the most popular tracer libraries, letting you instrument your JVM-based application code without vendor lock-in.\nIt is designed to add little to no overhead to your tracing collection activity while maximizing the portability of your tracing effort.\n\nIt also provides a tracing extension to Micrometer's `ObservationHandler` (from Micrometer 1.10.0).\nWhenever an `Observation` is used, a corresponding span will be created, started, stopped and reported.\n\n== Installing\n\nMicrometer Tracing comes with a Bill of Materials (BOM) which is a project that contains all the project versions for you.\n\nThe following example shows the required dependency in Gradle:\n\n[source,groovy,subs=+attributes]\n----\nimplementation platform('io.micrometer:micrometer-tracing-bom:latest.release')\nimplementation 'io.micrometer:micrometer-tracing'\n----\n\nThe following example shows the required dependency in Maven:\n\n[source,xml,subs=+attributes]\n----\n\n \n \n io.micrometer\n micrometer-tracing-bom\n ${micrometer-tracing.version}\n pom\n import\n \n \n\n\n\n \n io.micrometer\n micrometer-tracing\n \n\n----\n\nYou should add a tracing bridge you want to use, such as `micrometer-tracing-bridge-brave` or `micrometer-tracing-bridge-otel` and span exporters / reporters.\nIn case of adding a bridge the `micrometer-tracing` library is added transitively.\n\n== Glossary\n\n:leveloffset: +1\n\nMicrometer Tracing contains a core module with an instrumentation https://en.wikipedia.org/wiki/Service_provider_interface[SPI], a set of modules containing bridges to various tracers, a set of modules containing dedicated span reporting mechanisms, and a test kit.\nYou need to understand the following definitions for distributed tracing:\n\nMicrometer Tracing borrows https://research.google.com/pubs/pub36356.html[Dapper's] terminology.\n\n*Span*: The basic unit of work.\nFor example, sending an RPC is a new span, as is sending a response to an RPC.\nSpans also have other data, such as descriptions, timestamped events, key-value annotations (tags), the ID of the span that caused them, and process IDs (normally IP addresses).\n\nSpans can be started and stopped, and they keep track of their timing information.\nOnce you create a span, you must stop it at some point in the future.\n\n*Trace*: A set of spans forming a tree-like structure.\nFor example, if you run a distributed big-data store, a trace might be formed by a `PUT` request.\n\n*Annotation/Event*: Used to record the existence of an event in time.\n\n*Tracer*: A library that handles the lifecycle of a span.\nIt can create, start, stop and report spans to an external system via reporters / exporters.\n\n*Tracing context*: For distributed tracing to work the tracing context (trace identifier, span identifier, etc.) must be propagated through the process (e.g. over threads) and over the network.\n\n*Log correlation*: Parts of the tracing context (e.g. trace identifier, span identifier) can be populated to the logs of a given application.\nOne can then collect all logs in a single storage and group them via trace id.\nThat way one can get all logs, for a single business operation (trace) from all services put in a chronological order.\n\n*Latency analysis tools*: A tool that collects exported spans and visualizes the whole trace.\nAllows easy latency analysis.\n\nThe following image shows how *Span* and *Trace* look in a system.\n\nimage::"+n(83)+"[Trace Info propagation]\n\nEach color of a note signifies a span (there are seven spans - from *A* to *G*).\nConsider the following note:\n\n[source]\n----\nTrace Id = X\nSpan Id = D\nClient Sent\n----\n\nThis note indicates that the current span has *Trace Id* set to *X* and *Span Id* set to *D*.\nAlso, from the RPC perspective, the `Client Sent` event took place.\n\nLet's consider more notes:\n\n[source]\n----\nTrace Id = X\nSpan Id = A\n(no custom span)\n\nTrace Id = X\nSpan Id = C\n(custom span)\n----\n\nYou can continue with a created span (example with `no custom span` indication) or you can create child spans manually (example with `custom span` indication).\n\nThe following image shows how parent-child relationships of spans look:\n\nimage::"+n(84)+'[Parent child relationship]\n\n:leveloffset!:\n\n== Supported Tracers\n\n:leveloffset: +1\n\nMicrometer Tracing supports the following Tracers.\n\n* https://github.com/openzipkin/brave[*OpenZipkin Brave*]\n* https://opentelemetry.io/[*OpenTelemetry*]\n\n== Installing\n\nThe following example shows the required dependency in Gradle (assuming that Micrometer Tracing BOM has been added):\n\n.Brave Tracer\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-tracing-bridge-brave\'\n----\n\n.OpenTelemetry Tracer\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-tracing-bridge-otel\'\n----\n\nThe following example shows the required dependency in Maven (assuming that Micrometer Tracing BOM has been added):\n\n.Brave Tracer\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-tracing-bridge-brave\n\n----\n\n.OpenTelemetry Tracer\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-tracing-bridge-otel\n\n----\n\nIMPORTANT: Remember to pick *only one* bridge.\nYou *shouldn\'t have* two bridges on the classpath.\n\n:leveloffset!:\n\n== Supported Reporters\n\n:leveloffset: +1\n\nMicrometer Tracing supports directly the following Reporters.\n\n* https://tanzu.vmware.com/observability[*Tanzu Observability by Wavefront*]\n* https://zipkin.io[*OpenZipkin Zipkin*]\n\n== Installing\n\nThe following example shows the required dependency in Gradle (assuming that Micrometer Tracing BOM has been added):\n\n.Tanzu Observability by Wavefront\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-tracing-reporter-wavefront\'\n----\n\n.OpenZipkin Zipkin with Brave\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.zipkin.reporter2:zipkin-reporter-brave\'\n----\n\n.OpenZipkin Zipkin with OpenTelemetry\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.opentelemetry:opentelemetry-exporter-zipkin\'\n----\n\n.An OpenZipkin URL sender dependency to send out spans to Zipkin via a `URLConnectionSender`\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.zipkin.reporter2:zipkin-sender-urlconnection\'\n----\n\nThe following example shows the required dependency in Maven (assuming that Micrometer Tracing BOM has been added):\n\n.Tanzu Observability by Wavefront\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-tracing-reporter-wavefront\n\n----\n\n.OpenZipkin Zipkin with Brave\n[source,xml,subs=+attributes]\n----\n\n io.zipkin.reporter2\n zipkin-reporter-brave\n\n----\n\n.OpenZipkin Zipkin with OpenTelemetry\n[source,xml,subs=+attributes]\n----\n\n io.opentelemetry\n opentelemetry-exporter-zipkin\n\n----\n\n.An OpenZipkin URL sender dependency to send out spans to Zipkin via a `URLConnectionSender`\n[source,xml,subs=+attributes]\n----\n\n io.zipkin.reporter2\n zipkin-sender-urlconnection\n\n----\n\nIMPORTANT: Remember that Brave by default adds Zipkin as a dependency. If you want to use just Wavefront and you\'re using classpath dependant solutions such as Spring Boot, you might be required to exclude the transitive dependency on Zipkin when using Brave (e.g. via exlcuding the `io.zipkin.reporter2` group).\n\n:leveloffset!:\n\n== Using Micrometer Tracing Directly\n\n:leveloffset: +1\n\nIn this section we will describe how to use the Micrometer Tracing API directly to create and report spans.\n\n== Micrometer Tracing Examples\n\nBelow you can see basic operations on a span. Please read the comments in the snippet for details.\n\n[source,java,subs=+attributes]\n-----\n// Create a span. If there was a span present in this thread it will become\n// the `newSpan`\'s parent.\nSpan newSpan = this.tracer.nextSpan().name("calculateTax");\n// Start a span and put it in scope. Putting in scope means putting the span\n// in thread local\n// and, if configured, adjust the MDC to contain tracing information\ntry (Tracer.SpanInScope ws = this.tracer.withSpan(newSpan.start())) {\n // ...\n // You can tag a span - put a key value pair on it for better debugging\n newSpan.tag("taxValue", taxValue);\n // ...\n // You can log an event on a span - an event is an annotated timestamp\n newSpan.event("taxCalculated");\n}\nfinally {\n // Once done remember to end the span. This will allow collecting\n // the span to send it to a distributed tracing system e.g. Zipkin\n newSpan.end();\n}\n-----\n\nBelow you can see how to continue a span in a new thread, that was started in another thread.\n\n[source,java,subs=+attributes]\n-----\nSpan spanFromThreadX = this.tracer.nextSpan().name("calculateTax");\ntry (Tracer.SpanInScope ws = this.tracer.withSpan(spanFromThreadX.start())) {\n executorService.submit(() -> {\n // Pass the span from thread X\n Span continuedSpan = spanFromThreadX;\n // ...\n // You can tag a span\n continuedSpan.tag("taxValue", taxValue);\n // ...\n // You can log an event on a span\n continuedSpan.event("taxCalculated");\n }).get();\n}\nfinally {\n spanFromThreadX.end();\n}\n-----\n\nBelow you can see how to create a child span when explicitly knowing who the parent span is.\n\n[source,java,subs=+attributes]\n-----\n// let\'s assume that we\'re in a thread Y and we\'ve received\n// the `initialSpan` from thread X. `initialSpan` will be the parent\n// of the `newSpan`\nSpan newSpan = this.tracer.nextSpan(initialSpan).name("calculateCommission");\n// ...\n// You can tag a span\nnewSpan.tag("commissionValue", commissionValue);\n// ...\n// You can log an event on a span\nnewSpan.event("commissionCalculated");\n// Once done remember to end the span. This will allow collecting\n// the span to send it to e.g. Zipkin. The tags and events set on the\n// newSpan will not be present on the parent\nnewSpan.end();\n-----\n\n== Micrometer Tracing Brave Setup\n\nIn this subsection we will set up Micrometer Tracing with Brave.\n\nBelow you can see how to create a Micrometer Tracing `Tracer` using Brave components that would send completed spans to Zipkin.\n\n[source,java,subs=+attributes]\n-----\n// [Brave component] Example of using a SpanHandler. SpanHandler is a component\n// that gets called when a span is finished. Here we have an example of setting it\n// up with sending spans\n// in a Zipkin format to the provided location via the UrlConnectionSender\n// (through the dependency)\n// Another option could be to use a TestSpanHandler for testing purposes.\nSpanHandler spanHandler = ZipkinSpanHandler\n .create(AsyncReporter.create(URLConnectionSender.create("http://localhost:9411/api/v2/spans")));\n\n// [Brave component] CurrentTraceContext is a Brave component that allows you to\n// retrieve the current TraceContext.\nThreadLocalCurrentTraceContext braveCurrentTraceContext = ThreadLocalCurrentTraceContext.newBuilder()\n .addScopeDecorator(MDCScopeDecorator.get()) // Example of Brave\'s\n // automatic MDC setup\n .build();\n\n// [Micrometer Tracing component] A Micrometer Tracing wrapper for Brave\'s\n// CurrentTraceContext\nCurrentTraceContext bridgeContext = new BraveCurrentTraceContext(this.braveCurrentTraceContext);\n\n// [Brave component] Tracing is the root component that allows to configure the\n// tracer, handlers, context propagation etc.\nTracing tracing = Tracing.newBuilder()\n .currentTraceContext(this.braveCurrentTraceContext)\n .supportsJoin(false)\n .traceId128Bit(true)\n // For Baggage to work you need to provide a list of fields to propagate\n .propagationFactory(BaggagePropagation.newFactoryBuilder(B3Propagation.FACTORY)\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span_in_scope 1")))\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span_in_scope 2")))\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span")))\n .build())\n .sampler(Sampler.ALWAYS_SAMPLE)\n .addSpanHandler(this.spanHandler)\n .build();\n\n\n// [Brave component] Tracer is a component that handles the life-cycle of a span\nbrave.Tracer braveTracer = this.tracing.tracer();\n\n// [Micrometer Tracing component] A Micrometer Tracing wrapper for Brave\'s Tracer\nTracer tracer = new BraveTracer(this.braveTracer, this.bridgeContext, new BraveBaggageManager());\n\n-----\n\n== Micrometer Tracing OpenTelemetry Setup\n\nIn this subsection we will set up Micrometer Tracing with OpenTelemetry (OTel).\n\nBelow you can see how to create a Micrometer Tracing `Tracer` using OTel components that would send completed spans to Zipkin.\n\n[source,java,subs=+attributes]\n-----\n// [OTel component] Example of using a SpanExporter. SpanExporter is a component\n// that gets called when a span is finished. Here we have an example of setting it\n// up with sending spans\n// in a Zipkin format to the provided location via the UrlConnectionSender\n// (through the and\n// dependencies)\n// Another option could be to use an ArrayListSpanProcessor for testing purposes\nSpanExporter spanExporter = new ZipkinSpanExporterBuilder()\n .setSender(URLConnectionSender.create("http://localhost:9411/api/v2/spans"))\n .build();\n\n// [OTel component] SdkTracerProvider is an SDK implementation for TracerProvider\nSdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()\n .setSampler(alwaysOn())\n .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build())\n .build();\n\n// [OTel component] The SDK implementation of OpenTelemetry\nOpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder()\n .setTracerProvider(sdkTracerProvider)\n .setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader()))\n .build();\n\n// [OTel component] Tracer is a component that handles the life-cycle of a span\nio.opentelemetry.api.trace.Tracer otelTracer = openTelemetrySdk.getTracerProvider()\n .get("io.micrometer.micrometer-tracing");\n\n// [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel\nOtelCurrentTraceContext otelCurrentTraceContext = new OtelCurrentTraceContext();\n\n// [Micrometer Tracing component] A Micrometer Tracing listener for setting up MDC\nSlf4JEventListener slf4JEventListener = new Slf4JEventListener();\n\n// [Micrometer Tracing component] A Micrometer Tracing listener for setting\n// Baggage in MDC. Customizable\n// with correlation fields (currently we\'re setting empty list)\nSlf4JBaggageEventListener slf4JBaggageEventListener = new Slf4JBaggageEventListener(Collections.emptyList());\n\n// [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel\'s Tracer.\n// You can consider\n// customizing the baggage manager with correlation and remote fields (currently\n// we\'re setting empty lists)\nOtelTracer tracer = new OtelTracer(otelTracer, otelCurrentTraceContext, event -> {\n slf4JEventListener.onEvent(event);\n slf4JBaggageEventListener.onEvent(event);\n}, new OtelBaggageManager(otelCurrentTraceContext, Collections.emptyList(), Collections.emptyList()));\n\n-----\n\n== Micrometer Tracing Baggage API\n\nTraces connect from service to service using header propagation. Besides trace identifiers, other properties (called `Baggage`) can also be passed along with the request.\n\nBelow you can find an example on how to use the Tracer API (version `1.0.x`) to create and extract baggage.\n\n[source,java,subs=+attributes]\n-----\n// ---------------------------------------------------------------------------\n// Example for Tracing 1.0.x\n// ---------------------------------------------------------------------------\nSpan span = tracer.nextSpan().name("parent").start();\n\n// Assuming that there\'s a span in scope...\ntry (Tracer.SpanInScope ws = tracer.withSpan(span)) {\n\n // Not passing a TraceContext explicitly will bind the baggage to the\n // current TraceContext\n // If you want to retrieve the baggage value you should make it current\n // first\n try (BaggageInScope baggage = tracer.createBaggage("from_span_in_scope 1", "value 1").makeCurrent()) {\n // This is how you retrieve the baggage\n String baggageValue = baggage.get();\n then(baggageValue).as("[In scope] Baggage 1").isEqualTo("value 1");\n\n String baggageValueViaTracer = tracer.getBaggage("from_span_in_scope 1").get();\n then(baggageValueViaTracer).as("[In scope] Baggage 1").isEqualTo("value 1");\n }\n\n try (BaggageInScope baggage = tracer.createBaggage("from_span_in_scope 2", "value 2").makeCurrent()) {\n then(baggage.get()).as("[In scope] Baggage 2").isEqualTo("value 2");\n then(tracer.getBaggage("from_span_in_scope 2").get()).as("[In scope] Baggage 2")\n .isEqualTo("value 2");\n }\n}\n\n// Assuming that you have a handle to the span\ntry (BaggageInScope baggage = tracer.createBaggage("from_span")\n .set(span.context(), "value 3")\n .makeCurrent()) {\n String baggageValueFromATraceContext = baggage.get(span.context());\n then(baggageValueFromATraceContext).as("[Span passed explicitly] Baggage 3").isEqualTo("value 3");\n\n String baggageValueFromATraceContextThroughTracer = tracer.getBaggage("from_span").get(span.context());\n then(baggageValueFromATraceContextThroughTracer).as("[Span passed explicitly] Baggage 3")\n .isEqualTo("value 3");\n}\n\n// Assuming that there\'s no span in scope\n// When there\'s no span in scope, there will never be any baggage - even if\n// you make it current\ntry (BaggageInScope baggage = tracer.createBaggage("from_span_in_scope 1", "value 1").makeCurrent()) {\n then(baggage.get()).as("[Out of span scope] Baggage 1").isNull();\n then(tracer.getBaggage("from_span_in_scope 1").get()).as("[Out of span scope] Baggage 1").isNull();\n}\nthen(tracer.getBaggage("from_span_in_scope 1").get()).as("[Out of scope] Baggage 1").isNull();\nthen(tracer.getBaggage("from_span_in_scope 2").get()).as("[Out of scope] Baggage 2").isNull();\nthen(tracer.getBaggage("from_span").get()).as("[Out of scope] Baggage 3").isNull();\nthen(tracer.getBaggage("from_span").get(span.context())).as("[Out of scope - with context] Baggage 3")\n .isNull();\n-----\n\nBelow you can find an example on how to use the Tracer API (version `1.1.x`) to create and extract baggage.\n\n[source,java,subs=+attributes]\n-----\n// ---------------------------------------------------------------------------\n// Example for Tracing 1.1.x\n// ---------------------------------------------------------------------------\n\nSpan span = tracer.nextSpan().name("parent").start();\n\n// Assuming that there\'s a span in scope...\ntry (Tracer.SpanInScope ws = tracer.withSpan(span)) {\n\n // Not passing a TraceContext explicitly will bind the baggage to the\n // current TraceContext\n try (BaggageInScope baggage = tracer.createBaggageInScope("from_span_in_scope 1", "value 1")) {\n // This is how you retrieve the baggage\n String baggageValue = baggage.get();\n then(baggageValue).as("[In scope] Baggage 1").isEqualTo("value 1");\n\n String baggageValueViaTracer = tracer.getBaggage("from_span_in_scope 1").get();\n then(baggageValueViaTracer).as("[In scope] Baggage 1").isEqualTo("value 1");\n }\n\n try (BaggageInScope baggage = tracer.createBaggageInScope("from_span_in_scope 2", "value 2")) {\n then(baggage.get()).as("[In scope] Baggage 2").isEqualTo("value 2");\n then(tracer.getBaggage("from_span_in_scope 2").get()).as("[In scope] Baggage 2")\n .isEqualTo("value 2");\n }\n}\n\n// Assuming that you have a handle to the span\ntry (BaggageInScope baggage = tracer.createBaggageInScope(span.context(), "from_span", "value 3")) {\n String baggageValueFromATraceContext = baggage.get(span.context());\n then(baggageValueFromATraceContext).as("[Span passed explicitly] Baggage 3").isEqualTo("value 3");\n\n String baggageValueFromATraceContextThroughTracer = tracer.getBaggage("from_span").get(span.context());\n then(baggageValueFromATraceContextThroughTracer).as("[Span passed explicitly] Baggage 3")\n .isEqualTo("value 3");\n}\n\n// Assuming that there\'s no span in scope\n// When there\'s no span in scope, there will never be any baggage - even if\n// you make it current\ntry (BaggageInScope baggage = tracer.createBaggageInScope("from_span_in_scope 1", "value 1")) {\n then(baggage.get()).as("[Out of span scope] Baggage 1").isNull();\n then(tracer.getBaggage("from_span_in_scope 1").get()).as("[Out of span scope] Baggage 1").isNull();\n}\nthen(tracer.getBaggage("from_span_in_scope 1").get()).as("[Out of scope] Baggage 1").isNull();\nthen(tracer.getBaggage("from_span_in_scope 2").get()).as("[Out of scope] Baggage 2").isNull();\nthen(tracer.getBaggage("from_span").get()).as("[Out of scope] Baggage 3").isNull();\nthen(tracer.getBaggage("from_span").get(span.context())).as("[Out of scope - with context] Baggage 3")\n .isNull();\n-----\n\nIMPORTANT: For Brave, remember to set up the `PropagationFactory` so that it contains the baggage fields that you will be using in your code. Check the example below for details.\n\n[source,java,subs=+attributes]\n-----\nTracing tracing = Tracing.newBuilder()\n .currentTraceContext(this.braveCurrentTraceContext)\n .supportsJoin(false)\n .traceId128Bit(true)\n // For Baggage to work you need to provide a list of fields to propagate\n .propagationFactory(BaggagePropagation.newFactoryBuilder(B3Propagation.FACTORY)\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span_in_scope 1")))\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span_in_scope 2")))\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span")))\n .build())\n .sampler(Sampler.ALWAYS_SAMPLE)\n .addSpanHandler(this.spanHandler)\n .build();\n\n-----\n\n== Aspect Oriented Programming (starting from Micrometer Tracing 1.1.0)\n\nIMPORTANT: This section is applicable from Micrometer Tracing 1.1.0.\n\nMicrometer Tracing contains a `@NewSpan`, `@ContinueSpan` and `@SpanTag` annotations that frameworks can use to create or customize spans for either specific types of methods such as those serving web request endpoints or, more generally, to all methods.\n\nWARNING: Micrometer\'s Spring Boot configuration does _not_ recognize these aspects on arbitrary methods.\n\nAn AspectJ aspect is included. You can use it in your application either through compile/load time AspectJ weaving or through framework facilities that interpret AspectJ aspects and proxy targeted methods in some other way, such as Spring AOP. Here is a sample Spring AOP configuration:\n\n[source,java,subs=+attributes]\n-----\n@Configuration\npublic class SpanAspectConfiguration {\n\n @Bean\n NewSpanParser newSpanParser() {\n return new DefaultNewSpanParser();\n }\n\n // You can provide your own resolvers - here we go with a noop example.\n @Bean\n ValueResolver valueResolver() {\n return new NoOpValueResolver();\n }\n\n // Example of a SpEL resolver\n @Bean\n ValueExpressionResolver valueExpressionResolver() {\n return new SpelTagValueExpressionResolver();\n }\n\n @Bean\n MethodInvocationProcessor methodInvocationProcessor(NewSpanParser newSpanParser, Tracer tracer,\n BeanFactory beanFactory) {\n return new ImperativeMethodInvocationProcessor(newSpanParser, tracer, beanFactory::getBean,\n beanFactory::getBean);\n }\n\n @Bean\n SpanAspect spanAspect(MethodInvocationProcessor methodInvocationProcessor) {\n return new SpanAspect(methodInvocationProcessor);\n }\n\n}\n\n// Example of using SpEL to resolve expressions in @SpanTag\nstatic class SpelTagValueExpressionResolver implements ValueExpressionResolver {\n\n private static final Log log = LogFactory.getLog(SpelTagValueExpressionResolver.class);\n\n @Override\n public String resolve(String expression, Object parameter) {\n try {\n SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();\n ExpressionParser expressionParser = new SpelExpressionParser();\n Expression expressionToEvaluate = expressionParser.parseExpression(expression);\n return expressionToEvaluate.getValue(context, parameter, String.class);\n }\n catch (Exception ex) {\n log.error("Exception occurred while tying to evaluate the SpEL expression [" + expression + "]", ex);\n }\n return parameter.toString();\n }\n\n}\n-----\n\nApplying `SpanAspect` makes `@NewSpan` and `@ContinueSpan` usable on any arbitrary method in an AspectJ proxied instance, as the following example shows:\n\n[source,java,subs=+attributes]\n-----\n// In Sleuth @NewSpan and @ContinueSpan annotations would be taken into\n// consideration. In Micrometer Tracing due to limitations of @Aspect\n// we can\'t do that. The @SpanTag annotation will work well though.\nprotected interface TestBeanInterface {\n\n void testMethod2();\n\n void testMethod3();\n\n void testMethod10(@SpanTag("testTag10") String param);\n\n void testMethod10_v2(@SpanTag("testTag10") String param);\n\n}\n\n// Example of an implementation class\nprotected static class TestBean implements TestBeanInterface {\n\n @NewSpan\n @Override\n public void testMethod2() {\n }\n\n @NewSpan(name = "customNameOnTestMethod3")\n @Override\n public void testMethod3() {\n }\n\n @ContinueSpan(log = "customTest")\n @Override\n public void testMethod10(@SpanTag("customTestTag10") String param) {\n\n }\n\n @ContinueSpan(log = "customTest")\n @Override\n public void testMethod10_v2(String param) {\n\n }\n\n}\n\n// --------------------------\n// ----- USAGE EXAMPLE ------\n// --------------------------\n\n\n// Creates a new span with\ntestBean().testMethod2();\nthen(createdSpanViaAspect()).isEqualTo("test-method2");\n\n// Uses the name from the annotation\ntestBean().testMethod3();\nthen(createdSpanViaAspect()).isEqualTo("custom-name-on-test-method3");\n\n// Continues the previous span\nSpan span = this.tracer.nextSpan().name("foo");\ntry (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) {\n\n // Adds tags and events to an existing span\n testBean().testMethod10("tagValue");\n SimpleSpan continuedSpan = modifiedSpanViaAspect();\n then(continuedSpan.getName()).isEqualTo("foo");\n then(continuedSpan.getTags()).containsEntry("customTestTag10", "tagValue");\n then(continuedSpan.getEvents()).extracting("value").contains("customTest.before", "customTest.after");\n}\nspan.end();\n\n// Continues the previous span\nspan = this.tracer.nextSpan().name("foo");\ntry (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) {\n\n // Adds tags and events to an existing span (reusing setup from the parent\n // interface)\n testBean().testMethod10_v2("tagValue");\n SimpleSpan continuedSpan = modifiedSpanViaAspect();\n then(continuedSpan.getName()).isEqualTo("foo");\n then(continuedSpan.getTags()).containsEntry("testTag10", "tagValue");\n then(continuedSpan.getEvents()).extracting("value").contains("customTest.before", "customTest.after");\n}\nspan.end();\n\n\n-----\n\n\n:leveloffset!:\n\n== Configuring with Micrometer Observation\n\n:leveloffset: +1\n\n== Handler Configuration\n\n// TODO: We need to separately document that Micrometer provides a TimerObservationHandler\n\nFor Micrometer Tracing to work with Micrometer Observation, you need to add a tracing related `ObservationHandler`.\nCheck the example below for an example of adding and using a single `DefaultTracingObservationHandler`.\n\n[source,java,subs=+attributes]\n-----\nTracer tracer = Tracer.NOOP; // The real tracer will come from your tracer\n // implementation (Brave /\n// OTel)\nPropagator propagator = Propagator.NOOP; // The real propagator will come from\n // your tracer implementation (Brave /\n // OTel)\nMeterRegistry meterRegistry = new SimpleMeterRegistry();\n\nObservationRegistry registry = ObservationRegistry.create();\nregistry.observationConfig()\n // assuming that micrometer-core is on the classpath\n .observationHandler(new DefaultMeterObservationHandler(meterRegistry))\n // we set up a first matching handler that creates spans - it comes from\n // Micrometer\n // Tracing. We set up spans for sending and receiving data over the wire\n // and a default one\n .observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler(\n new PropagatingSenderTracingObservationHandler<>(tracer, propagator),\n new PropagatingReceiverTracingObservationHandler<>(tracer, propagator),\n new DefaultTracingObservationHandler(tracer)));\n\n// Creating and starting a new observation\n// via the `DefaultTracingObservationHandler` that will create a new Span and\n// start it\nObservation observation = Observation.start("my.operation", registry)\n .contextualName("This name is more readable - we can reuse it for e.g. spans")\n .lowCardinalityKeyValue("this.tag", "will end up as a meter tag and a span tag")\n .highCardinalityKeyValue("but.this.tag", "will end up as a span tag only");\n\n// Put the observation in scope\n// This will result in making the previously created Span, the current Span - it\'s\n// in ThreadLocal\ntry (Observation.Scope scope = observation.openScope()) {\n // Run your code that you want to measure - still the attached Span is the\n // current one\n // This means that e.g. logging frameworks could inject to e.g. MDC tracing\n // information\n yourCodeToMeasure();\n}\nfinally {\n // The corresponding Span will no longer be in ThreadLocal due to\n // try-with-resources block (Observation.Scope is an AutoCloseable)\n // Stop the Observation\n // The corresponding Span will be stopped and reported to an external system\n observation.stop();\n}\n-----\n\nYou can also use a shorter version to perform measurements via the `observe` method.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\n\nObservation.createNotStarted("my.operation", registry)\n .contextualName("This name is more readable - we can reuse it for e.g. spans")\n .lowCardinalityKeyValue("this.tag", "will end up as a meter tag and a span tag")\n .highCardinalityKeyValue("but.this.tag", "will end up as a span tag only")\n .observe(this::yourCodeToMeasure);\n-----\n\nThis will result in the following Micrometer Metrics:\n\n```\nGathered the following metrics\n Meter with name and type has the following measurements\n <[\n Measurement{statistic=\'COUNT\', value=1.0},\n Measurement{statistic=\'TOTAL_TIME\', value=1.011949454},\n Measurement{statistic=\'MAX\', value=1.011949454}\n ]>\n and has the following tags <[tag(this.tag=will end up as a meter tag and a span tag)]>\n```\n\nAnd the following trace view in e.g. Zipkin\n\nimage::'+n(85)+'[Trace Info propagation]\n\n=== Ordered Handler Configuration\n\nMicrometer Tracing comes with multiple `ObservationHandler` implementations.\nTo introduce ordering, you can use the `ObservationHandler.AllMatchingCompositeObservationHandler` to run logic for all ``ObservationHandler``s that are matching the given predicate and `ObservationHandler.FirstMatchingCompositeObservationHandler` to run logic only for the first `ObservationHandler` that matches the predicate.\nThe former can group handlers and the latter can be chosen to e.g. run only one matching `TracingObservationHandler`.\n\n== Context Propagation with Micrometer Tracing\n\nIn order to make https://micrometer.io/docs/contextPropagation[Context Propagation] work with Micrometer Tracing you need to manually register the proper `ThreadLocalAccessor` as presented below.\n\n[source,java,subs=+attributes]\n-----\nContextRegistry.getInstance().registerThreadLocalAccessor(new ObservationAwareSpanThreadLocalAccessor(tracer));\n-----\n\n== Exemplars\n\nTo add support for https://grafana.com/docs/grafana/latest/fundamentals/exemplars/[exemplars] instead of using the `DefaultMeterObservationHandler` you should use the `TracingAwareMeterObservationHandler` like presented below.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nregistry.observationConfig()\n // Don\'t register the DefaultMeterObservationHandler...\n // .observationHandler(new DefaultMeterObservationHandler(meterRegistry))\n // ...instead register the tracing aware version\n .observationHandler(new TracingAwareMeterObservationHandler<>(\n new DefaultMeterObservationHandler(meterRegistry), tracer));\n-----\n\n:leveloffset!:\n\n== Testing\n\n:leveloffset: +1\n\nMicrometer Tracing comes with `micrometer-tracing-test` and `micrometer-tracing-integration-test` modules.\n\nFor unit tests it provides a `SimpleTracer` that is a test implementation of a `Tracer`.\n\nFor the integration tests it provides a `SampleTestRunner` mechanism that you can hook into your samples.\nIt will\n\n* Configure an OpenZipkin Brave Tracer\n** Set it up with Tanzu Observability by Wavefront Reporter\n** Set it up with OpenZipkin Zipkin Reporter\n* Configure an OpenTelemetry Tracer\n** Set it up with Tanzu Observability by Wavefront Exporter\n** Set it up with OpenZipkin Zipkin Exporter\n* Run all the combinations above against the user code and running infrastructure\n\n== Installing\n\nThe following example shows the required dependency in Gradle (assuming that Micrometer Tracing BOM has been added):\n\n[source,groovy,subs=+attributes]\n-----\ntestImplementation \'io.micrometer:micrometer-tracing-test\' // for unit tests\ntestImplementation \'io.micrometer:micrometer-tracing-integration-test\' // for integration tests\n-----\n\nThe following example shows the required dependency in Maven (assuming that Micrometer Tracing BOM has been added):\n\n[source,xml,subs=+attributes]\n-----\n\n io.micrometer\n micrometer-tracing-test \x3c!-- For unit tests --\x3e\n test\n\n\n io.micrometer\n micrometer-tracing-integration-test \x3c!-- For integration tests --\x3e\n test\n\n-----\n\n== Running Tracing Unit Tests\n\nTo run unit tests of your custom handler you may want to use the `SimpleTracer` test `Tracer` implementation. Let\'s assume the following custom `TracingObservationHandler`.\n\n[source,java,subs=+attributes]\n-----\nstatic class MyTracingObservationHandler implements TracingObservationHandler {\n\n private final Tracer tracer;\n\n MyTracingObservationHandler(Tracer tracer) {\n this.tracer = tracer;\n }\n\n @Override\n public void onStart(CustomContext context) {\n String databaseName = context.getDatabaseName();\n Span.Builder builder = this.tracer.spanBuilder().kind(Span.Kind.CLIENT).remoteServiceName(databaseName);\n getTracingContext(context).setSpan(builder.start());\n }\n\n @Override\n public void onError(CustomContext context) {\n getTracingContext(context).getSpan().error(context.getError());\n }\n\n @Override\n public void onStop(CustomContext context) {\n Span span = getRequiredSpan(context);\n span.name(context.getContextualName() != null ? context.getContextualName() : context.getName());\n tagSpan(context, span);\n span.end();\n }\n\n @Override\n public boolean supportsContext(Observation.Context context) {\n return context instanceof CustomContext;\n }\n\n @Override\n public Tracer getTracer() {\n return this.tracer;\n }\n\n}\n-----\n\nTo verify whether the spans got properly created we can use the `SimpleTracer` as follows:\n\n[source,java,subs=+attributes]\n-----\nclass SomeComponentThatIsUsingMyTracingObservationHandlerTests {\n\n ObservationRegistry registry = ObservationRegistry.create();\n\n SomeComponent someComponent = new SomeComponent(registry);\n\n SimpleTracer simpleTracer = new SimpleTracer();\n\n MyTracingObservationHandler handler = new MyTracingObservationHandler(simpleTracer);\n\n @BeforeEach\n void setup() {\n registry.observationConfig().observationHandler(handler);\n }\n\n @Test\n void should_store_a_span() {\n // this code will call actual Observation API\n someComponent.doSthThatShouldCreateSpans();\n\n TracerAssert.assertThat(simpleTracer)\n .onlySpan()\n .hasNameEqualTo("insert user")\n .hasKindEqualTo(Span.Kind.CLIENT)\n .hasRemoteServiceNameEqualTo("mongodb-database")\n .hasTag("mongodb.command", "insert")\n .hasTag("mongodb.collection", "user")\n .hasTagWithKey("mongodb.cluster_id")\n .assertThatThrowable()\n .isInstanceOf(IllegalStateException.class)\n .backToSpan()\n .hasIpThatIsBlank()\n .hasPortThatIsNotSet();\n }\n\n}\n-----\n\n== Running integration tests\n\nThe following example shows how you can run your code to test your integrations\n\n* by asserting spans that were stored without emitting them to a reporting system\n* against running Tanzu Observability by Wavefront instance (this option turns on when you have passed the Wavefront related configuration in the constructor - otherwise the test will be disabled)\n* against running Zipkin instance (this option turns on when Zipkin is running - otherwise the test will be disabled)\n\n[source,java,subs=+attributes]\n-----\nclass ObservabilitySmokeTest extends SampleTestRunner {\n\n ObservabilitySmokeTest() {\n super(SampleRunnerConfig.builder().wavefrontApplicationName("my-app").wavefrontServiceName("my-service")\n .wavefrontToken("...")\n .wavefrontUrl("...")\n .zipkinUrl("...") // defaults to localhost:9411\n .build());\n }\n\n @Override\n public BiConsumer>> customizeObservationHandlers() {\n return (bb, handlers) -> {\n ObservationHandler defaultHandler = handlers.removeLast();\n handlers.addLast(new MyTracingObservationHandler(bb.getTracer()));\n handlers.addLast(defaultHandler);\n };\n }\n\n @Override\n public SampleTestRunnerConsumer yourCode() {\n return (bb, meterRegistry) -> {\n // here you would be running your code\n yourCode();\n\n SpansAssert.assertThat(bb.getFinishedSpans())\n .haveSameTraceId()\n .hasNumberOfSpansEqualTo(8)\n .hasNumberOfSpansWithNameEqualTo("handle", 4)\n .forAllSpansWithNameEqualTo("handle", span -> span.hasTagWithKey("rsocket.request-type"))\n .hasASpanWithNameIgnoreCase("request_stream")\n .thenASpanWithNameEqualToIgnoreCase("request_stream")\n .hasTag("rsocket.request-type", "REQUEST_STREAM")\n .backToSpans()\n .hasASpanWithNameIgnoreCase("request_channel")\n .thenASpanWithNameEqualToIgnoreCase("request_channel")\n .hasTag("rsocket.request-type", "REQUEST_CHANNEL")\n .backToSpans()\n .hasASpanWithNameIgnoreCase("request_fnf")\n .thenASpanWithNameEqualToIgnoreCase("request_fnf")\n .hasTag("rsocket.request-type", "REQUEST_FNF")\n .backToSpans()\n .hasASpanWithNameIgnoreCase("request_response")\n .thenASpanWithNameEqualToIgnoreCase("request_response")\n .hasTag("rsocket.request-type", "REQUEST_RESPONSE");\n\n MeterRegistryAssert.assertThat(meterRegistry)\n .hasTimerWithNameAndTags("rsocket.response", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_RESPONSE")))\n .hasTimerWithNameAndTags("rsocket.fnf", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_FNF")))\n .hasTimerWithNameAndTags("rsocket.request", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_RESPONSE")))\n .hasTimerWithNameAndTags("rsocket.channel", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_CHANNEL")))\n .hasTimerWithNameAndTags("rsocket.stream", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_STREAM")));\n };\n }\n\n}\n-----\n\n:leveloffset!:\n'},function(e,t,n){"use strict";n.r(t),t.default=n.p+"7043f6f1d0731de0cc0943e896c32fca.jpg"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"e4aa3da5f34dc789ae92d1f759a97b85.jpg"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"b8a4008ab6f119930854d422d6bd39b9.jpg"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Context Propagation support\n:toc:\n:sectnums:\n:dimensional: true\n\n== Purpose\n\nhttps://github.com/micrometer-metrics/context-propagation[A library] that assists with context propagation across different types of context\nmechanisms such as `ThreadLocal`, Reactor https://projectreactor.io/docs/core/release/reference/#context[Context]\nand others.\n\nAbstractions:\n\n* `ThreadLocalAccessor` - contract to assist with access to a `ThreadLocal` value.\n* `ContextAccessor` - contract to assist with access to a `Map`-like context.\n* `ContextRegistry` - registry for instances of `ThreadLocalAccessor` and `ContextAccessor`.\n* `ContextSnapshot` - holder of contextual values, that provides methods to capture and to propagate.\n\nExample Scenarios:\n\n* In imperative code, e.g. Spring MVC controller, capture `ThreadLocal` values into a\n`ContextSnapshot`. After that use the snapshot to populate a Reactor `Context` with the\ncaptured values, or to wrap a task (e.g. `Runnable`, `Callable`, etc) or an `Executor`\nwith a decorator that restores `ThreadLocal` values when the task executes.\n* In reactive code, e.g. Spring WebFlux controller, create a `ContextSnapshot` from\nReactor `Context` values. After that use the snapshot to restore `ThreadLocal` values\nwithin a specific stage (operator) of the reactive chain.\n\nContext values can originate from any context mechanism and propagate to any other, any\nnumber of times. For example, a value in a `Reactor` context may originate as a\n`ThreadLocal`, and may yet become a `ThreadLocal` again, and so on.\n\nGenerally, imperative code should interact with `ThreadLocal` values as usual, and\nlikewise Reactor code should interact with the Reactor `Context` as usual. The Context\nPropagation library is not intended to replace those, but to assist with propagation when\ncrossing from one type of context to another, e.g. when imperative code invokes a Reactor\nchain, or when a Reactor chain invokes an imperative component that expects\n`ThreadLocal` values.\n\nThe library is not limited to context propagation from imperative to reactive. It can\nassist in asynchronous scenarios to propagate `ThreadLocal` values from one thread to\nanother. It can also propagate to any other type of context for which there is a\nregistered `ContextAccesor` instance.\n\n== Installing\n\nSnapshots are published to https://repo.spring.io/snapshot for every successful build on the `main` branch and maintenance branches.\n\nMilestone releases are published to https://repo.spring.io/milestone. Include that as a Maven repository in your build\nconfiguration to use milestone releases. Note that milestone releases are for testing purposes and are not intended for\nproduction use.\n\nThe following example shows the required dependency in Gradle:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:context-propagation:latest.integration\'\n----\n\nThe following example shows the required dependency in Maven:\n\n[source,xml,subs=+attributes]\n----\n\n \n io.micrometer\n context-propagation\n ${micrometer-context-propagation.version}\n \n\n----\n\n== Usage Examples\n\n:leveloffset: +1\n\n== `ThreadLocal` Population\n\nBelow you can find a holder for `ThreadLocal` values.\n\n.ObservationThreadLocalHolder\n[source,java,subs=+attributes]\n-----\n/**\n * Example of a wrapper around ThreadLocal values.\n */\npublic class ObservationThreadLocalHolder {\n\n private static final ThreadLocal holder = new ThreadLocal<>();\n\n public static void setValue(String value) {\n holder.set(value);\n }\n\n public static String getValue() {\n return holder.get();\n }\n\n public static void reset() {\n holder.remove();\n }\n\n}\n-----\n\nBelow you can find a `ThreadLocalAccessor` that interacts with the holder.\n\n.ObservationThreadLocalAccessor\n[source,java,subs=+attributes]\n-----\n/**\n * Example {@link ThreadLocalAccessor} implementation.\n */\npublic class ObservationThreadLocalAccessor implements ThreadLocalAccessor {\n\n public static final String KEY = "micrometer.observation";\n\n @Override\n public Object key() {\n return KEY;\n }\n\n @Override\n public String getValue() {\n return ObservationThreadLocalHolder.getValue();\n }\n\n @Override\n public void setValue(String value) {\n ObservationThreadLocalHolder.setValue(value);\n }\n\n @Override\n public void setValue() {\n ObservationThreadLocalHolder.reset();\n }\n\n}\n-----\n\nBelow you can find an example of how to store and restore thread local values via `ThreadLocalAccessor`, `ContextSnapshot` and `ContextRegistry`.\n\n[source,java,subs=+attributes]\n-----\n// Create a new Context Registry (you can use a global too)\nContextRegistry registry = new ContextRegistry();\n// Register thread local accessors (you can use SPI too)\nregistry.registerThreadLocalAccessor(new ObservationThreadLocalAccessor());\n\n// When you set a thread local value...\nObservationThreadLocalHolder.setValue("hello");\n// ... we can capture it using ContextSnapshot\nContextSnapshot snapshot = ContextSnapshotFactory.builder().contextRegistry(registry).build().captureAll();\n\n// After capturing if you change the thread local value again ContextSnapshot will\n// not see it\nObservationThreadLocalHolder.setValue("hola");\ntry {\n // We\'re populating the thread local values with what we had in\n // ContextSnapshot\n try (Scope scope = snapshot.setThreadLocals()) {\n // Within this scope you will see the stored thread local values\n then(ObservationThreadLocalHolder.getValue()).isEqualTo("hello");\n }\n // After the scope is closed we will come back to the previously present\n // values in thread local\n then(ObservationThreadLocalHolder.getValue()).isEqualTo("hola");\n}\nfinally {\n // We\'re clearing the thread local values so that we don\'t pollute the thread\n ObservationThreadLocalHolder.reset();\n}\n-----\n\n:leveloffset!:\n'},function(e,t,n){var r={"./appOptics.adoc":88,"./atlas.adoc":91,"./azure-monitor.adoc":95,"./cloudwatch.adoc":96,"./datadog.adoc":97,"./dynatrace.adoc":98,"./elastic.adoc":99,"./ganglia.adoc":100,"./graphite.adoc":102,"./hierarchical-name-mapping.adoc":104,"./humio.adoc":105,"./influx.adoc":107,"./install.adoc":108,"./instana.adoc":109,"./jmx.adoc":110,"./kairos.adoc":112,"./new-relic.adoc":113,"./otlp.adoc":118,"./prometheus.adoc":119,"./signalFx.adoc":125,"./stackdriver.adoc":131,"./statsD.adoc":132,"./wavefront.adoc":133};function a(e){var t=i(e);return n(t)}function i(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=i,e.exports=a,a.id=87},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer AppOptics\n:toc:\n:sectnums:\n:system: appoptics\n\nAppOptics is a dimensional time-series SaaS with built-in dashboarding.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures an AppOptics instance:\n\n[source,java]\n----\nAppOpticsConfig appopticsConfig = new AppOpticsConfig() {\n @Override\n public String apiToken() {\n return MY_TOKEN;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null;\n }\n};\nMeterRegistry registry = new AppOpticsMeterRegistry(appopticsConfig, Clock.SYSTEM);\n----\n\n`AppOpticsConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.appoptics` directly to the `AppOpticsConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.appoptics:\n api-token: YOURKEY\n\n # You will probably want disable AppOptics publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to AppOptics. The default is 1 minute.\n step: 1m\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in AppOptics for metrics that originate in Micrometer.\n\n=== Timers\n\nThe AppOptics implementation of `Timer` produces three fields in AppOptics:\n\n* `sum`: Rate of calls per second.\n* `count`: Rate of total time per second.\n* `max`: A sliding window maximum amount recorded.\n\n.Dimensionally aggregable average in AppOptics.\nimage::"+n(89)+"[AppOptics timer average]\n\nAppOptics performs the `sum/count` division dimensionally to generate aggregable averages on your behalf.\n\n.Timer over a simulated service.\nimage::"+n(90)+"[AppOptics-rendered timer]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"0592f647d85fc092e402fdfb77234e24.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"6bf5cf6071710d129ffaadb50d2b0874.png"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Atlas\n:toc:\n:sectnums:\n:system: atlas\n\nAtlas is an in-memory dimensional time series database with built-in graphing, a custom stack-based query language, and advanced math operations. Atlas originated at Netflix, where it remains the operational metrics solution.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\n[source,java]\n----\nAtlasConfig atlasConfig = new AtlasConfig() {\n @Override\n public Duration step() {\n return Duration.ofSeconds(10);\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM);\n----\n\nMicrometer uses Netflix's https://github.com/netflix/spectator[Spectator] as the underlying instrumentation library when recording metrics destined for Atlas. `AtlasConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties prefixed with `management.metrics.export.atlas` directly to the `AtlasConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.atlas:\n # The location of your Atlas server\n uri: http://localhost:7101/api/v1/publish\n\n # You will probably want to conditionally disable Atlas publishing in local development.\n enabled: true\n\n # The interval at which metrics are sent to Atlas. The default is 1 minute.\n step: 1m\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Atlas for metrics originating in Micrometer. See the https://github.com/netflix/atlas/wiki[Atlas wiki] for a far more complete reference of what is possible in Atlas.\n\n=== Counters\n\nAtlas serves up graphs in the form of PNG images (and other https://github.com/Netflix/atlas/wiki/Output-Formats[output formats] as well).\n\nWe use the following query to visualize the counter from Atlas. Note that the value is rate-normalized over the step interval rather than monotonically increasing. Atlas always expects link:/docs/concepts#_client_side[rate-aggregated] data for counters from Micrometer.\n\n.Counter over a positive-biased random walk.\nimage::"+n(92)+'[Atlas-rendered counter]\n\n[source,http]\n----\nGET /api/v1/graph?\n q=\n name,counter,:eq,\n 2,:lw\n &tz=US/Central\n &s=e-15m <1>\n &w=400 <2>\n &l=0 <3>\nHost: localhost:7101\n----\n<1> The range of time we want to visualize along the x-axis. `e` represents the end time or "`now`". This graph\'s axis is from 15 minutes ago until now. Atlas automatically chooses the finest grained step interval available from the data that would render at least 1px wide on the resultant image.\n<2> The overall width of the PNG image returned should be 400px.\n<3> Set the y-axis lower limit to 0 so that random perturbation in the walk does not look so dramatic.\n\n=== Timers\n\nWhile reading directly from a `Timer` returns a `double`, the underlying value is\nstored in https://github.com/netflix/spectator[Spectator] as a nanosecond-precise `long`. What precision is lost by\nconverting to a `double` in the `Timer` interface does not affect a system like\nAtlas, because it has been configured to read measurements from the underlying\nSpectator `Timer` that Micrometer is hiding from you.\n\nThe Spectator Atlas `Timer` produces four time series, each with a different `statistic` tag:\n\n* `count`: Rate of calls per second.\n* `totalTime`: Rate of total time per second.\n* `totalOfSquares`: Rate of total time squared per second (useful for standard deviation).\n* `max`: The maximum amount recorded.\n\nTherefore, you can achieve a throughput (requests/second) line with the following query:\n\n```http\nname,timer,:eq,statistic,count,:eq,:and\n```\n\nNotice that `statistic` is just a dimension that can be drilled down and selected like any other.\n\nFurthermore, `totalTime/count` represents average latency and can be selected with a short-hand `:dist-avg` query, which selects the `totalTime` and `count` time series and performs the division for us:\n\n```http\nname,timer,:eq,:dist-avg\n```\n\nIn the preceding example, you can see these two lines plotted on a single dual-axis graph.\n\n.Timer over a simulated service.\nimage::'+n(93)+"[Atlas-rendered timer]\n\n\n=== Long task timers\n\nSuppose we had a task that took two minutes to complete when it was expected to complete in less than 70 seconds. A key benefit of long task timers is the ability to receive an alert at the first reporting interval after we have exceeded the threshold. With a regular timer, we would not receive an alert until the first reporting interval after the process completed. If we had a ten-second publishing interval, the regular timer alert would arrive almost a minute after the long task timer alert.\n\n.Simulated back-to-back long tasks.\nimage::"+n(94)+"[Atlas-rendered long task timer]\n\n[source, http]\n----\nGET /api/v1/graph?\n q=\n name,longTaskTimer,:eq,statistic,duration,:eq,:and, <1>\n :dup,\n 70,:gt,:vspan,f00,:color,40,:alpha,alerted,:legend, <2>\n 70,f00,:color,alert+threshold,:legend <3>\n &tz=US/Central\n &s=e-15m\n &w=400\n &l=0\n &title=Peaks+of+Long+Tasks\n &ylabel=time\nHost: localhost:7101\n----\n<1> A representation of long tasks that are happening back-to-back.\n<2> A vertical span that appears whenever the long task exceeds our threshold of 70 seconds. So that it does not overwhelm the graph, we also decrease the opacity of the vspan.\n<3> Plot the threshold of 70 seconds as a separate line.\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"b22351bea254b10bef1c26d3cc3ef397.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"4ce8699a76d05ed4311151acc1772b1f.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"1127e78d5d7524b0c0540de3d2a732b2.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Azure Monitor\n:toc:\n:sectnums:\n:system: azure-monitor\n\nAzure Monitor is a dimensional time-series SaaS with built-in dashboarding.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures a Micrometer Azure Monitor:\n\n[source,java]\n----\nAzureMonitorConfig azureMonitorConfig = new AzureMonitorConfig() {\n @Override\n public String instrumentationKey() {\n return MY_KEY;\n }\n\n @Override\n public String get(String key) {\n return null;\n }\n};\nMeterRegistry registry = new AzureMonitorMeterRegistry(azureMonitorConfig, Clock.SYSTEM);\n----\n\n`AzureMonitorConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.azure-monitor` directly to the `AzureMonitorConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.azure-monitor:\n instrumentation-key: YOURKEY\n\n # You will probably want disable Azure Monitor publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Azure Monitor. The default is 1 minute.\n step: 1m\n----\n"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer CloudWatch\nTommy Ludwig \n:toc:\n:sectnums:\n:system: cloudwatch2\n\nhttps://aws.amazon.com/cloudwatch/[Amazon CloudWatch] is a dimensional time-series SaaS on Amazon's cloud.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\nNOTE: The `micrometer-registry-cloudwatch2` module uses AWS SDK v2. `micrometer-registry-cloudwatch` is for AWS SDK v1.\n\n== Configuring\n\nThe following example configures Micrometer CloudWatch:\n\n[source,java]\n----\nCloudWatchConfig cloudWatchConfig = new CloudWatchConfig() {\n @Override\n public String get(String s) {\n return null;\n }\n\n @Override\n public String namespace() {\n return \"mynamespace\";\n }\n};\nMeterRegistry meterRegistry = new CloudWatchMeterRegistry(cloudWatchConfig, Clock.SYSTEM, CloudWatchAsyncClient.create());\n----\n\nYou can provide your own `CloudWatchAsyncClient` to the constructor of the registry.\n\n`CloudWatchConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, https://docs.awspring.io/spring-cloud-aws/docs/current/reference/html/index.html#cloudwatch-metrics[Micrometer support in Spring Cloud AWS] binds properties prefixed with `management.metrics.export.cloudwatch` directly to the `CloudWatchConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.cloudwatch:\n namespace: YOURNAMESPACE\n\n # You will probably want to disable publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to CloudWatch. The default is 1 minute.\n step: 1m\n----\n"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Datadog\n:toc:\n:sectnums:\n:system: datadog\n\nDatadog is a dimensional time-series SaaS with built-in dashboarding and alerting.\n\n== Installation and Configuration\n\nMicrometer supports shipping metrics to Datadog directly by using its HTTP API or by using DogStatsD through the link:/docs/registry/statsD[StatsD registry].\nThe API approach is far more efficient if you need to choose between the two.\n\n=== Direct to Datadog API Approach\n\nFor Gradle, add the following implementation:\n\n[source,groovy]\n----\nimplementation \'io.micrometer:micrometer-registry-datadog:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml]\n----\n\n io.micrometer\n micrometer-registry-datadog\n ${micrometer.version}\n\n----\n\nMetrics are rate-aggregated and pushed to `datadoghq` on a periodic interval. Rate aggregation performed by the registry yields datasets that are similar to those produced by `dogstatsd`.\n\n[source, java]\n----\nDatadogConfig config = new DatadogConfig() {\n @Override\n public Duration step() {\n return Duration.ofSeconds(10);\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new DatadogMeterRegistry(config, Clock.SYSTEM);\n----\n\n`DatadogConfig` is an interface with a set of default methods.\nIf, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration through properties.\nFor example, Spring Boot\'s Micrometer support binds properties directly to the `DatadogConfig`.\nSee the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.export.datadog[Datadog] section in the Spring Boot reference documentation.\n\n`DatadogConfig.hostTag()` specifies a tag key that is mapped to https://docs.datadoghq.com/api/v1/metrics/#submit-metrics[the `host` field] when shipping metrics to Datadog.\nFor example, if `DatadogConfig.hostTag()` returns `host`, the tag having `host` as its key is used.\nYou can set the tag by using common tags, as follows:\n\n[source,java]\n----\nregistry.config().commonTags("host", "my-host");\n----\n\n`uri` is an important property to configure.\nThe default value is `https://api.datadoghq.com`.\nDepending on the Datadog site (region), the api endpoint will be different.\nTo find your the correct `uri` for your account, do the following:\n\n1. Read about https://docs.datadoghq.com/getting_started/site/[Datadog site].\n2. Go to https://docs.datadoghq.com/api/latest/metrics/[Metrics API reference] and select your own option from the "DATADOG SITE" dropdown.\n3. Check any API request\'s endpoint.\n\nE.g. For `US5` site, the correct API endpoint is `https://api.us5.datadoghq.com` while for `US3` site, it is `https://api.us3.datadoghq.com/`.\n\n=== Through DogStatsD Approach\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-statsd:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-statsd\n ${micrometer.version}\n\n----\n\nMetrics are immediately shipped to DogStatsD using Datadog\'s flavor of the StatsD line protocol. `java-dogstatsd-client` is _not_ needed on the classpath for this to work, as Micrometer uses its own implementation.\n\n[source,java]\n----\nStatsdConfig config = new StatsdConfig() {\n @Override\n public String get(String k) {\n return null;\n }\n\n @Override\n public StatsdFlavor flavor() {\n return StatsdFlavor.DATADOG;\n }\n};\n\nMeterRegistry registry = new StatsdMeterRegistry(config, Clock.SYSTEM);\n----\n\nMicrometer supports DogStatsD\'s https://docs.datadoghq.com/developers/dogstatsd/?tab=kubernetes#origin-detection-over-udp[origin detection over UDP] feature on Kubernetes if the `DD_ENTITY_ID` environment variable is properly set.\n\nMicrometer, by default, publishes `Timer` meters to DogStatsD as the StatsD "timing" metric type `ms`,\nwhich are sent to Datadog as https://docs.datadoghq.com/metrics/types/?tab=histogram#metric-types[histogram] type metrics.\nMicrometer publishes `DistributionSummary` meters as histogram type metrics by default, also.\n\nWhen `percentileHistogram` is enabled for the meter, Micrometer sends `Timer` and `DistributionSummary` meters as Datadog https://docs.datadoghq.com/metrics/distributions[Distributions] to DogStatsD.\nYou can make a `DistributionSummary` with `percentileHistogram` enabled as follows:\n\n[source,java]\n----\nDistributionSummary responseSizeSummary = DistributionSummary.builder("http.server.response.size")\n .baseUnit("bytes")\n .publishPercentileHistogram()\n .register(registry);\n----\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Dynatrace\n:toc:\n:sectnums:\n:system: dynatrace\n\nhttps://www.dynatrace.com/[*Dynatrace*] is a Software Intelligence Platform featuring application performance monitoring (APM), artificial intelligence for operations (AIOps), IT infrastructure monitoring, digital experience management (DEM), and digital business analytics capabilities.\nIt can ingest multi-purpose dimensional time-series data and has built-in dashboarding.\nBoth SaaS and self-hosted (Managed) deployments are offered.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-{system}:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nFor setting up new integrations with Dynatrace, it is recommended to use the latest version of the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/[Dynatrace Metrics API] (v2).\nIf you are using Micrometer with Spring Boot, please also refer to the https://www.dynatrace.com/support/help/extend-dynatrace/extend-metrics/ingestion-methods/micrometer/[Dynatrace documentation] and/or the https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.metrics.export.dynatrace[Spring Boot documentation].\nDynatrace provides different ways of setting up integrations:\n\n=== Using Dynatrace auto-configuration (preferred) [[bookmark-auto-configuration]]\n\nDynatrace auto-configuration is available for hosts that are monitored by a OneAgent or by the Dynatrace Operator for Kubernetes.\n\nIf a Dynatrace OneAgent is installed on the host running Micrometer, metrics can be exported directly using the OneAgent without having to specify an endpoint URI or API token.\nIf running in Kubernetes with the Dynatrace operator installed, the registry will pick up your endpoint URI and API token from the operator instead.\nIn this case there is no need to configure anything, so you can use the following code in your project to export Micrometer metrics to Dynatrace:\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n @Nullable\n public String get(String k) {\n // This method of the interface is used by the other configuration methods and needs to be\n // implemented here. Returning null accepts the defaults for the other configuration items.\n return null;\n }\n};\nMeterRegistry registry = new DynatraceMeterRegistry(dynatraceConfig, Clock.SYSTEM);\n----\n\nIf you are using Micrometer 1.10.0 or above, you can also use the DEFAULT config to achieve the same with less code:\n\n[source,java]\n----\nMeterRegistry registry = new DynatraceMeterRegistry(DynatraceConfig.DEFAULT, Clock.SYSTEM);\n----\n\nIt is also possible to change other properties by creating an instance of the `DynatraceConfig` and overwriting the respective methods.\nFor example, you can specify the exporter version, which defaults to `v2` unless a deviceId is set:\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n public DynatraceApiVersion apiVersion() {\n return DynatraceApiVersion.V2;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new DynatraceMeterRegistry(dynatraceConfig, Clock.SYSTEM);\n----\n\n`DynatraceConfig` is an interface with a set of default methods.\nSpring Boot\'s Micrometer support binds properties prefixed with `management.dynatrace.metrics.export` directly to the `DynatraceConfig`.\n\nNOTE: Property names for binding attributes from Spring Boot have changed in Spring Boot version 3.0.0. If you use a Spring Boot version before 3.0.0, use `management.metrics.export.dynatrace` instead of `management.dynatrace.metrics.export`.\n\nUsing Spring Boot Micrometer support allows configuring the Dynatrace exporter by using <>.\nWhen using Micrometer with Spring Boot, you don\'t have to instantiate the `DynatraceMeterRegistry` manually as Spring Boot will do it automatically for you.\nAll configuration options that can be set by overwriting methods can also be set via Spring Boot properties, and adding a separate MeterRegistry can lead to metrics not being exported as auto-configuration might break.\n\nTo use the Dynatrace metrics exporter for Micrometer in your Spring Boot project, it is enough to include the `runtimeOnly \'io.micrometer:micrometer-registry-dynatrace\'` dependency.\nIn this default configuration, metrics will be exported to the local OneAgent or Kubernetes operator-provided endpoint.\n\n=== Using a custom endpoint\n\nIf auto-configuration is not available on the host, both the Dynatrace Metrics API v2 endpoint and an API token have to be specified.\nThe https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication/[Dynatrace API token documentation] contains more information on how to create an API token.\nThe \'Ingest metrics\' (`metrics.ingest`) permission is required on the token in order to ingest metrics.\nIt is recommended to limit scope to only this permission.\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n public DynatraceApiVersion apiVersion() {\n // not strictly required, but makes the code more clear/explicit\n return DynatraceApiVersion.V2;\n }\n\n @Override\n public String uri() {\n // The endpoint of the Dynatrace Metrics API v2 including path, e.g.:\n // "https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest"\n String endpoint = System.getenv("ENVVAR_METRICS_INGEST_URL");\n return endpoint != null ? endpoint : DynatraceConfig.super.uri();\n }\n\n @Override\n public String apiToken() {\n // should be read from a secure source\n String token = System.getenv("ENVVAR_METRICS_INGEST_TOKEN");\n return token != null ? token : "";\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new DynatraceMeterRegistry(dynatraceConfig, Clock.SYSTEM);\n----\n\nThese properties can also be set via Spring Boot, using property or yaml files.\nIt is also possible to reference environment variables using the Spring property placeholders (e.g.: `management.dynatrace.metrics.export.uri: ${DT_METRICS_INGEST_URL}`).\n\nNOTE: `v2` is used as the default API version unless a `deviceId` is set (<>).\n\n[source,yml]\n----\n# For Spring Boot 3.0.0 and above:\nmanagement.dynatrace.metrics.export:\n# For Spring Boot versions below 3.0.0, use the line below instead of the line above:\n# management.metrics.export.dynatrace:\n # for SaaS: https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest\n # for managed deployments: https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest\n uri: YOUR_METRICS_INGEST_URL\n\n # should be read from a secure source\n api-token: YOUR_METRICS_INGEST_TOKEN\n----\n\n=== Meter metadata\n\nStarting with Micrometer 1.12.0, the Dynatrace registry v2 exports meter metadata to Dynatrace.\nCurrently supported types of metadata are *unit* (called "base unit" in Micrometer) and *description*.\nNo changes are required to start exporting Metadata to Dynatrace - upgrading to version 1.12.0 or above is enough.\nFind more information about metrics metadata in the https://www.dynatrace.com/support/help/extend-dynatrace/extend-metrics/reference/metric-ingestion-protocol#metadata[Dynatrace documentation].\n\nThe export of metrics metadata can be disabled by setting the `exportMeterMetadata` property on the `DynatraceConfig` (see <> below) to `false`.\n\n== API Versions\n\n=== API v2 [[bookmark-apiv2]]\n\nWhen the API version is configured to `v2`, the registry will send data using the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/[Metrics API v2].\nIn order to maintain backwards compatibility, when a `deviceId` is set (which is required for `v1` and not used in `v2`), `v1` is used as the default.\nOtherwise, the version defaults to `v2`, and does not have to be set explicitly.\nWith no endpoint URI and token set, metrics will be exported to the local OneAgent endpoint or, if running in Kubernetes with the Dynatrace operator installed, to the endpoint provided by the operator.\nIf no auto-configuration is desired, it is possible to specify endpoint and token explicitly, in order to export metrics to that specific endpoint.\nExplicitly specifying these will overwrite auto-configuration.\n\n*Minimal configuration with Dynatrace auto-configuration*\n\nIn the minimal configuration <> (no URI or API token), the v2 registry will attempt to retrieve the endpoint provided by the Dynatrace Kubernetes operator.\nIf the operator is not set up or does not provide this information, the exporter will attempt to send metrics to the https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/local-api/[local OneAgent metrics ingest endpoint].\nNote that this only works if a OneAgent is running on the host and the https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/local-api/#enable-the-oneagent-metric-api[local OneAgent Metric API] is available.\nIf the ingestion port for the local OneAgent was changed to a custom one, the full endpoint URI has to be provided for the URI property (with API token left empty).\n\n*Configuration with URI and API token*\n\nIf no auto-configuration is available or the metrics should be sent to a different endpoint (e.g. a different tenant), the Dynatrace v2 exporter can be configured with an explicit endpoint URI and an https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication/[API token].\nThe https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication/[API token] must have the https://www.dynatrace.com/support/help/shortlink/api-authentication#token-permissions["Ingest metrics"] (`metrics.ingest`) permission set.\nIt is recommended to limit scope to only this permission.\n\nThe entire Metrics v2 API endpoint URI has to be specified including its path, i.e.: with the path `/api/v2/metrics/ingest` on SaaS and managed deployments, or `/metrics/ingest` for OneAgent endpoints as mentioned in the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/[documentation].\n\n*Properties available in the v2 exporter* [[bookmark-available-properties]]\n\nWhen using the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/[Dynatrace metrics API v2], the following properties can be set:\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n public DynatraceApiVersion apiVersion() {\n return DynatraceApiVersion.V2;\n }\n\n @Override\n public String uri() {\n // The endpoint of the Dynatrace Metrics API v2 including path, e.g.:\n // "https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest".\n String endpoint = System.getenv("ENVVAR_METRICS_INGEST_URL");\n return endpoint != null ? endpoint : DynatraceConfig.super.uri();\n }\n\n @Override\n public String apiToken() {\n // should be read from a secure source\n String token = System.getenv("ENVVAR_METRICS_INGEST_TOKEN");\n return token != null ? token : "";\n }\n\n @Override\n public String metricKeyPrefix() {\n // will be prepended to all metric keys\n return "your.desired.prefix";\n }\n\n @Override\n public boolean enrichWithDynatraceMetadata() {\n return true;\n }\n\n @Override\n public Map defaultDimensions() {\n // create and return a map containing the desired key-value pairs.\n Map dims = new HashMap<>();\n dims.put("dimensionKey", "dimensionValue");\n return dims;\n }\n\n // Only available in Micrometer 1.9.0 and above\n @Override\n public boolean useDynatraceSummaryInstruments() {\n return false;\n }\n\n // Only available in Micrometer 1.12.0 and above\n @Override\n public boolean exportMeterMetadata() {\n return true;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n----\n\nThese properties can also be set in Spring Boot configuration files:\n\n[source,yml]\n----\nmanagement.dynatrace.metrics.export:\n # Required only if not using the OneAgent endpoint\n # For SaaS: https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest\n # For managed deployments: https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest\n uri: YOUR_METRICS_INGEST_URL\n\n # should be read from a secure source\n api-token: YOUR_METRICS_INGEST_TOKEN\n\n # These properties can only be used with the v2 exporter.\n v2:\n # Sets a prefix that is prepended to each exported metric key.\n metric-key-prefix: my.metric.key.prefix\n\n # If set to true and a local OneAgent or operator is running, retrieves metadata\n # and adds it as additional dimensions to all data points (default: true)\n enrich-with-dynatrace-metadata: true\n\n # Sets an arbitrary number of key-value pairs as default dimensions.\n # Micrometer tags will overwrite these dimensions, if they have the same key.\n # Each exported metric will contain these dimensions.\n default-dimensions:\n key1: "value1"\n key2: "value2"\n\n # (since 1.9.0) Whether or not to use the Dynatrace-specific summary instruments. (default: true)\n # This should only be disabled if problems with existing instrumentation are discovered after upgrading to 1.9.0.\n # Set to false, this will restore the previous (1.8.x) behavior for Timers and DistributionSummaries.\n use-dynatrace-summary-instruments: true\n\n # (since 1.12.0) Determines whether meter metadata (unit, description) should be exported.\n export-meter-metadata: true\n\n # The export interval in which metrics are sent to Dynatrace (default: 60s).\n step: 60s\n----\n\nFor more information about the metadata picked up by the Dynatrace metadata enrichment feature, see https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/enrich-metrics/[the Dynatrace documentation].\n\nIn Micrometer 1.9.0, Dynatrace-specific summary instruments (`DynatraceTimer` and `DynatraceDistributionSummary`) were introduced.\nThese specialized instruments are tailored to the Dynatrace metrics ingest, and prevent the creation of invalid metrics.\nThey are available from version 1.9.0 and are used as a drop-in replacement by default.\nNo action is needed from users upgrading to 1.9.0. If there is a discrepancy in the observed metrics, it is possible to return to the previous behavior by setting the `useDynatraceSummaryInstruments` toggle to `false`.\n\n=== API v1 (Legacy) [[bookmark-apiv1]]\n\nWhen the apiVersion is configured to `v1`, the registry will send data using the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v1/custom-metrics/[Dynatrace Timeseries API v1 for custom metrics].\nIf a `deviceId` is specified, it will default to `v1` for backwards compatibility with earlier setups.\nThe `device-id` property is required for `v1` and not used in `v2`.\nExisting setups will continue to work when updating to newer versions of Micrometer.\nThe reported metrics will be assigned to https://www.dynatrace.com/support/help/dynatrace-api/environment-api/topology-and-smartscape/custom-device-api/report-custom-device-metric-via-rest-api/[custom devices] in Dynatrace.\n\nFor the v1 API, do not specify the ingest path, but only the base URL of your environment, e.g.: `uri: https://{your-environment-id}.live.dynatrace.com`\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n public String uri() {\n // The Dynatrace environment URI without any path, e.g.:\n // https://{your-environment-id}.live.dynatrace.com\n return MY_DYNATRACE_URI;\n }\n\n @Override\n public String apiToken() {\n // should be read from a secure source\n return MY_TOKEN;\n }\n\n @Override\n public String deviceId() {\n return MY_DEVICE_ID;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new DynatraceMeterRegistry(dynatraceConfig, Clock.SYSTEM);\n----\n\n[source,yml]\n----\nmanagement.dynatrace.metrics.export:\n # For v1 export, do not append a path to the endpoint URL, e.g.:\n # For SaaS: https://{your-environment-id}.live.dynatrace.com\n # For managed deployments: https://{your-domain}/e/{your-environment-id}\n uri: https://{your-environment-id}.live.dynatrace.com\n\n # should be read from a secure source\n api-token: MY_TOKEN\n\n # When setting the device id, metrics will be exported to the v1 timeseries endpoint\n # Using just device-id (without the v1 prefix) is deprecated, but will work to maintain backwards compatibility.\n v1:\n device-id: sample\n\n # To disable Dynatrace publishing, e.g. in a local development profile, use:\n # enabled: false\n\n # The interval at which metrics are sent to Dynatrace. The default is 1 minute.\n step: 1m\n----\n'},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Elastic\n:toc:\n:sectnums:\n:system: elastic\n\nElasticsearch is an open source search and analytics platform. Metrics stored in Elasticsearch can be visualized in Kibana.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures an ElasticSearch instance:\n\n[source,java]\n----\nElasticConfig elasticConfig = new ElasticConfig() {\n @Override\n @Nullable\n public String get(String k) {\n return null;\n }\n};\nMeterRegistry registry = new ElasticMeterRegistry(elasticConfig, Clock.SYSTEM);\n----\n\n`ElasticConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.elastic` directly to the `ElasticConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.elastic:\n # You will probably want disable Elastic publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Elastic. The default is 1 minute.\n step: 1m\n\n # The index to store metrics in, defaults to \"micrometer-metrics\"\n index: micrometer-metrics\n----\n\n== Elastic APM agent integration\n\nIf you are using the Elastic APM agent, it can collect metrics from Micrometer `MeterRegistry` instances automatically. You can use the `SimpleMeterRegistry` if you only want metrics collected by the Elastic APM agent and not shipped anywhere else. See the https://www.elastic.co/guide/en/apm/agent/java/current/metrics.html#metrics-micrometer[Elastic docs] for more details.\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Ganglia\n:toc:\n:sectnums:\n:system: ganglia\n\nGanglia is an aging hierarchical metrics system which enjoyed wide popularity in Linux system monitoring and is still in place in many organizations. It originated in the early 2000s at the University of California, Berkeley.\n\nNOTE: The `micrometer-registry-ganglia` module uses the https://github.com/ganglia/gmetric4j[gmetric4j] library, which contains classes generated by the LGPL licensed https://sourceforge.net/projects/remotetea/[remotetea project].\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures a Ganglia instance:\n\n[source,java]\n----\nGangliaConfig gangliaConfig = new GangliaConfig() {\n @Override\n public String host() {\n return \"mygraphitehost\";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n\nMeterRegistry registry = new GangliaMeterRegistry(gangliaConfig, Clock.SYSTEM);\n----\n\nMicrometer uses Dropwizard Metrics as the underlying instrumentation library when recording metrics destined for Ganglia. `GangliaConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.ganglia` directly to the `GangliaConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.ganglia:\n # The location of your Ganglia server\n host: mygraphitehost\n\n # You will probably want to conditionally disable Ganglia publishing in local development.\n enabled: true\n\n # The interval at which metrics are sent to Ganglia. The default is 1 minute.\n step: 1m\n----\n\n== Hierarchical name mapping\n\nMicrometer provides a `HierarchicalNameMapper` interface that governs how a dimensional meter ID is mapped to flat hierarchical names.\n\nThe default (`HierarchicalNameMapper.DEFAULT`) sorts tags alphabetically by key and appends tag key/value pairs to the base meter name with '.' -- for example, `http_server_requests.method.GET.response.200`. The name and tag keys have the registry's naming convention applied to them first.\n\nIf there is something special about your naming scheme that you need to honor, you can provide your own `HierarchicalNameMapper` implementation. The most common cause of a custom mapper comes from a need to prefix something to the front of every metric (generally something like `app..http_server_requests.method.GET.response.200`).\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Ganglia for metrics originating in Micrometer.\n\n=== Counters\n\nGanglia counters measure mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughputs.\n\n.A Ganglia rendered graph of the random walk counter.\nimage::"+n(101)+"[Ganglia-rendered counter]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"80865a7f65d47863b3b264a9ed09629a.png"},function(e,t,n){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Graphite\n:toc:\n:sectnums:\n:system: graphite\n\nGraphite is one of the most popular current hierarchical metrics systems backed by a fixed-size database, similar in design and purpose to RRDtool. It originated at Orbitz in 2006 and was open sourced in 2008.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-{system}:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures a Graphite instance:\n\n[source,java]\n----\nGraphiteConfig graphiteConfig = new GraphiteConfig() {\n @Override\n public String host() {\n return "mygraphitehost";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n\nMeterRegistry registry = new GraphiteMeterRegistry(graphiteConfig, Clock.SYSTEM, HierarchicalNameMapper.DEFAULT);\n----\n\nMicrometer uses Dropwizard Metrics as the underlying instrumentation library when recording metrics destined for Graphite. `GraphiteConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer\'s Spring Boot support binds properties that are prefixed with `management.metrics.export.graphite` directly to the `GraphiteConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.graphite:\n # The location of your Graphite server\n host: mygraphitehost\n\n # You will probably want to conditionally disable Graphite publishing in local development.\n enabled: true\n\n # The interval at which metrics are sent to Graphite. The default is 1 minute.\n step: 1m\n----\n\n== Graphite Tag Support\n\nAs of Micrometer version 1.4.0, Micrometer supports exporting Graphite metrics by using tags instead of the traditional hierarchical format. By default, metrics are exported by using the tag format, unless any `tagsAsPrefix` values are configured.\nhttps://graphite.readthedocs.io/en/latest/tags.html[Tag support] was added to Graphite in the 1.1.0 Graphite release.\nIf you wish to revert to the traditional hierarchical format, ensure that the `graphiteTagsEnabled` config value is set to `false`.\nThe following documentation sections on hierarchical name mapping and metrics prefixing are only applicable if tag support is disabled.\n\n== Hierarchical name mapping\n\nMicrometer provides a `HierarchicalNameMapper` interface that governs how a dimensional meter ID is mapped to flat hierarchical names.\n\nThe default (`HierarchicalNameMapper.DEFAULT`) sorts tags alphabetically by key and appends tag key/value pairs to the base meter name with \'.\' -- for example, `http_server_requests.method.GET.response.200`. The name and tag keys have the registry\'s naming convention applied to them first.\n\nIf there is something special about your naming scheme that you need to honor, you can provide your own `HierarchicalNameMapper` implementation. The most common cause of a custom mapper comes from a need to prefix something to the front of every metric (generally something like `app..http_server_requests.method.GET.response.200`).\n\n== Prefixing your metrics\n\nTo add a prefix to all metrics that go to graphite, use the `GraphiteConfig#tagsAsPrefix` configuration option. This option applies the tag value of a set of common tags as a prefix. For example, if `tagsAsPrefix` contains `application`, and a meter named `myTimer` is created with a tag of `application=APPNAME`, it appears in Graphite as `APPNAME.myTimer`.\n\nGenerally, when you use `tagsAsPrefix`, you should add common tags to the registry so that the tags are present on all meters that belong to that registry:\n\n[source,java]\n----\n@Bean\npublic MeterRegistryCustomizer commonTags() {\n return r -> r.config().commonTags("application", "APPNAME");\n}\n----\n\nWe do it this way because, generally, a tag prefix in Graphite is correlated to a common tag elsewhere. Prefixes tend to be something like app name or host. By applying those values as common tags, you make your metrics more portable (that is, if you ever switch to a dimensional monitoring system, you are set).\n\nYou can use this when the order of the prefix matters. Micrometer always sorts tags, but the order of tag keys in `tagsAsPrefix` is preserved, so adding `host` and `application` to `tagsAsPrefix` results in a prefixed metric, such as `HOST.APP.myCounter`.\n\nTo meet your specific naming needs, you can also provide a custom hierarchical name mapper when creating `GraphiteMeterRegistry`, as follows:\n\n[source,java]\n----\nGraphiteMeterRegistry r = new GraphiteMeterRegistry(\n GraphiteConfig.DEFAULT,\n Clock.SYSTEM,\n (id, convention) -> "prefix." + HierarchicalNameMapper.DEFAULT.toHierarchicalName(id, convention));\n----\n\nNOTE: If you use a custom `HierarchicalNameMapper`, `tagsAsPrefix` is ignored.\n\n== Further Customizing the `GraphiteReporter`\n\nWe give you the option to configure `GraphiteReporter` yourself if you need further customization. To do so, use this constructor and provide your own `GraphiteReporter`:\n\n[source,java]\n----\nGraphiteMeterRegistry(GraphiteConfig config, Clock clock, HierarchicalNameMapper nameMapper,\n MetricRegistry metricRegistry, GraphiteReporter reporter)\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Graphite for metrics originating in Micrometer.\n\n=== Counters\n\nGraphite counters measure mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughputs.\n\n.A Graphite rendered graph of the random walk counter.\nimage::'+n(103)+"[Graphite-rendered counter]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"cd6b0314737fa94a81e42689517fd7fb.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n== Hierarchical name mapping\n\nMicrometer provides a `HierarchicalNameMapper` interface that governs how a dimensional meter ID is mapped to flat hierarchical names.\n\nThe default (`HierarchicalNameMapper.DEFAULT`) sorts tags alphabetically by key and appends tag key/value pairs to the base meter name with '.' -- for example, `http_server_requests.method.GET.response.200`. The name and tag keys have the registry's naming convention applied to them first.\n\nIf there is something special about your naming scheme that you need to honor, you can provide your own `HierarchicalNameMapper` implementation. The most common cause of a custom mapper comes from a need to prefix something to the front of every metric (generally something like `app..http_server_requests.method.GET.response.200`).\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Humio\n:toc:\n:sectnums:\n:system: humio\n\nHumio is a dimensional time-series SaaS with built-in dashboarding.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures a Humio instance:\n\n[source,java]\n----\nHumioConfig humioConfig = new HumioConfig() {\n @Override\n public String apiToken() {\n return MY_TOKEN;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null;\n }\n};\nMeterRegistry registry = new HumioMeterRegistry(humioConfig, Clock.SYSTEM);\n----\n\n`HumioConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.humio` directly to the `HumioConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.humio:\n api-token: YOURKEY\n\n # You will probably want disable Humio publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Humio. The default is 1 minute.\n step: 1m\n\n # The cluster Micrometer will send metrics to. The default is \"https://cloud.humio.com\"\n uri: https://myhumiohost\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Humio for metrics originating in Micrometer.\n\n=== Timers\n\nThe Humio implementation of `Timer` produces four fields in Humio:\n\n* `sum`: Rate of calls per second.\n* `count`: Rate of total time per second.\n* `max`: A sliding window maximum amount recorded.\n* `avg`: A non-aggregable average for only this set of tag values.\n\nThe following query constructs a dimensionally aggregable average latency per URI:\n\n[source, text]\n----\nname = http_server_requests\n| timechart(uri, function=max(avg))\n----\n\n.Timer over a simulated service.\nimage::"+n(106)+"[Humio-rendered timer]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"6584fae5a94c66dd29f69056b031d7e3.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Influx\n:toc:\n:sectnums:\n:system: influx\n\nThe InfluxData suite of tools supports real-time stream processing and storage of time-series data. It supports downsampling, automatically expiring and deleting unwanted data, as well as backup and restore.\n\nThe InfluxMeterRegistry supports the 1.x InfluxDB API as well as the v2 API.\n\n== Configuring\n\nMicrometer supports shipping metrics to InfluxDB directly or through Telegraf through the StatsD registry.\n\n=== Direct to InfluxDB\n\nThe following example adds the required library in Gradle:\n\n[source,groovy]\n----\nimplementation 'io.micrometer:micrometer-registry-influx:latest.release'\n----\n\nThe following example adds the required library in Maven:\n\n[source,xml]\n----\n\n io.micrometer\n micrometer-registry-influx\n ${micrometer.version}\n\n----\n\nMetrics are rate-aggregated and pushed to InfluxDB on a periodic interval. Rate aggregation performed by the registry yields datasets that are quite similar to those produced by Telegraf. The following example configures a meter registry for InfluxDB:\n\n.InfluxDB 1.x configuration example\n[source, java]\n----\nInfluxConfig config = new InfluxConfig() {\n @Override\n public Duration step() {\n return Duration.ofSeconds(10);\n }\n\n @Override\n public String db() {\n return \"mydb\";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new InfluxMeterRegistry(config, Clock.SYSTEM);\n----\n\nTo ship metrics to InfluxDB 2.x, make sure to configure the `org` and `bucket` to which to write the metrics, as well as the authentication `token`.\n\n.InfluxDB v2 configuration example\n[source, java]\n----\nInfluxConfig config = new InfluxConfig() {\n\n @Override\n public String org() {\n return \"myorg\";\n }\n\n @Override\n public String bucket() {\n return \"app-metrics\";\n }\n\n @Override\n public String token() {\n return \"auth_token_here\"; // FIXME: This should be securely bound rather than hard-coded, of course.\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new InfluxMeterRegistry(config, Clock.SYSTEM);\n----\n\n`InfluxConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.influx` directly to the `InfluxConfig`:\n\n[source, yaml]\n----\nmanagement.metrics.export.influx:\n api-version: v2 # API version of InfluxDB to use. Defaults to 'v1' unless an org is configured. If an org is configured, defaults to 'v2'.\n auto-create-db: true # Whether to create the InfluxDB database if it does not exist before attempting to publish metrics to it. InfluxDB v1 only. (Default: true)\n batch-size: 10000 # Number of measurements per request to use for this backend. If more measurements are found, then multiple requests will be made. (Default: 10000)\n bucket: mybucket # Bucket for metrics. Use either the bucket name or ID. Defaults to the value of the db property if not set. InfluxDB v2 only.\n compressed: true # Whether to enable GZIP compression of metrics batches published to InfluxDB. (Default: true)\n connect-timeout: 1s # Connection timeout for requests to this backend. (Default: 1s)\n consistency: one # Write consistency for each point. (Default: one)\n db: mydb # Database to send metrics to. InfluxDB v1 only. (Default: mydb)\n enabled: true # Whether exporting of metrics to this backend is enabled. (Default: true)\n num-threads: 2 # Number of threads to use with the metrics publishing scheduler. (Default: 2)\n org: myorg # Org to write metrics to. InfluxDB v2 only.\n password: mysecret # Login password of the InfluxDB server. InfluxDB v1 only.\n read-timeout: 10s # Read timeout for requests to this backend. (Default: 10s)\n retention-policy: my_rp # Retention policy to use (InfluxDB writes to the DEFAULT retention policy if one is not specified). InfluxDB v1 only.\n step: 1m # Step size (i.e. reporting frequency) to use. (Default: 1m)\n token: AUTH_TOKEN_HERE # Authentication token to use with calls to the InfluxDB backend. For InfluxDB v1, the Bearer scheme is used. For v2, the Token scheme is used.\n uri: http://localhost:8086 # URI of the InfluxDB server. (Default: http://localhost:8086)\n user-name: myusername # Login user of the InfluxDB server. InfluxDB v1 only.\n----\n\n=== Through Telegraf\n\nTelegraf is a StatsD agent that expects a modified flavor of the StatsD line protocol.\n\nThe following listing adds the relevant library in Gradle:\n\n[source,groovy]\n----\nimplementation 'io.micrometer:micrometer-registry-statsd:latest.release'\n----\n\nThe following listing adds the relevant library in Maven:\n\n[source,xml]\n----\n\n io.micrometer\n micrometer-registry-statsd\n ${micrometer.version}\n\n----\n\nMetrics are shipped immediately over UDP to Telegraf by using Telegraf's flavor of the StatsD line protocol.\n\n[source,java]\n----\nStatsdConfig config = new StatsdConfig() {\n @Override\n public String get(String k) {\n return null;\n }\n\n @Override\n public StatsdFlavor flavor() {\n return StatsdFlavor.Telegraf;\n }\n};\n\nMeterRegistry registry = new StatsdMeterRegistry(config, Clock.SYSTEM);\n----\n"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Instana\nFabian Lange \n:toc:\n:sectnums:\n:system: instana\n\nInstana is an automatic application performance management and infrastructure monitoring system.\n\n== Installation and Configuration\n\nInstana automatically detects and reports all metrics without the need of any additional dependency or configuration.\nIt does so by detecting all instances of `io.micrometer.core.instrument.MeterRegistry` and collecting all registered `io.micrometer.core.instrument.Meter` instances from them.\n\nYou can run the Instana agent alongside your application by using Micrometer, and the Instana agent automatically monitors it.\n\n== Supported Metrics\n\n* **Timer**: The total time of recorded events, scaled to milliseconds.\n* **Counter**: The cumulative count since this counter was created.\n* **Gauge**: The current value.\n* **DistributionSummary**: The total number of all recorded events.\n* **LongTaskTimer**: The current number of tasks being executed.\n* **FunctionCounter**: The cumulative count since this counter was created.\n* **FunctionTimer**: The total time of all occurrences of the timed event.\n* **TimeGauge**: The current value, scaled to the appropriate base unit.\n\nThe metrics show up on the Java Virtual Machine dashboard in Instana. You can configure alerting based on these metrics.\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer JMX\n:toc:\n:sectnums:\n:system: jmx\n\nMicrometer provides a hierarchical mapping to JMX, primarily as a cheap and portable way to view metrics locally. Where JMX exporting is found in production, the same metrics are generally exported to another, more purpose-fit monitoring system.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\nMicrometer also sometimes scrapes data from JMX beans for use in reporting metrics. This registry implementation is not needed for these uses. Rather, this module is strictly used to _export_ data to JMX.\n\n== Hierarchical name mapping\n\nMicrometer provides a `HierarchicalNameMapper` interface that governs how a dimensional meter ID is mapped to flat hierarchical names.\n\nThe default (`HierarchicalNameMapper.DEFAULT`) sorts tags alphabetically by key and appends tag key/value pairs to the base meter name with '.' -- for example, `http_server_requests.method.GET.response.200`. The name and tag keys have the registry's naming convention applied to them first.\n\nIf there is something special about your naming scheme that you need to honor, you can provide your own `HierarchicalNameMapper` implementation. The most common cause of a custom mapper comes from a need to prefix something to the front of every metric (generally something like `app..http_server_requests.method.GET.response.200`).\n\n== Counters\n\nJMX counters measure mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughputs.\n\n.The JMX rendered values of the random walk counter.\nimage::"+n(111)+"[JMX-rendered counter]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"8077bd6e31e279e04a38e631b2d61a1e.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer KairosDB\n:toc:\n:sectnums:\n:system: kairos\n\nKairosDB is a dimensional time-series database built on top of Cassandra. Charting can be accomplished in Grafana by using a link:https://docs.grafana.org/v4.0/datasources/kairosdb/[Kairos datasource].\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures KairosDB:\n\n[source,java]\n----\nKairosConfig kairosConfig = new KairosConfig() {\n @Override\n @Nullable\n public String get(String k) {\n return null;\n }\n};\nMeterRegistry registry = new KairosMeterRegistry(kairosConfig, Clock.SYSTEM);\n----\n\n`KairosConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.kairos` directly to the `KairosConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.kairos:\n # You will probably want disable Kairos publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Kairos. The default is 1 minute.\n step: 1m\n\n # Authentication may be required, depending on how you have Kairos configured\n user-name: MYUSER\n password: MYPASSWORD\n----\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer New Relic\n:toc:\n:sectnums:\n:system: new-relic\n\nNew Relic offers a dimensional monitoring system product called Insights. It includes a full UI and a query language called NRQL. New Relic Insights operates on a push model. Some features of NRQL assume that Insights receives a distinct event payload for every timing, count, and so on. Micrometer instead ships aggregates at a prescribed interval, letting your app's throughput scale without concern for event propagation to Insights becoming a bottleneck.\n\nNOTE: New Relic provides its own Micrometer `MeterRegistry` implementation based on dimensional metrics.\nIt intends to supersede Micrometer's `NewRelicMeterRegistry` (which uses custom events in New Relic), because New Relic's dimensional metrics are a better fit for metrics than custom events.\nYou can find more details in https://github.com/newrelic/micrometer-registry-newrelic[its GitHub repository].\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures New Relic:\n\n[source,java]\n----\nNewRelicConfig newRelicConfig = new NewRelicConfig() {\n @Override\n public String accountId() {\n return \"MYACCOUNT\";\n }\n\n @Override\n public String apiKey() {\n return \"MY_INSIGHTS_API_KEY\";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n\nMeterRegistry registry = new NewRelicMeterRegistry(newRelicConfig, Clock.SYSTEM);\n----\n\nThere are two distinct sources of API keys in New Relic.\n\n`NewRelicConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.newrelic` directly to the `NewRelicConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.newrelic:\n account-id: MYACCOUNT\n api-key: MY_INSIGHTS_API_KEY\n\n # The interval at which metrics are sent to New Relic. See Duration.parse for the expected format.\n # The default is 1 minute.\n step: 1m\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in New Relic for metrics originating in Micrometer. See the https://docs.newrelic.com/docs/insights/nrql-new-relic-query-language/using-nrql/introduction-nrql[New Relic NRQL docs] for a far more complete reference of what is possible in New Relic.\n\n=== Timers\n\nAt each publishing interval, the New Relic `Timer` produces a single event with the timer's name and several attributes:\n\n* `avg`: A mean latency for the publishing interval.\n* `count`: Throughput per second over the publishing interval.\n* `totalTime`: Total time per second over the publishing interval (used with `count`) to create aggregable means.\n\nAdditionally, if any percentiles or SLO buckets are defined on the timer, additional events are produced:\n\n* `${name}.percentiles`: Micrometer calculated percentiles for the publishing interval. One event is produced for each percentile, with a tag of `phi` in the range of [0,1].\n* `${name}.histogram`: One event is produced for each SLO boundary with a tag of 'le', indicating that it represents a cumulative count of events less than or equal to SLO boundaries over the publishing interval.\n\nTo generate an aggregable view of latency in New Relic, divide `totalTime` by `count`:\n\n[source,sql]\n----\nSELECT sum(totalTime)/sum(count) as 'Average Latency', max(max) as 'Max' FROM timer since 30 minutes ago TIMESERIES auto\n----\n\n.Timer latency.\nimage::"+n(114)+"[New Relic-rendered timer]\n\nTo generate a throughput chart:\n\n[source,sql]\n----\nSELECT average(count) as 'Average Throughput' FROM timer since 30 minutes ago TIMESERIES auto\n----\n\n.Timer throughput.\nimage::"+n(115)+"[New Relic-rendered timer throughput]\n\nTo generate a plot of client-side percentiles:\n\n[source,sql]\n----\nSELECT latest(value) from timerPercentile FACET phi since 30 minutes ago TIMESERIES auto\n----\n\n.Timer Percentiles.\nimage::"+n(116)+"[New Relic-rendered percentiles]\n\nNote how these percentiles are _not aggregable_. We have selected the `latest(value)` function to display this chart (it isn't correct to `average(value)` on a percentile value). The more dimensions you add to a timer, the less useful these values become.\n\nFinally, if you define SLO boundaries with the fluent builder for `Timer`, you can view throughput below certain SLO boundaries. In this example, we set SLO boundaries at 275 (yellow), 300 (red), and 500 (blue) milliseconds for a simulated `Timer` that is recording samples normally distributed around 250 ms. These counts represent the rate/second of samples less than or equal to each SLO boundary.\n\n[source,sql]\n----\nSELECT sum(value) from timerHistogram FACET le since 30 minutes ago TIMESERIES auto\n----\n\n.Timer SLO boundaries.\nimage::"+n(117)+"[New Relic-rendered SLO boundaries]\n\nWhere the lines converge at various points, it is evident that no sample exceeded the 275 ms SLO boundary.\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"4d25b7833c033d6e2380ec4ecc988d66.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"02f1215f99df0a7a03ac1d5986ace874.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"03797aa9d08a216ed90efab2b59e0114.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"e1fdf4994baf8640b183e76694d53390.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer OTLP\n:toc:\n:sectnums:\n:system: otlp\n\nOpenTelemetry is a CNCF incubating project for providing standards for telemetry data. OpenTelemetry protocol (OTLP) is a vendor neutral protocol that can be used to send data to various backends which support it. You can read the corresponding docs on how the metrics are ingested and can be visualized in the respective vendor docs.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\nThe following example configures an OTLP registry:\n\n[source,java]\n----\nOtlpConfig otlpConfig = new OtlpConfig() {\n @Override\n public String get(final String key) {\n return null;\n }\n};\n\nMeterRegistry registry = new OtlpMeterRegistry(otlpConfig, Clock.SYSTEM);\n----\n\n`OtlpConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source (e.g.: a simple `Map` can work), you can override the default configuration through properties. For example, Micrometer's Spring Boot support binds properties prefixed with `management.otlp.metrics.export` directly to the `OtlpConfig`:\n\n[source, yaml]\n----\nmanagement:\n otlp:\n metrics:\n export:\n # Supported configs\n url: \"https://otlp.example.com:4318/v1/metrics\"\n aggregationTemporality: \"cumulative\"\n headers:\n header1: value1\n resourceAttributes:\n key1: value1\n----\n\n1. `url` - The URL to which data will be reported. Defaults to `http://localhost:4318/v1/metrics`\n2. `aggregationTemporality` - https://opentelemetry.io/docs/specs/otel/metrics/data-model/#temporality[Aggregation temporality, window=_blank] determines how the additive quantities are expressed, in relation to time. The supported values are `cumulative` or `delta`. Defaults to `cumulative`. +\n*Note*: This config was introduced in version 1.11.0.\n3. `headers` - Additional headers to send with exported metrics. This can be used for authorization headers. By default, headers will be loaded from the config. If that is not set, they can be taken from the environment variables `OTEL_EXPORTER_OTLP_HEADERS` and `OTEL_EXPORTER_OTLP_METRICS_HEADERS`. If a header is set in both the environmental variables, the header in the latter will override the former.\n4. `resourceAttributes` - https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service[Resource attributes, window=_blank] that will be used for all metrics published. By default, Micrometer adds the following resource attributes:\n\n[%autowidth]\n|===\n|Key | Default value\n\n|telemetry.sdk.name\n|io.micrometer\n\n|telemetry.sdk.language\n|java\n\n|telemetry.sdk.version\n| (e.g.: 1.11.0)\n\n|service.name\n|unknown_service\n|===\n\nIf this config is empty, then resource attributes will be loaded from the environmental variable `OTEL_RESOURCE_ATTRIBUTES`. `service.name` can be overridden by the environmental variable `OTEL_SERVICE_NAME` and this takes precedence over other configs.\n\n== Supported metrics\nhttps://opentelemetry.io/docs/specs/otel/metrics/data-model/#metric-points[Metric points, window=_blank] define the different data points that are supported in OTLP. Micrometer supports exporting the below data points in OTLP format,\n\n1. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#sums[Sums, window=_blank]\n2. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#gauge[Gauge, window=_blank]\n3. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#histogram[Histogram, window=_blank]\n4. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#summary-legacy[Summary, window=_blank]\n\nThe below table maps OTLP data points and the Micrometer meters:\n\n[%autowidth]\n|===\n|OTLP data point | Micrometer meter type\n\n|Sums\n|Counter, FunctionCounter\n\n|Gauge\n|Gauge, TimeGauge, MultiGauge\n\n|Histogram\n|Timer, DistributionSummary, LongTaskTimer, FunctionTimer (only sum and count are set)\n\n|Summary\n|Timer, DistributionSummary, LongTaskTimer\n|===\n\n*Note*:\n\n1. `max` on Histogram data point is only supported in delta aggregation temporality. This is because the values represented by cumulative min and max will stabilize as more events are recorded and are less useful when recorded over application's lifecycle.\n2. Currently, Micrometer only exports metadata for type `Meter` to OTLP.\n\n== Histograms and Percentiles\nMicrometer `Timer` and `DistributionSummary` support configuring link:/docs/concepts#_histograms_and_percentiles[client-side percentiles and percentile histograms]. OTLP specification terms Summary data point (client-side percentiles) as legacy and not recommended for new applications. Summary data point also cannot have min/max associated with it. Due to these reasons Micrometer prefers exporting Timers and DistributionSummary as Histogram data point. By default, a Timer/DistributionSummary without any additional percentile/histogram config will be exported as Histogram data point. However, by configuring the timer to generate only client-side percentiles using `publishPercentiles` this can be changed to a Summary data point exporting pre-calculated percentiles. When both `publishPercentiles` and (`publishPercentileHistogram` or `serviceLevelObjectives`) are configured, Histogram data point is preferred and pre-calculated percentiles *will not* be generated. See the below table on which data point will be used with different configurations:\n\n[%autowidth]\n|===\n|Configuration | OTLP data point\n\n| publishPercentiles\n| Summary\n\n| publishPercentileHistogram\n| Histogram\n\n| serviceLevelObjectives\n| Histogram\n\n| publishPercentiles and (publishPercentileHistogram/serviceLevelObjectives)\n| Histogram\n|===\n\nAlternatively, if you are using Spring Boot, you can use the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.customizing.per-meter-properties[per-meter properties, window=_blank] to configure this behaviour.\n\nIf you want to generate Histogram data point for a Timer with name `test.timer` and default buckets generated by Micrometer, use:\n\n[source,properties]\n----\nmanagement.metrics.distribution.percentiles-histogram.test.timer=true\n----\n\nand for buckets with customized SLO, use:\n\n[source,properties]\n----\nmanagement.metrics.distribution.slo.test.timer=10.0,100.0,500.0,1000.0\n----\n\nAlternatively, if you want to generate Summary data point for a timer with name `test.timer` and 90th and 99th percentiles, you can use the below config:\n\n[source,properties]\n----\nmanagement.metrics.distribution.percentiles.test.timer=0.9,0.99\n----\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Prometheus\n:toc:\n:sectnums:\n:system: prometheus\n\nPrometheus is a dimensional time series database with a built-in UI, a custom query language, and math operations.\nPrometheus is designed to operate on a pull model, periodically scraping metrics from application instances, based on service discovery.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nPrometheus expects to scrape or poll individual application instances for metrics.\nIn addition to creating a Prometheus registry, you also need to expose an HTTP endpoint to Prometheus' scraper.\nIn a Spring Boot application, a https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#prometheus[Prometheus actuator endpoint] is auto-configured in the presence of Spring Boot Actuator.\nOtherwise, you can use any JVM-based HTTP server implementation to expose scrape data to Prometheus.\n\nThe following example uses the JDK's `com.sun.net.httpserver.HttpServer` to expose a scrape endpoint:\n\n[source,java]\n----\nPrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);\n\ntry {\n HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);\n server.createContext(\"/prometheus\", httpExchange -> {\n String response = prometheusRegistry.scrape(); <1>\n httpExchange.sendResponseHeaders(200, response.getBytes().length);\n try (OutputStream os = httpExchange.getResponseBody()) {\n os.write(response.getBytes());\n }\n });\n\n new Thread(server::start).start();\n} catch (IOException e) {\n throw new RuntimeException(e);\n}\n----\n<1> The `PrometheusMeterRegistry` has a `scrape()` function that knows how to supply the String data necessary for the scrape. All you have to do is wire it to an endpoint.\n\nYou can alternatively use `io.prometheus.client.exporter.HTTPServer` that you can find in `io.prometheus:simpleclient_httpserver`:\n[source,java]\n----\nPrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);\n// you can set the daemon flag to false if you want the server to block\nnew HTTPServer(new InetSocketAddress(8080), prometheusRegistry.getPrometheusRegistry(), true);\n----\n\nAnother alternative can be `io.prometheus.client.exporter.MetricsServlet` that you can find in `io.prometheus:simpleclient_servlet` in case your app is running in a servlet container (e.g.: Tomcat):\n[source,java]\n----\nPrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);\nHttpServlet metricsServlet = new MetricsServlet(prometheusRegistry.getPrometheusRegistry());\n----\n\n=== Scrape format\n\nBy default, the https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format[Prometheus text format] is returned from the `PrometheusMeterRegistry` `scrape()` method.\n\nThe https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md[OpenMetrics] format can also be produced.\nTo specify the format to be returned, you can pass a content type to the `scrape` method.\nFor example, to get the OpenMetrics 1.0.0 format scrape, you could use the Prometheus Java client constant for it, as follows:\n\n[source,java]\n----\nString openMetricsScrape = registry.scrape(TextFormat.CONTENT_TYPE_OPENMETRICS_100);\n----\n\nIn Spring Boot applications, the https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#prometheus[Prometheus Actuator endpoint] supports scraping in either format, defaulting to the Prometheus text format in absence of a specific `Accept` header.\n\n=== The Prometheus Rename Filter\n\nIn some cases, Micrometer provides instrumentation that overlaps with commonly used Prometheus simple client modules but has chosen a different naming scheme for consistency and portability.\nIf you wish to use the Prometheus \"standard\" names, add the following filter:\n\n[source,java]\n----\nprometheusRegistry.config().meterFilter(new PrometheusRenameFilter());\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Prometheus for metrics originating in Micrometer.\nSee the https://prometheus.io/docs/querying/basics[Prometheus docs] for a far more complete reference of what is possible in Prometheus.\n\n=== Grafana Dashboard\n\nA publicly available Grafana dashboard for Micrometer-sourced JVM and Tomcat metrics is available https://grafana.com/grafana/dashboards/4701-jvm-micrometer/[here].\n\nimage::"+n(120)+'[Grafana dashboard for JVM and Tomcat binders]\n\nThe dashboard features:\n\n* JVM memory\n* Process memory (provided by https://github.com/mweirauch/micrometer-jvm-extras[micrometer-jvm-extras])\n* CPU-Usage, Load, Threads, File Descriptors, and Log Events\n* JVM Memory Pools (Heap, Non-Heap)\n* Garbage Collection\n* Classloading\n* Direct/Mapped buffer sizes\n\nInstead of using the `job` tag to distinguish different applications, this dashboard makes use of a common tag called `application`, which is applied to every metric.\nYou can apply the common tag as follows:\n\n[source,java]\n----\nregistry.config().commonTags("application", "MYAPPNAME");\n----\n\nIn Spring Boot applications, you can use the https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.metrics.customizing.common-tags[property support for common tags]:\n\n[source,properties]\n----\nmanagement.metrics.tags.application=MYAPPNAME\n----\n\n=== Counters\n\nThe query that generates a graph for the random-walk counter is\n`rate(counter[10s])`.\n\n.A Grafana rendered graph of the random walk counter.\nimage::'+n(121)+"[Grafana-rendered Prometheus counter]\n\nRepresenting a counter without rate normalization over some time window is rarely useful, as the representation is a function of both the rapidity with which the counter is incremented and the longevity of the service. It is generally most useful to rate-normalize these time series to reason about them. Since Prometheus keeps track of discrete events across all time, it has the advantage of allowing for the selection of an arbitrary time window across which to normalize at query time (for example, `rate(counter[10s])` provides a notion of requests per second over 10 second windows). The rate-normalized graph in the preceding image would return back to a value around 55 as soon as the new instance (say on a production deployment) was in service.\n\n.Counter over the same random walk, no rate normalization.\nimage::"+n(122)+"[Grafana-rendered Prometheus counter (no rate)]\n\nIn contrast, without rate normalization, the counter drops back to zero on service restart, and the count increases without bound for the duration of the service's uptime.\n\n=== Timers\n\nThe Prometheus `Timer` produces two counter time series with different names:\n\n* `${name}_count`: Total number of all calls.\n* `${name}_sum`: Total time of all calls.\n\nRepresenting a counter without rate normalization over some time window is rarely useful, as the representation is a function of both the rapidity with which the counter is incremented and the longevity of the service.\n\nUsing the following Prometheus queries, we can graph the most commonly used statistics about timers:\n\n* Average latency: `rate(timer_sum[10s])/rate(timer_count[10s])`\n* Throughput (requests per second): `rate(timer_count[10s])`\n\n.Timer over a simulated service.\nimage::"+n(123)+"[Grafana-rendered Prometheus timer]\n\n=== Long task timers\n\nThe Prometheus query to plot the duration of a long task timer for a serial task is `long_task_timer_sum`. In Grafana, we can set an alert threshold at some fixed point.\n\n.Simulated back-to-back long tasks with a fixed alert threshold.\nimage::"+n(124)+"[Grafana-rendered Prometheus long task timer]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"39fa40459993be394ab40488bf8c6a8f.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"594dce115710525d7771ebe3ab82db19.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"439c0eea1918ef143002cf62a9393e2a.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"fc8d290e3f5356fad7d93f76c6abe188.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"fb6c59cc88b85301c5bbb890afc71593.png"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer SignalFx\n:toc:\n:sectnums:\n:system: signalfx\n\nSignalFx is a dimensional monitoring system SaaS with a full UI that operates on a push model. It has a rich set of alert \"`detectors`\".\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures SignalFx:\n\n[source,java]\n----\nSignalFxConfig signalFxConfig = new SignalFxConfig() {\n @Override\n public String accessToken() {\n return \"MYTOKEN\";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n\nMeterRegistry registry = new SignalFxMeterRegistry(signalFxConfig, Clock.SYSTEM);\n----\n\nThere are two distinct sources of API keys in SignalFx.\n\n`SignalFxConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.signalfx` directly to the `SignalFxConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.signalfx:\n access-token: MYTOKEN\n\n # The interval at which metrics are sent to Ganglia. See Duration.parse for the expected format.\n # The default is 1 minute.\n step: 1m\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in SignalFx for metrics originating in Micrometer. See the https://docs.signalfx.com/en/latest/charts/index.html[SignalFx docs] for a far more complete reference of what is possible in SignalFx.\n\n=== Timers\n\nAt each publishing interval, the SignalFx `Timer` produces several time series in SignalFx:\n\n* `${name}.avg`: A mean latency for the publishing interval.\n* `${name}.count`: Throughput per second over the publishing interval.\n* `${name}.totalTime`: Total time per second over the publishing interval (used with `count`) to create aggregable means.\n* `${name}.percentiles`: Micrometer calculated percentiles for the publishing interval. One time series is produced for each percentile, with a tag of `phi` in the range of [0,1].\n* `${name}.histogram`: One event is produced for each SLO boundary with a tag of 'le', indicating that it represents a cumulative count of events less than or equal to SLO boundaries over the publishing interval.\n\nTo generate an aggregable view of latency in SignalFx, divide `totalTime` by `count`:\n\nimage::"+n(126)+"[SignalFx-aggregable latency query]\n\nThis is accomplished by adding signals for `${name}.totalTime` and `${name}.count`, adding a formula that divides them, and hiding the inputs to the formula.\n\n.Timer latency.\nimage::"+n(127)+"[SignalFx-rendered timer]\n\nTo generate a throughput chart, use the `${name}.count` signal:\n\n.Timer throughput.\nimage::"+n(128)+"[SignalFx-rendered timer throughput]\n\nTo generate a plot of client-side percentiles, use the `${name}.percentiles` signal:\n\n.Timer Percentiles.\nimage::"+n(129)+"[SignalFx-rendered percentiles]\n\nNote that these percentiles are _not aggregable_. The more dimensions you add to a timer, the less useful these values become.\n\nFinally, if you define SLO boundaries with the fluent builder for `Timer`, you can view throughput below certain SLO boundaries by using the `${name}.histogram` signal. In this example, we set SLO boundaries at 275 (green), 300 (blue), and 500 (purple) milliseconds for a simulated `Timer` that is recording samples normally distributed around 250 ms. These counts represent the rate/second of samples less than or equal to each SLO boundary.\n\n.Timer SLO boundaries.\nimage::"+n(130)+"[SignalFx-rendered SLO boundaries]\n\nWhere the lines converge at various points it is evident that no sample exceeded the 275 ms SLO boundary.\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"d77cd7a4fea8c37d7dc37e7bbe069b5e.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"beec842accba1a667e0e288745191070.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"02141ef2139e0de5d180b5b76dff1982.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"a165f31f4506fa9b07f0f2319dd58aa0.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"b19ccbd241a3bfd9377d4066961b1620.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Stackdriver Monitoring\nRay Tsang \n:toc:\n:sectnums:\n:system: stackdriver\n\nStackdriver Monitoring is a dimensional time-series SaaS with built-in dashboarding.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures Stackdriver:\n\n[source,java]\n----\nStackdriverConfig stackdriverConfig = new StackdriverConfig() {\n @Override\n public String projectId() {\n return MY_PROJECT_ID;\n }\n\n @Override\n public String get(String key) {\n return null;\n }\n}\n\nMeterRegistry registry = StackdriverMeterRegistry.builder(stackdriverConfig).build();\n----\n\n`StackdriverConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.stackdriver` directly to the `StackdriverConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.stackdriver:\n project-id: MY_PROJECT_ID\n resource-type: global\n\n # You will probably want to disable Stackdriver Monitoring publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Stackdriver Monitoring. The default is 1 minute.\n step: 1m\n----\n\nFor most environments, you need to create and configure credentials to push metrics to Stackdriver Monitoring.\nIn most cases, you should create a service account with Stackdriver Monitoring permissions and configure a\n`GOOGLE_APPLICATION_CREDENTIALS` environmental variable to the path of the service account key file.\nThe following example shows how to do so:\n\n[source]\n----\nexport PROJECT_ID=MY_PROJECT_ID\nexport APP_NAME=MY_APP\n\n# Create a service account\ngcloud iam service-accounts create $APP_NAME\n\n# Grant the service account Stackdriver Monitoring writer permission\ngcloud projects add-iam-policy-binding $PROJECT_ID \\\n --member serviceAccount:$APP_NAME@$PROJECT_ID.iam.gserviceaccount.com \\\n --role roles/monitoring.metricWriter\n\n# Create a key JSON file\ngcloud iam service-accounts keys create $HOME/$APP_NAME-key.json \\\n --iam-account $APP_NAME@$PROJECT_ID.iam.gserviceaccount.com\n\n# Configure GOOGLE_APPLICATION_CREDENTIALS env var\nexport GOOGLE_APPLICATION_CREDENTIALS=$HOME/$APP_NAME-key.json\n----\n\nWhen running in managed environments (such as Google App Engine, Google Cloud Run, Google Cloud Function)\nyou need not configure this environmental variable. In those environments, a service account is\nautomatically associated with the application instance. The underlying Stackdriver Monitoring client\nlibrary can automatically detect and use those credentials.\n\n== Stackdriver Labels\n\nMicrometer metrics tags are mapped to https://cloud.google.com/monitoring/api/v3/metrics-details#intro-time-series[Stackdriver metrics labels]. With tags and labels, you can further filter or group\nby the tag or label. See link:/docs/concepts#_tag_naming[Micrometer Concepts] for more information on tags.\nThe following example filters by tags:\n\n[source,java]\n----\nMeterRegistry registry = StackdriverMeterRegistry.builder(stackdriverConfig).build();\nregistry.config().commonTags(\"application\", \"my-application\");\n----\n\nYou can also configure resource labels with the `StackdriverConfig` method `resourceLabels`. Depending on the configured `resourceType`, there will be required resource labels. See the documentation on https://cloud.google.com/monitoring/custom-metrics/creating-metrics#which-resource[choosing a monitored resource type].\n\nIMPORTANT: When using Micrometer across multiple applications/instances, it is necessary that Stackdriver labels are unique per application/instance. Otherwise, you will see errors like `One or more TimeSeries could not be written: One or more points were written more frequently than the maximum sampling period configured`. If using a resource type other than `global`, the resource labels may already make metrics unique per application instance. If not, a common tag with the hostname or platform-provided instance ID may be a good candidate for achieving this.\n\n== Spring Boot\n\nSpring Boot provides auto-configuration for Micrometer's StackdriverMeterRegistry. For more information, see the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-metrics-export-stackdriver[Spring Boot documentation].\n\nYou can manually configure or register the `StackdriverMeterRegistry`.\nIn addition to using Spring Boot Actuator, make sure you create the `StackdriverMeterRegistry` bean:\n\n[source,java]\n----\n@Bean\nStackdriverConfig stackdriverConfig() {\n return new StackdriverConfig() {\n @Override\n public String projectId() {\n return MY_PROJECT_ID;\n }\n\n @Override\n public String get(String key) {\n return null;\n }\n }\n}\n\n@Bean\nStackdriverMeterRegistry meterRegistry(StackdriverConfig stackdriverConfig) {\n return StackdriverMeterRegistry.builder(stackdriverConfig).build();\n}\n----\n\nYou can also use https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics-common-tags[Spring Boot Actuator Common Tags configuration] to configure common tags:\n\n[source]\n----\nspring.application.name=my-application\nmanagement.metrics.tags.application=${spring.application.name}\n----\n\n== GraalVM native image compilation\n\nTo compile an application using `micrometer-registry-stackdriver` to a https://www.graalvm.org/reference-manual/native-image/[native image using GraalVM], add the https://github.com/GoogleCloudPlatform/native-image-support-java[native-image-support-java] library as a dependency. This will ensure the correct native image configuration is available and avoid errors like `Classes that should be initialized at run time got initialized during image building`.\n"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer StatsD\n:toc:\n:sectnums:\n:system: statsd\n\nStatsD is a UDP-based sidecar-driven metrics collection system. The maintainer of the original StatsD line protocol specification is Etsy. Datadog\'s DogStatsD and Influx\'s Telegraf each accept a modified version of the line protocol, having each enriched the original specification with dimensionality in different ways.\n\nIf you intend to use the Datadog or Telegraf flavors, see the documentation for Micrometer\'s link:/docs/registry/datadog[Datadog] or link:/docs/registry/influx[Influx] support.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-{system}:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThis configuration is used to ship metrics to a StatsD agent that is compatible with the original Etsy protocol. Metrics are shipped immediately over UDP to the agent.\n\n[source,java]\n----\nStatsdConfig config = new StatsdConfig() {\n @Override\n public String get(String k) {\n return null;\n }\n\n\n @Override\n public StatsdFlavor flavor() {\n return StatsdFlavor.Etsy;\n }\n};\n\nMeterRegistry registry = new StatsdMeterRegistry(config, Clock.SYSTEM);\n----\n\nNOTE: You can also configure Telegraf to accept the dogstatsd format. If you use Telegraf, configuring Micrometer to ship Telegraf-formatted StatsD lines eases the requirements of your Telegraf configuration.\n\n`StatsdConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer\'s Spring Boot support binds properties that are prefixed with `management.metrics.export.statsd` directly to the `StatsdConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.statsd:\n flavor: etsy\n\n # You will probably want to conditionally disable StatsD publishing in local development.\n enabled: true\n\n # The interval at which metrics are sent to StatsD. The default is 1 minute.\n step: 1m\n----\n\n== Customizing the Metrics Sink\n\nBy default, Micrometer publishes StatsD line protocol over UDP, as the vast majority of existing StatsD agents are UDP servers. It is possible to fully customize how the line protocol is shipped by modifying the builder for `StatsdMeterRegistry`:\n\n[source,java]\n----\nConsumer lineLogger = line -> logger.info(line); <1>\n\nMeterRegistry registry = StatsdMeterRegistry.builder(StatsdConfig.DEFAULT) <2>\n .clock(clock)\n .lineSink(lineLogger)\n .build();\n----\n<1> Define what to do with lines.\n<2> The flavor configuration option determines the structure of the line for the default line builder. It has no effect if you override the line builder with a customization.\n\n=== Using Apache Kafka for Line Sink\n\nYou can also use Apache Kafka for line sink, as follows:\n\n[source,java]\n----\nProperties properties = new Properties();\nproperties.setProperty(BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");\nproperties.setProperty(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());\nproperties.setProperty(VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());\n\nProducer producer = new KafkaProducer<>(properties);\n\nStatsdMeterRegistry.builder(statsdConfig)\n .lineSink((line) -> producer.send(new ProducerRecord<>("my-metrics", line)))\n .build();\n----\n\nNow Micrometer produces lines for metrics to the `my-metrics` topic and you can consume the lines on the topic.\n\n== Customizing the Line Format\n\nThe built-in Etsy, dogstatsd, and Telegraf flavors cover most known public StatsD agents, but you can completely customize the line format to satisfy closed, proprietary agents. Again, we use the `StatsdMeterRegistry` builder to establish a line builder for each ID. Providing an instance of the builder _per ID_ offers you the opportunity to eagerly cache the serialization of the ID\'s name and tags to optimize the serialization of a StatsD line based on that ID as samples are recorded. The following listing defines a fictional format:\n\n[source,java]\n----\nFunction nameAndUnits = id -> new StatsdLineBuilder() {\n String name = id.getName() + "/" + (id.getBaseUnit() == null ? "unknown" : id.getBaseUnit());\n\n @Override\n public String count(long amount, Statistic stat) {\n return name + ":" + amount + "|c";\n }\n\n ... // implement gauge, histogram, and timing similarly\n}\n\nMeterRegistry registry = StatsdMeterRegistry.builder(StatsdConfig.DEFAULT) <1>\n .clock(clock)\n .lineBuilder(nameAndUnits)\n .build();\n----\n<1> Because you have taken control of line building, the flavor is ignored.\n'},function(e,t,n){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Wavefront\n:toc:\n:sectnums:\n:system: wavefront\n\nWavefront is a dimensional monitoring system offered as a SaaS with a full UI, custom query language, and advanced math operations. Wavefront operates on a push model. Metrics may either be pushed through a sidecar process running on the same host (called the Wavefront proxy) or directly to the Wavefront API.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-{system}:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThis section describes how to configure Wavefront when you send data:\n\n* <>\n* <>\n\n[[configuring-directly-to-wavefront]]\n=== Directly to Wavefront\n\nThe following example configures sending directly to Wavefront:\n\n[source,java]\n----\nWavefrontConfig config = new WavefrontConfig() {\n @Override\n public String uri() {\n return "https://longboard.wavefront.com"; <1>\n }\n\n @Override\n public String apiToken() {\n return "MYAPIKEY"; <2>\n }\n\n @Override\n public String get(String key) {\n return null; <3>\n }\n};\nMeterRegistry registry = new WavefrontMeterRegistry(config, Clock.SYSTEM);\n----\n<1> `longboard` is the name of the co-tenant instance on which most organizations start. Once you reach a sufficient scale, Wavefront may move you\nto a dedicated host.\n<2> This is required when pushing directly to Wavefront\'s API.\n<3> Accept the rest of the defaults.\n\n[[configuring-through-a-wavefront-proxy-sidecar]]\n=== Through a Wavefront Proxy Sidecar\n\nThe following example configures sending through a Wavefront proxy sidecar:\n\n[source,java]\n----\nMeterRegistry registry = new WavefrontMeterRegistry(WavefrontConfig.DEFAULT_PROXY, Clock.SYSTEM);\n----\n\nThe default proxy configuration pushs metrics and histogram distributions to a Wavefront proxy sitting on `localhost:2878`.\n\nNOTE: If publishing metrics to a Wavefront proxy, the URI must be expressed in the form of `proxy://HOST:PORT`.\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Wavefront for metrics originating in Micrometer. See the https://docs.wavefront.com/query_language_getting_started.html[Wavefront docs] for a far more complete reference of what is possible in Wavefront.\n\n=== Counters\n\nThe query that generates a graph for a random-walk counter is `rate(ts(counter))`.\n\n.A Wavefront rendered graph of the random walk counter.\nimage::'+n(134)+"[Wavefront-rendered counter]\n\nRepresenting a counter without rate normalization over some time window is rarely useful, as the\nrepresentation is a function of both the rapidity with which the counter is incremented and the\nlongevity of the service. It is generally most useful to rate-normalize these time series to\nreason about them.\n\nBecause Wavefront keeps track of cumulative counts across all time, it has the\nadvantage of allowing for the selection of a particular time function at query time (for example,\n`rate(ts(counter))` to compute the per-second rate of change).\n\n=== Timers\n\nThe Wavefront `Timer` produces different time series depending on whether or not\n`publishPercentileHistogram` is enabled.\n\nIf `publishPercentileHistogram` is enabled, the Wavefront `Timer` produces histogram distributions\nthat let you query for the latency at any percentile using `hs()` queries. For example, you can\nvisualize latency at the 95th percentile (`percentile(95, hs(timer.m))`) or the 99.9th percentile\n(`percentile(99.9, hs(timer.m))`). For more information on histogram distributions, see\n<>, later in this section.\n\nIf `publishPercentileHistogram` is disabled, the Wavefront `Timer` produces several\ntime series:\n\n* `${name}.avg`: Mean latency across all calls.\n* `${name}.count`: Total number of all calls.\n* `${name}.sum`: Total time of all calls.\n* `${name}.max`: Max latency over the publishing interval.\n* `${name}.percentiles`: Micrometer-calculated percentiles for the publishing interval. These\ncannot be aggregated across dimensions.\n\nYou can use these time series to generate a quick view of latency in Wavefront:\n\n.Timer latency.\nimage::"+n(135)+'[Wavefront-rendered timer]\n\nThe preceding chart shows the average latency (`rate(ts(timer.sum))/rate(ts(timer.count))` in\ngreen), 95th percentile (`ts(timer.percentile, phi="0.95")` in orange), and max (`ts(timer.max)`\nin blue).\n\nAdditionally, `rate(ts(timer.count))` represents a rate/second throughput of events being timed:\n\n.Timer throughput.\nimage::'+n(136)+"[Wavefront-rendered timer throughput]\n\n[[wavefront-histograms]]\n=== Wavefront Histograms\n\nWavefront's histogram implementation stores an actual distribution of metrics, as opposed to single metrics. This lets you apply any percentile and aggregation function on the distribution at query time without having to specify specific percentiles and metrics to keep during metric collection.\n\nWavefront histogram distributions are collected and reported for any `Timer` or `DistributionSummary` that has `publishPercentileHistogram` enabled.\n\nBy default, distributions that are reported to Wavefront get aggregated by the minute, providing you with a histogram distribution for each minute. You also have the option of aggregating by hour or day. You can customize this with the following configuration options:\n\n* `reportMinuteDistribution`: Boolean specifying whether to aggregate by minute. Enabled by default. Metric name in Wavefront has `.m` suffix.\n* `reportHourDistribution`: Boolean specifying whether to aggregate by hour. Disabled by default. Metric name in Wavefront has `.h` suffix.\n* `reportDayDistribution`: Boolean specifying whether to aggregate by day. Disabled by default. Metric name in Wavefront has `.d` suffix.\n\nIf you are sending to a Wavefront proxy, by default, both metrics and histogram distributions are published to the same port: 2878 in the default proxy configuration. If your proxy is configured to listen for histogram distributions on a different port, you can specify the port to which to publish by using the `distributionPort` configuration option.\n\nYou can query histogram distributions in Wavefront by using `hs()` queries. For example, `percentile(98, hs(${name}.m))` returns the 98th percentile for a particular histogram aggregated over each minute. Each histogram metric name has a suffix (`.m`, `.h`, or `.d`), depending on the histogram's aggregation interval.\n\nSee the https://docs.wavefront.com/proxies_histograms.html[Wavefront Histograms documentation] for more information.\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"105c9d351e5cabcc4504a9b863032ca3.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"10ee7658d93746e240978c7ca9b3467b.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"9cc263d6b012a89bad0374b418540a31.png"},,function(e,t,n){},function(e,t,n){"use strict";n.r(t);var r=n(1),a=n(13),i=n.n(a),o=n(148),s=n(147),c=n(30),l=n(146),u=n(22),d=n.p+"static/media/logo.e2f91fdb.svg",m=(n(36),n(37),n(144)),g=n(145),p=n(0);function h(){return Object(p.jsxs)(m.a,{collapseOnSelect:!0,expand:"lg",bg:"dark",variant:"dark",style:{borderTop:"8px solid #1ba89c"},children:[Object(p.jsx)(m.a.Brand,{as:u.a,to:"/",children:Object(p.jsx)("img",{src:d,className:"img-fluid",style:{maxHeight:80},alt:"Micrometer"})}),Object(p.jsx)(m.a.Toggle,{"aria-controls":"responsive-navbar-nav"}),Object(p.jsx)(m.a.Collapse,{id:"responsive-navbar-nav",children:Object(p.jsxs)(g.a,{className:"ml-auto mt-2 mt-lg-0",children:[Object(p.jsx)(g.a.Item,{href:"/docs",children:Object(p.jsxs)(g.a.Link,{as:u.a,to:"/docs",children:[Object(p.jsx)("i",{className:"fa fa-lg fa-book"})," Documentation"]})}),Object(p.jsx)(g.a.Item,{children:Object(p.jsxs)(g.a.Link,{href:"https://github.com/micrometer-metrics/micrometer",children:[Object(p.jsx)("i",{className:"fa fa-lg fa-github-alt"})," GitHub"]})}),Object(p.jsx)(g.a.Item,{children:Object(p.jsxs)(g.a.Link,{href:"https://twitter.com/micrometerio",children:[Object(p.jsx)("i",{className:"fa fa-lg fa-twitter"})," Twitter"]})}),Object(p.jsx)(g.a.Item,{children:Object(p.jsxs)(g.a.Link,{href:"https://slack.micrometer.io",children:[Object(p.jsx)("i",{className:"fa fa-lg fa-slack"})," Slack"]})})]})})]})}function f(){return Object(p.jsx)("div",{className:"bg-dark text-center",children:Object(p.jsx)("div",{className:"col-12",style:{padding:10,color:"white"},children:Object(p.jsxs)("p",{children:['Copyright \xa9 2005 - 2023 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. See ',Object(p.jsx)("a",{href:"https://www.vmware.com/help/legal.html",children:"Terms of Use"})," and ",Object(p.jsx)("a",{href:"https://www.vmware.com/help/privacy.html",children:"Privacy Policy"}),"."]})})})}var b=n.p+"static/media/logo-no-title.c52ef844.svg",y=n.p+"static/media/playback-latency.b7cc768a.png";function v(){return Object(p.jsxs)("div",{children:[Object(p.jsx)("div",{className:"jumbotron text-center",style:{background:"url(".concat(y,") no-repeat center center")},children:Object(p.jsxs)("div",{className:"container",children:[Object(p.jsx)("img",{src:b,className:"img-fluid",alt:""}),Object(p.jsx)("h1",{className:"jumbotron-heading mt-3",style:{color:"white",background:"rgba(52, 48, 45, 0.8)"},children:"Vendor-neutral application observability facade"}),Object(p.jsx)("p",{className:"lead",style:{padding:8,color:"white",background:"rgba(52, 48, 45, 0.8)"},children:"Micrometer provides a simple facade over the instrumentation clients for the most popular observability systems, allowing you to instrument your JVM-based application code without vendor lock-in. Think SLF4J, but for observability."})]})}),Object(p.jsxs)("div",{className:"container-fluid",children:[Object(p.jsxs)("div",{className:"row",style:{background:"rgba(17, 122, 113, 0.8)",color:"white",padding:30},children:[Object(p.jsxs)("div",{className:"col-lg-4 text-center",children:[Object(p.jsx)("i",{className:"fa fa-4x fa-database","aria-hidden":"true"}),Object(p.jsx)("h2",{children:"Dimensional Metrics"}),Object(p.jsxs)("p",{children:["Micrometer provides vendor-neutral interfaces for ",Object(p.jsx)("strong",{children:"timers"}),", ",Object(p.jsx)("strong",{children:"gauges"}),", ",Object(p.jsx)("strong",{children:"counters"}),", ",Object(p.jsx)("strong",{children:"distribution summaries"}),", and ",Object(p.jsx)("strong",{children:"long task timers"})," with a dimensional data model that, when paired with a dimensional monitoring system, allows for efficient access to a particular named metric with the ability to drill down across its dimensions."]})]}),Object(p.jsxs)("div",{className:"col-lg-4 text-center",children:[Object(p.jsx)("i",{className:"fa fa-4x fa-area-chart","aria-hidden":"true"}),Object(p.jsx)("h2",{children:"Pre-configured Bindings"}),Object(p.jsx)("p",{children:"Out-of-the-box instrumentation of caches, the class loader, garbage collection, processor utilization, thread pools, and more tailored to actionable insight."})]}),Object(p.jsxs)("div",{className:"col-lg-4 text-center",children:[Object(p.jsx)("i",{className:"fa fa-4x fa-leaf","aria-hidden":"true"}),Object(p.jsx)("h2",{children:"Integrated into Spring"}),Object(p.jsx)("p",{children:"Micrometer is the instrumentation library powering the delivery of application observability from Spring Boot applications."})]})]}),Object(p.jsx)("div",{className:"row justify-content-center",style:{padding:30},children:Object(p.jsxs)("div",{className:"col-lg-6 col-md-12",children:[Object(p.jsx)("h2",{children:"Support for popular observability systems"}),Object(p.jsx)("p",{children:"As an instrumentation facade, Micrometer allows you to instrument your code with dimensional metrics, spans with a vendor-neutral interface and decide on the observability system as a last step. Instrumenting your core library code with Micrometer allows the libraries to be included in applications that ship data to different backends."}),Object(p.jsxs)("p",{children:["Contains built-in support for ",Object(p.jsx)("strong",{children:"AppOptics"}),", ",Object(p.jsx)("strong",{children:"Azure Monitor"}),", Netflix ",Object(p.jsx)("strong",{children:"Atlas"}),", ",Object(p.jsx)("strong",{children:"CloudWatch"}),", ",Object(p.jsx)("strong",{children:"Datadog"}),", ",Object(p.jsx)("strong",{children:"Dynatrace"}),", ",Object(p.jsx)("strong",{children:"Elastic"}),", ",Object(p.jsx)("strong",{children:"Ganglia"}),", ",Object(p.jsx)("strong",{children:"Graphite"}),", ",Object(p.jsx)("strong",{children:"Humio"}),", ",Object(p.jsx)("strong",{children:"Influx/Telegraf"}),", ",Object(p.jsx)("strong",{children:"JMX"}),", ",Object(p.jsx)("strong",{children:"KairosDB"}),", ",Object(p.jsx)("strong",{children:"New Relic"}),", ",Object(p.jsx)("strong",{children:"OpenTelemetry"})," Protocol (OTLP), ",Object(p.jsx)("strong",{children:"Prometheus"}),", ",Object(p.jsx)("strong",{children:"SignalFx"}),", Google ",Object(p.jsx)("strong",{children:"Stackdriver"}),", ",Object(p.jsx)("strong",{children:"StatsD"}),", and ",Object(p.jsx)("strong",{children:"Wavefront"}),"."]}),Object(p.jsxs)("p",{children:["Through ",Object(p.jsx)("strong",{children:"Micrometer Observation"})," and ",Object(p.jsx)("strong",{children:"Micrometer Tracing"})," you can ship your spans via ",Object(p.jsx)("strong",{children:"OpenZipkin Brave"})," or ",Object(p.jsx)("strong",{children:"OpenTelemetry"})," tracers to different backends (e.g. ",Object(p.jsx)("strong",{children:"OpenZipkin"})," or ",Object(p.jsx)("strong",{children:"Wavefront"}),")."]})]})})]})]})}var w=n(143);n(43);function T(){return Object(p.jsxs)("div",{className:"container-fluid mt-4",style:{paddingRight:30,paddingLeft:30},children:[Object(p.jsx)("h1",{children:"Micrometer Documentation"}),Object(p.jsx)("p",{children:"Micrometer provides a simple facade over the instrumentation clients for the most popular observability systems, allowing you to instrument your JVM-based application code without vendor lock-in. Think SLF4J, but for application observability! Data recorded by Micrometer are intended to be used to observe, alert, and react to the current/recent operational state of your environment."}),Object(p.jsxs)("p",{children:["Join the discussion with any comments and feature requests on Micrometer's public ",Object(p.jsxs)("a",{href:"https://slack.micrometer.io",children:[Object(p.jsx)("i",{className:"fa fa-lg fa-slack"})," Slack Workspace"]}),"."]}),Object(p.jsxs)("ol",{children:[Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/installing",children:"Installing"}),". Where to get the latest release and snapshot builds."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/concepts",children:"Concepts"}),". An introduction to the abstraction provided by Micrometer."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)("span",{className:"doc-section",children:"Setup"}),". Instructions for how to configure Micrometer for use with different monitoring systems. As a facade over multiple monitoring systems, the point of Micrometer is to allow you to instrument your code in the same way and be able to visualize the results in your monitoring system of choice.",Object(p.jsxs)("ul",{children:[Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/appOptics",children:"AppOptics"}),". AppOptics is a dimensional time-series SAAS with built-in dashboarding. Micrometer supports shipping metrics to AppOptics directly via its API."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/atlas",children:"Atlas"}),". An in-memory dimensional time series database with built-in graphing, a custom stack-based query language, and advanced math operations. Atlas originated at Netflix, where it remains the operational metrics solution."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/cloudwatch",children:"CloudWatch"}),". CloudWatch is a dimensional time-series SaaS on Amazon's cloud."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/datadog",children:"Datadog"}),". Datadog is a dimensional time-series SAAS with built-in dashboarding and alerting. Micrometer supports shipping metrics to Datadog directly via its API or through Dogstatsd via the StatsD registry."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/dynatrace",children:"Dynatrace"}),". Dynatrace is a Software Intelligence Platform featuring application performance monitoring (APM), artificial intelligence for operations (AIOps), IT infrastructure monitoring, digital experience management (DEM), and digital business analytics capabilities. Micrometer supports shipping metrics to Dynatrace directly via its API."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/elastic",children:"Elastic"}),". Elasticsearch is an open source search and analytics platform. Metrics stored in Elasticsearch can be visualized in Kibana."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/ganglia",children:"Ganglia"}),". An aging hierarchical metrics system which enjoyed wide popularity in Linux system monitoring and is still in place in many organizations. It originated in the early 2000s at the University of California, Berkeley."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/graphite",children:"Graphite"}),". One of the most popular current hierarchical metrics systems backed by a fixed-size database, similar in design and purpose to RRD. It originated at Orbitz in 2006 and was open sourced in 2008."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/humio",children:"Humio"}),". Humio is a dimensional time-series SAAS with built-in dashboarding. Micrometer supports shipping metrics to Humio directly via its API."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/influx",children:"Influx"}),". The InfluxData suite of tools supports real-time stream processing and storage of time-series data. It supports downsampling, automatically expiring and deleting unwanted data, as well as backup and restore. Analysis of data is done via a SQL-like query language."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/instana",children:"Instana"}),". Instana is an automatic application performance management and infrastructure monitoring system."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/jmx",children:"JMX"}),". Micrometer provides a hierarchical mapping to JMX, primarily as a cheap and portable way to view metrics locally. Where JMX exporting is found in production, the same metrics are generally exported to another, more purpose-fit monitoring system."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/kairos",children:"KairosDB"}),". KairosDB is a dimensional time-series database built on top of Cassandra. Charting can be accomplished in Grafana."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/new-relic",children:"New Relic"}),". Micrometer publishes to New Relic Insights, a SaaS offering with a full UI and a query language called NRQL. New Relic Insights operates on a push model."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/otlp",children:"OpenTelemetry Protocol (OTLP)"}),". OpenTelemetry is a CNCF incubating project for providing standards for telemetry data. Micrometer can publish metrics using the OpenTelemetry protocol (OTLP) to the backends that support it."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/prometheus",children:"Prometheus"}),". An in-memory dimensional time series database with a simple built-in UI, a custom query language, and math operations. Prometheus is designed to operate on a pull model, scraping metrics from application instances periodically based on service discovery."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/signalFx",children:"SignalFx"}),'. SignalFx is a dimensional monitoring system SaaS with a full UI operating on a push model. It has a rich set of alert "detectors".']}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/stackdriver",children:"Stackdriver"}),". Stackdriver Monitoring is a dimensional time-series SAAS with built-in dashboarding and alerting. Micrometer supports shipping metrics to Stackdriver directly via its API using a push model. Alternatively, you can export Micrometer metrics via Prometheus and use a Prometheus to Stackdriver sidecar."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/statsD",children:"StatsD"}),". Micrometer supports three flavors of StatsD: the original Etsy format plus the Datadog and Telegraf (Influx) extensions of StatsD that add dimensional support. Use this registry if you prefer to publish metrics to a StatsD agent. Also use this registry with Datadog flavor to publish metrics to Splunk."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/registry/wavefront",children:"Wavefront"}),". Wavefront is a SaaS-based metrics monitoring and analytics platform that lets you visualize, query, and alert over data from across your entire stack (infrastructure, network, custom app metrics, business KPIs, etc.)"]})]})]}),Object(p.jsxs)("li",{children:[Object(p.jsx)("span",{className:"doc-section",children:"Reference"}),". Detailed list of out-of-the-box instrumentation provided by Micrometer.",Object(p.jsxs)("ul",{children:[Object(p.jsxs)("li",{children:[Object(p.jsx)("a",{className:"doc-section",href:"https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-metrics",children:"Spring Boot"}),". Micrometer is the instrumentation library powering the delivery of application metrics from Spring."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/ref/jvm",children:"JVM"}),". Metrics on classloaders, memory, garbage collection, threads, etc."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/ref/cache",children:"Cache"}),". Instrumentation for the most popular caching frameworks."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/ref/okhttpclient",children:"OkHttpClient"}),". Instrumentation for OkHttpClient."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/ref/jetty",children:"Jetty and Jersey"}),". Instrumentation for Jetty and Jersey."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/ref/netty",children:"Netty"}),". Instrumentation for Netty."]})]})]}),Object(p.jsxs)("li",{children:[Object(p.jsx)("span",{className:"doc-section",children:"Guides"}),".",Object(p.jsx)("ul",{children:Object(p.jsx)("li",{children:Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/guide/consoleReporter",children:"Passing through to Dropwizard's console reporter"})})}),Object(p.jsx)("ul",{children:Object(p.jsx)("li",{children:Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/guide/httpSenderResilience4jRetry",children:"HttpSender with Resilience4j retry"})})}),Object(p.jsx)("ul",{children:Object(p.jsx)("li",{children:Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/guide/customMeterRegistry",children:"Custom meter registry"})})})]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/observation",children:"Micrometer Observation"}),". An introduction to the Micrometer Observation API."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/tracing",children:"Micrometer Tracing"}),". An introduction to the abstraction provided by Micrometer Tracing."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/contextPropagation",children:"Micrometer Context Propagation"}),". An introduction to the abstraction provided by Micrometer Context Propagation."]}),Object(p.jsxs)("li",{children:[Object(p.jsx)(w.a,{className:"doc-section",to:"/docs/support",children:"Support policy"}),". Micrometer's support policy for releases."]})]})]})}var x=n(26),S=n(27),O=n(17),M=n(31),C=n(29),I=(n(45),n(28)),k=n.n(I),E=(n(54),n(55)());E.Extensions.register((function(){this.treeProcessor((function(){this.process((function(e){return e.findBy({context:"image"}).forEach((function(e){var t=e.getAttribute("alt");e.setAttribute("alt",t+'" class="img-fluid',!0)})),e}))}))}));var R=function(e){Object(M.a)(n,e);var t=Object(C.a)(n);function n(e){var r;return Object(x.a)(this,n),(r=t.call(this,e)).highlightCode=r.highlightCode.bind(Object(O.a)(r)),r}return Object(S.a)(n,[{key:"componentDidMount",value:function(){this.highlightCode()}},{key:"componentDidUpdate",value:function(){this.highlightCode()}},{key:"highlightCode",value:function(){this.root.querySelectorAll("pre code").forEach((function(e){return k.a.highlightBlock(e)}))}},{key:"render",value:function(){var e=this,t=E.convert(this.props.source,{attributes:this.props.attrs,safe:"safe"});return Object(p.jsx)("div",{ref:function(t){e.root=t},dangerouslySetInnerHTML:{__html:t}})}}]),n}(r.Component);n(63);function A(e){var t=e.title,n=e.content,r=e.attrs;return Object(p.jsxs)("div",{className:"container-fluid mt-4 ml-3 mr-3",children:[Object(p.jsx)("h1",{children:t}),Object(p.jsx)("hr",{}),Object(p.jsx)(R,{source:n,attrs:r})]})}A.defaultProps={attrs:{}};var j=n(64),D=n(65),N=n(69),P=n(70),_=n(74),L=n(75),B=n(76),H=n(77),F=n(78),G=n(79),q=n(80),W=n(81),U=n(82),V=n(86),z=["appOptics","atlas","azure-monitor","cloudwatch","datadog","dynatrace","elastic","ganglia","graphite","humio","influx","instana","jmx","kairos","new-relic","otlp","prometheus","signalFx","stackdriver","statsD","wavefront"],Y={};function K(){return Object(p.jsxs)("div",{children:[Object(p.jsx)(c.a,{exact:!0,path:"/docs",component:T}),Object(p.jsx)(c.a,{path:"/docs/installing",render:function(){return Object(p.jsx)(A,{title:"Installing",content:j})}}),Object(p.jsx)(c.a,{exact:!0,path:"/docs/concepts",render:function(){return Object(p.jsx)(A,{title:"Concepts",content:D})}}),Object(p.jsx)(c.a,{path:"/docs/registry/:system",render:function(e){var t=e.match.params.system;return z.includes(t)?Object(p.jsx)(A,{title:"Micrometer ".concat(t.split("-").map((function(e){return e[0].toUpperCase()+e.slice(1)})).join(" ")),content:Y[t]}):Object(p.jsx)(l.a,{to:"/docs"})}}),Object(p.jsx)(c.a,{path:"/docs/ref/jvm",render:function(){return Object(p.jsx)(A,{title:"JVM and System Metrics",content:N})}}),Object(p.jsx)(c.a,{path:"/docs/ref/cache",render:function(){return Object(p.jsx)(A,{title:"Cache Metrics",content:P})}}),Object(p.jsx)(c.a,{path:"/docs/ref/okhttpclient",render:function(){return Object(p.jsx)(A,{title:"OkHttpClient Metrics",content:_})}}),Object(p.jsx)(c.a,{path:"/docs/ref/jetty",render:function(){return Object(p.jsx)(A,{title:"Jetty and Jersey Metrics",content:L})}}),Object(p.jsx)(c.a,{path:"/docs/ref/netty",render:function(){return Object(p.jsx)(A,{title:"Netty Metrics",content:B})}}),Object(p.jsx)(c.a,{path:"/docs/guide/consoleReporter",render:function(){return Object(p.jsx)(A,{title:"Passing through to Dropwizard's console reporter",content:H})}}),Object(p.jsx)(c.a,{path:"/docs/guide/httpSenderResilience4jRetry",render:function(){return Object(p.jsx)(A,{title:"HttpSender with Resilience4j retry",content:F})}}),Object(p.jsx)(c.a,{path:"/docs/guide/customMeterRegistry",render:function(){return Object(p.jsx)(A,{title:"Custom meter registry",content:G})}}),Object(p.jsx)(c.a,{path:"/docs/support",render:function(){return Object(p.jsx)(A,{title:"Micrometer Support Policy",content:q})}}),Object(p.jsx)(c.a,{path:"/docs/observation",render:function(){return Object(p.jsx)(A,{title:"Observation",content:W})}}),Object(p.jsx)(c.a,{path:"/docs/tracing",render:function(){return Object(p.jsx)(A,{title:"Tracing",content:U})}}),Object(p.jsx)(c.a,{path:"/docs/contextPropagation",render:function(){return Object(p.jsx)(A,{title:"Context Propagation",content:V})}})]})}function J(){return Object(p.jsxs)("div",{children:[Object(p.jsx)(h,{}),Object(p.jsxs)(s.a,{children:[Object(p.jsx)(c.a,{exact:!0,path:"/",component:v}),Object(p.jsx)(c.a,{path:"/security-policy",component:function(){return window.location.href="https://tanzu.vmware.com/security",null}}),Object(p.jsx)(K,{}),Object(p.jsx)(c.a,{path:"**",render:function(){return Object(p.jsx)(l.a,{to:"/"})}})]}),Object(p.jsx)("div",{className:"container-fluid",style:{paddingRight:0,paddingLeft:0},children:Object(p.jsx)(f,{className:"row"})})]})}z.forEach((function(e){return Y[e]=n(87)("./".concat(e,".adoc"))}));Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));var $=n(11),Z=(n(137),n(138),Object($.a)());i.a.render(Object(p.jsx)(o.a,{history:Z,children:Object(p.jsx)(J,{})}),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((function(e){e.unregister()})).catch((function(e){console.error(e.message)}))}]),[[139,1,2]]]); -//# sourceMappingURL=main.5d59054d.chunk.js.map \ No newline at end of file +(this["webpackJsonpmicrometer-docs"]=this["webpackJsonpmicrometer-docs"]||[]).push([[0],Array(28).concat([function(e,t,n){var r=n(46);r.registerLanguage("gradle",n(47)),r.registerLanguage("groovy",n(48)),r.registerLanguage("http",n(49)),r.registerLanguage("java",n(50)),r.registerLanguage("xml",n(51)),r.registerLanguage("yaml",n(52)),r.registerLanguage("json",n(53)),e.exports=r},,,,,,,,,function(e,t,n){},,,,,,function(e,t,n){},,,function(e,t,n){!function(e){"object"===typeof window&&window||"object"===typeof self&&self;(function(e){var t=[],n=Object.keys,r={},a={},i=/^(no-?highlight|plain|text)$/i,o=/\blang(?:uage)?-([\w-]+)\b/i,s=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,c="",l={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function u(e){return e.replace(/&/g,"&").replace(//g,">")}function d(e){return e.nodeName.toLowerCase()}function m(e,t){var n=e&&e.exec(t);return n&&0===n.index}function g(e){return i.test(e)}function h(e){var t,n,r,a,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",n=o.exec(i))return R(n[1])?n[1]:"no-highlight";for(t=0,r=(i=i.split(/\s+/)).length;t"}function l(e){i+=""}function m(e){("start"===e.event?c:l)(e.node)}for(;e.length||n.length;){var g=s();if(i+=u(r.substring(a,g[0].offset)),a=g[0].offset,g===e){o.reverse().forEach(l);do{m(g.splice(0,1)[0]),g=s()}while(g===e&&g.length&&g[0].offset===a);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),m(g.splice(0,1)[0])}return i+u(r.substr(a))}function y(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(t){return p(e,{variants:null},t)}))),e.cached_variants||e.endsWithParent&&[p(e)]||[e]}function v(e){function t(e){return e&&e.source||e}function r(n,r){return new RegExp(t(n),"m"+(e.case_insensitive?"i":"")+(r?"g":""))}function a(i,o){if(!i.compiled){if(i.compiled=!0,i.keywords=i.keywords||i.beginKeywords,i.keywords){var s={},c=function(t,n){e.case_insensitive&&(n=n.toLowerCase()),n.split(" ").forEach((function(e){var n=e.split("|");s[n[0]]=[t,n[1]?Number(n[1]):1]}))};"string"===typeof i.keywords?c("keyword",i.keywords):n(i.keywords).forEach((function(e){c(e,i.keywords[e])})),i.keywords=s}i.lexemesRe=r(i.lexemes||/\w+/,!0),o&&(i.beginKeywords&&(i.begin="\\b("+i.beginKeywords.split(" ").join("|")+")\\b"),i.begin||(i.begin=/\B|\b/),i.beginRe=r(i.begin),i.end||i.endsWithParent||(i.end=/\B|\b/),i.end&&(i.endRe=r(i.end)),i.terminator_end=t(i.end)||"",i.endsWithParent&&o.terminator_end&&(i.terminator_end+=(i.end?"|":"")+o.terminator_end)),i.illegal&&(i.illegalRe=r(i.illegal)),null==i.relevance&&(i.relevance=1),i.contains||(i.contains=[]),i.contains=Array.prototype.concat.apply([],i.contains.map((function(e){return y("self"===e?i:e)}))),i.contains.forEach((function(e){a(e,i)})),i.starts&&a(i.starts,o);var l=i.contains.map((function(e){return e.beginKeywords?"\\.?("+e.begin+")\\.?":e.begin})).concat([i.terminator_end,i.illegal]).map(t).filter(Boolean);i.terminators=l.length?r(l.join("|"),!0):{exec:function(){return null}}}}a(e)}function w(e,t,n,a){function i(e,t){var n,r;for(n=0,r=t.contains.length;n')+t+(n?"":c)}function h(){var e,t,n,r;if(!O.keywords)return u(I);for(r="",t=0,O.lexemesRe.lastIndex=0,n=O.lexemesRe.exec(I);n;)r+=u(I.substring(t,n.index)),(e=d(O,n))?(k+=e[1],r+=g(e[0],u(n[0]))):r+=u(n[0]),t=O.lexemesRe.lastIndex,n=O.lexemesRe.exec(I);return r+u(I.substr(t))}function p(){var e="string"===typeof O.subLanguage;if(e&&!r[O.subLanguage])return u(I);var t=e?w(O.subLanguage,I,!0,M[O.subLanguage]):T(I,O.subLanguage.length?O.subLanguage:void 0);return O.relevance>0&&(k+=t.relevance),e&&(M[O.subLanguage]=t.top),g(t.language,t.value,!1,!0)}function f(){C+=null!=O.subLanguage?p():h(),I=""}function b(e){C+=e.className?g(e.className,"",!0):"",O=Object.create(e,{parent:{value:O}})}function y(e,t){if(I+=e,null==t)return f(),0;var n=i(t,O);if(n)return n.skip?I+=t:(n.excludeBegin&&(I+=t),f(),n.returnBegin||n.excludeBegin||(I=t)),b(n,t),n.returnBegin?0:t.length;var r=o(O,t);if(r){var a=O;a.skip?I+=t:(a.returnEnd||a.excludeEnd||(I+=t),f(),a.excludeEnd&&(I=t));do{O.className&&(C+=c),O.skip||O.subLanguage||(k+=O.relevance),O=O.parent}while(O!==r.parent);return r.starts&&b(r.starts,""),a.returnEnd?0:t.length}if(s(t,O))throw new Error('Illegal lexeme "'+t+'" for mode "'+(O.className||"")+'"');return I+=t,t.length||1}var x=R(e);if(!x)throw new Error('Unknown language: "'+e+'"');v(x);var S,O=a||x,M={},C="";for(S=O;S!==x;S=S.parent)S.className&&(C=g(S.className,"",!0)+C);var I="",k=0;try{for(var E,A,j=0;O.terminators.lastIndex=j,E=O.terminators.exec(t);)A=y(t.substring(j,E.index),E[0]),j=E.index+A;for(y(t.substr(j)),S=O;S.parent;S=S.parent)S.className&&(C+=c);return{relevance:k,value:C,language:e,top:O}}catch(D){if(D.message&&-1!==D.message.indexOf("Illegal"))return{relevance:0,value:u(t)};throw D}}function T(e,t){t=t||l.languages||n(r);var a={relevance:0,value:u(e)},i=a;return t.filter(R).forEach((function(t){var n=w(t,e,!1);n.language=t,n.relevance>i.relevance&&(i=n),n.relevance>a.relevance&&(i=a,a=n)})),i.language&&(a.second_best=i),a}function x(e){return l.tabReplace||l.useBR?e.replace(s,(function(e,t){return l.useBR&&"\n"===e?"
":l.tabReplace?t.replace(/\t/g,l.tabReplace):""})):e}function S(e,t,n){var r=t?a[t]:n,i=[e.trim()];return e.match(/\bhljs\b/)||i.push("hljs"),-1===e.indexOf(r)&&i.push(r),i.join(" ").trim()}function O(e){var t,n,r,a,i,o=h(e);g(o)||(l.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):t=e,i=t.textContent,r=o?w(o,i,!0):T(i),(n=f(t)).length&&((a=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=r.value,r.value=b(n,f(a),i)),r.value=x(r.value),e.innerHTML=r.value,e.className=S(e.className,o,r.language),e.result={language:r.language,re:r.relevance},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.relevance}))}function M(e){l=p(l,e)}function C(){if(!C.called){C.called=!0;var e=document.querySelectorAll("pre code");t.forEach.call(e,O)}}function I(){addEventListener("DOMContentLoaded",C,!1),addEventListener("load",C,!1)}function k(t,n){var i=r[t]=n(e);i.aliases&&i.aliases.forEach((function(e){a[e]=t}))}function E(){return n(r)}function R(e){return e=(e||"").toLowerCase(),r[e]||r[a[e]]}e.highlight=w,e.highlightAuto=T,e.fixMarkup=x,e.highlightBlock=O,e.configure=M,e.initHighlighting=C,e.initHighlightingOnLoad=I,e.registerLanguage=k,e.listLanguages=E,e.getLanguage=R,e.inherit=p,e.IDENT_RE="[a-zA-Z]\\w*",e.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",e.NUMBER_RE="\\b\\d+(\\.\\d+)?",e.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BINARY_NUMBER_RE="\\b(0b[01]+)",e.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BACKSLASH_ESCAPE={begin:"\\\\[\\s\\S]",relevance:0},e.APOS_STRING_MODE={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},e.QUOTE_STRING_MODE={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},e.PHRASAL_WORDS_MODE={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.COMMENT=function(t,n,r){var a=e.inherit({className:"comment",begin:t,end:n,contains:[]},r||{});return a.contains.push(e.PHRASAL_WORDS_MODE),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|XXX):",relevance:0}),a},e.C_LINE_COMMENT_MODE=e.COMMENT("//","$"),e.C_BLOCK_COMMENT_MODE=e.COMMENT("/\\*","\\*/"),e.HASH_COMMENT_MODE=e.COMMENT("#","$"),e.NUMBER_MODE={className:"number",begin:e.NUMBER_RE,relevance:0},e.C_NUMBER_MODE={className:"number",begin:e.C_NUMBER_RE,relevance:0},e.BINARY_NUMBER_MODE={className:"number",begin:e.BINARY_NUMBER_RE,relevance:0},e.CSS_NUMBER_MODE={className:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},e.REGEXP_MODE={className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0,contains:[e.BACKSLASH_ESCAPE]}]},e.TITLE_MODE={className:"title",begin:e.IDENT_RE,relevance:0},e.UNDERSCORE_TITLE_MODE={className:"title",begin:e.UNDERSCORE_IDENT_RE,relevance:0},e.METHOD_GUARD={begin:"\\.\\s*"+e.UNDERSCORE_IDENT_RE,relevance:0}})(t)}()},function(e,t){e.exports=function(e){return{case_insensitive:!0,keywords:{keyword:"task project allprojects subprojects artifacts buildscript configurations dependencies repositories sourceSets description delete from into include exclude source classpath destinationDir includes options sourceCompatibility targetCompatibility group flatDir doLast doFirst flatten todir fromdir ant def abstract break case catch continue default do else extends final finally for if implements instanceof native new private protected public return static switch synchronized throw throws transient try volatile while strictfp package import false null super this true antlrtask checkstyle codenarc copy boolean byte char class double float int interface long short void compile runTime file fileTree abs any append asList asWritable call collect compareTo count div dump each eachByte eachFile eachLine every find findAll flatten getAt getErr getIn getOut getText grep immutable inject inspect intersect invokeMethods isCase join leftShift minus multiply newInputStream newOutputStream newPrintWriter newReader newWriter next plus pop power previous print println push putAt read readBytes readLines reverse reverseEach round size sort splitEachLine step subMap times toInteger toList tokenize upto waitForOrKill withPrintWriter withReader withStream withWriter withWriterAppend write writeLine"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,e.REGEXP_MODE]}}},function(e,t){e.exports=function(e){return{keywords:{literal:"true false null",keyword:"byte short char int long boolean float double void def as in assert trait super this abstract static volatile transient public private protected synchronized final class interface enum if else for while switch case break default continue throw throws try catch finally implements extends new import package return instanceof"},contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string",begin:'"""',end:'"""'},{className:"string",begin:"'''",end:"'''"},{className:"string",begin:"\\$/",end:"/\\$",relevance:10},e.APOS_STRING_MODE,{className:"regexp",begin:/~?\/[^\/\n]+\//,contains:[e.BACKSLASH_ESCAPE]},e.QUOTE_STRING_MODE,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},e.BINARY_NUMBER_MODE,{className:"class",beginKeywords:"class interface trait enum",end:"{",illegal:":",contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"},{className:"string",begin:/[^\?]{0}[A-Za-z0-9_$]+ *:/},{begin:/\?/,end:/\:/},{className:"symbol",begin:"^\\s*[A-Za-z0-9_$]+:",relevance:0}],illegal:/#|<\//}}},function(e,t){e.exports=function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],illegal:"\\S",contains:[{begin:"^"+t,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+t+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:t},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}},function(e,t){e.exports=function(e){var t="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",n={className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0};return{aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(<[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(\\s*,\\s*[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},n,{className:"meta",begin:"@[A-Za-z]+"}]}}},function(e,t){e.exports=function(e){var t={endsWithParent:!0,illegal:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[{begin:"\\[",end:"\\]"}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{begin:/<\?(php)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0}]},{className:"tag",begin:"|$)",end:">",keywords:{name:"style"},contains:[t],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:"|$)",end:">",keywords:{name:"script"},contains:[t],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["actionscript","javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},t]}]}}},function(e,t){e.exports=function(e){var t="true false yes no null",n="^[ \\-]*",r="[a-zA-Z_][\\w\\-]*",a={className:"attr",variants:[{begin:n+r+":"},{begin:'^[ \\-]*"'+r+'":'},{begin:"^[ \\-]*'"+r+"':"}]},i={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]};return{case_insensitive:!0,aliases:["yml","YAML","yaml"],contains:[a,{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>] *$",returnEnd:!0,contains:i.contains,end:a.variants[0].begin},{className:"type",begin:"!!"+e.UNDERSCORE_IDENT_RE},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"^ *-",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},e.C_NUMBER_MODE,i]}}},function(e,t){e.exports=function(e){var t={literal:"true false null"},n=[e.QUOTE_STRING_MODE,e.C_NUMBER_MODE],r={end:",",endsWithParent:!0,excludeEnd:!0,contains:n,keywords:t},a={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE],illegal:"\\n"},e.inherit(r,{begin:/:/})],illegal:"\\S"},i={begin:"\\[",end:"\\]",contains:[e.inherit(r)],illegal:"\\S"};return n.splice(n.length,0,a,i),{contains:n,keywords:t,illegal:"\\S"}}},function(e,t,n){},,,,,,,,,function(e,t,n){},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer contains a core library with the instrumentation SPI and an in-memory implementation that does not export data anywhere, a series of modules with implementations for various monitoring systems, and a test module.\n\nTo use Micrometer, add the dependency for your monitoring system.\n\nThe following example adds Prometheus in Gradle:\n\n[source,groovy]\n----\nimplementation 'io.micrometer:micrometer-registry-prometheus:latest.release'\n----\n\nThe following example adds Prometheus in Maven:\n\n[source,xml]\n----\n\n io.micrometer\n micrometer-registry-prometheus\n ${micrometer.version}\n\n----\n\nThrough Micrometer's composite meter registry (described in greater detail in link:/docs/concepts#_composite_registries[\"Concepts\"]), you may configure more than one registry implementation if you intend to publish metrics to more than one monitoring system.\n\nIf you have not decided on a monitoring system yet and want only to try out the instrumentation SPI, you can add a dependency on `micrometer-core` instead and configure the `SimpleMeterRegistry`.\n\n== Snapshots\n\nEvery successful https://app.circleci.com/pipelines/github/micrometer-metrics/micrometer[build] of Micrometer's `main` and maintenance branches (for example, `1.7.x`) results in the publication of a new snapshot version. You can use the latest snapshot by adding the Maven repository `https://repo.spring.io/snapshot` to your build and using the corresponding snapshot version -- for example, `1.8.0-SNAPSHOT`.\n\n== Milestones\n\nMilestone releases are made available for early testing purposes and are not intended for production use.\nMilestone releases are published to https://repo.spring.io/milestone.\nInclude that as a Maven repository in your build configuration to use milestone releases.\nMilestones are marked as \"`pre-releases`\" on GitHub, and the version has a suffix, such as `-M1` or `-RC1` (milestone 1 or release candidate 1, respectively).\n"},function(e,t,n){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Concepts\n:toc:\n:sectnums:\n:dimensional: true\n\n== Purpose\n\nMicrometer is a metrics instrumentation library for JVM-based applications. It provides a simple facade over the instrumentation clients for the most popular monitoring systems, letting you instrument your JVM-based application code without vendor lock-in. It is designed to add little to no overhead to your metrics collection activity while maximizing the portability of your metrics effort.\n\nStarting from Micrometer 1.10, Micrometer provides the link:../docs/observation[Observation API] and a plugin mechanism that allows you to add capabilities including the tracing features. You can read more about this in the link:../docs/tracing[Micrometer Tracing documentation].\n\nFor better understanding the differences among these different types of systems (Metrics, Distributed Tracing, and Logging) we recommend Adrian Cole\'s talk, titled https://www.dotconferences.com/2017/04/adrian-cole-observability-3-ways-logging-metrics-tracing[Observability\n3 Ways]. To learn more about Micrometer Observation API we recommend Tommy Ludwig\'s and Marcin Grzejszczak\'s talk, titled https://www.youtube.com/watch?v=fh3VbrPvAjg[Observability of Your Application].\n\n== Dependencies\n\nThe `micrometer-core` module aims to have minimal dependencies. It does not require any third-party (non-Micrometer) dependencies on the classpath at compile time for applications using Micrometer.\n\nUse of the link:#_pause_detection[pause detection] feature requires the https://github.com/LatencyUtils/LatencyUtils[LatencyUtils] dependency to be available on the classpath at runtime. If your application does not use the pause detection feature, you can exclude LatencyUtils from your runtime classpath.\n\nhttps://github.com/HdrHistogram/HdrHistogram[HdrHistogram] is needed on the classpath at runtime if you use link:#_histograms_and_percentiles[client-side percentiles]. If you are not using client-side percentiles, you may exclude HdrHistogram from your application\'s runtime classpath.\n\n== Supported Monitoring Systems\n\n:leveloffset: +1\n\nMicrometer contains a core module with an instrumentation https://en.wikipedia.org/wiki/Service_provider_interface[SPI], a set of modules containing implementations for various monitoring systems (each is called a registry), and a test kit. You need to understand three important characteristics of monitoring systems:\n\n* *Dimensionality*. Whether the system supports metric names to be enriched with tag key/value pairs. If a system is not _dimensional_, it is _hierarchical_, which means it supports only a flat metric name. When publishing metrics to hierarchical systems, Micrometer flattens the set of tag key/value pairs and adds them to the name.\n\n[cols=2*,options="header"]\n|===\n|Dimensional\n|Hierarchical\n\n|AppOptics, Atlas, Azure Monitor, Cloudwatch, Datadog, Datadog StatsD, Dynatrace, Elastic, Humio, Influx, KairosDB, New Relic, Prometheus, SignalFx, Sysdig StatsD, Telegraf StatsD, Wavefront\n|Graphite, Ganglia, JMX, Etsy StatsD\n|===\n\n\n* *<>*. In this context, we mean aggregation of a set of samples over a prescribed time interval. Some monitoring systems expect some types of discrete samples (such as counts) to be converted to a rate by the application prior to being published. Other systems expect cumulative values to always be sent. Still others have no opinion on it either way.\n\n[cols=2*,options="header"]\n|===\n|Client-side\n|Server-side\n\n|AppOptics, Atlas, Azure Monitor, Datadog, Dynatrace, Elastic, Graphite, Ganglia, Humio, Influx, JMX, Kairos, New Relic, all StatsD flavors, SignalFx\n|Prometheus, Wavefront footnote:[As of 1.2.0, Micrometer sends cumulative values to Wavefront.]\n|===\n\n* *Publishing*. Some systems expect to poll applications for metrics at their leisure, while others expect metrics to be pushed to them on a regular interval.\n\n[cols=2*,options="header"]\n|===\n|Client pushes\n|Server polls\n\n|AppOptics, Atlas, Azure Monitor, Datadog, Dynatrace, Elastic, Graphite, Ganglia, Humio, Influx, JMX, Kairos, New Relic, SignalFx, Wavefront\n|Prometheus, all StatsD flavors\n|===\n\nThere are other, more minor, variations in expectations from one monitoring system to another, such as their conception of base units of measurement (particularly time) and the canonical naming convention for metrics. Micrometer customizes your metrics to meet these demands on a per-registry basis.\n\n:leveloffset!:\n\n== Registry\n\n:leveloffset: +1\n\nA `Meter` is the interface for collecting a set of measurements (which we individually call metrics) about your application. Meters in Micrometer are created from and held in a `MeterRegistry`. Each supported monitoring system has an implementation of `MeterRegistry`. How a registry is created varies for each implementation.\n\nMicrometer includes a `SimpleMeterRegistry` that holds the latest value of each meter in memory and does not export the data anywhere. If you do not yet have a preferred monitoring system, you can get started playing with metrics by using the simple registry:\n\n====\n[source,java]\n----\nMeterRegistry registry = new SimpleMeterRegistry();\n----\n====\n\nNOTE: A `SimpleMeterRegistry` is autowired for you in Spring-based applications.\n\n== Composite Registries\n\nMicrometer provides a `CompositeMeterRegistry` to which you can add multiple registries, letting you publish metrics to more than one monitoring system simultaneously:\n\n====\n[source,java]\n----\nCompositeMeterRegistry composite = new CompositeMeterRegistry();\n\nCounter compositeCounter = composite.counter("counter");\ncompositeCounter.increment(); <1>\n\nSimpleMeterRegistry simple = new SimpleMeterRegistry();\ncomposite.add(simple); <2>\n\ncompositeCounter.increment(); <3>\n----\n\n1. Increments are NOOP\'d until there is a registry in the composite. The counter\'s count still yields 0 at this point.\n2. A counter named `counter` is registered to the simple registry.\n3. The simple registry counter is incremented, along with counters for any other registries in the composite.\n====\n\n== Global Registry\n\nMicrometer provides a static global registry `Metrics.globalRegistry` and a set of static builders for generating meters based on this registry (note that `globalRegistry` is a composite registry):\n\n====\n[source,java]\n----\nclass MyComponent {\n Counter featureCounter = Metrics.counter("feature", "region", "test"); <1>\n\n void feature() {\n featureCounter.increment();\n }\n\n void feature2(String type) {\n Metrics.counter("feature.2", "type", type).increment(); <2>\n }\n}\n\nclass MyApplication {\n void start() {\n // wire your monitoring system to global static state\n Metrics.addRegistry(new SimpleMeterRegistry()); <3>\n }\n}\n----\n\n1. Wherever possible (and especially where instrumentation performance is critical), store `Meter` instances in fields to avoid a lookup on their name or tags on each use.\n2. When tags need to be determined from local context, you have no choice but to construct or lookup the Meter inside your method body. The lookup cost is just a single hash lookup, so it is acceptable for most use cases.\n3. It is OK to add registries _after_ meters have been created with code like `Metrics.counter(...)`. These meters are added to each registry, as it is bound to the global composite.\n====\n\n:leveloffset!:\n\n== Meters\n\n:leveloffset: +1\n\nMicrometer supports a set of `Meter` primitives, including `Timer`, `Counter`, `Gauge`, `DistributionSummary`, `LongTaskTimer`, `FunctionCounter`, `FunctionTimer`, and `TimeGauge`. Different meter types result in a different number of time series metrics. For example, while there is a single metric that represents a `Gauge`, a `Timer` measures both the count of timed events and the total time of all timed events.\n\nA meter is uniquely identified by its name and dimensions. We use the terms, "`dimensions`" and "`tags,`" interchangeably, and the Micrometer interface is `Tag` simply because it is shorter. As a general rule, it should be possible to use the name as a pivot. Dimensions let a particular named metric be sliced to drill down and reason about the data. This means that, if only the name is selected, you can drill down by using other dimensions and reason about the value being shown.\n\n:leveloffset!:\n\n== Naming Meters\n\n:leveloffset: +1\n\nMicrometer employs a naming convention that separates lowercase words with a `.` (dot) character. Different monitoring systems have different recommendations regarding naming convention, and some naming conventions may be incompatible between one system and another. Each Micrometer implementation for a monitoring system comes with a naming convention that transforms lowercase dot notation names to the monitoring system\'s recommended naming convention. Additionally, this naming convention implementation removes special characters that are disallowed by the monitoring system from the metric names and tags. You can override the default naming convention for a registry by implementing `NamingConvention` and setting it on the registry:\n\n====\n[source,java]\n----\nregistry.config().namingConvention(myCustomNamingConvention);\n----\n====\n\nWith naming conventions in place, the following timer registered in Micrometer looks good natively in a wide variety of monitoring systems:\n\n====\n[source,java]\n----\nregistry.timer("http.server.requests");\n----\n====\n\n. Prometheus - `http_server_requests_duration_seconds`\n. Atlas - `httpServerRequests`\n. Graphite - `http.server.requests`\n. InfluxDB - `http_server_requests`\n\nBy adhering to Micrometer\'s lowercase dot notation convention, you guarantee the maximum degree of portability for your metric names across monitoring systems.\n\n== Tag Naming\n\nTIP: We recommend that you follow the same lowercase dot notation described for meter names when naming tags. Using this consistent naming convention for tags allows for better translation into the respective monitoring system\'s idiomatic naming schemes.\n\nSuppose we are trying to measure the number of http requests and the number of database calls.\n\n*Recommended Approach*\n\n====\n[source,java]\n----\nregistry.counter("database.calls", "db", "users")\nregistry.counter("http.requests", "uri", "/api/users")\n----\n====\n\nThis variant provides enough context so that, if only the name is selected, the value can be reasoned about and is at least potentially meaningful. For example if we select `database.calls`, we can see the total number of calls to all databases. Then we can group by or select by `db` to drill down further or perform comparative analysis on the contribution of calls to each database.\n\n*Bad Approach*\n\n====\n[source,java]\n----\nregistry.counter("calls",\n "class", "database",\n "db", "users");\n\nregistry.counter("calls",\n "class", "http",\n "uri", "/api/users");\n----\n====\n\nIn this approach, if we select `calls`, we get a value that is an aggregate of the number of calls to the database and to our API endpoint. This time series is not useful without further dimensional drill-down.\n\n== Common Tags\n\nYou can define common tags at the registry level and add them to every metric reported to the monitoring system. This is generally used for dimensional drill-down on the operating environment, such as host, instance, region, stack, and others:\n\n====\n[source,java]\n----\nregistry.config().commonTags("stack", "prod", "region", "us-east-1");\nregistry.config().commonTags(Arrays.asList(Tag.of("stack", "prod"), Tag.of("region", "us-east-1"))); // equivalently\n----\n====\n\nCalls to `commonTags` append additional common tags.\n\n[IMPORTANT]\n====\nCommon tags generally have to be added to the registry _before_ any (possibly autoconfigured) meter binders. Depending on your environment, there are different ways to achieve this.\n\nIf you use Spring Boot, you have two options:\n\n* Add your common tags with https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.customizing.common-tags[configuration properties]\n* If you need more flexibility (for example, you have to add common tags to a registry defined in a shared library), register a `MeterRegistryCustomizer` callback interface as a bean to add your common tags. See the\nhttps://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.getting-started[Spring Boot Reference Documentation] for more information.\n====\n\n== Tag Values\n\nTag values must be non-null.\n\nWARNING: Beware of the potential for tag values coming from user-supplied sources to blow up the cardinality of a metric. You should always carefully normalize and add bounds to user-supplied input. Sometimes, the cause is sneaky. Consider the URI tag for recording HTTP requests on service endpoints. If we do not constrain 404\'s to a value like NOT_FOUND, the dimensionality of the metric would grow with each resource that cannot be found.\n\n:leveloffset!:\n\n== Meter Filters\n\n:leveloffset: +1\n\nYou can configure each registry with meter filters, which give you greater control over how and when meters are registered and what kinds of statistics they emit. Meter filters serve three basic functions:\n\n1. **Deny** (or **Accept**) meters being registered.\n2. **Transform** meter IDs (for example, changing the name, adding or removing tags, and changing description or base units).\n3. **Configure** distribution statistics for some meter types.\n\nImplementations of `MeterFilter` are added to the registry programmatically:\n\n====\n[source, java]\n----\nregistry.config()\n .meterFilter(MeterFilter.ignoreTags("too.much.information"))\n .meterFilter(MeterFilter.denyNameStartsWith("jvm"));\n----\n====\n\nMeter filters are applied in order, and the results of transforming or configuring a meter are chained.\n\n== Deny or Accept Meters\n\nThe verbose form of an accept or deny filter is:\n\n====\n[source, java]\n----\nnew MeterFilter() {\n @Override\n public MeterFilterReply accept(Meter.Id id) {\n if(id.getName().contains("test")) {\n return MeterFilterReply.DENY;\n }\n return MeterFilterReply.NEUTRAL;\n }\n}\n----\n====\n\n`MeterFilterReply` has three possible states:\n\n* `DENY`: Do not let this meter be registered. When you try to register a meter against a registry and the filter returns `DENY`, the registry returns a NOOP version of that meter (for example, `NoopCounter` or `NoopTimer`). Your code can continue to interact with the NOOP meter, but anything recorded to it is discarded immediately with minimal overhead.\n* `NEUTRAL`: If no other meter filter has returned `DENY`, registration of meters proceeds normally.\n* `ACCEPT`: If a filter returns `ACCEPT`, the meter is immediately registered without interrogating the accept methods of any further filters.\n\n=== Convenience Methods\n\n`MeterFilter` provides several convenience static builders for deny and accept type filters:\n\n* `accept()`: Accept every meter, overriding the decisions of any filters that follow.\n* `accept(Predicate)`: Accept any meter matching the predicate.\n* `acceptNameStartsWith(String)`: Accept every meter with a matching prefix.\n* `deny()`: Deny every meter, overriding the decisions of any filters that follow.\n* `denyNameStartsWith(String)`: Deny every meter with a matching prefix. All `MeterBinder` implementations provided by Micrometer have names with common prefixes to allow for easy grouping visualization in UIs but also to make them easy to disable or enable as a group with a prefix. For example, you can deny all JVM metrics with `MeterFilter.denyNameStartsWith("jvm")`.\n* `deny(Predicate)`: Deny any meter that matches the predicate.\n* `maximumAllowableMetrics(int)`: Deny any meter after the registry has reached a certain number of meters.\n* `maximumAllowableTags(String meterNamePrefix, String tagKey, int maximumTagValues, MeterFilter onMaxReached)`: Places an upper bound on the number of tags produced by matching series.\n\n**Whitelisting** only a certain group of metrics is a particularly common case for monitoring systems that are _expensive_. This can be achieved with a static call:\n\n* `denyUnless(Predicate)`: Deny all meters that _do not_ match the predicate.\n\n=== Chaining Deny Accept Meters\n\nMeter filters are applied in the order in which they are configured on the registry, so it is possible to stack deny and accept filters to achieve more complex rules:\n\n====\n[source, java]\n----\nregistry.config()\n .meterFilter(MeterFilter.acceptNameStartsWith("http"))\n .meterFilter(MeterFilter.deny()); <1>\n----\n====\n\nThis achieves another form of whitelisting by stacking two filters together. Only `http` metrics are allowed to exist in this registry.\n\n== Transforming metrics\n\nThe following example shows a transform filter:\n\n====\n[source, java]\n----\nnew MeterFilter() {\n @Override\n public Meter.Id map(Meter.Id id) {\n if(id.getName().startsWith("test")) {\n return id.withName("extra." + id.getName()).withTag("extra.tag", "value");\n }\n return id;\n }\n}\n----\n====\n\nThis filter adds a name prefix and an additional tag conditionally to meters starting with a name of `test`.\n\n`MeterFilter` provides convenience builders for many common transformation cases:\n\n* `commonTags(Iterable)`: Adds a set of tags to all metrics. Adding common tags for application name, host, region, and others is a highly recommended practice.\n* `ignoreTags(String...)`: Drops matching tag keys from every meter. This is particularly useful when a tag provably comes to have\ntoo high cardinality and starts stressing your monitoring system or costing too much but you cannot change all the instrumentation points quickly.\n* `replaceTagValues(String tagKey, Function replacement, String... exceptions)`: Replace tag values according to the provided mapping for all matching tag keys. You can use this to reduce the total cardinality of a tag by mapping some portion of tag values to something else.\n* `renameTag(String meterNamePrefix, String fromTagKey, String toTagKey)`: Rename a tag key for every metric that begins with a given prefix.\n\n== Configuring Distribution Statistics\n\n`Timer` and `DistributionSummary` contain a set of optional distribution statistics (in addition to the basics of count, total, and max) that you can configure through filters. These distribution statistics include pre-computed percentiles, SLOs, and histograms.\n\n====\n[source, java]\n----\nnew MeterFilter() {\n @Override\n public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {\n if (id.getName().startsWith(prefix)) {\n return DistributionStatisticConfig.builder()\n .publishPercentiles(0.9, 0.95)\n .build()\n .merge(config);\n }\n return config;\n }\n};\n----\n====\n\nGenerally, you should create a new `DistributionStatisticConfig` with only the pieces you wish to configure and then `merge` it with the input configuration. This lets you drop down on registry-provided defaults for distribution statistics and to chain multiple filters together, each configuring some part of the distribution statistics (for example, you might want a 100ms SLO for all HTTP requests but only percentile histograms on a few critical endpoints).\n\n`MeterFilter` provides convenience builders for:\n\n* `maxExpected(Duration/long)`: Governs the upper bound of percentile histogram buckets shipped from a timer or summary.\n* `minExpected(Duration/long)`: Governs the lower bound of percentile histogram buckets shipped from a timer or summary.\n\nSpring Boot offers property-based filters for configuring SLOs, percentiles, and percentile histograms by name prefix.\n\n:leveloffset!:\n\n[[rate-aggregation]]\n== Rate Aggregation\n\n:leveloffset: +1\n\nMicrometer is aware of whether a particular monitoring system expects rate aggregation to happen client-side before metrics are published or ad-hoc as part of the query on the server. It accumulates metrics according to which style the monitoring system expects.\n\nNot all measurements are reported or best viewed as a rate. For example, gauge values and the active task count long task timers are not rates.\n\n== Server-side\n\nMonitoring systems that perform server-side rate math expect absolute values to be reported at each publishing interval. For example, the absolute count of all increments to a counter since the beginning of the application is sent on each publishing interval.\n\nSuppose we have a slightly positively biased random walk that chooses to increment a counter once every 10 milliseconds. If we view the raw counter value in a system like Prometheus, we see a step-wise monotonically increasing function (the width of the step is the interval at which Prometheus is polling or scraping for data).\n\nimage::'+n(66)+"[Absolute counter value]\n\nRepresenting a counter without rate aggregation over some time window is rarely useful, as the representation is a function of both the rapidity with which the counter is incremented and the longevity of the service. In the preceding example, the counter drops back to zero on service restart. The rate-aggregated graph would return back to a value around 55 as soon as the new instance (say on a production deployment) was in service.\n\nimage::"+n(67)+'[Rate-aggregated counter]\n\nIf you have achieved zero-downtime deployments (for example, through red-black deployments), you should be able to comfortably set _minimum_ alert thresholds on the rate-aggregated graph without service restarts causing dips in the counter value.\n\nIMPORTANT: For most production purposes, whether it be alerting, automated canary analysis, or other use cases, base your automation off of rate-aggregated data.\n\n== Client-side\n\nTwo other classes of monitoring system either:\n\n. Expect rate-aggregated data. Given the key insight that for most production purposes, we should be basing our decisions off of rates rather than absolute values, such systems benefit from having to do less math to satisfy queries.\n. Have relatively little or no math operations that would let us rate-aggregate data through our queries. For these systems, publishing pre-aggregated data is the only way to build meaningful representations.\n\nMicrometer efficiently maintains rate data by means of a step value that accumulates data for the current publishing interval. When the step value is polled (when publishing, for example), if the step value detects that the current interval has elapsed, it moves current data to "`previous`" state. This previous state is what is reported until the next time current data overwrites it. The following image shows the interaction of current and previous state, along with polling:\n\nimage::'+n(68)+'[Behavior of a step value,width=1200]\n\nThe value returned by the poll function is always _rate per second * interval_. If the step value shown in the preceding image represents the values of a counter, we could say that the counter saw "`0.3 increments per second`" in the first interval, which is reportable to the backend at any time during the second interval.\n\nMicrometer timers track at least a count and the total time as separate measurements. Suppose we configure publishing at 10-second intervals and we saw 20 requests that each took 100ms. Then, for the first interval:\n\n. `count` = 10 seconds * (20 requests / 10 seconds) = 20 requests\n. `totalTime` = 10 seconds * (20 * 100 ms / 10 seconds) = 2 seconds\n\nThe `count` statistic is meaningful by itself: It is a measure of _throughput_. `totalTime` represents the total latency of all requests in the interval. Additionally:\n\n`totalTime / count` = 2 seconds / 20 requests = 0.1 seconds / request = 100 ms / request\n\nThis is a useful measure of _average latency_. When the same idea is applied to the `totalAmount` and `count` emanating from distribution summaries, the measure is called a _distribution average_. Average latency is just the distribution average for a distribution summary measured in time (a timer). Some monitoring systems (such as Atlas) provide facilities for computing the distribution average from these statistics, and Micrometer includes `totalTime` and `count` as separate statistics. Others, (such as Datadog) do not have this kind of operation built-in, and Micrometer calculates the distribution average client-side and ships that.\n\nShipping the rate for the publishing interval is sufficient to reason about the rate over any time window greater than or equal to the publishing interval. In our example, if a service continues to receive 20 requests that each take 100ms for every 10 second interval in a given minute, we could say:\n\n. Micrometer reported "`20 requests`" for `count` on every 10 second interval. The monitoring system sums these six 10 second intervals and arrives at the conclusion that there are 120 requests / minute. Note that it is the monitoring system doing this summation, not Micrometer.\n. Micrometer reported "`2 seconds`" of `totalTime` on every 10 second interval. The monitoring system can sum all total time statistics over the minute to yield "`12 seconds`" of total time in the minute interval. Then, the average latency is as we expect: 12 seconds / 120 requests = 100 ms / request.\n\n:leveloffset!:\n\n== Counters\n\n:leveloffset: +1\n\nCounters report a single metric: a count. The `Counter` interface lets you increment by a fixed amount, which must be positive.\n\nTIP: Never count something you can time with a `Timer` or summarize with a `DistributionSummary`! Both `Timer` and `DistributionSummary` always publish a count of events in addition to other measurements.\n\nWhen building graphs and alerts off of counters, you should generally be most interested in measuring the rate at which some event occurs over a given time interval. Consider a simple queue. You could use counters to measure various things, such as the rate at which items are being inserted and removed.\n\nIt is tempting, at first, to conceive of visualizing absolute counts rather than a rate, but the absolute count is usually both a function of the rapidity with which something is used *and* the longevity of the application instance under instrumentation. Building dashboards and alerts of the rate of a counter per some interval of time disregards the longevity of the app, letting you see aberrant behavior long after the application has started.\n\nNOTE: Be sure to read through the timer section before jumping into using counters, as timers record a count of timed events as part of the suite of metrics that go into timing. For those pieces of code you intend to time, you do NOT need to add a separate counter.\n\nThe following code simulates a real counter whose rate exhibits some perturbation over a short time window:\n\n[source,java]\n----\nNormal rand = ...; // a random generator\n\nMeterRegistry registry = ...\nCounter counter = registry.counter("counter"); <1>\n\nFlux.interval(Duration.ofMillis(10))\n .doOnEach(d -> {\n if (rand.nextDouble() + 0.1 > 0) { <2>\n counter.increment(); <3>\n }\n })\n .blockLast();\n----\n<1> Most counters can be created off of the registry itself with a name and, optionally, a set of tags.\n<2> A slightly positively biased random walk.\n<3> This is how you interact with a counter. You could also call `counter.increment(n)` to increment by more than one in a single operation.\n\nA fluent builder for counters on the `Counter` interface itself provides access to less frequently used options, such as\nbase units and description. You can register the counter as the last step of its construction by calling `register`:\n\n[source, java]\n----\nCounter counter = Counter\n .builder("counter")\n .baseUnit("beans") // optional\n .description("a description of what this counter does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\n== Function-tracking Counters\n\nMicrometer also provides a more infrequently used counter pattern that tracks a monotonically increasing function (a function that stays the same or increases over time but never decreases). Some monitoring systems, such as Prometheus, push cumulative values for counters to the backend, but others publish the rate at which a counter is incrementing over the push interval. By employing this pattern, you let the Micrometer implementation for your monitoring system choose whether to rate-normalize the counter, and your counter remains portable across different types of monitoring systems.\n\n[source, java]\n-----\nCache cache = ...; // suppose we have a Guava cache with stats recording on\nregistry.more().counter("evictions", tags, cache, c -> c.stats().evictionCount()); <1>\n-----\n\n<1> `evictionCount()` is a monotonically increasing function that increments with every cache eviction from the beginning of its life.\n\nThe function-tracking counter, in concert with the monitoring system\'s rate normalizing functionality (whether this is an artifact of the query language or the way data is pushed to the system), adds a layer of richness on top of the cumulative value of the function itself. You can reason about the _rate_ at which the value is increasing, whether that rate is within an acceptable bound, is increasing or decreasing over time, and so on.\n\nWARNING: Micrometer cannot guarantee the monotonicity of the function for you. By using this signature, you are asserting its monotonicity based on what you know about its definition.\n\nA fluent builder for function counters on the `FunctionCounter` interface itself provides access to less frequently used options, such as base units and description. You can register the counter as the last step of its construction by calling `register(MeterRegistry)`.\n\n[source, java]\n----\nMyCounterState state = ...;\n\nFunctionCounter counter = FunctionCounter\n .builder("counter", state, state -> state.count())\n .baseUnit("beans") // optional\n .description("a description of what this counter does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\n:leveloffset!:\n\n== Gauges\n\n:leveloffset: +1\n\nA gauge is a handle to get the current value. Typical examples for gauges would be the size of a collection or map or number of threads in a running state.\n\nTIP: Gauges are useful for monitoring things with natural upper bounds. We do not recommend using a gauge to monitor things like request count, as they can grow without bound for the duration of an application instance\'s life.\n\nTIP: Never gauge something you can count with a `Counter`!\n\nMicrometer takes the stance that gauges should be sampled and not be set, so there is no information about what might have occurred between samples. Any intermediate values set on a gauge are lost by the time the gauge value is reported to a metrics backend, so there is little value in setting those intermediate values in the first place.\n\nThink of a `Gauge` as a "`heisen-gauge`": a meter that changes only when it is observed. Every other meter type accumulates intermediate counts toward the point where the data is sent to the metrics backend.\n\nThe `MeterRegistry` interface contains methods for building gauges to observe numeric values, functions, collections, and maps:\n\n[source, java]\n----\nList list = registry.gauge("listGauge", Collections.emptyList(), new ArrayList<>(), List::size); <1>\nList list2 = registry.gaugeCollectionSize("listSize2", Tags.empty(), new ArrayList<>()); <2>\nMap map = registry.gaugeMapSize("mapGauge", Tags.empty(), new HashMap<>());\n----\n<1> A slightly more common form of gauge is one that monitors some non-numeric object. The last argument establishes the function that is used to determine the value of the gauge when the gauge is observed.\n<2> A more convenient form of (1) for when you want to monitor collection size.\n\nAll of the different forms of creating a gauge maintain only a _weak reference_ to the object being observed, so as not to prevent garbage collection of the object.\n\n== Manually Incrementing or Decrementing a Gauge\n\nA gauge can be made to track any `java.lang.Number` subtype that is settable, such as `AtomicInteger` and `AtomicLong` found in `java.util.concurrent.atomic` and similar types, such as Guava\'s `AtomicDouble`:\n\n[source,java]\n----\n// maintain a reference to myGauge\nAtomicInteger myGauge = registry.gauge("numberGauge", new AtomicInteger(0));\n\n// ... elsewhere you can update the value it holds using the object reference\nmyGauge.set(27);\nmyGauge.set(11);\n----\n\nNote that, in this form, unlike other meter types, you do not get a reference to the `Gauge` when creating one. Rather, you get a reference to the thing being observed. This is because of the "`heisen-gauge`" principal: The gauge is self-sufficient once created, so you should never need to interact with it. This lets us give you back only the instrumented object, which allows for quick one-liners that both create the object to be observed and set up metrics around it.\n\nThis pattern should be less common than the `DoubleFunction` form. Remember that frequent setting of the observed `Number` results in a lot of intermediate values that never get published. Only the _instantaneous value_ of the gauge at publish time is ever sent to the monitoring system.\n\nWARNING: Attempting to construct a gauge with a primitive number or one of its `java.lang` object forms is always incorrect. These numbers are immutable. Thus, the gauge cannot ever be changed. Attempting to "`re-register`" the gauge with a different number does not work, as the registry maintains only one meter for each unique combination of name and tags.\n\n== Gauge Fluent Builder\n\nThe interface contains a fluent builder for gauges:\n\n[source, java]\n----\nGauge gauge = Gauge\n .builder("gauge", myObj, myObj::gaugeValue)\n .description("a description of what this gauge does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\nGenerally the returned `Gauge` instance is not useful except in testing, as the gauge is already set up to track a value automatically upon registration.\n\n== Why is My Gauge Reporting NaN or Disappearing?\n\nIt is your responsibility to hold a strong reference to the state object that you are measuring with a `Gauge`. Micrometer is careful to not create strong references to objects that would otherwise be garbage collected. Once the object being gauged is de-referenced and is garbage collected, Micrometer starts reporting a NaN or nothing for a gauge, depending on the registry implementation.\n\nIf you see your gauge reporting for a few minutes and then disappearing or reporting NaN, it almost certainly suggests that the underlying object being gauged has been garbage collected.\n\n== `TimeGauge`\n\n`TimeGauge` is a specialized gauge that tracks a time value, to be scaled to the base unit of time expected by each registry implementation.\n\n`TimeGauge` can be registered with `TimeUnit` as follows:\n\n[source, java]\n----\nAtomicInteger msTimeGauge = new AtomicInteger(4000);\nAtomicInteger usTimeGauge = new AtomicInteger(4000);\nTimeGauge.builder("my.gauge", msTimeGauge, TimeUnit.MILLISECONDS, AtomicInteger::get).register(registry);\nTimeGauge.builder("my.other.gauge", usTimeGauge, TimeUnit.MICROSECONDS, AtomicInteger::get).register(registry);\n----\n\nAnd for example, if the registry is Prometheus, they will be converted in seconds as follows:\n\n```\n# HELP my_gauge_seconds\n# TYPE my_gauge_seconds gauge\nmy_gauge_seconds 4.0\n# HELP my_other_gauge_seconds\n# TYPE my_other_gauge_seconds gauge\nmy_other_gauge_seconds 0.004\n```\n\n== Multi-gauge\n\nMicrometer supports one last special type of `Gauge`, called a `MultiGauge`, to help manage gauging a growing or shrinking list of criteria.\nThis feature lets you select a set of well-bounded but slightly changing set of criteria from something like an SQL query and report some metric for each row as a `Gauge`. The following example creates a `MultiGauge`:\n\n[source, java]\n----\n// SELECT count(*) from job group by status WHERE job = \'dirty\'\nMultiGauge statuses = MultiGauge.builder("statuses")\n .tag("job", "dirty")\n .description("The number of widgets in various statuses")\n .baseUnit("widgets")\n .register(registry);\n\n...\n\n// run this periodically whenever you re-run your query\nstatuses.register(\n resultSet.stream()\n .map(result -> Row.of(Tags.of("status", result.getAsString("status")), result.getAsInt("count")))\n .collect(toList())\n);\n----\n\n:leveloffset!:\n\n== Timers\n\n:leveloffset: +1\n\nTimers are intended for measuring short-duration latencies and the frequency of such events. All implementations of `Timer` report at least the total time and the count of events as separate time series. While you can use timers for other use cases, note that negative values are not supported, and recording many longer durations could cause overflow of the total time at `Long.MAX_VALUE` nanoseconds (292.3 years).\n\nAs an example, consider a graph showing request latency to a typical web server. The server can be expected to respond to many requests quickly, so the timer gets updated many times per second.\n\nThe appropriate base unit for timers varies by metrics backend, and for good reason. Micrometer is decidedly un-opinionated about this. However, because of the potential for confusion, Micrometer requires a `TimeUnit` when interacting with `Timer` implementations. Micrometer is aware of the preferences of each implementation and publishes your timing in the appropriate base unit based on the implementation. The following listing shows part of the `Timer` interface:\n\n[source,java]\n----\npublic interface Timer extends Meter {\n ...\n void record(long amount, TimeUnit unit);\n void record(Duration duration);\n double totalTime(TimeUnit unit);\n}\n----\n\nThe interface contains a fluent builder for timers:\n\n[source,java]\n----\nTimer timer = Timer\n .builder("my.timer")\n .description("a description of what this timer does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\nNOTE: The maximum statistical value for basic `Timer` implementations, such as `CumulativeTimer` and `StepTimer`, is a time window maximum (`TimeWindowMax`).\nIt means that its value is the maximum value during a time window.\nIf no new values are recorded for the time window length, the max is reset to 0 as a new time window starts.\nTime window size is the step size of the meter registry, unless expiry in `DistributionStatisticConfig` is explicitly set to other value.\nA time window maximum is used to capture maximum latency in a subsequent interval after heavy resource pressure triggers the latency and prevents metrics from being published.\nPercentiles are also time window percentiles (`TimeWindowPercentileHistogram`).\n\n== Recording Blocks of Code\n\nThe `Timer` interface exposes several convenience overloads for recording timings inline, including the following:\n\n[source,java]\n----\ntimer.record(() -> dontCareAboutReturnValue());\ntimer.recordCallable(() -> returnValue());\n\nRunnable r = timer.wrap(() -> dontCareAboutReturnValue()); <1>\nCallable c = timer.wrap(() -> returnValue());\n----\n<1> Wrap `Runnable` or `Callable` and return the instrumented version of it for use later.\n\nNOTE: A `Timer` is really a specialized distribution summary that is aware of how to scale durations to the base unit of time of each monitoring system and has an automatically\ndetermined base unit. In every case where you want to measure time, you should use a `Timer` rather than a `DistributionSummary`.\n\n== Storing Start State in `Timer.Sample`\n\nYou may also store start state in a sample instance that can be stopped later. The sample records a start time based on the registry\'s clock. After starting a sample, execute the code to be timed and finish the operation by calling `stop(Timer)` on the sample:\n\n[source, java]\n----\nTimer.Sample sample = Timer.start(registry);\n\n// do stuff\nResponse response = ...\n\nsample.stop(registry.timer("my.timer", "response", response.status()));\n----\n\nNote how we do not decide the timer to which to accumulate the sample until it is time to stop the sample. This lets us dynamically determine certain tags from the end state of the operation we are timing.\n\n== The `@Timed` annotation\n\nThe `micrometer-core` modules contains a `@Timed` annotation that frameworks can use to add timing support to either specific types of methods such as those serving web request endpoints or, more generally, to all methods.\n\nWARNING: Micrometer\'s Spring Boot configuration does _not_ recognize `@Timed` on arbitrary methods.\n\nAlso, an incubating AspectJ aspect is included in `micrometer-core`. You can use it in your application either through compile/load time AspectJ weaving or through framework facilities that interpret AspectJ aspects and proxy targeted methods in some other way, such as Spring AOP. Here is a sample Spring AOP configuration:\n\n[source,java]\n----\n@Configuration\npublic class TimedConfiguration {\n @Bean\n public TimedAspect timedAspect(MeterRegistry registry) {\n return new TimedAspect(registry);\n }\n}\n----\n\nApplying `TimedAspect` makes `@Timed` usable on any arbitrary method in an AspectJ proxied instance, as the following example shows:\n\n[source,java]\n----\n@Service\npublic class ExampleService {\n\n @Timed\n public void sync() {\n // @Timed will record the execution time of this method,\n // from the start and until it exits normally or exceptionally.\n ...\n }\n\n @Async\n @Timed\n public CompletableFuture async() {\n // @Timed will record the execution time of this method,\n // from the start and until the returned CompletableFuture\n // completes normally or exceptionally.\n return CompletableFuture.supplyAsync(...);\n }\n\n}\n----\n\n=== @MeterTag on Method Parameters\n\nTo support `@MeterTag` annotation on method parameters you need to configure the `@TimedAspect` to add the `MeterTagAnnotationHandler`.\n\n[source,java,subs=+attributes]\n-----\nValueResolver valueResolver = parameter -> "Value from myCustomTagValueResolver [" + parameter + "]";\n\n// Example of a ValueExpressionResolver that uses Spring Expression Language\nValueExpressionResolver valueExpressionResolver = new SpelValueExpressionResolver();\n\n\n// Setting the handler on the aspect\ntimedAspect.setMeterTagAnnotationHandler(\n new MeterTagAnnotationHandler(aClass -> valueResolver, aClass -> valueExpressionResolver));\n-----\n\nLet\'s assume that we have the following interface.\n\n[source,java,subs=+attributes]\n-----\ninterface MeterTagClassInterface {\n\n @Timed\n void getAnnotationForTagValueResolver(@MeterTag(key = "test", resolver = ValueResolver.class) String test);\n\n @Timed\n void getAnnotationForTagValueExpression(\n @MeterTag(key = "test", expression = "\'hello\' + \' characters\'") String test);\n\n @Timed\n void getAnnotationForArgumentToString(@MeterTag("test") Long param);\n\n}\n-----\n\nWhen its implementations would be called with different arguments (remember that the implementation needs to be annotated with `@Timed` annotation too) the following timers would be created:\n\n[source,java,subs=+attributes]\n-----\n// Example for returning on the parameter\nservice.getAnnotationForArgumentToString(15L);\n\nassertThat(registry.get("method.timed").tag("test", "15").timer().count()).isEqualTo(1);\n\n// Example for calling the provided on the parameter\nservice.getAnnotationForTagValueResolver("foo");\n\nassertThat(registry.get("method.timed")\n .tag("test", "Value from myCustomTagValueResolver [foo]")\n .timer()\n .count()).isEqualTo(1);\n\n// Example for calling the provided \nservice.getAnnotationForTagValueExpression("15L");\n\nassertThat(registry.get("method.timed").tag("test", "hello characters").timer().count()).isEqualTo(1);\n-----\n\n== Function-tracking Timers\n\nMicrometer also provides a more infrequently used timer pattern that tracks two monotonically increasing functions (a function that stays the same or increases over time but never decreases): a count function and a total time function. Some monitoring systems, such as Prometheus, push cumulative values for counters (which apply to both the count and total time functions in this case) to the backend, but others publish the rate at which a counter increments over the push interval. By employing this pattern, you let the Micrometer implementation for your monitoring system choose whether to rate normalize the timer, and your timer remains portable across different types of monitoring systems.\n\n[source, java]\n-----\nIMap cache = ...; // suppose we have a Hazelcast cache\nregistry.more().timer("cache.gets.latency", Tags.of("name", cache.getName()), cache,\n c -> c.getLocalMapStats().getGetOperationCount(), <1>\n c -> c.getLocalMapStats().getTotalGetLatency(),\n TimeUnit.NANOSECONDS <2>\n);\n-----\n\n<1> `getGetOperationCount()` is a monotonically increasing function incrementing with every cache get from the beginning of its life.\n<2> This represents the unit of time represented by `getTotalGetLatency()`. Each registry implementation specifies what its expected base unit of time is, and the total time reported will be scaled to this value.\n\nThe function-tracking timer, in concert with the monitoring system\'s rate normalizing functionality (whether this is an artifact of the query language or the way data is pushed to the system), adds a layer of richness to the cumulative value of the functions themselves. You can reason about the _rate_ of throughput and latency, whether that rate is within an acceptable bound, is increasing or decreasing over time, and so on.\n\nWARNING: Micrometer cannot guarantee the monotonicity of the count and total time functions for you. By using this signature, you are asserting their monotonicity based on what you know about their definitions.\n\nThere is also a fluent builder for function timers on the `FunctionTimer` interface itself, providing access to less frequently used options, such as base units and description. You can register the timer as the last step of its construction by calling `register(MeterRegistry)`:\n\n[source, java]\n----\nIMap cache = ...\n\nFunctionTimer.builder("cache.gets.latency", cache,\n c -> c.getLocalMapStats().getGetOperationCount(),\n c -> c.getLocalMapStats().getTotalGetLatency(),\n TimeUnit.NANOSECONDS)\n .tags("name", cache.getName())\n .description("Cache gets")\n .register(registry);\n----\n\n== Pause Detection\n\nMicrometer uses the `LatencyUtils` package to compensate for https://highscalability.com/blog/2015/10/5/your-load-generator-is-probably-lying-to-you-take-the-red-pi.html[coordinated omission] -- extra latency arising from system and VM pauses that skew your latency statistics downward. Distribution statistics, such as percentiles and SLO counts, are influenced by a pause detector implementation that adds additional latency here and there to compensate for pauses.\n\nMicrometer supports two pause detector implementations: a clock-drift based detector and a no-op detector. Before Micrometer 1.0.10/1.1.4/1.2.0, a clock-drift detector was configured by default to report as-accurate-as-possible metrics without further configuration. Since 1.0.10/1.1.4/1.2.0, the no-op detector is configured by default, but the clock-drift detector can be configured as shown in the next example.\n\nThe clock-drift based detector has a configurable sleep interval and pause threshold. CPU consumption is inversely proportional to `sleepInterval`, as is pause detection accuracy. 100ms for both values is a reasonable default to offer decent detection of long pause events while consuming a negligible amount of CPU time.\n\nYou can customize the pause detector as follows:\n\n[source,java]\n----\nregistry.config().pauseDetector(new ClockDriftPauseDetector(sleepInterval, pauseThreshold));\nregistry.config().pauseDetector(new NoPauseDetector());\n----\n\nIn the future, we may provide further detector implementations. Some pauses may be able to be inferred from GC logging in some circumstances, for example, without requiring a constant CPU load, however minimal. Also, a future JDK may provide direct access to pause events.\n\n== Memory Footprint Estimation\n\nTimers are the most memory-consuming meter, and their total footprint can vary dramatically, depending on which options you choose. The following table of memory consumption is based on the use of various features. These figures assume no tags and a ring buffer length of 3. Adding tags adds somewhat to the total, as does increasing the buffer length. Total storage can also vary somewhat depending on the registry implementation.\n\n* R = Ring buffer length. We assume the default of 3 in all examples. R is set with `Timer.Builder#distributionStatisticBufferLength`.\n* B = Total histogram buckets. Can be SLO boundaries or percentile histogram buckets. By default, timers are clamped to a minimum expected value of 1ms and a maximum expected value of 30 seconds, yielding 66 buckets for percentile histograms, when applicable.\n* I = Interval estimator for pause compensation. 1.7 kb.\n* M = Time-decaying max. 104 bytes.\n* Fb = Fixed boundary histogram. 8b * B * R.\n* Pp = Percentile precision. By default, it is 1. Generally in the range [0, 3]. Pp is set with `Timer.Builder#percentilePrecision`.\n* Hdr(Pp) = High dynamic range histogram.\n - When Pp = 0: 1.9kb * R + 0.8kb\n - When Pp = 1: 3.8kb * R + 1.1kb\n - When Pp = 2: 18.2kb * R + 4.7kb\n - When Pp = 3: 66kb * R + 33kb\n\n[width="80%",options="header"]\n|=========================================================\n|Pause detection |Client-side percentiles |Histogram and/or SLOs |Formula | Example\n\n|Yes |No |No |I + M| ~1.8kb\n|Yes |No |Yes |I + M + Fb|For default percentile histogram, ~7.7kb\n|Yes |Yes |Yes |I + M + Hdr(Pp)|For the addition of a 0.95 percentile with defaults otherwise, ~14.3kb\n|No |No |No |M| ~0.1kb\n|No |No |Yes |M + Fb|For default percentile histogram, ~6kb\n|No |Yes |Yes |M + Hdr(Pp)|For the addition of a 0.95 percentile with defaults otherwise, ~12.6kb\n|=========================================================\n\nNOTE: For Prometheus, specifically, R is _always_ equal to 1, regardless of how you attempt to configure it through `Timer.Builder`. This special case exists because Prometheus expects cumulative histogram data that never rolls over.\n\n:leveloffset!:\n\n== Distribution Summaries\n\n:leveloffset: +1\n\nA distribution summary tracks the distribution of events. It is similar to a timer structurally, but records values that do not represent a unit of time. For example, you could use a distribution summary to measure the payload sizes of requests hitting a server.\n\nThe following example creates a distribution summary:\n\n[source, java]\n----\nDistributionSummary summary = registry.summary("response.size");\n----\n\nThe interface contains a fluent builder for distribution summaries:\n\n[source, java]\n----\nDistributionSummary summary = DistributionSummary\n .builder("response.size")\n .description("a description of what this summary does") // optional\n .baseUnit("bytes") // optional <1>\n .tags("region", "test") // optional\n .scale(100) // optional <2>\n .register(registry);\n----\n\n<1> Add base units for maximum portability. Base units are part of the naming convention for some monitoring systems. Leaving it off and violating the naming convention has no adverse effect if you forget.\n<2> Optionally, you can provide a scaling factor by which each recorded sample is multiplied as it is recorded.\n\nNOTE: The maximum (which is named `max`) for basic `DistributionSummary` implementations, such as `CumulativeDistributionSummary` and `StepDistributionSummary`, is a time window maximum (`TimeWindowMax`).\nIt means that its value is the maximum value during a time window.\nIf no new values are recorded for the time window length, the maximum is reset to 0 as a new time window starts.\nTime window size is the step size of the meter registry, unless expiry in `DistributionStatisticConfig` is explicitly set to another value.\nA time window max is used to capture the maximum latency in a subsequent interval after heavy resource pressure triggers the latency and prevents metrics from being published.\nPercentiles are also time window percentiles (`TimeWindowPercentileHistogram`).\n\n== Scaling and Histograms\n\nMicrometer\'s preselected percentile histogram buckets are all integers from 1 to `Long.MAX_VALUE`. Currently, `minimumExpectedValue` and `maximumExpectedValue` serve to control the cardinality of the bucket set. If we try to detect that your min/max yields a small range and scale the preselected bucket domain to your summary\'s range, we do not have another lever to control bucket cardinality.\n\nInstead, if your summary\'s domain is more constrained, scale your summary\'s range by a fixed factor. The use case we have heard so far is for summaries of ratios whose domain is [0,1]. Given that scenario, we can use the following code to create values from 0 to 100:\n\n[source,java]\n----\nDistributionSummary.builder("my.ratio").scale(100).register(registry)\n----\n\nThis way, the ratio winds up in the range [0,100] and we can set `maximumExpectedValue` to 100. You can pair this with custom SLO boundaries if you care about particular ratios:\n\n[source,java]\n----\nDistributionSummary.builder("my.ratio")\n .scale(100)\n .serviceLevelObjectives(70, 80, 90)\n .register(registry)\n----\n\n\n== Memory Footprint Estimation\n\nThe total memory footprint of a distribution summary can vary dramatically, depending on which options you choose. The following table of memory consumption is based on the use of various features. These figures assume no tags and a ring buffer length of 3. Adding tags adds somewhat to the total, as does increasing the buffer length. Total storage can also vary somewhat depending on the registry implementation.\n\n* R = Ring buffer length. We assume the default of 3 in all examples. R is set with `DistributionSummary.Builder#distributionStatisticBufferLength`.\n* B = Total histogram buckets. It can be SLO boundaries or percentile histogram buckets. By default, summaries have NO minimum and maximum expected value, so we ship all 276 predetermined histogram buckets. You should always clamp distribution summaries with a `minimumExpectedValue` and `maximumExpectedValue` when you intend to ship percentile histograms.\n* M = Time-decaying max. 104 bytes.\n* Fb = Fixed boundary histogram. 8b * B * R.\n* Pp = Percentile precision. By default, it is 1. It is generally in the range of [0, 3]. Pp is set with `DistributionSummary.Builder#percentilePrecision`.\n* Hdr(Pp) = High dynamic range histogram.\n - When Pp = 0: 1.9kb * R + 0.8kb\n - When Pp = 1: 3.8kb * R + 1.1kb\n - When Pp = 2: 18.2kb * R + 4.7kb\n - When Pp = 3: 66kb * R + 33kb\n\n\n[width="80%",options="header"]\n|=========================================================\n|Client-side percentiles |Histogram and/or SLOs |Formula | Example\n\n|No |No |M| ~0.1kb\n|No |Yes |M + Fb|For percentile histogram clamped to 66 buckets, ~6kb\n|Yes |Yes |M + Hdr(Pp)|For the addition of a 0.95 percentile with defaults otherwise, ~12.6kb\n|=========================================================\n\nNOTE: For Prometheus, R is _always_ equal to 1, regardless of how you attempt to configure it through `DistributionSummary.Builder`. This special case exists for Prometheus because it expects cumulative histogram data that never rolls over.\n\n:leveloffset!:\n\n== Long Task Timers\n\n:leveloffset: +1\n\nThe long task timer is a special type of timer that lets you measure time while an event being measured is *still running*. A normal Timer only records the duration *after* the task is complete.\n\nLong task timers publish at least the following statistics:\n\n* Active task count\n* Total duration of active tasks\n* The maximum duration of active tasks\n\nUnlike a regular `Timer`, a long task timer does not publish statistics about completed tasks.\n\nConsider a background process to refresh metadata from a data store. For example, https://github.com/Netflix/edda[Edda] caches AWS resources, such as instances, volumes, auto-scaling groups, and others. Normally all data can be refreshed in a few minutes. If the AWS services have problems, it can take much longer. A long task timer can be used to track the active time for refreshing the metadata.\n\nFor example, in a Spring application, it is common for such long running processes to be implemented with `@Scheduled`. Micrometer provides a special `@Timed` annotation for instrumenting these processes with a long task timer:\n\n[source, java]\n----\n@Timed(value = "aws.scrape", longTask = true)\n@Scheduled(fixedDelay = 360000)\nvoid scrapeResources() {\n // find instances, volumes, auto-scaling groups, etc...\n}\n----\n\nIt is up to the application framework to make something happen with `@Timed`. If your framework of choice does not support it, you can still use the long task timer:\n\n[source, java]\n----\nLongTaskTimer scrapeTimer = registry.more().longTaskTimer("scrape");\nvoid scrapeResources() {\n scrapeTimer.record(() => {\n // find instances, volumes, auto-scaling groups, etc...\n });\n}\n----\n\nIf we wanted to alert when this process exceeds a threshold, with a long task timer, we receive that alert at the first reporting interval after we have exceeded the threshold. With a regular timer, we would not receive the alert until the first reporting interval after the process completed, over an hour later!\n\nThe interface contains a fluent builder for long task timers:\n\n[source, java]\n----\nLongTaskTimer longTaskTimer = LongTaskTimer\n .builder("long.task.timer")\n .description("a description of what this timer does") // optional\n .tags("region", "test") // optional\n .register(registry);\n----\n\n:leveloffset!:\n\n== Histograms and Percentiles\n\n:leveloffset: +1\n\nTimers and distribution summaries support collecting data to observe their percentile distributions. There are two main approaches to viewing percentiles:\n\n* *Percentile histograms*: Micrometer accumulates values to an underlying histogram and ships a predetermined set of buckets to the monitoring system. The monitoring system\'s query language is responsible for calculating percentiles off of this histogram. Currently, only Prometheus, Atlas, and Wavefront support histogram-based percentile approximations, through `histogram_quantile`, `:percentile`, and `hs()`, respectively. If you target Prometheus, Atlas, or Wavefront, prefer this approach, since you can aggregate the histograms across dimensions (by summing the values of the buckets across a set of dimensions) and derive an aggregable percentile from the histogram.\n* *Client-side percentiles*: Micrometer computes a percentile approximation for each meter ID (set of name and tags) and ships the percentile value to the monitoring system. This is not as flexible as a percentile histogram because it is not possible to aggregate percentile approximations across tags. Nevertheless, it provides some level of insight into percentile distributions for monitoring systems that do not support server-side percentile calculation based on a histogram.\n\nThe following example builds a timer with a histogram:\n\n[source,java]\n----\nTimer.builder("my.timer")\n .publishPercentiles(0.5, 0.95) // median and 95th percentile <1>\n .publishPercentileHistogram() // <2>\n .serviceLevelObjectives(Duration.ofMillis(100)) // <3>\n .minimumExpectedValue(Duration.ofMillis(1)) // <4>\n .maximumExpectedValue(Duration.ofSeconds(10))\n----\n\n<1> `publishPercentiles`: Used to publish percentile values computed in your application. These values are non-aggregable across dimensions.\n<2> `publishPercentileHistogram`: Used to publish a histogram suitable for computing aggregable (across dimensions) percentile approximations in Prometheus (by using `histogram_quantile`), Atlas (by using `:percentile`), and Wavefront (by using `hs()`). For Prometheus and Atlas, the buckets in the resulting histogram are preset by Micrometer based on a generator that has been determined empirically by Netflix to yield a reasonable error bound on most real world timers and distribution summaries. By default, the generator yields 276 buckets, but Micrometer includes only those that are within the range set by `minimumExpectedValue` and `maximumExpectedValue`, inclusive. Micrometer clamps timers by default to a range of 1 millisecond to 1 minute, yielding 73 histogram buckets per timer dimension. `publishPercentileHistogram` has no effect on systems that do not support aggregable percentile approximations. No histogram is shipped for these systems.\n<3> `serviceLevelObjectives`: Used to publish a cumulative histogram with buckets defined by your SLOs. When used in concert with `publishPercentileHistogram` on a monitoring system that supports aggregable percentiles, this setting adds additional buckets to the published histogram. When used on a system that does not support aggregable percentiles, this setting causes a histogram to be published with only these buckets.\n<4> `minimumExpectedValue`/`maximumExpectedValue`: Controls the number of buckets shipped by `publishPercentileHistogram` and controls the accuracy and memory footprint of the underlying HdrHistogram structure.\n\nSince shipping percentiles to the monitoring system generates additional time series, it is generally preferable to *not* configure them in core libraries that are included as dependencies in applications. Instead, applications can turn on this behavior for some set of timers and distribution summaries by using a meter filter.\n\nFor example, suppose we have a handful of timers in a common library. We have prefixed these timer names with `myservice`:\n\n[source,java]\n----\nregistry.timer("myservice.http.requests").record(..);\nregistry.timer("myservice.db.requests").record(..);\n----\n\nWe can turn on client-side percentiles for both timers by using a meter filter:\n\n[source,java]\n----\nregistry.config().meterFilter(\n new MeterFilter() {\n @Override\n public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {\n if(id.getName().startsWith("myservice")) {\n return DistributionStatisticConfig.builder()\n .percentiles(0.95)\n .build()\n .merge(config);\n }\n return config;\n }\n });\n----\n\n:leveloffset!:\n'},function(e,t,n){"use strict";n.r(t),t.default=n.p+"439c0eea1918ef143002cf62a9393e2a.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"594dce115710525d7771ebe3ab82db19.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"5cdb40b089820639454d90a5e4874aee.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer provides several binders for monitoring the JVM:\n\n[source, java]\n----\nnew ClassLoaderMetrics().bindTo(registry); <1>\nnew JvmMemoryMetrics().bindTo(registry); <2>\nnew JvmGcMetrics().bindTo(registry); <3>\nnew ProcessorMetrics().bindTo(registry); <4>\nnew JvmThreadMetrics().bindTo(registry); <5>\n----\n<1> Gauges loaded and unloaded classes.\n<2> Gauges buffer and memory pool utilization.\n<3> Gauges max and live data size, promotion and allocation rates, and times GC pauses (or concurrent phase time in the case of CMS).\n<4> Gauges current CPU total and load average.\n<5> Gauges thread peak, number of daemon threads, and live threads.\n\nMicrometer also provides a meter binder for `ExecutorService`. You can instrument your `ExecutorService` as follows:\n\n[source, java]\n----\nnew ExecutorServiceMetrics(executor, executorServiceName, tags).bindTo(registry);\n----\n\nMetrics created from the binder vary based on the type of `ExecutorService`.\n\nFor `ThreadPoolExecutor`, the following metrics are provided:\n\n* `executor.completed` (`FunctionCounter`): The approximate total number of tasks that have completed execution.\n* `executor.active` (`Gauge`): The approximate number of threads that are actively executing tasks.\n* `executor.queued` (`Gauge`): The approximate number of tasks that are queued for execution.\n* `executor.pool.size` (`Gauge`): The current number of threads in the pool.\n\nFor `ForkJoinPool`, the following metrics are provided:\n\n* `executor.steals` (`FunctionCounter`): Estimate of the total number of tasks stolen from one thread's work queue by\nanother. The reported value underestimates the actual total number of steals when the pool is not quiescent.\n* `executor.queued` (`Gauge`): An estimate of the total number of tasks currently held in queues by worker threads.\n* `executor.active` (`Gauge`): An estimate of the number of threads that are currently stealing or executing tasks.\n* `executor.running` (`Gauge`): An estimate of the number of worker threads that are not blocked but are waiting to join tasks or for other managed synchronization threads.\n"},function(e,t,n){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer supports binding metrics to a variety of different popular caching libraries. Each implementation supports basic features, such as cache hits versus misses, from which you can derive basic information about the cache hit ratio over a period of time. Micrometer uses a function tracking counter to monitor such things as hits and misses, giving you a notion not only of hits and misses over the total life of the cache (the basic metric exposed from Guava\'s `CacheStats`, for example) but hits and misses inside a given interval.\n\nTo demonstrate the features of cache monitoring, we start with a simple program that uses `reactor-netty` to read the entirety of Mary Shelley\'s _Frankenstein_ and put each word in the cache if it has not yet been seen:\n\n====\n[source,java]\n----\n// read all of Frankenstein\nHttpClient.create("www.gutenberg.org")\n .get("/cache/epub/84/pg84.txt")\n .flatMapMany(res -> res.addHandler(wordDecoder()).receive().asString())\n .delayElements(Duration.ofMillis(10)) // one word per 10 ms\n .filter(word -> !word.isEmpty())\n .doOnNext(word -> {\n if (cache.getIfPresent(word) == null)\n cache.put(word, 1);\n })\n .blockLast();\n----\n====\n\nThe following image shows the hits versus misses on a cache that has been tuned to hold a maximum of 10,000 entries:\n\n.Hits vs. misses, viewed in Prometheus\nimage::'+n(71)+"[Hits vs. misses,width=800]\n\n```\nrate(book_guava_requests_total[10s])\n```\n\nBy dividing the hits by the sum of all `get` operations (regardless of whether or not each one was a hit or a miss), we can arrive at a notion of the upper bound for the hit ratio for reading Frankenstein with only 10,000 words:\n\n.Hit ratio, viewed by Prometheus\nimage::"+n(72)+'[Hit ratio,width=800]\n\n```\nsum(rate(book_guava_requests_total{result="hit"}[1m])) / sum(rate(book_guava_requests_total[1m]))\n```\n\nIn a real-world scenario, we tune caches according to how we evaluate the tradeoff between storage and load efficiency. You could create an alert based on some upper bound for the rate at which misses occur or on a lower bound for the hit ratio. Setting an upper bound on miss ratio is better than a lower bound on hit ratio. For both ratios, an absence of any activity drops the value to 0.\nThe following image shows the miss ratio when it exceeds 10%:\n\n.Alerting when the miss ratio exceeds 10%\nimage::'+n(73)+"[Miss ratio (alerted),width=800]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"fd12bde9dc50409da244eb7ba80f6548.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"aa70057042488eef46660369434e99d4.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"3c191f6ce81fefe9b7d1980fc9d5b4fe.png"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer supports binding metrics to `OkHttpClient` through `EventListener`.\n\nYou can collect metrics from `OkHttpClient` by adding `OkHttpMetricsEventListener` as follows:\n\n[source,java]\n----\nOkHttpClient client = new OkHttpClient.Builder()\n .eventListener(OkHttpMetricsEventListener.builder(registry, "okhttp.requests")\n .tags(Tags.of("foo", "bar"))\n .build())\n .build();\n----\n\nNOTE: The `uri` tag is usually limited to URI patterns to mitigate tag cardinality explosion, but `OkHttpClient` does not\nprovide URI patterns. We provide `URI_PATTERN` header to support `uri` tag, or you can configure a URI mapper to provide\nyour own tag values for `uri` tag.\n\nTo configure a URI mapper, you can use `uriMapper()` as follows:\n\n[source,java]\n----\nOkHttpClient client = new OkHttpClient.Builder()\n .eventListener(OkHttpMetricsEventListener.builder(registry, "okhttp.requests")\n .uriMapper(req -> req.url().encodedPath())\n .tags(Tags.of("foo", "bar"))\n .build())\n .build();\n----\n\nWARNING: The sample might trigger tag cardinality explosion, as a URI path itself is being used for tag values.\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer supports binding metrics to `Jetty` through `Connection.Listener`.\n\nYou can collect metrics from `Jetty` by adding `JettyConnectionMetrics` as follows:\n\n[source,java]\n----\n Server server = new Server(0);\n Connector connector = new ServerConnector(server);\n connector.addBean(new JettyConnectionMetrics(registry, connector, Tags.of("foo", "bar"));\n server.setConnectors(new Connector[] { connector });\n----\n\nMicrometer also supports binding metrics to `Jersey` through `ApplicationEventListener`.\n\nYou can collect metrics from `Jersey` by adding `MetricsApplicationEventListener` as follows:\n\n[source,java]\n----\nResourceConfig resourceConfig = new ResourceConfig();\nresourceConfig.register(new MetricsApplicationEventListener(\n registry,\n new DefaultJerseyTagsProvider(),\n "http.server.requests",\n true));\nServletContainer servletContainer = new ServletContainer(resourceConfig);\n----\n'},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer supports binding metrics to `Netty`.\n\nYou can collect metrics from `ByteBuf` allocators and from `EventLoopGroup` instances.\nInstrumentation can be done once at startup, if resources are already known:\n\n[source,java,subs=+attributes]\n-----\n// Create or get an existing resources\nDefaultEventLoopGroup eventExecutors = new DefaultEventLoopGroup();\nUnpooledByteBufAllocator unpooledByteBufAllocator = new UnpooledByteBufAllocator(false);\n// Use binders to instrument them\nnew NettyEventExecutorMetrics(eventExecutors).bindTo(this.registry);\nnew NettyAllocatorMetrics(unpooledByteBufAllocator).bindTo(this.registry);\n-----\n\nNetty infrastructure can be configured in many ways, so you can also instrument lazily at runtime as resources are used.\nIf you do so, you must ensure that you will not bind metrics for the same resource multiple times as this can have runtime drawbacks:\n\n[source,java,subs=+attributes]\n-----\n@Override\nprotected void initChannel(SocketChannel channel) throws Exception {\n EventLoop eventLoop = channel.eventLoop();\n if (!isEventLoopInstrumented(eventLoop)) {\n new NettyEventExecutorMetrics(eventLoop).bindTo(this.meterRegistry);\n }\n ByteBufAllocator allocator = channel.alloc();\n if (!isAllocatorInstrumented(allocator) && allocator instanceof ByteBufAllocatorMetricProvider) {\n ByteBufAllocatorMetricProvider allocatorMetric = (ByteBufAllocatorMetricProvider) allocator;\n new NettyAllocatorMetrics(allocatorMetric).bindTo(this.meterRegistry);\n }\n}\n-----\n"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Passing through to Dropwizard\'s Console Reporter\n:toc:\n\nThis guide shows how to plug in less commonly used Dropwizard `Reporter` implementations -- in this case, the `ConsoleReporter`.\n\n[source,java]\n----\n @Bean\n public MetricRegistry dropwizardRegistry() {\n return new MetricRegistry();\n }\n\n @Bean\n public ConsoleReporter consoleReporter(MetricRegistry dropwizardRegistry) {\n ConsoleReporter reporter = ConsoleReporter.forRegistry(dropwizardRegistry)\n .convertRatesTo(TimeUnit.SECONDS)\n .convertDurationsTo(TimeUnit.MILLISECONDS)\n .build();\n reporter.start(1, TimeUnit.SECONDS);\n return reporter;\n }\n\n @Bean\n public MeterRegistry consoleLoggingRegistry(MetricRegistry dropwizardRegistry) {\n DropwizardConfig consoleConfig = new DropwizardConfig() {\n\n @Override\n public String prefix() {\n return "console";\n }\n\n @Override\n public String get(String key) {\n return null;\n }\n\n };\n\n return new DropwizardMeterRegistry(consoleConfig, dropwizardRegistry, HierarchicalNameMapper.DEFAULT, Clock.SYSTEM) {\n @Override\n protected Double nullGaugeValue() {\n return null;\n }\n };\n }\n----\n\nNote that, in a Spring environment, this registry is added to other implementations in a composite and is used for all metrics, both custom and\nauto-configured.\n\n[source,java]\n----\nclass MyComponent {\n private final MeterRegistry registry;\n\n public MyComponent(MeterRegistry registry) {\n this.registry = registry;\n }\n\n public void doSomeWork(String lowCardinalityInput) {\n registry.timer("my.latency", "input", lowCardinalityInput).record(() -> {\n // do work\n });\n }\n}\n----\n\nThe following listing shows typical output for this custom timer:\n\n[source,txt]\n----\n3/2/18 10:14:27 AM =============================================================\n\n-- Timers ----------------------------------------------------------------------\nmyLatency.lowCardinalityInput.INPUT\n count = 1\n mean rate = 1.02 calls/second\n 1-minute rate = 0.00 calls/second\n 5-minute rate = 0.00 calls/second\n 15-minute rate = 0.00 calls/second\n min = 100.00 milliseconds\n max = 100.00 milliseconds\n mean = 100.00 milliseconds\n stddev = 0.00 milliseconds\n median = 100.00 milliseconds\n 75% <= 100.00 milliseconds\n 95% <= 100.00 milliseconds\n 98% <= 100.00 milliseconds\n 99% <= 100.00 milliseconds\n 99.9% <= 100.00 milliseconds\n----\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= HttpSender with Resilience4j Retry\n\n`HttpSender` is an interface for HTTP clients that are used in meter registries for HTTP-based metrics publication. There\nare two implementations:\n\n* `HttpUrlConnectionSender`: `HttpURLConnection`-based `HttpSender`\n* `OkHttpSender`: OkHttp-based `HttpSender`\n\nThere is no out-of-the-box support for retry, but you can decorate it with Resilience4j retry, as follows:\n\n[source,java]\n----\n @Bean\n public DatadogMeterRegistry datadogMeterRegistry(DatadogConfig datadogConfig, Clock clock) {\n return DatadogMeterRegistry.builder(datadogConfig)\n .clock(clock)\n .httpClient(new RetryHttpClient())\n .build();\n }\n\n private static class RetryHttpClient extends HttpUrlConnectionSender {\n\n private final RetryConfig retryConfig = RetryConfig.custom()\n .maxAttempts(2)\n .waitDuration(Duration.ofSeconds(5))\n .build();\n\n private final Retry retry = Retry.of("datadog-metrics", this.retryConfig);\n\n @Override\n public Response send(Request request) {\n CheckedFunction0 retryableSupplier = Retry.decorateCheckedSupplier(\n this.retry,\n () -> super.send(request));\n return Try.of(retryableSupplier).get();\n }\n\n }\n----\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Custom Meter Registry\n\nMicrometer supports popular meter registries out of the box, so you should check those first.\nFor an existing meter registry, if you think that your requirements are generally useful, consider creating an issue or PR against the Micrometer issue tracker.\nFor a non-existent meter registry, if it is widely-used, consider creating an issue or PR for it.\n\nIf you need to create your own custom meter registry, you can create it by extending one of the base classes for meter registries: `MeterRegistry`, `PushMeterRegistry`, or `StepMeterRegistry`.\n\nThe most common way is to extend `StepMeterRegistry`.\nYou can create your own custom `StepMeterRegistry`.\n\nFirst, define your own meter registry configuration by extending `StepRegistryConfig`, as follows:\n\n[source,java]\n----\npublic interface CustomRegistryConfig extends StepRegistryConfig {\n\n CustomRegistryConfig DEFAULT = k -> null;\n\n @Override\n default String prefix() {\n return "custom";\n }\n\n}\n----\n\nSecond, define your own meter registry by extending `StepMeterRegistry`, as follows:\n\n[source,java]\n----\npublic class CustomMeterRegistry extends StepMeterRegistry {\n\n public CustomMeterRegistry(CustomRegistryConfig config, Clock clock) {\n super(config, clock);\n\n start(new NamedThreadFactory("custom-metrics-publisher"));\n }\n\n @Override\n protected void publish() {\n getMeters().stream().forEach(meter -> System.out.println("Publishing " + meter.getId()));\n }\n\n @Override\n protected TimeUnit getBaseTimeUnit() {\n return TimeUnit.MILLISECONDS;\n }\n\n}\n----\n\nFinally, create the meter registry configuration and the meter registry.\nIf you use Spring Boot, you can do so as follows:\n\n[source,java]\n----\n@Configuration\npublic class MetricsConfig {\n\n @Bean\n public CustomRegistryConfig customRegistryConfig() {\n return CustomRegistryConfig.DEFAULT;\n }\n\n @Bean\n public CustomMeterRegistry customMeterRegistry(CustomRegistryConfig customRegistryConfig, Clock clock) {\n return new CustomMeterRegistry(customRegistryConfig, clock);\n }\n\n}\n----\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\nMicrometer\'s open source support policy is defined as follows for different types of releases. Release versions follow a MAJOR.MINOR.PATCH convention, as defined in https://semver.org/[semantic versioning].\n\n* *Major release lines* (such as 1.x or 2.x) are supported with patch releases for a minimum of 3 years from the date the major release (such as `1.0.0` or `2.0.0`) was made available for download.\n* *Minor release lines* (such as 1.1.x or 1.2.x) are supported with patch releases for a minimum of 12 months from the date the minor release (such as `1.1.0` or `1.2.0`) was made available for download.\n* Any confirmed security vulnerabilities on supported release lines should result in a patch release to Maven Central.\n\nCommercial support that extends beyond the OSS support period described on this page is https://tanzu.vmware.com/spring-runtime[available from VMware].\n\nWe generally plan to release a new major or minor version every 6 months (in May and November).\n\n## Released versions\n\nThe following releases are actively maintained:\n\n.Supported minor releases\n[width="35%",options="header"]\n|===========\n| Minor release | OSS Support Until\n| 1.12.x | November 2024\n| 1.11.x | May 2024\n|===========\n\nThe following releases are out of OSS support:\n\n.Out of OSS support minor releases\n[width="35%",options="header"]\n|===========\n| Minor release | Final patch\n| 1.10.x | `1.10.13`\n| 1.9.x | `1.9.17`\n| 1.8.x | `1.8.13`\n| 1.7.x | `1.7.12`\n| 1.6.x | `1.6.13`\n| 1.5.x | `1.5.17`\n| 1.4.x | `1.4.2`\n| 1.3.x | `1.3.20`\n| 1.2.x | `1.2.2`\n| 1.1.x | `1.1.19`\n| 1.0.x | `1.0.11`\n|===========\n\n## Examples\n\nThe following examples demonstrate how the support policy applies:\n\n** Micrometer 1.0.0 was released in February 2018. At a minimum, support for the 1.x line extends through February 2021 (Major Releases statement). Practically, the Micrometer 1.x line is supported for at least as long as Spring Boot 2.x and major versions of other dependent web frameworks are supported.\n** Micrometer 1.1.0 was released in October 2018, minimally extending support through October 2019 (Minor Releases statement). Practically, the Micrometer 1.1.x line is supported for at least as long as the Spring Boot 2.1.x line is supported.\n** If Micrometer 1.2.x were to be released, support for the 1.x line would be extended another 12 months from the 1.2.x release date.\n** Patch releases, such as Micrometer 1.0.7, do not increase the support obligations for the 1.0.x release line.\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Observation\n:toc:\n:sectnums:\n:dimensional: true\n\n== Purpose\n\nMicrometer Observation is part of the https://github.com/micrometer-metrics/micrometer[Micrometer] project and contains the Observation API. The main idea behind it is that you\n\n> Instrument code once, and get multiple benefits out of it\n\n== Installing\n\nMicrometer comes with a Bill of Materials (BOM) which is a project that manages all the project versions for consistency.\n\nThe following example shows the required dependency for Micrometer Observation in Gradle:\n\n[source,groovy,subs=+attributes]\n----\nimplementation platform(\'io.micrometer:micrometer-bom:latest.release\')\nimplementation \'io.micrometer:micrometer-observation\'\n----\n\nThe following example shows the required dependency in Maven:\n\n[source,xml,subs=+attributes]\n----\n\n \n \n io.micrometer\n micrometer-bom\n ${micrometer.version}\n pom\n import\n \n \n\n\n\n \n io.micrometer\n micrometer-observation\n \n\n----\n\n== Introduction\n\n:leveloffset: +1\n\nFor any Observation to happen, you need to register `ObservationHandler` objects through an `ObservationRegistry`. An `ObservationHandler` reacts only to supported implementations of an `Observation.Context` and can create timers, spans, and logs by reacting to the lifecycle events of an Observation such as\n\n* `start` - Observation has been started. Happens when the `Observation#start()` method gets called.\n* `stop` - Observation has been stopped. Happens when the `Observation#stop()` method gets called.\n* `error` - An error occurred while observing. Happens when the `Observation#error(exception)` method gets called.\n* `event` - An event happened when observing. Happens when the `Observation#event(event)` method gets called.\n* `scope started` - Observation opens a Scope. The Scope must be closed when no longer used. Handlers can create thread local variables on start that are cleared upon closing of the scope. Happens when the `Observation#openScope()` method gets called.\n* `scope stopped` - Observation stops a Scope. Happens when the `Observation.Scope#close()` method gets called.\n\nWhenever any of these methods is called, an `ObservationHandler` method (such as `onStart(T extends Observation.Context ctx)`, `onStop(T extends Observation.Context ctx)`, and so on) is called. To pass state between the handler methods, you can use the `Observation.Context`.\n\nThis is how Observation state diagram looks like:\n\n[source]\n----\n Observation Observation\n Context Context\nCreated ----------\x3e Started ----------\x3e Stopped\n----\n\nThis is how Observation Scope state diagram looks like:\n\n[source]\n----\n Observation\n Context\nScope Started ----------\x3e Scope Finished\n----\n\nTo make it possible to debug production problems an Observation needs additional metadata such as key-value pairs (also known as tags). You can then query your metrics or distributed tracing backend by those tags to find the required data. Tags can be either of high or low cardinality.\n\nIMPORTANT: *High cardinality* means that a pair will have an unbounded number of possible values. An HTTP URL is a good\nexample of such a key value (e.g. `/foo/user1234`, `/foo/user2345` etc.). *Low cardinality* means that a key value will have a bounded number of possible values. A *templated* HTTP URL is a good example of such a key value (e.g. `/foo/{userId}`).\n\nTo separate Observation lifecycle operations from an Observation configuration (such as names and low and high cardinality tags), you can use the `ObservationConvention` that provides an easy way of overriding the default naming conventions.\n\nBelow you can find an example of using the Observation API.\n\n[source,java,subs=+attributes]\n-----\nstatic class Example {\n\n private final ObservationRegistry registry;\n\n Example(ObservationRegistry registry) {\n this.registry = registry;\n }\n\n void run() {\n Observation.createNotStarted("foo", registry)\n .lowCardinalityKeyValue("lowTag", "lowTagValue")\n .highCardinalityKeyValue("highTag", "highTagValue")\n .observe(() -> System.out.println("Hello"));\n }\n\n}\n-----\n\nTIP: Calling `observe(() -> ...)` leads to starting the Observation, putting it in scope, running the lambda, putting an error on the Observation if one took place, closing the scope and stopping the Observation.\n\n:leveloffset!:\n\n== ObservationHandler\n\n:leveloffset: +1\n\nA popular way to record them is storing the start state in a `Timer.Sample` instance and stopping it when the event has ended.\nRecording such measurements could look like this:\n\n[source,java,subs=+attributes]\n-----\nMeterRegistry registry = new SimpleMeterRegistry();\nTimer.Sample sample = Timer.start(registry);\ntry {\n // do some work here\n}\nfinally {\n sample.stop(Timer.builder("my.timer").register(registry));\n}\n-----\n\nIf you want to have more observation options (such as metrics and tracing out of the box plus anything else you will plug in) then you\'ll need to rewrite that code to use the `Observation` API.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nObservation.createNotStarted("my.operation", registry).observe(this::doSomeWorkHere);\n-----\n\nOne of the new features in Micrometer 1.10 is the ability to register "handlers" (`ObservationHandler`) that are notified about the lifecycle event of an observation (e.g.: you can run custom code when an observation is started/stopped).\nUsing this feature lets you add tracing capabilities to your existing metrics instrumentation (see: `DefaultTracingObservationHandler`). The implementation of these handlers does not need to be tracing related, it is completely up to you how you are going to implement them (e.g.: you can add logging capabilities) if you want.\n\n=== Observation.Context\n\nIn order to pass information between the instrumented code and the handler (or between handler methods, e.g.: `onStart` and `onStop`), you can utilize an `Observation.Context`. An `Observation.Context` is a `Map`-like container that can store values for you while your handler can access the data inside the context.\n\n=== ObservationHandler Example\n\nBased on this, we can implement a simple handler that lets the users know about its invocations by printing them out to `stdout`.\n\n[source,java,subs=+attributes]\n-----\nstatic class SimpleHandler implements ObservationHandler {\n\n @Override\n public void onStart(Observation.Context context) {\n System.out.println("START " + "data: " + context.get(String.class));\n }\n\n @Override\n public void onError(Observation.Context context) {\n System.out.println("ERROR " + "data: " + context.get(String.class) + ", error: " + context.getError());\n }\n\n @Override\n public void onEvent(Observation.Event event, Observation.Context context) {\n System.out.println("EVENT " + "event: " + event + " data: " + context.get(String.class));\n }\n\n @Override\n public void onStop(Observation.Context context) {\n System.out.println("STOP " + "data: " + context.get(String.class));\n }\n\n @Override\n public boolean supportsContext(Observation.Context handlerContext) {\n // you can decide if your handler should be invoked for this context object or\n // not\n return true;\n }\n\n}\n-----\n\nYou need to register the handler to the `ObservationRegistry`.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nregistry.observationConfig().observationHandler(new SimpleHandler());\n-----\n\nYou can use the `observe` method to instrument your codebase.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nObservation.Context context = new Observation.Context().put(String.class, "test");\n// using a context is optional, so you can call createNotStarted without it:\n// Observation.createNotStarted(name, registry)\nObservation.createNotStarted("my.operation", () -> context, registry).observe(this::doSomeWorkHere);\n-----\n\nYou can also take full control of the scoping mechanism.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nObservation.Context context = new Observation.Context().put(String.class, "test");\n// using a context is optional, so you can call start without it:\n// Observation.start(name, registry)\nObservation observation = Observation.start("my.operation", () -> context, registry);\ntry (Observation.Scope scope = observation.openScope()) {\n doSomeWorkHere();\n}\ncatch (Exception ex) {\n observation.error(ex); // and don\'t forget to handle exceptions\n throw ex;\n}\nfinally {\n observation.stop();\n}\n-----\n\n=== Signaling Errors and Arbitrary Events\n\nWhen **instrumenting** code we might want to signal that an error happened or signal that an arbitrary event happened. The observation API lets us do it via it `error` and `event` methods.\n\nOne use-case for signaling arbitrary event can be attaching annotations to `Span` for Distributed Tracing but you can also process them however you want in your own handler, e.g.: emit log events based on them.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nObservation observation = Observation.start("my.operation", registry);\ntry (Observation.Scope scope = observation.openScope()) {\n observation.event(Observation.Event.of("my.event", "look what happened"));\n doSomeWorkHere();\n}\ncatch (Exception exception) {\n observation.error(exception);\n throw exception;\n}\nfinally {\n observation.stop();\n}\n-----\n\n=== Observation.ObservationConvention Example\n\nWhen **instrumenting** code we want to provide sensible defaults for tags but also we want to allow users to easily change those defaults. An `ObservationConvention` interface is a description of what tags and name we should create for an `Observation.Context`. See the full usage example of an instrumentation together with overriding the default tags.\n\n[source,java,subs=+attributes]\n-----\n\n/**\n * A dedicated {@link Observation.Context} used for taxing.\n */\nclass TaxContext extends Observation.Context {\n\n private final String taxType;\n\n private final String userId;\n\n TaxContext(String taxType, String userId) {\n this.taxType = taxType;\n this.userId = userId;\n }\n\n String getTaxType() {\n return taxType;\n }\n\n String getUserId() {\n return userId;\n }\n\n}\n\n/**\n * An example of an {@link ObservationFilter} that will add the key-values to all\n * observations.\n */\nclass CloudObservationFilter implements ObservationFilter {\n\n @Override\n public Observation.Context map(Observation.Context context) {\n return context.addLowCardinalityKeyValue(KeyValue.of("cloud.zone", CloudUtils.getZone()))\n .addHighCardinalityKeyValue(KeyValue.of("cloud.instance.id", CloudUtils.getCloudInstanceId()));\n }\n\n}\n\n/**\n * An example of an {@link ObservationConvention} that renames the tax related\n * observations and adds cloud related tags to all contexts. When registered via the\n * `ObservationRegistry#observationConfig#observationConvention` will override the\n * default {@link TaxObservationConvention}. If the user provides a custom\n * implementation of the {@link TaxObservationConvention} and passes it to the\n * instrumentation, the custom implementation wins.\n *\n * In other words\n *\n * 1) Custom {@link ObservationConvention} has precedence 2) If no custom convention\n * was passed and there\'s a matching {@link GlobalObservationConvention} it will be\n * picked 3) If there\'s no custom, nor matching global convention, the default\n * {@link ObservationConvention} will be used\n *\n * If you need to add some key-values regardless of the used\n * {@link ObservationConvention} you should use an {@link ObservationFilter}.\n */\nclass GlobalTaxObservationConvention implements GlobalObservationConvention {\n\n // this will be applicable for all tax contexts - it will rename all the tax\n // contexts\n @Override\n public boolean supportsContext(Observation.Context context) {\n return context instanceof TaxContext;\n }\n\n @Override\n public String getName() {\n return "global.tax.calculate";\n }\n\n}\n\n// Interface for an ObservationConvention related to calculating Tax\ninterface TaxObservationConvention extends ObservationConvention {\n\n @Override\n default boolean supportsContext(Observation.Context context) {\n return context instanceof TaxContext;\n }\n\n}\n\n/**\n * Default convention of tags related to calculating tax. If no user one or global\n * convention will be provided then this one will be picked.\n */\nclass DefaultTaxObservationConvention implements TaxObservationConvention {\n\n @Override\n public KeyValues getLowCardinalityKeyValues(TaxContext context) {\n return KeyValues.of(TAX_TYPE.withValue(context.getTaxType()));\n }\n\n @Override\n public KeyValues getHighCardinalityKeyValues(TaxContext context) {\n return KeyValues.of(USER_ID.withValue(context.getUserId()));\n }\n\n @Override\n public String getName() {\n return "default.tax.name";\n }\n\n}\n\n/**\n * If micrometer-docs-generator is used, we will automatically generate documentation\n * for your observations. Check this URL\n * https://github.com/micrometer-metrics/micrometer-docs-generator#documentation for\n * setup example and read the {@link ObservationDocumentation} javadocs.\n */\nenum TaxObservationDocumentation implements ObservationDocumentation {\n\n CALCULATE {\n @Override\n public Class> getDefaultConvention() {\n return DefaultTaxObservationConvention.class;\n }\n\n @Override\n public String getContextualName() {\n return "calculate tax";\n }\n\n @Override\n public String getPrefix() {\n return "tax";\n }\n\n @Override\n public KeyName[] getLowCardinalityKeyNames() {\n return TaxLowCardinalityKeyNames.values();\n }\n\n @Override\n public KeyName[] getHighCardinalityKeyNames() {\n return TaxHighCardinalityKeyNames.values();\n }\n };\n\n enum TaxLowCardinalityKeyNames implements KeyName {\n\n TAX_TYPE {\n @Override\n public String asString() {\n return "tax.type";\n }\n }\n\n }\n\n enum TaxHighCardinalityKeyNames implements KeyName {\n\n USER_ID {\n @Override\n public String asString() {\n return "tax.user.id";\n }\n }\n\n }\n\n}\n\n/**\n * Our business logic that we want to observe.\n */\nclass TaxCalculator {\n\n private final ObservationRegistry observationRegistry;\n\n // If the user wants to override the default they can override this. Otherwise,\n // it will be {@code null}.\n @Nullable\n private final TaxObservationConvention observationConvention;\n\n TaxCalculator(ObservationRegistry observationRegistry,\n @Nullable TaxObservationConvention observationConvention) {\n this.observationRegistry = observationRegistry;\n this.observationConvention = observationConvention;\n }\n\n void calculateTax(String taxType, String userId) {\n // Create a new context\n TaxContext taxContext = new TaxContext(taxType, userId);\n // Create a new observation\n TaxObservationDocumentation.CALCULATE\n .observation(this.observationConvention, new DefaultTaxObservationConvention(), () -> taxContext,\n this.observationRegistry)\n // Run the actual logic you want to observe\n .observe(this::calculateInterest);\n }\n\n private void calculateInterest() {\n // do some work\n }\n\n}\n\n/**\n * Example of user changing the default conventions.\n */\nclass CustomTaxObservationConvention extends DefaultTaxObservationConvention {\n\n @Override\n public KeyValues getLowCardinalityKeyValues(TaxContext context) {\n return super.getLowCardinalityKeyValues(context)\n .and(KeyValue.of("additional.low.cardinality.tag", "value"));\n }\n\n @Override\n public KeyValues getHighCardinalityKeyValues(TaxContext context) {\n return KeyValues.of("this.would.override.the.default.high.cardinality.tags", "value");\n }\n\n @Override\n public String getName() {\n return "tax.calculate";\n }\n\n}\n\n/**\n * A utility class to set cloud related arguments.\n */\nstatic class CloudUtils {\n\n static String getZone() {\n return "...";\n }\n\n static String getCloudInstanceId() {\n return "...";\n }\n\n}\n-----\n\nBelow you can find an example of setting the whole code together.\n\n[source,java,subs=+attributes]\n-----\n// Registry setup\nObservationRegistry observationRegistry = ObservationRegistry.create();\n// add metrics\nSimpleMeterRegistry registry = new SimpleMeterRegistry();\nobservationRegistry.observationConfig().observationHandler(new DefaultMeterObservationHandler(registry));\nobservationRegistry.observationConfig().observationConvention(new GlobalTaxObservationConvention());\n// This will be applied to all observations\nobservationRegistry.observationConfig().observationFilter(new CloudObservationFilter());\n\n// In this case we\'re overriding the default convention by passing the custom one\nTaxCalculator taxCalculator = new TaxCalculator(observationRegistry, new CustomTaxObservationConvention());\n// run the logic you want to observe\ntaxCalculator.calculateTax("INCOME_TAX", "1234567890");\n-----\n\n=== Observation Predicates and Filters\n\nTo globally disable observations under given conditions you can use an `ObservationPredicate`. To mutate the `Observation.Context` you can use an `ObservationFilter`.\n\nTo set these just call `ObservationRegistry#observationConfig()#observationPredicate()` and `ObservationRegistry#observationConfig()#observationFilter()` methods respectively.\n\nBelow you can find an example of predicates and filters.\n\n[source,java,subs=+attributes]\n-----\n// Example using a metrics handler - we need a MeterRegistry\nMeterRegistry meterRegistry = new SimpleMeterRegistry();\n\n// Create an ObservationRegistry\nObservationRegistry registry = ObservationRegistry.create();\n// Add predicates and filter to the registry\nregistry.observationConfig()\n // ObservationPredicate can decide whether an observation should be\n // ignored or not\n .observationPredicate((observationName, context) -> {\n // Creates a noop observation if observation name is of given name\n if ("to.ignore".equals(observationName)) {\n // Will be ignored\n return false;\n }\n if (context instanceof MyContext) {\n // For the custom context will ignore a user with a given name\n return !"user to ignore".equals(((MyContext) context).getUsername());\n }\n // Will proceed for all other types of context\n return true;\n })\n // ObservationFilter can modify a context\n .observationFilter(context -> {\n // We\'re adding a low cardinality key to all contexts\n context.addLowCardinalityKeyValue(KeyValue.of("low.cardinality.key", "low cardinality value"));\n if (context instanceof MyContext) {\n // We\'re mutating a specific type of a context\n MyContext myContext = (MyContext) context;\n myContext.setUsername("some username");\n // We want to remove a high cardinality key value\n return myContext.removeHighCardinalityKeyValue("high.cardinality.key.to.ignore");\n }\n return context;\n })\n // Example of using metrics\n .observationHandler(new DefaultMeterObservationHandler(meterRegistry));\n\n// Observation will be ignored because of the name\nthen(Observation.start("to.ignore", () -> new MyContext("don\'t ignore"), registry)).isSameAs(Observation.NOOP);\n// Observation will be ignored because of the entries in MyContext\nthen(Observation.start("not.to.ignore", () -> new MyContext("user to ignore"), registry))\n .isSameAs(Observation.NOOP);\n\n// Observation will not be ignored...\nMyContext myContext = new MyContext("user not to ignore");\nmyContext.addHighCardinalityKeyValue(KeyValue.of("high.cardinality.key.to.ignore", "some value"));\nObservation.createNotStarted("not.to.ignore", () -> myContext, registry).observe(this::yourCodeToMeasure);\n// ...and will have the context mutated\nthen(myContext.getLowCardinalityKeyValue("low.cardinality.key").getValue()).isEqualTo("low cardinality value");\nthen(myContext.getUsername()).isEqualTo("some username");\nthen(myContext.getHighCardinalityKeyValues())\n .doesNotContain(KeyValue.of("high.cardinality.key.to.ignore", "some value"));\n-----\n\n=== Using Annotations With @Observed\n\nIf you have turned on Aspect Oriented Programming (e.g. via `org.aspectj:aspectjweaver`) you can use the `@Observed` annotation to create observations. You can put that annotation either on a method to observe it or a class to observe all methods in it. Let\'s look at the following example.\n\nHere you can see an `ObservedService` that has an annotation on a method.\n\n[source,java,subs=+attributes]\n-----\nstatic class ObservedService {\n\n @Observed(name = "test.call", contextualName = "test#call",\n lowCardinalityKeyValues = { "abc", "123", "test", "42" })\n void call() {\n System.out.println("call");\n }\n\n}\n-----\n\nThe following test asserts whether the proper observation gets created when a proxied `ObservedService` instance gets called.\n\n[source,java,subs=+attributes]\n-----\n// create a test registry\nTestObservationRegistry registry = TestObservationRegistry.create();\n// add a system out printing handler\nregistry.observationConfig().observationHandler(new ObservationTextPublisher());\n\n// create a proxy around the observed service\nAspectJProxyFactory pf = new AspectJProxyFactory(new ObservedService());\npf.addAspect(new ObservedAspect(registry));\n\n// make a call\nObservedService service = pf.getProxy();\nservice.call();\n\n// assert that observation has been properly created\nTestObservationRegistryAssert.assertThat(registry)\n .hasSingleObservationThat()\n .hasBeenStopped()\n .hasNameEqualTo("test.call")\n .hasContextualNameEqualTo("test#call")\n .hasLowCardinalityKeyValue("abc", "123")\n .hasLowCardinalityKeyValue("test", "42")\n .hasLowCardinalityKeyValue("class", ObservedService.class.getName())\n .hasLowCardinalityKeyValue("method", "call").doesNotHaveError();\n-----\n\n:leveloffset!:\n\n== Instrumenting\n\n:leveloffset: +1\n\nIn this section we will see some common examples of reusing existing Micrometer and Micrometer Tracing handlers and context types to do instrumentation.\n\nIMPORTANT: Before you decide to instrument a project yourself, please double-check whether it hasn\'t already been instrumented!\n\nTo better understand how you can do instrumentation we need to distinguish two concepts.\n\n- Context propagation\n- Creation of Observations\n\n*Context propagation* - we propagate existing context through threads or network. We\'re using the https://micrometer.io/docs/contextPropagation[Micrometer Context Propagation] library to define the context and to propagate it through threads. We\'re using dedicated `SenderContext` and `ReceiverContext` objects, together with Micrometer Tracing handlers, to create Observations that will propagate context over the wire.\n\n*Creation of Observations* - we want to wrap an operation in an Observation to get measurements. We need to know if there previously has been a parent Observation to maintain the parent-child relationship of Observations.\n\n[[instrumentation_of_thread_switching_components]]\n== Instrumentation of Thread Switching Components\n\nWe might want to create an Observation around a `Runnable` or `Callable` that we\'re submitting through an `Executor`. For that to work we need to know if in the parent thread there was an Observation that the new thread should continue, or for which a child Observation should be created.\n\nLet\'s look at the following example.\n\n[source,java,subs=+attributes]\n-----\n// Example of an Executor Service\nExecutorService executor = Executors.newCachedThreadPool();\n\n\n// This snippet shows an example of how to wrap in an observation code that would\n// be executed in a separate thread\n\n// Let\'s assume that we have a parent observation\nObservation parent = Observation.createNotStarted("parent", registry);\n// Observation is put in scope via the method\nFuture child = parent.observe(() -> {\n // [Thread 1] Current Observation is the same as \n then(registry.getCurrentObservation()).isSameAs(parent);\n // [Thread 1] We\'re wrapping the executor in a Context Propagating version.\n // comes from Context Propagation library\n return ContextExecutorService.wrap(executor).submit(() -> {\n // [Thread 2] Current Observation is same as - context got\n // propagated\n then(registry.getCurrentObservation()).isSameAs(parent);\n // Wraps the code that should be run in a separate thread in an\n // observation\n return Observation.createNotStarted("child", registry).observe(this::yourCodeToMeasure);\n });\n});\n-----\n\n[[instrumentation_of_reactive_libraries]]\n== Instrumentation of Reactive Libraries\n\nIn this section we\'ll discuss how to wrap Reactive libraries in Observations and how to use Reactor Context to safely propagate Observations between threads.\n\n[[instrumentation_of_reactive_libraries_after_reactor_3_5_3]]\n=== For Reactor 3.5.3 and After\n\nIn Reactor 3.5.3 release (through this https://github.com/reactor/reactor-core/pull/3335[PR]) an option to turn on automated context propagation was added. To use this, please ensure that you\'re using the following projects at minimum in the following versions:\n\n- Reactor https://github.com/reactor/reactor-core/releases/tag/v3.5.7[3.5.7]\n- Micrometer Context-Propagation https://github.com/micrometer-metrics/context-propagation/releases/tag/v1.0.3[1.0.3]\n- Micrometer https://github.com/micrometer-metrics/micrometer/releases/tag/v1.10.8[1.10.8]\n- Micrometer Tracing https://github.com/micrometer-metrics/tracing/releases/tag/v1.0.7[1.0.7]\n\nTo use the feature call the new Reactor\'s Hook method (e.g. in your `public static void main` method) like this\n\n[source,java,subs=+attributes]\n-----\nHooks.enableAutomaticContextPropagation();\n-----\n\nThis will automatically wrap Reactor internal mechanisms to propagate context between operators, threads etc. Usage of `tap` and `handle` or Context Propagation API is not required.\n\nLet\'s look at the following example.\n\n[source,java,subs=+attributes]\n-----\n// This snippet shows an example of how to use the new Hook API with Reactor\nHooks.enableAutomaticContextPropagation();\n// Starting from Micrometer 1.10.8 you need to set your registry on this singleton\n// instance of OTLA\nObservationThreadLocalAccessor.getInstance().setObservationRegistry(registry);\n\n// Let\'s assume that we have a parent observation\nObservation parent = Observation.start("parent", registry);\n// Now we put it in thread local\nparent.scoped(() -> {\n\n // Example of propagating whatever there was in thread local\n Integer block = Mono.just(1).publishOn(Schedulers.boundedElastic()).doOnNext(integer -> {\n log.info("Context Propagation happens - the observation gets propagated ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isSameAs(parent);\n })\n .flatMap(integer -> Mono.just(integer).map(monoInteger -> monoInteger + 1))\n .transformDeferredContextual((integerMono, contextView) -> integerMono.doOnNext(integer -> {\n log.info("Context Propagation happens - the observation gets propagated ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isSameAs(parent);\n }))\n // Let\'s assume that we\'re modifying the context\n .contextWrite(context -> context.put("foo", "bar"))\n // Since we are NOT part of the Reactive Chain (e.g. this is not a\n // WebFlux application)\n // you MUST call to capture all ThreadLocal values\n // and store them in a Reactor Context.\n // ----------------------\n // If you were part of the\n // Reactive Chain (e.g. returning Mono from endpoint)\n // there is NO NEED to call . If you need to propagate\n // your e.g. Observation\n // to the Publisher you just created (e.g. Mono or Flux) please\n // consider adding it\n // to the Reactor Context directly instead of opening an Observation\n // scope and calling (see example below).\n .contextCapture()\n .block();\n\n // We\'re still using as current observation\n then(registry.getCurrentObservation()).isSameAs(parent);\n\n then(block).isEqualTo(2);\n\n // Now, we want to create a child observation for a Reactor stream and put it\n // to Reactor Context\n // Automatically its parent will be observation since is in\n // Thread Local\n Observation child = Observation.start("child", registry);\n block = Mono.just(1).publishOn(Schedulers.boundedElastic()).doOnNext(integer -> {\n log.info(\n "Context Propagation happens - the observation from Reactor Context takes precedence over thread local observation ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isSameAs(child);\n })\n .flatMap(integer -> Mono.just(integer).map(monoInteger -> monoInteger + 1))\n .transformDeferredContextual((integerMono, contextView) -> integerMono.doOnNext(integer -> {\n log.info(\n "Context Propagation happens - the observation from Reactor Context takes precedence over thread local observation ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isSameAs(child);\n }))\n // Remember to stop the child Observation!\n .doFinally(signalType -> child.stop())\n // When using Reactor we ALWAYS search for\n // ObservationThreadLocalAccessor.KEY entry in the Reactor Context to\n // search for an Observation. You DON\'T have to use \n // because\n // you have manually provided the ThreadLocalAccessor key\n .contextWrite(context -> context.put(ObservationThreadLocalAccessor.KEY, child))\n .block();\n\n // We\'re back to having as current observation\n then(registry.getCurrentObservation()).isSameAs(parent);\n\n then(block).isEqualTo(2);\n});\n\n// There should be no remaining observation\nthen(registry.getCurrentObservation()).isNull();\n\n// We need to stop the parent\nparent.stop();\n-----\n\nIf performance of this approach is not satisfactory, please verify whether disabling the hook and explicitly using `handle` or `tap` operators improves the performance.\n\n[[instrumentation_of_reactive_libraries_before_reactor_3_5_3]]\n=== Before Reactor 3.5.3\n\nThe preferred way of propagating elements through the Flux using Reactor is not via ``ThreadLocal``s but through Reactor Context. Reactor however gives you two operators, `tap()` and `handle()` where, if https://micrometer.io/docs/contextPropagation[Micrometer Context Propagation] library is on the classpath, it will set thread local values for you.\n\nLet\'s look at the following example.\n\n[source,java,subs=+attributes]\n-----\n// This snippet shows an example of how to wrap code that is using Reactor\n\n// Let\'s assume that we have a parent observation\nObservation parent = Observation.start("parent", registry);\n\n// We want to create a child observation for a Reactor stream\nObservation child = Observation.start("child", registry)\n // There\'s no thread local entry, so we will pass parent observation\n // manually. If we put the Observation in scope we could then call\n // <.contextCapture()> method from Reactor to capture all thread locals\n // and store them in Reactor Context.\n .parentObservation(parent);\nInteger block = Mono.just(1)\n // Example of not propagating context by default\n .doOnNext(integer -> {\n log.info(\n "No context propagation happens by default in Reactor - there will be no Observation in thread local here ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isNull();\n })\n // Example of having entries in thread local for operator\n .tap(() -> new DefaultSignalListener() {\n @Override\n public void doFirst() throws Throwable {\n log.info("We\'re using tap() -> there will be Observation in thread local here ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isNotNull();\n }\n })\n .flatMap(integer -> Mono.just(integer).map(monoInteger -> monoInteger + 1))\n // Example of retrieving ThreadLocal entries via ReactorContext\n .transformDeferredContextual((integerMono, contextView) -> integerMono.doOnNext(integer -> {\n try (ContextSnapshot.Scope scope = ContextSnapshot.setAllThreadLocalsFrom(contextView)) {\n log.info(\n "We\'re retrieving thread locals from Reactor Context - there will be Observation in thread local here ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isNotNull();\n }\n }))\n // Example of having entries in thread local for operator\n .handle((BiConsumer>) (integer, synchronousSink) -> {\n log.info("We\'re using handle() -> There will be Observation in thread local here ["\n + registry.getCurrentObservation() + "]");\n then(registry.getCurrentObservation()).isNotNull();\n synchronousSink.next(integer);\n })\n // Remember to stop the child Observation!\n .doFinally(signalType -> child.stop())\n // When using Reactor we ALWAYS search for\n // ObservationThreadLocalAccessor.KEY entry in the Reactor Context to\n // search for an Observation\n .contextWrite(context -> context.put(ObservationThreadLocalAccessor.KEY, child))\n // If there were ThreadLocal entries that are using Micrometer Context\n // Propagation they would be caught here. All implementations of\n // will store their thread local entries under their\n // keys in Reactor Context\n .contextCapture()\n .block();\n\n// We didn\'t have any observations in thread local\nthen(registry.getCurrentObservation()).isNull();\n\n// We need to stop the parent\nparent.stop();\n\nthen(block).isEqualTo(2);\n-----\n\n[[instrumentation_of_http_communication]]\n== Instrumentation of HTTP Communication\n\nIn order to instrument an HTTP-based communication we need to use the `RequestReplySenderContext` and `RequestReplyReceiverContext` for the client and server side respectively.\n\nAs an example for the client side we will use a handler that instruments the HTTP request by adding a `foo:bar` header (if you have Micrometer Tracing on the classpath you could reuse the `PropagatingSenderTracingObservationHandler` and `PropagatingReceiverTracingObservationHandler` to propagate tracing context over the wire). Let\'s look at the example of such a handler.\n\n[source,java,subs=+attributes]\n-----\nstatic class HeaderPropagatingHandler implements ObservationHandler> {\n\n @Override\n public void onStart(SenderContext context) {\n context.getSetter().set(context.getCarrier(), "foo", "bar");\n }\n\n @Override\n public boolean supportsContext(Observation.Context context) {\n return context instanceof SenderContext;\n }\n\n}\n-----\n\nLet\'s look at the following of HTTP client side instrumentation that reuses the handler.\n\n[source,java,subs=+attributes]\n-----\n// This example can be combined with the idea of ObservationConvention to allow\n// users to easily customize the key values. Please read the rest of the\n// documentation on how to do it.\n\n// In Micrometer Tracing we would have predefined\n// PropagatingSenderTracingObservationHandler but for the sake of this demo we\n// create our own handler that puts "foo":"bar" headers into the request\nregistry.observationConfig().observationHandler(new HeaderPropagatingHandler());\n\n// We\'re using WireMock to stub the HTTP GET call to "/foo" with a response "OK"\nstubFor(get("/foo").willReturn(ok().withBody("OK")));\n\n// RequestReplySenderContext is a special type of context used for request-reply\n// communication. It requires to define what the Request type is and how we can\n// instrument it. It also needs to know what the Response type is\nRequestReplySenderContext context = new RequestReplySenderContext<>(\n (carrier, key, value) -> Objects.requireNonNull(carrier).addHeader(key, value));\n\n// We\'re instrumenting the Apache HTTPClient\ntry (CloseableHttpClient httpclient = HttpClients.createDefault()) {\n // The HttpGet is our carrier (we can mutate it to instrument the headers)\n HttpGet httpget = new HttpGet(info.getHttpBaseUrl() + "/foo");\n // We must set the carrier BEFORE we run \n context.setCarrier(httpget);\n // You can set the remote service address to provide more debugging\n // information\n context.setRemoteServiceAddress(info.getHttpBaseUrl());\n // Examples of setting key values from the request\n Observation observation = Observation.createNotStarted("http.client.requests", () -> context, registry)\n .contextualName("HTTP " + httpget.getMethod())\n .lowCardinalityKeyValue("http.url", info.getHttpBaseUrl() + "/{name}")\n .highCardinalityKeyValue("http.full-url", httpget.getRequestUri());\n observation.observeChecked(() -> {\n String response = httpclient.execute(httpget, classicHttpResponse -> {\n // We should set the response before we stop the observation\n context.setResponse(classicHttpResponse);\n // Example of setting key values from the response\n observation.highCardinalityKeyValue("http.content.length",\n String.valueOf(classicHttpResponse.getEntity().getContentLength()));\n return EntityUtils.toString(classicHttpResponse.getEntity());\n });\n\n then(response).isEqualTo("OK");\n });\n}\n\n// We want to be sure that we have successfully enriched the HTTP headers\nverify(getRequestedFor(urlEqualTo("/foo")).withHeader("foo", equalTo("bar")));\n-----\n\nAs an example for the server side we will use a handler that instruments the Observation by adding the `foo` low cardinality key with the value being the matched path from the HTTP request. Let\'s look at the example of such a handler.\n\n[source,java,subs=+attributes]\n-----\nstatic class HeaderReadingHandler implements ObservationHandler> {\n\n @Override\n public void onStart(ReceiverContext context) {\n String fooHeader = context.getGetter().get(context.getCarrier(), "foo");\n // We\'re setting the value of the header as a low cardinality key value\n context.addLowCardinalityKeyValue(KeyValue.of("foo", fooHeader));\n }\n\n @Override\n public boolean supportsContext(Observation.Context context) {\n return context instanceof ReceiverContext;\n }\n\n}\n-----\n\nLet\'s look at the following of HTTP server side instrumentation that reuses the handler.\n\n[source,java,subs=+attributes]\n-----\n// This example can be combined with the idea of ObservationConvention to allow\n// users to easily customize the key values. Please read the rest of the\n// documentation on how to do it.\n\n// In Micrometer Tracing we would have predefined\n// PropagatingReceiverTracingObservationHandler but for the sake of this demo we\n// create our own handler that will reuse the header from the request as a\n// low cardinality key value\nregistry.observationConfig().observationHandler(new HeaderReadingHandler());\n\ntry (Javalin javalin = Javalin.create().before("/hello/{name}", ctx -> {\n // We\'re creating the special RequestReplyReceiverContext that will reuse the\n // information from the HTTP headers\n RequestReplyReceiverContext receiverContext = new RequestReplyReceiverContext<>(\n Context::header);\n // Remember to set the carrier!!!\n receiverContext.setCarrier(ctx);\n String remoteServiceAddress = ctx.scheme() + "://" + ctx.host();\n receiverContext.setRemoteServiceAddress(remoteServiceAddress);\n // We\'re starting an Observation with the context\n Observation observation = Observation\n .createNotStarted("http.server.requests", () -> receiverContext, registry)\n .contextualName("HTTP " + ctx.method() + " " + ctx.matchedPath())\n .lowCardinalityKeyValue("http.url", remoteServiceAddress + ctx.matchedPath())\n .highCardinalityKeyValue("http.full-url", remoteServiceAddress + ctx.path())\n .lowCardinalityKeyValue("http.method", ctx.method())\n .start();\n // Let\'s be consistent and always set the Observation related objects under\n // the same key\n ctx.attribute(ObservationThreadLocalAccessor.KEY, observation);\n}).get("/hello/{name}", ctx -> {\n // We need to be thread-safe - we\'re not using ThreadLocals, we\'re retrieving\n // information from the attributes\n Observation observation = ctx.attribute(ObservationThreadLocalAccessor.KEY);\n observation.scoped(() -> {\n // If we need thread locals (e.g. MDC entries) we can use \n log.info("We\'re using scoped - Observation in thread local here [" + registry.getCurrentObservation()\n + "]");\n then(registry.getCurrentObservation()).isNotNull();\n });\n // We\'re returning body\n ctx.result("Hello World [" + observation.getContext().getLowCardinalityKeyValue("foo").getValue() + "]");\n}).after("/hello/{name}", ctx -> {\n // After sending the response we want to stop the Observation\n Observation observation = ctx.attribute(ObservationThreadLocalAccessor.KEY);\n observation.stop();\n}).start(0)) {\n // We\'re sending an HTTP request with a header. We\'re expecting that\n // it will be reused in the response\n String response = sendRequestToHelloEndpointWithHeader(javalin.port(), "foo", "bar");\n\n // The response must contain the value from the header\n then(response).isEqualTo("Hello World [bar]");\n}\n-----\n\n== Instrumentation of Messaging Communication\n\nTo instrument messaging components you should proceed in the same way as you would with <>, however instead of `RequestReplySenderContext` and `RequestReplyReceiverContext` you would use `SenderContext` and `ReceiverContext`. You can also set the `remoteServiceName` on a context to suggest the name of the broker (e.g. `kafka` or `rabbitmq`).\n\n:leveloffset!:\n\n== Testing\n\n:leveloffset: +1\n\nMicrometer Observation comes with `micrometer-observation-test` module that allows you to unit-test your Observations.\n\n== Installing\n\nThe following example shows the required dependency in Gradle (assuming that Micrometer BOM has been added):\n\n[source,groovy,subs=+attributes]\n-----\ntestImplementation \'io.micrometer:micrometer-observation-test\'\n-----\n\nThe following example shows the required dependency in Maven (assuming that Micrometer BOM has been added):\n\n[source,xml,subs=+attributes]\n-----\n\n io.micrometer\n micrometer-observation-test\n test\n\n-----\n\n== Running Observation Unit Tests\n\nLet\'s say that you have the following production code. It will create an observation with 2 tags (low and high cardinality) and then call `observe` that will start the observation, put it in scope, close the scope and stop the observation.\n\n[source,java,subs=+attributes]\n-----\nstatic class Example {\n\n private final ObservationRegistry registry;\n\n Example(ObservationRegistry registry) {\n this.registry = registry;\n }\n\n void run() {\n Observation.createNotStarted("foo", registry)\n .lowCardinalityKeyValue("lowTag", "lowTagValue")\n .highCardinalityKeyValue("highTag", "highTagValue")\n .observe(() -> System.out.println("Hello"));\n }\n\n}\n-----\n\nTo unit-test this code you can use the `TestObservationRegistry` class.\n\n[source,java,subs=+attributes]\n-----\n@Test\nvoid should_assert_your_observation() {\n // create a test registry in your tests\n TestObservationRegistry registry = TestObservationRegistry.create();\n\n // run your production code with the TestObservationRegistry\n new Example(registry).run();\n\n // check your observation\n TestObservationRegistryAssert.assertThat(registry)\n .doesNotHaveAnyRemainingCurrentObservation()\n .hasObservationWithNameEqualTo("foo")\n .that()\n .hasHighCardinalityKeyValue("highTag", "highTagValue")\n .hasLowCardinalityKeyValue("lowTag", "lowTagValue")\n .hasBeenStarted()\n .hasBeenStopped();\n}\n-----\n\n:leveloffset!:\n\n== Documentation Generation\n\n:leveloffset: +1\n\n== Automated Documentation Generation\n\nBy using the https://github.com/micrometer-metrics/micrometer-docs-generator[Micrometer Docs Generator] project and by implementing the `ObservationDocumentation`, `SpanDocumentation` or `MeterDocumentation` interfaces as an `enum` we can scan your sources and generate Asciidoctor documentation. This allows you to maintain the docuemntation for your observability instrumentation in code, and as long as you use the `enum` implementation in your instrumentation, it will ensure that your documentation stays in-sync with the instrumentation.\n\nBelow you can find an example of a Maven `pom.xml` with the Micrometer Docs Generator project.\n\n.pom.xml\n[source,xml,subs=+attributes]\n-----\n\n\n\t4.0.0\n\tcom.example\n\tmicrometer-docs-generator-example\n\tjar\n\tmicrometer-docs-generator-example\n\tmicrometer-docs-generator-example\n\t\n\t\t1.0.0\n\t\t${maven.multiModuleProjectDirectory}/folder-with-sources-to-scan/\n\t\t.*\n\t\t${maven.multiModuleProjectDirectory}/target/output-folder-with-adocs/\'\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\torg.codehaus.mojo\n\t\t\t\texec-maven-plugin\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tgenerate-docs\n\t\t\t\t\t\tprepare-package\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tjava\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tio.micrometer.docs.DocsGeneratorCommand\n\t\t\t\t\t\t\ttrue\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t${micrometer-docs-generator.inputPath}\n\t\t\t\t\t\t\t\t${micrometer-docs-generator.inclusionPattern}\n\t\t\t\t\t\t\t\t${micrometer-docs-generator.outputPath}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tio.micrometer\n\t\t\t\t\t\tmicrometer-docs-generator\n\t\t\t\t\t\t${micrometer-docs-generator.version}\n\t\t\t\t\t\tjar\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\n\t\n\t\t\n\t\t\tspring-snapshots\n\t\t\tSpring Snapshots\n\t\t\thttps://repo.spring.io/snapshot \x3c!-- For Snapshots --\x3e\n\t\t\t\n\t\t\t\ttrue\n\t\t\t\n\t\t\t\n\t\t\t\tfalse\n\t\t\t\n\t\t\n\t\t\n\t\t\tspring-milestones\n\t\t\tSpring Milestones\n\t\t\thttps://repo.spring.io/milestone \x3c!-- For Milestones --\x3e\n\t\t\t\n\t\t\t\tfalse\n\t\t\t\n\t\t\n\t\n\n-----\n\nBelow you can find an example of a Gradle `build.gradle` with the Micrometer Docs Generator project.\n\n.build.gradle\n[source,groovy,subs=+attributes]\n-----\nrepositories {\n\tmaven { url \'https://repo.spring.io/snapshot\' } // for snapshots\n\tmaven { url \'https://repo.spring.io/milestone\' } // for milestones\n\tmavenCentral() // for GA\n}\n\next {\n\tmicrometerDocsVersion="1.0.2"\n}\n\nconfigurations {\n\tadoc\n}\n\ndependencies {\n\tadoc "io.micrometer:micrometer-docs-generator:$micrometerDocsVersion"\n}\n\ntask generateObservabilityDocs(type: JavaExec) {\n\tmainClass = "io.micrometer.docs.DocsGeneratorCommand"\n\tclasspath configurations.adoc\n\t// input folder, inclusion pattern, output folder\n\targs project.rootDir.getAbsolutePath(), ".*", project.rootProject.buildDir.getAbsolutePath()\n}\n-----\n\nRunning these tasks would lead to generation of adoc files similar to these ones.\n\n._metrics.adoc\n[source,adoc,subs=+attributes]\n-----\n[[observability-metrics]]\n=== Observability - Metrics\n\nBelow you can find a list of all samples declared by this project.\n\n[[observability-metrics-task-runner-observation]]\n==== Task Runner Observation\n\n> Observation created when a task runner is executed.\n\n**Metric name** `spring.cloud.task.runner` (defined by convention class `org.springframework.cloud.task.configuration.observation.DefaultTaskObservationConvention`). **Type** `timer` and **base unit** `seconds`.\n\nFully qualified name of the enclosing class `org.springframework.cloud.task.configuration.observation.TaskDocumentedObservation`.\n\nIMPORTANT: All tags must be prefixed with `spring.cloud.task` prefix!\n\n.Low cardinality Keys\n|===\n|Name | Description\n|`spring.cloud.task.runner.bean-name`|Name of the bean that was executed by Spring Cloud Task.\n|===\n-----\n\n._spans.adoc\n[source,adoc,subs=+attributes]\n-----\n[[observability-spans]]\n=== Observability - Spans\n\nBelow you can find a list of all spans declared by this project.\n\n[[observability-spans-task-runner-observation]]\n==== Task Runner Observation Span\n\n> Observation created when a task runner is executed.\n\n**Span name** `spring.cloud.task.runner` (defined by convention class `org.springframework.cloud.task.configuration.observation.DefaultTaskObservationConvention`).\n\nFully qualified name of the enclosing class `org.springframework.cloud.task.configuration.observation.TaskDocumentedObservation`.\n\nIMPORTANT: All tags and event names must be prefixed with `spring.cloud.task` prefix!\n\n.Tag Keys\n|===\n|Name | Description\n|`spring.cloud.task.runner.bean-name`|Name of the bean that was executed by Spring Cloud Task.\n|===\n-----\n\n=== Options\n\nThe main entry class for the docs generation is `DocsGeneratorCommand` class.\nThis class takes following options.\n\n.Optional parameters\n[cols="1,1"]\n|===\n| `--metrics`\n| Generate metrics documentation.\n\n| `--spans`\n| Generate spans documentation.\n\n| `--conventions`\n| Generate observation conventions documentation.\n\n| `--metrics-template=`\n| Handlebars template file location. This can be a path in the classpath or file system. +\ne.g. `templates/metrics.adoc.hbs`, `/home/foo/bar.hbs`\n\n| `--spans-template=`\n| Handlebars template file location. This can be a path in the classpath or file system. +\ne.g. `templates/spans.adoc.hbs`, `/home/foo/bar.hbs`\n\n| `--conventions-template=`\n| Handlebars template file location. This can be a path in the classpath or file system. +\ne.g. `templates/conventions.adoc.hbs`, `/home/foo/bar.hbs`\n\n| `--metrics-output=`\n| Generated metrics doc file location. This can be an absolute path or relative path to the output directory. +\nDefault: `_metrics.adoc`\n\n| `--spans-output=`\n| Generated spans doc file location. This can be an absolute path or relative path to the output directory. +\nDefault: `_spans.adoc`\n| `--conventions-output=`\n| Generated observation convention doc file location. This can be an absolute path or relative path to the output directory. +\nDefault: `_conventions.adoc`\n|===\n\n:leveloffset!:\n\n== Existing Instrumentations\n\n:leveloffset: +1\n\nMicrometer Observation is used to instrument various projects. Below you can find a table of projects that are using Micrometer Observation to _"instrument once and have multiple benefits out of it"_.\n\n.External Project Instrumentations\n|===\n|Project Name |Link\n\n| Apache Camel | https://issues.apache.org/jira/browse/CAMEL-19023[Issue]\n| Apache CXF | https://github.com/apache/cxf/pull/1346#event-10091735987[PR]\n| Apache Dubbo | https://github.com/apache/dubbo/pull/11021[PR]\n| Apache HttpComponents | https://github.com/micrometer-metrics/micrometer/tree/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents[Repo]\n| Apache Skywalking | https://github.com/apache/skywalking-java/pull/401[PR], https://skywalking.apache.org/docs/skywalking-java/next/en/setup/service-agent/java-agent/application-toolkit-micrometer-1.10/[Docs]\n| Armeria | https://github.com/line/armeria/pull/4980[PR]\n| Appsmith | https://github.com/appsmithorg/appsmith/commit/5e46a2f4b7bf184aba03b4b93038edce8a615366[Commit]\n| gRPC | https://github.com/micrometer-metrics/micrometer/pull/3427[PR]\n| Halo | https://github.com/halo-dev/halo/commit/d192b8c956887e4701b94e3ed302fb88e4771583[Commit]\n| JDBC | https://github.com/jdbc-observations/datasource-micrometer[Repo]\n| JDK Http Client | https://github.com/micrometer-metrics/micrometer/blob/main/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/MicrometerHttpClient.java[Repo]\n| Jetty | https://github.com/micrometer-metrics/micrometer/pull/3416[PR]\n| Jersey | https://github.com/micrometer-metrics/micrometer/tree/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jersey/server[Repo]\n| JMS | https://github.com/micrometer-metrics/micrometer/blob/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jms/JmsInstrumentation.java[Repo]\n| Kotlin Coroutines | https://github.com/micrometer-metrics/micrometer/pull/3256[PR]\n| Lettuce | https://github.com/lettuce-io/lettuce-core/commit/6604fbe9e9cff476806c50716e17803e11d1e0ca[Commit]\n| Micronaut | https://github.com/micronaut-projects/micronaut-micrometer/issues/492[Issue]\n| OkHttp | https://github.com/micrometer-metrics/micrometer/tree/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3[Repo]\n| OpenFeign | https://github.com/OpenFeign/feign/pull/1760[PR]\n| RabbitMQ | https://github.com/rabbitmq/rabbitmq-java-client/issues/952[Issue]\n| RabbitMQ Stream | https://github.com/rabbitmq/rabbitmq-stream-java-client/pull/384[PR]\n| Resilience4j | https://github.com/resilience4j/resilience4j/pull/1698[PR]\n| R2DBC | https://github.com/r2dbc/r2dbc-proxy/issues/122[Issue]\n| Reactor | https://micrometer.io/docs/observation#instrumentation_of_reactive_libraries[Docs]\n| Reactor Netty | https://projectreactor.io/docs/netty/release/reference/index.html#_tracing_3[Docs]\n| Redisson | https://github.com/redisson/redisson/issues/4976[Issue],\nhttps://github.com/redisson/redisson/wiki/16.-Observability#162-tracing[Docs]\n| RSocket | https://github.com/rsocket/rsocket-java/tree/master/rsocket-micrometer/src/main/java/io/rsocket/micrometer/observation[Repo]\n| Spring Amqp | https://docs.spring.io/spring-amqp/docs/current/reference/html/index.html#observation[Docs]\n| Spring Batch | https://docs.spring.io/spring-batch/docs/current/reference/html/monitoring-and-metrics.html#tracing[Docs]\n| Spring Cloud Config | https://docs.spring.io/spring-cloud-config/docs/current/reference/html/#observability[Docs]\n| Spring Cloud CircuitBreaker | https://github.com/spring-cloud/spring-cloud-circuitbreaker/commit/4aa6883274a26b4c01b2c38e256d0b985978052e[Commit]\n| Spring Cloud Function | https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability[Repo]\n| Spring Cloud Gateway | https://github.com/spring-cloud/spring-cloud-gateway/tree/main/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/observation[Repo]\n| Spring Cloud OpenFeign | https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#micrometer-support[Docs]\n| Spring Cloud Task | https://docs.spring.io/spring-cloud-task/docs/current/reference/html/#enabling-observations-for-applicationrunner-and-commandlinerunner[Docs]\n| Spring Cloud Vault | https://github.com/spring-cloud/spring-cloud-vault/commit/1116f81971f16f9f9e42ad0994ee12a24404610e[Commit]\n| Spring Data Cassandra | https://docs.spring.io/spring-data/cassandra/docs/current/reference/html/#cassandra.observability[Docs]\n| Spring Data MongoDB | https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongodb.observability[Docs]\n| Spring Data Redis | https://docs.spring.io/spring-data-redis/docs/current/reference/html/#redis.observability[Docs]\n| Spring GraphQL | https://docs.spring.io/spring-graphql/docs/current/reference/html/#observability[Docs]\n| Spring Integration | https://docs.spring.io/spring-integration/reference/metrics.html#micrometer-observation[Docs]\n| Spring Kafka | https://docs.spring.io/spring-kafka/reference/html/#x30-obs[Docs]\n| Spring Security | https://docs.spring.io/spring-security/reference/reactive/integrations/observability.html[Docs]\n| Spring Modulith | https://docs.spring.io/spring-modulith/docs/current/reference/html/#observability[Docs]\n| Spring MVC | https://docs.spring.io/spring-framework/reference/integration/observability.html[Docs]\n| Spring Pulsar | https://docs.spring.io/spring-pulsar/docs/current/reference/html/#micrometer[Docs]\n| Spring WebFlux | https://docs.spring.io/spring-framework/reference/integration/observability.html[Docs]\n|===\n\nIf your project is instrumented using Micrometer Observation, and it\'s not listed in the table above, https://github.com/micrometer-metrics/micrometer-docs/edit/main/src/docs/observation/observation-projects.adoc[please file a PR] to our documentation! If you want to instrument your project and need our help just mention us in your issue - https://github.com/shakuzen/[@shakuzen], https://github.com/jonatan-ivanov/[@jonatan-ivanov], https://github.com/marcingrzejszczak/[@marcingrzejszczak].\n\n:leveloffset!:\n'},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Tracing support\n:toc:\n:sectnums:\n:dimensional: true\n\n== Purpose\n\nThe problem of tracing is not new.\nApplication developers have been creating ways to track the state of their applications for a long time.\nFor much of that time, developers had to create the necessary tracing framework themselves.\n\nIn 2016, the Spring Cloud team created a tracing library that could help a lot of developers.\nIt was called https://github.com/spring-cloud/spring-cloud-sleuth[Spring Cloud Sleuth].\nThe Spring team realized that tracing could be separated from Spring Cloud and created the Micrometer Tracing project, which is, essentially, a Spring-agnostic copy of Spring Cloud Sleuth.\nMicrometer Tracing had its 1.0.0 GA release in November 2022 and has been getting steadily better ever since.\n\nhttps://github.com/micrometer-metrics/tracing[Micrometer Tracing] provides a simple facade for the most popular tracer libraries, letting you instrument your JVM-based application code without vendor lock-in.\nIt is designed to add little to no overhead to your tracing collection activity while maximizing the portability of your tracing effort.\n\nIt also provides a tracing extension to Micrometer's `ObservationHandler` (from Micrometer 1.10.0).\nWhenever an `Observation` is used, a corresponding span will be created, started, stopped and reported.\n\n== Installing\n\nMicrometer Tracing comes with a Bill of Materials (BOM) which is a project that contains all the project versions for you.\n\nThe following example shows the required dependency in Gradle:\n\n[source,groovy,subs=+attributes]\n----\nimplementation platform('io.micrometer:micrometer-tracing-bom:latest.release')\nimplementation 'io.micrometer:micrometer-tracing'\n----\n\nThe following example shows the required dependency in Maven:\n\n[source,xml,subs=+attributes]\n----\n\n \n \n io.micrometer\n micrometer-tracing-bom\n ${micrometer-tracing.version}\n pom\n import\n \n \n\n\n\n \n io.micrometer\n micrometer-tracing\n \n\n----\n\nYou should add a tracing bridge you want to use, such as `micrometer-tracing-bridge-brave` or `micrometer-tracing-bridge-otel` and span exporters / reporters.\nIn case of adding a bridge the `micrometer-tracing` library is added transitively.\n\n== Glossary\n\n:leveloffset: +1\n\nMicrometer Tracing contains a core module with an instrumentation https://en.wikipedia.org/wiki/Service_provider_interface[SPI], a set of modules containing bridges to various tracers, a set of modules containing dedicated span reporting mechanisms, and a test kit.\nYou need to understand the following definitions for distributed tracing:\n\nMicrometer Tracing borrows https://research.google.com/pubs/pub36356.html[Dapper's] terminology.\n\n*Span*: The basic unit of work.\nFor example, sending an RPC is a new span, as is sending a response to an RPC.\nSpans also have other data, such as descriptions, timestamped events, key-value annotations (tags), the ID of the span that caused them, and process IDs (normally IP addresses).\n\nSpans can be started and stopped, and they keep track of their timing information.\nOnce you create a span, you must stop it at some point in the future.\n\n*Trace*: A set of spans forming a tree-like structure.\nFor example, if you run a distributed big-data store, a trace might be formed by a `PUT` request.\n\n*Annotation/Event*: Used to record the existence of an event in time.\n\n*Tracer*: A library that handles the lifecycle of a span.\nIt can create, start, stop and report spans to an external system via reporters / exporters.\n\n*Tracing context*: For distributed tracing to work the tracing context (trace identifier, span identifier, etc.) must be propagated through the process (e.g. over threads) and over the network.\n\n*Log correlation*: Parts of the tracing context (e.g. trace identifier, span identifier) can be populated to the logs of a given application.\nOne can then collect all logs in a single storage and group them via trace id.\nThat way one can get all logs, for a single business operation (trace) from all services put in a chronological order.\n\n*Latency analysis tools*: A tool that collects exported spans and visualizes the whole trace.\nAllows easy latency analysis.\n\nThe following image shows how *Span* and *Trace* look in a system.\n\nimage::"+n(83)+"[Trace Info propagation]\n\nEach color of a note signifies a span (there are seven spans - from *A* to *G*).\nConsider the following note:\n\n[source]\n----\nTrace Id = X\nSpan Id = D\nClient Sent\n----\n\nThis note indicates that the current span has *Trace Id* set to *X* and *Span Id* set to *D*.\nAlso, from the RPC perspective, the `Client Sent` event took place.\n\nLet's consider more notes:\n\n[source]\n----\nTrace Id = X\nSpan Id = A\n(no custom span)\n\nTrace Id = X\nSpan Id = C\n(custom span)\n----\n\nYou can continue with a created span (example with `no custom span` indication) or you can create child spans manually (example with `custom span` indication).\n\nThe following image shows how parent-child relationships of spans look:\n\nimage::"+n(84)+'[Parent child relationship]\n\n:leveloffset!:\n\n== Supported Tracers\n\n:leveloffset: +1\n\nMicrometer Tracing supports the following Tracers.\n\n* https://github.com/openzipkin/brave[*OpenZipkin Brave*]\n* https://opentelemetry.io/[*OpenTelemetry*]\n\n== Installing\n\nThe following example shows the required dependency in Gradle (assuming that Micrometer Tracing BOM has been added):\n\n.Brave Tracer\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-tracing-bridge-brave\'\n----\n\n.OpenTelemetry Tracer\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-tracing-bridge-otel\'\n----\n\nThe following example shows the required dependency in Maven (assuming that Micrometer Tracing BOM has been added):\n\n.Brave Tracer\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-tracing-bridge-brave\n\n----\n\n.OpenTelemetry Tracer\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-tracing-bridge-otel\n\n----\n\nIMPORTANT: Remember to pick *only one* bridge.\nYou *shouldn\'t have* two bridges on the classpath.\n\n:leveloffset!:\n\n== Supported Reporters\n\n:leveloffset: +1\n\nMicrometer Tracing supports directly the following Reporters.\n\n* https://tanzu.vmware.com/observability[*Tanzu Observability by Wavefront*]\n* https://zipkin.io[*OpenZipkin Zipkin*]\n\n== Installing\n\nThe following example shows the required dependency in Gradle (assuming that Micrometer Tracing BOM has been added):\n\n.Tanzu Observability by Wavefront\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-tracing-reporter-wavefront\'\n----\n\n.OpenZipkin Zipkin with Brave\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.zipkin.reporter2:zipkin-reporter-brave\'\n----\n\n.OpenZipkin Zipkin with OpenTelemetry\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.opentelemetry:opentelemetry-exporter-zipkin\'\n----\n\n.An OpenZipkin URL sender dependency to send out spans to Zipkin via a `URLConnectionSender`\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.zipkin.reporter2:zipkin-sender-urlconnection\'\n----\n\nThe following example shows the required dependency in Maven (assuming that Micrometer Tracing BOM has been added):\n\n.Tanzu Observability by Wavefront\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-tracing-reporter-wavefront\n\n----\n\n.OpenZipkin Zipkin with Brave\n[source,xml,subs=+attributes]\n----\n\n io.zipkin.reporter2\n zipkin-reporter-brave\n\n----\n\n.OpenZipkin Zipkin with OpenTelemetry\n[source,xml,subs=+attributes]\n----\n\n io.opentelemetry\n opentelemetry-exporter-zipkin\n\n----\n\n.An OpenZipkin URL sender dependency to send out spans to Zipkin via a `URLConnectionSender`\n[source,xml,subs=+attributes]\n----\n\n io.zipkin.reporter2\n zipkin-sender-urlconnection\n\n----\n\nIMPORTANT: Remember that Brave by default adds Zipkin as a dependency. If you want to use just Wavefront and you\'re using classpath dependant solutions such as Spring Boot, you might be required to exclude the transitive dependency on Zipkin when using Brave (e.g. via exlcuding the `io.zipkin.reporter2` group).\n\n:leveloffset!:\n\n== Using Micrometer Tracing Directly\n\n:leveloffset: +1\n\nIn this section we will describe how to use the Micrometer Tracing API directly to create and report spans.\n\n== Micrometer Tracing Examples\n\nBelow you can see basic operations on a span. Please read the comments in the snippet for details.\n\n[source,java,subs=+attributes]\n-----\n// Create a span. If there was a span present in this thread it will become\n// the `newSpan`\'s parent.\nSpan newSpan = this.tracer.nextSpan().name("calculateTax");\n// Start a span and put it in scope. Putting in scope means putting the span\n// in thread local\n// and, if configured, adjust the MDC to contain tracing information\ntry (Tracer.SpanInScope ws = this.tracer.withSpan(newSpan.start())) {\n // ...\n // You can tag a span - put a key value pair on it for better debugging\n newSpan.tag("taxValue", taxValue);\n // ...\n // You can log an event on a span - an event is an annotated timestamp\n newSpan.event("taxCalculated");\n}\nfinally {\n // Once done remember to end the span. This will allow collecting\n // the span to send it to a distributed tracing system e.g. Zipkin\n newSpan.end();\n}\n-----\n\nBelow you can see how to continue a span in a new thread, that was started in another thread.\n\n[source,java,subs=+attributes]\n-----\nSpan spanFromThreadX = this.tracer.nextSpan().name("calculateTax");\ntry (Tracer.SpanInScope ws = this.tracer.withSpan(spanFromThreadX.start())) {\n executorService.submit(() -> {\n // Pass the span from thread X\n Span continuedSpan = spanFromThreadX;\n // ...\n // You can tag a span\n continuedSpan.tag("taxValue", taxValue);\n // ...\n // You can log an event on a span\n continuedSpan.event("taxCalculated");\n }).get();\n}\nfinally {\n spanFromThreadX.end();\n}\n-----\n\nBelow you can see how to create a child span when explicitly knowing who the parent span is.\n\n[source,java,subs=+attributes]\n-----\n// let\'s assume that we\'re in a thread Y and we\'ve received\n// the `initialSpan` from thread X. `initialSpan` will be the parent\n// of the `newSpan`\nSpan newSpan = this.tracer.nextSpan(initialSpan).name("calculateCommission");\n// ...\n// You can tag a span\nnewSpan.tag("commissionValue", commissionValue);\n// ...\n// You can log an event on a span\nnewSpan.event("commissionCalculated");\n// Once done remember to end the span. This will allow collecting\n// the span to send it to e.g. Zipkin. The tags and events set on the\n// newSpan will not be present on the parent\nnewSpan.end();\n-----\n\n== Micrometer Tracing Brave Setup\n\nIn this subsection we will set up Micrometer Tracing with Brave.\n\nBelow you can see how to create a Micrometer Tracing `Tracer` using Brave components that would send completed spans to Zipkin.\n\n[source,java,subs=+attributes]\n-----\n// [Brave component] Example of using a SpanHandler. SpanHandler is a component\n// that gets called when a span is finished. Here we have an example of setting it\n// up with sending spans\n// in a Zipkin format to the provided location via the UrlConnectionSender\n// (through the dependency)\n// Another option could be to use a TestSpanHandler for testing purposes.\nSpanHandler spanHandler = ZipkinSpanHandler\n .create(AsyncReporter.create(URLConnectionSender.create("http://localhost:9411/api/v2/spans")));\n\n// [Brave component] CurrentTraceContext is a Brave component that allows you to\n// retrieve the current TraceContext.\nThreadLocalCurrentTraceContext braveCurrentTraceContext = ThreadLocalCurrentTraceContext.newBuilder()\n .addScopeDecorator(MDCScopeDecorator.get()) // Example of Brave\'s\n // automatic MDC setup\n .build();\n\n// [Micrometer Tracing component] A Micrometer Tracing wrapper for Brave\'s\n// CurrentTraceContext\nCurrentTraceContext bridgeContext = new BraveCurrentTraceContext(this.braveCurrentTraceContext);\n\n// [Brave component] Tracing is the root component that allows to configure the\n// tracer, handlers, context propagation etc.\nTracing tracing = Tracing.newBuilder()\n .currentTraceContext(this.braveCurrentTraceContext)\n .supportsJoin(false)\n .traceId128Bit(true)\n // For Baggage to work you need to provide a list of fields to propagate\n .propagationFactory(BaggagePropagation.newFactoryBuilder(B3Propagation.FACTORY)\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span_in_scope 1")))\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span_in_scope 2")))\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span")))\n .build())\n .sampler(Sampler.ALWAYS_SAMPLE)\n .addSpanHandler(this.spanHandler)\n .build();\n\n\n// [Brave component] Tracer is a component that handles the life-cycle of a span\nbrave.Tracer braveTracer = this.tracing.tracer();\n\n// [Micrometer Tracing component] A Micrometer Tracing wrapper for Brave\'s Tracer\nTracer tracer = new BraveTracer(this.braveTracer, this.bridgeContext, new BraveBaggageManager());\n\n-----\n\n== Micrometer Tracing OpenTelemetry Setup\n\nIn this subsection we will set up Micrometer Tracing with OpenTelemetry (OTel).\n\nBelow you can see how to create a Micrometer Tracing `Tracer` using OTel components that would send completed spans to Zipkin.\n\n[source,java,subs=+attributes]\n-----\n// [OTel component] Example of using a SpanExporter. SpanExporter is a component\n// that gets called when a span is finished. Here we have an example of setting it\n// up with sending spans\n// in a Zipkin format to the provided location via the UrlConnectionSender\n// (through the and\n// dependencies)\n// Another option could be to use an ArrayListSpanProcessor for testing purposes\nSpanExporter spanExporter = new ZipkinSpanExporterBuilder()\n .setSender(URLConnectionSender.create("http://localhost:9411/api/v2/spans"))\n .build();\n\n// [OTel component] SdkTracerProvider is an SDK implementation for TracerProvider\nSdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()\n .setSampler(alwaysOn())\n .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build())\n .build();\n\n// [OTel component] The SDK implementation of OpenTelemetry\nOpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder()\n .setTracerProvider(sdkTracerProvider)\n .setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader()))\n .build();\n\n// [OTel component] Tracer is a component that handles the life-cycle of a span\nio.opentelemetry.api.trace.Tracer otelTracer = openTelemetrySdk.getTracerProvider()\n .get("io.micrometer.micrometer-tracing");\n\n// [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel\nOtelCurrentTraceContext otelCurrentTraceContext = new OtelCurrentTraceContext();\n\n// [Micrometer Tracing component] A Micrometer Tracing listener for setting up MDC\nSlf4JEventListener slf4JEventListener = new Slf4JEventListener();\n\n// [Micrometer Tracing component] A Micrometer Tracing listener for setting\n// Baggage in MDC. Customizable\n// with correlation fields (currently we\'re setting empty list)\nSlf4JBaggageEventListener slf4JBaggageEventListener = new Slf4JBaggageEventListener(Collections.emptyList());\n\n// [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel\'s Tracer.\n// You can consider\n// customizing the baggage manager with correlation and remote fields (currently\n// we\'re setting empty lists)\nOtelTracer tracer = new OtelTracer(otelTracer, otelCurrentTraceContext, event -> {\n slf4JEventListener.onEvent(event);\n slf4JBaggageEventListener.onEvent(event);\n}, new OtelBaggageManager(otelCurrentTraceContext, Collections.emptyList(), Collections.emptyList()));\n\n-----\n\n== Micrometer Tracing Baggage API\n\nTraces connect from service to service using header propagation. Besides trace identifiers, other properties (called `Baggage`) can also be passed along with the request.\n\nBelow you can find an example on how to use the Tracer API (version `1.0.x`) to create and extract baggage.\n\n[source,java,subs=+attributes]\n-----\n// ---------------------------------------------------------------------------\n// Example for Tracing 1.0.x\n// ---------------------------------------------------------------------------\nSpan span = tracer.nextSpan().name("parent").start();\n\n// Assuming that there\'s a span in scope...\ntry (Tracer.SpanInScope ws = tracer.withSpan(span)) {\n\n // Not passing a TraceContext explicitly will bind the baggage to the\n // current TraceContext\n // If you want to retrieve the baggage value you should make it current\n // first\n try (BaggageInScope baggage = tracer.createBaggage("from_span_in_scope 1", "value 1").makeCurrent()) {\n // This is how you retrieve the baggage\n String baggageValue = baggage.get();\n then(baggageValue).as("[In scope] Baggage 1").isEqualTo("value 1");\n\n String baggageValueViaTracer = tracer.getBaggage("from_span_in_scope 1").get();\n then(baggageValueViaTracer).as("[In scope] Baggage 1").isEqualTo("value 1");\n }\n\n try (BaggageInScope baggage = tracer.createBaggage("from_span_in_scope 2", "value 2").makeCurrent()) {\n then(baggage.get()).as("[In scope] Baggage 2").isEqualTo("value 2");\n then(tracer.getBaggage("from_span_in_scope 2").get()).as("[In scope] Baggage 2")\n .isEqualTo("value 2");\n }\n}\n\n// Assuming that you have a handle to the span\ntry (BaggageInScope baggage = tracer.createBaggage("from_span")\n .set(span.context(), "value 3")\n .makeCurrent()) {\n String baggageValueFromATraceContext = baggage.get(span.context());\n then(baggageValueFromATraceContext).as("[Span passed explicitly] Baggage 3").isEqualTo("value 3");\n\n String baggageValueFromATraceContextThroughTracer = tracer.getBaggage("from_span").get(span.context());\n then(baggageValueFromATraceContextThroughTracer).as("[Span passed explicitly] Baggage 3")\n .isEqualTo("value 3");\n}\n\n// Assuming that there\'s no span in scope\n// When there\'s no span in scope, there will never be any baggage - even if\n// you make it current\ntry (BaggageInScope baggage = tracer.createBaggage("from_span_in_scope 1", "value 1").makeCurrent()) {\n then(baggage.get()).as("[Out of span scope] Baggage 1").isNull();\n then(tracer.getBaggage("from_span_in_scope 1").get()).as("[Out of span scope] Baggage 1").isNull();\n}\nthen(tracer.getBaggage("from_span_in_scope 1").get()).as("[Out of scope] Baggage 1").isNull();\nthen(tracer.getBaggage("from_span_in_scope 2").get()).as("[Out of scope] Baggage 2").isNull();\nthen(tracer.getBaggage("from_span").get()).as("[Out of scope] Baggage 3").isNull();\nthen(tracer.getBaggage("from_span").get(span.context())).as("[Out of scope - with context] Baggage 3")\n .isNull();\n-----\n\nBelow you can find an example on how to use the Tracer API (version `1.1.x`) to create and extract baggage.\n\n[source,java,subs=+attributes]\n-----\n// ---------------------------------------------------------------------------\n// Example for Tracing 1.1.x\n// ---------------------------------------------------------------------------\n\nSpan span = tracer.nextSpan().name("parent").start();\n\n// Assuming that there\'s a span in scope...\ntry (Tracer.SpanInScope ws = tracer.withSpan(span)) {\n\n // Not passing a TraceContext explicitly will bind the baggage to the\n // current TraceContext\n try (BaggageInScope baggage = tracer.createBaggageInScope("from_span_in_scope 1", "value 1")) {\n // This is how you retrieve the baggage\n String baggageValue = baggage.get();\n then(baggageValue).as("[In scope] Baggage 1").isEqualTo("value 1");\n\n String baggageValueViaTracer = tracer.getBaggage("from_span_in_scope 1").get();\n then(baggageValueViaTracer).as("[In scope] Baggage 1").isEqualTo("value 1");\n }\n\n try (BaggageInScope baggage = tracer.createBaggageInScope("from_span_in_scope 2", "value 2")) {\n then(baggage.get()).as("[In scope] Baggage 2").isEqualTo("value 2");\n then(tracer.getBaggage("from_span_in_scope 2").get()).as("[In scope] Baggage 2")\n .isEqualTo("value 2");\n }\n}\n\n// Assuming that you have a handle to the span\ntry (BaggageInScope baggage = tracer.createBaggageInScope(span.context(), "from_span", "value 3")) {\n String baggageValueFromATraceContext = baggage.get(span.context());\n then(baggageValueFromATraceContext).as("[Span passed explicitly] Baggage 3").isEqualTo("value 3");\n\n String baggageValueFromATraceContextThroughTracer = tracer.getBaggage("from_span").get(span.context());\n then(baggageValueFromATraceContextThroughTracer).as("[Span passed explicitly] Baggage 3")\n .isEqualTo("value 3");\n}\n\n// Assuming that there\'s no span in scope\n// When there\'s no span in scope, there will never be any baggage - even if\n// you make it current\ntry (BaggageInScope baggage = tracer.createBaggageInScope("from_span_in_scope 1", "value 1")) {\n then(baggage.get()).as("[Out of span scope] Baggage 1").isNull();\n then(tracer.getBaggage("from_span_in_scope 1").get()).as("[Out of span scope] Baggage 1").isNull();\n}\nthen(tracer.getBaggage("from_span_in_scope 1").get()).as("[Out of scope] Baggage 1").isNull();\nthen(tracer.getBaggage("from_span_in_scope 2").get()).as("[Out of scope] Baggage 2").isNull();\nthen(tracer.getBaggage("from_span").get()).as("[Out of scope] Baggage 3").isNull();\nthen(tracer.getBaggage("from_span").get(span.context())).as("[Out of scope - with context] Baggage 3")\n .isNull();\n-----\n\nIMPORTANT: For Brave, remember to set up the `PropagationFactory` so that it contains the baggage fields that you will be using in your code. Check the example below for details.\n\n[source,java,subs=+attributes]\n-----\nTracing tracing = Tracing.newBuilder()\n .currentTraceContext(this.braveCurrentTraceContext)\n .supportsJoin(false)\n .traceId128Bit(true)\n // For Baggage to work you need to provide a list of fields to propagate\n .propagationFactory(BaggagePropagation.newFactoryBuilder(B3Propagation.FACTORY)\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span_in_scope 1")))\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span_in_scope 2")))\n .add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("from_span")))\n .build())\n .sampler(Sampler.ALWAYS_SAMPLE)\n .addSpanHandler(this.spanHandler)\n .build();\n\n-----\n\n== Aspect Oriented Programming (starting from Micrometer Tracing 1.1.0)\n\nIMPORTANT: This section is applicable from Micrometer Tracing 1.1.0.\n\nMicrometer Tracing contains a `@NewSpan`, `@ContinueSpan` and `@SpanTag` annotations that frameworks can use to create or customize spans for either specific types of methods such as those serving web request endpoints or, more generally, to all methods.\n\nWARNING: Micrometer\'s Spring Boot configuration does _not_ recognize these aspects on arbitrary methods.\n\nAn AspectJ aspect is included. You can use it in your application either through compile/load time AspectJ weaving or through framework facilities that interpret AspectJ aspects and proxy targeted methods in some other way, such as Spring AOP. Here is a sample Spring AOP configuration:\n\n[source,java,subs=+attributes]\n-----\n@Configuration\npublic class SpanAspectConfiguration {\n\n @Bean\n NewSpanParser newSpanParser() {\n return new DefaultNewSpanParser();\n }\n\n // You can provide your own resolvers - here we go with a noop example.\n @Bean\n ValueResolver valueResolver() {\n return new NoOpValueResolver();\n }\n\n // Example of a SpEL resolver\n @Bean\n ValueExpressionResolver valueExpressionResolver() {\n return new SpelTagValueExpressionResolver();\n }\n\n @Bean\n MethodInvocationProcessor methodInvocationProcessor(NewSpanParser newSpanParser, Tracer tracer,\n BeanFactory beanFactory) {\n return new ImperativeMethodInvocationProcessor(newSpanParser, tracer, beanFactory::getBean,\n beanFactory::getBean);\n }\n\n @Bean\n SpanAspect spanAspect(MethodInvocationProcessor methodInvocationProcessor) {\n return new SpanAspect(methodInvocationProcessor);\n }\n\n}\n\n// Example of using SpEL to resolve expressions in @SpanTag\nstatic class SpelTagValueExpressionResolver implements ValueExpressionResolver {\n\n private static final Log log = LogFactory.getLog(SpelTagValueExpressionResolver.class);\n\n @Override\n public String resolve(String expression, Object parameter) {\n try {\n SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();\n ExpressionParser expressionParser = new SpelExpressionParser();\n Expression expressionToEvaluate = expressionParser.parseExpression(expression);\n return expressionToEvaluate.getValue(context, parameter, String.class);\n }\n catch (Exception ex) {\n log.error("Exception occurred while tying to evaluate the SpEL expression [" + expression + "]", ex);\n }\n return parameter.toString();\n }\n\n}\n-----\n\nApplying `SpanAspect` makes `@NewSpan` and `@ContinueSpan` usable on any arbitrary method in an AspectJ proxied instance, as the following example shows:\n\n[source,java,subs=+attributes]\n-----\n// In Sleuth @NewSpan and @ContinueSpan annotations would be taken into\n// consideration. In Micrometer Tracing due to limitations of @Aspect\n// we can\'t do that. The @SpanTag annotation will work well though.\nprotected interface TestBeanInterface {\n\n void testMethod2();\n\n void testMethod3();\n\n void testMethod10(@SpanTag("testTag10") String param);\n\n void testMethod10_v2(@SpanTag("testTag10") String param);\n\n}\n\n// Example of an implementation class\nprotected static class TestBean implements TestBeanInterface {\n\n @NewSpan\n @Override\n public void testMethod2() {\n }\n\n @NewSpan(name = "customNameOnTestMethod3")\n @Override\n public void testMethod3() {\n }\n\n @ContinueSpan(log = "customTest")\n @Override\n public void testMethod10(@SpanTag("customTestTag10") String param) {\n\n }\n\n @ContinueSpan(log = "customTest")\n @Override\n public void testMethod10_v2(String param) {\n\n }\n\n}\n\n// --------------------------\n// ----- USAGE EXAMPLE ------\n// --------------------------\n\n\n// Creates a new span with\ntestBean().testMethod2();\nthen(createdSpanViaAspect()).isEqualTo("test-method2");\n\n// Uses the name from the annotation\ntestBean().testMethod3();\nthen(createdSpanViaAspect()).isEqualTo("custom-name-on-test-method3");\n\n// Continues the previous span\nSpan span = this.tracer.nextSpan().name("foo");\ntry (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) {\n\n // Adds tags and events to an existing span\n testBean().testMethod10("tagValue");\n SimpleSpan continuedSpan = modifiedSpanViaAspect();\n then(continuedSpan.getName()).isEqualTo("foo");\n then(continuedSpan.getTags()).containsEntry("customTestTag10", "tagValue");\n then(continuedSpan.getEvents()).extracting("value").contains("customTest.before", "customTest.after");\n}\nspan.end();\n\n// Continues the previous span\nspan = this.tracer.nextSpan().name("foo");\ntry (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) {\n\n // Adds tags and events to an existing span (reusing setup from the parent\n // interface)\n testBean().testMethod10_v2("tagValue");\n SimpleSpan continuedSpan = modifiedSpanViaAspect();\n then(continuedSpan.getName()).isEqualTo("foo");\n then(continuedSpan.getTags()).containsEntry("testTag10", "tagValue");\n then(continuedSpan.getEvents()).extracting("value").contains("customTest.before", "customTest.after");\n}\nspan.end();\n\n\n-----\n\n\n:leveloffset!:\n\n== Configuring with Micrometer Observation\n\n:leveloffset: +1\n\n== Handler Configuration\n\n// TODO: We need to separately document that Micrometer provides a TimerObservationHandler\n\nFor Micrometer Tracing to work with Micrometer Observation, you need to add a tracing related `ObservationHandler`.\nCheck the example below for an example of adding and using a single `DefaultTracingObservationHandler`.\n\n[source,java,subs=+attributes]\n-----\nTracer tracer = Tracer.NOOP; // The real tracer will come from your tracer\n // implementation (Brave /\n// OTel)\nPropagator propagator = Propagator.NOOP; // The real propagator will come from\n // your tracer implementation (Brave /\n // OTel)\nMeterRegistry meterRegistry = new SimpleMeterRegistry();\n\nObservationRegistry registry = ObservationRegistry.create();\nregistry.observationConfig()\n // assuming that micrometer-core is on the classpath\n .observationHandler(new DefaultMeterObservationHandler(meterRegistry))\n // we set up a first matching handler that creates spans - it comes from\n // Micrometer\n // Tracing. We set up spans for sending and receiving data over the wire\n // and a default one\n .observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler(\n new PropagatingSenderTracingObservationHandler<>(tracer, propagator),\n new PropagatingReceiverTracingObservationHandler<>(tracer, propagator),\n new DefaultTracingObservationHandler(tracer)));\n\n// Creating and starting a new observation\n// via the `DefaultTracingObservationHandler` that will create a new Span and\n// start it\nObservation observation = Observation.start("my.operation", registry)\n .contextualName("This name is more readable - we can reuse it for e.g. spans")\n .lowCardinalityKeyValue("this.tag", "will end up as a meter tag and a span tag")\n .highCardinalityKeyValue("but.this.tag", "will end up as a span tag only");\n\n// Put the observation in scope\n// This will result in making the previously created Span, the current Span - it\'s\n// in ThreadLocal\ntry (Observation.Scope scope = observation.openScope()) {\n // Run your code that you want to measure - still the attached Span is the\n // current one\n // This means that e.g. logging frameworks could inject to e.g. MDC tracing\n // information\n yourCodeToMeasure();\n}\nfinally {\n // The corresponding Span will no longer be in ThreadLocal due to\n // try-with-resources block (Observation.Scope is an AutoCloseable)\n // Stop the Observation\n // The corresponding Span will be stopped and reported to an external system\n observation.stop();\n}\n-----\n\nYou can also use a shorter version to perform measurements via the `observe` method.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\n\nObservation.createNotStarted("my.operation", registry)\n .contextualName("This name is more readable - we can reuse it for e.g. spans")\n .lowCardinalityKeyValue("this.tag", "will end up as a meter tag and a span tag")\n .highCardinalityKeyValue("but.this.tag", "will end up as a span tag only")\n .observe(this::yourCodeToMeasure);\n-----\n\nThis will result in the following Micrometer Metrics:\n\n```\nGathered the following metrics\n Meter with name and type has the following measurements\n <[\n Measurement{statistic=\'COUNT\', value=1.0},\n Measurement{statistic=\'TOTAL_TIME\', value=1.011949454},\n Measurement{statistic=\'MAX\', value=1.011949454}\n ]>\n and has the following tags <[tag(this.tag=will end up as a meter tag and a span tag)]>\n```\n\nAnd the following trace view in e.g. Zipkin\n\nimage::'+n(85)+'[Trace Info propagation]\n\n=== Ordered Handler Configuration\n\nMicrometer Tracing comes with multiple `ObservationHandler` implementations.\nTo introduce ordering, you can use the `ObservationHandler.AllMatchingCompositeObservationHandler` to run logic for all ``ObservationHandler``s that are matching the given predicate and `ObservationHandler.FirstMatchingCompositeObservationHandler` to run logic only for the first `ObservationHandler` that matches the predicate.\nThe former can group handlers and the latter can be chosen to e.g. run only one matching `TracingObservationHandler`.\n\n== Context Propagation with Micrometer Tracing\n\nIn order to make https://micrometer.io/docs/contextPropagation[Context Propagation] work with Micrometer Tracing you need to manually register the proper `ThreadLocalAccessor` as presented below.\n\n[source,java,subs=+attributes]\n-----\nContextRegistry.getInstance().registerThreadLocalAccessor(new ObservationAwareSpanThreadLocalAccessor(tracer));\n-----\n\n== Exemplars\n\nTo add support for https://grafana.com/docs/grafana/latest/fundamentals/exemplars/[exemplars] instead of using the `DefaultMeterObservationHandler` you should use the `TracingAwareMeterObservationHandler` like presented below.\n\n[source,java,subs=+attributes]\n-----\nObservationRegistry registry = ObservationRegistry.create();\nregistry.observationConfig()\n // Don\'t register the DefaultMeterObservationHandler...\n // .observationHandler(new DefaultMeterObservationHandler(meterRegistry))\n // ...instead register the tracing aware version\n .observationHandler(new TracingAwareMeterObservationHandler<>(\n new DefaultMeterObservationHandler(meterRegistry), tracer));\n-----\n\n:leveloffset!:\n\n== Testing\n\n:leveloffset: +1\n\nMicrometer Tracing comes with `micrometer-tracing-test` and `micrometer-tracing-integration-test` modules.\n\nFor unit tests it provides a `SimpleTracer` that is a test implementation of a `Tracer`.\n\nFor the integration tests it provides a `SampleTestRunner` mechanism that you can hook into your samples.\nIt will\n\n* Configure an OpenZipkin Brave Tracer\n** Set it up with Tanzu Observability by Wavefront Reporter\n** Set it up with OpenZipkin Zipkin Reporter\n* Configure an OpenTelemetry Tracer\n** Set it up with Tanzu Observability by Wavefront Exporter\n** Set it up with OpenZipkin Zipkin Exporter\n* Run all the combinations above against the user code and running infrastructure\n\n== Installing\n\nThe following example shows the required dependency in Gradle (assuming that Micrometer Tracing BOM has been added):\n\n[source,groovy,subs=+attributes]\n-----\ntestImplementation \'io.micrometer:micrometer-tracing-test\' // for unit tests\ntestImplementation \'io.micrometer:micrometer-tracing-integration-test\' // for integration tests\n-----\n\nThe following example shows the required dependency in Maven (assuming that Micrometer Tracing BOM has been added):\n\n[source,xml,subs=+attributes]\n-----\n\n io.micrometer\n micrometer-tracing-test \x3c!-- For unit tests --\x3e\n test\n\n\n io.micrometer\n micrometer-tracing-integration-test \x3c!-- For integration tests --\x3e\n test\n\n-----\n\n== Running Tracing Unit Tests\n\nTo run unit tests of your custom handler you may want to use the `SimpleTracer` test `Tracer` implementation. Let\'s assume the following custom `TracingObservationHandler`.\n\n[source,java,subs=+attributes]\n-----\nstatic class MyTracingObservationHandler implements TracingObservationHandler {\n\n private final Tracer tracer;\n\n MyTracingObservationHandler(Tracer tracer) {\n this.tracer = tracer;\n }\n\n @Override\n public void onStart(CustomContext context) {\n String databaseName = context.getDatabaseName();\n Span.Builder builder = this.tracer.spanBuilder().kind(Span.Kind.CLIENT).remoteServiceName(databaseName);\n getTracingContext(context).setSpan(builder.start());\n }\n\n @Override\n public void onError(CustomContext context) {\n getTracingContext(context).getSpan().error(context.getError());\n }\n\n @Override\n public void onStop(CustomContext context) {\n Span span = getRequiredSpan(context);\n span.name(context.getContextualName() != null ? context.getContextualName() : context.getName());\n tagSpan(context, span);\n span.end();\n }\n\n @Override\n public boolean supportsContext(Observation.Context context) {\n return context instanceof CustomContext;\n }\n\n @Override\n public Tracer getTracer() {\n return this.tracer;\n }\n\n}\n-----\n\nTo verify whether the spans got properly created we can use the `SimpleTracer` as follows:\n\n[source,java,subs=+attributes]\n-----\nclass SomeComponentThatIsUsingMyTracingObservationHandlerTests {\n\n ObservationRegistry registry = ObservationRegistry.create();\n\n SomeComponent someComponent = new SomeComponent(registry);\n\n SimpleTracer simpleTracer = new SimpleTracer();\n\n MyTracingObservationHandler handler = new MyTracingObservationHandler(simpleTracer);\n\n @BeforeEach\n void setup() {\n registry.observationConfig().observationHandler(handler);\n }\n\n @Test\n void should_store_a_span() {\n // this code will call actual Observation API\n someComponent.doSthThatShouldCreateSpans();\n\n TracerAssert.assertThat(simpleTracer)\n .onlySpan()\n .hasNameEqualTo("insert user")\n .hasKindEqualTo(Span.Kind.CLIENT)\n .hasRemoteServiceNameEqualTo("mongodb-database")\n .hasTag("mongodb.command", "insert")\n .hasTag("mongodb.collection", "user")\n .hasTagWithKey("mongodb.cluster_id")\n .assertThatThrowable()\n .isInstanceOf(IllegalStateException.class)\n .backToSpan()\n .hasIpThatIsBlank()\n .hasPortThatIsNotSet();\n }\n\n}\n-----\n\n== Running integration tests\n\nThe following example shows how you can run your code to test your integrations\n\n* by asserting spans that were stored without emitting them to a reporting system\n* against running Tanzu Observability by Wavefront instance (this option turns on when you have passed the Wavefront related configuration in the constructor - otherwise the test will be disabled)\n* against running Zipkin instance (this option turns on when Zipkin is running - otherwise the test will be disabled)\n\n[source,java,subs=+attributes]\n-----\nclass ObservabilitySmokeTest extends SampleTestRunner {\n\n ObservabilitySmokeTest() {\n super(SampleRunnerConfig.builder().wavefrontApplicationName("my-app").wavefrontServiceName("my-service")\n .wavefrontToken("...")\n .wavefrontUrl("...")\n .zipkinUrl("...") // defaults to localhost:9411\n .build());\n }\n\n @Override\n public BiConsumer>> customizeObservationHandlers() {\n return (bb, handlers) -> {\n ObservationHandler defaultHandler = handlers.removeLast();\n handlers.addLast(new MyTracingObservationHandler(bb.getTracer()));\n handlers.addLast(defaultHandler);\n };\n }\n\n @Override\n public SampleTestRunnerConsumer yourCode() {\n return (bb, meterRegistry) -> {\n // here you would be running your code\n yourCode();\n\n SpansAssert.assertThat(bb.getFinishedSpans())\n .haveSameTraceId()\n .hasNumberOfSpansEqualTo(8)\n .hasNumberOfSpansWithNameEqualTo("handle", 4)\n .forAllSpansWithNameEqualTo("handle", span -> span.hasTagWithKey("rsocket.request-type"))\n .hasASpanWithNameIgnoreCase("request_stream")\n .thenASpanWithNameEqualToIgnoreCase("request_stream")\n .hasTag("rsocket.request-type", "REQUEST_STREAM")\n .backToSpans()\n .hasASpanWithNameIgnoreCase("request_channel")\n .thenASpanWithNameEqualToIgnoreCase("request_channel")\n .hasTag("rsocket.request-type", "REQUEST_CHANNEL")\n .backToSpans()\n .hasASpanWithNameIgnoreCase("request_fnf")\n .thenASpanWithNameEqualToIgnoreCase("request_fnf")\n .hasTag("rsocket.request-type", "REQUEST_FNF")\n .backToSpans()\n .hasASpanWithNameIgnoreCase("request_response")\n .thenASpanWithNameEqualToIgnoreCase("request_response")\n .hasTag("rsocket.request-type", "REQUEST_RESPONSE");\n\n MeterRegistryAssert.assertThat(meterRegistry)\n .hasTimerWithNameAndTags("rsocket.response", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_RESPONSE")))\n .hasTimerWithNameAndTags("rsocket.fnf", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_FNF")))\n .hasTimerWithNameAndTags("rsocket.request", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_RESPONSE")))\n .hasTimerWithNameAndTags("rsocket.channel", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_CHANNEL")))\n .hasTimerWithNameAndTags("rsocket.stream", Tags.of(Tag.of("error", "none"), Tag.of("rsocket.request-type", "REQUEST_STREAM")));\n };\n }\n\n}\n-----\n\n:leveloffset!:\n'},function(e,t,n){"use strict";n.r(t),t.default=n.p+"7043f6f1d0731de0cc0943e896c32fca.jpg"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"e4aa3da5f34dc789ae92d1f759a97b85.jpg"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"b8a4008ab6f119930854d422d6bd39b9.jpg"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Context Propagation support\n:toc:\n:sectnums:\n:dimensional: true\n\n== Purpose\n\nhttps://github.com/micrometer-metrics/context-propagation[A library] that assists with context propagation across different types of context\nmechanisms such as `ThreadLocal`, Reactor https://projectreactor.io/docs/core/release/reference/#context[Context]\nand others.\n\nAbstractions:\n\n* `ThreadLocalAccessor` - contract to assist with access to a `ThreadLocal` value.\n* `ContextAccessor` - contract to assist with access to a `Map`-like context.\n* `ContextRegistry` - registry for instances of `ThreadLocalAccessor` and `ContextAccessor`.\n* `ContextSnapshot` - holder of contextual values, that provides methods to capture and to propagate.\n\nExample Scenarios:\n\n* In imperative code, e.g. Spring MVC controller, capture `ThreadLocal` values into a\n`ContextSnapshot`. After that use the snapshot to populate a Reactor `Context` with the\ncaptured values, or to wrap a task (e.g. `Runnable`, `Callable`, etc) or an `Executor`\nwith a decorator that restores `ThreadLocal` values when the task executes.\n* In reactive code, e.g. Spring WebFlux controller, create a `ContextSnapshot` from\nReactor `Context` values. After that use the snapshot to restore `ThreadLocal` values\nwithin a specific stage (operator) of the reactive chain.\n\nContext values can originate from any context mechanism and propagate to any other, any\nnumber of times. For example, a value in a `Reactor` context may originate as a\n`ThreadLocal`, and may yet become a `ThreadLocal` again, and so on.\n\nGenerally, imperative code should interact with `ThreadLocal` values as usual, and\nlikewise Reactor code should interact with the Reactor `Context` as usual. The Context\nPropagation library is not intended to replace those, but to assist with propagation when\ncrossing from one type of context to another, e.g. when imperative code invokes a Reactor\nchain, or when a Reactor chain invokes an imperative component that expects\n`ThreadLocal` values.\n\nThe library is not limited to context propagation from imperative to reactive. It can\nassist in asynchronous scenarios to propagate `ThreadLocal` values from one thread to\nanother. It can also propagate to any other type of context for which there is a\nregistered `ContextAccesor` instance.\n\n== Installing\n\nSnapshots are published to https://repo.spring.io/snapshot for every successful build on the `main` branch and maintenance branches.\n\nMilestone releases are published to https://repo.spring.io/milestone. Include that as a Maven repository in your build\nconfiguration to use milestone releases. Note that milestone releases are for testing purposes and are not intended for\nproduction use.\n\nThe following example shows the required dependency in Gradle:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:context-propagation:latest.integration\'\n----\n\nThe following example shows the required dependency in Maven:\n\n[source,xml,subs=+attributes]\n----\n\n \n io.micrometer\n context-propagation\n ${micrometer-context-propagation.version}\n \n\n----\n\n== Usage Examples\n\n:leveloffset: +1\n\n== `ThreadLocal` Population\n\nBelow you can find a holder for `ThreadLocal` values.\n\n.ObservationThreadLocalHolder\n[source,java,subs=+attributes]\n-----\n/**\n * Example of a wrapper around ThreadLocal values.\n */\npublic class ObservationThreadLocalHolder {\n\n private static final ThreadLocal holder = new ThreadLocal<>();\n\n public static void setValue(String value) {\n holder.set(value);\n }\n\n public static String getValue() {\n return holder.get();\n }\n\n public static void reset() {\n holder.remove();\n }\n\n}\n-----\n\nBelow you can find a `ThreadLocalAccessor` that interacts with the holder.\n\n.ObservationThreadLocalAccessor\n[source,java,subs=+attributes]\n-----\n/**\n * Example {@link ThreadLocalAccessor} implementation.\n */\npublic class ObservationThreadLocalAccessor implements ThreadLocalAccessor {\n\n public static final String KEY = "micrometer.observation";\n\n @Override\n public Object key() {\n return KEY;\n }\n\n @Override\n public String getValue() {\n return ObservationThreadLocalHolder.getValue();\n }\n\n @Override\n public void setValue(String value) {\n ObservationThreadLocalHolder.setValue(value);\n }\n\n @Override\n public void setValue() {\n ObservationThreadLocalHolder.reset();\n }\n\n}\n-----\n\nBelow you can find an example of how to store and restore thread local values via `ThreadLocalAccessor`, `ContextSnapshot` and `ContextRegistry`.\n\n[source,java,subs=+attributes]\n-----\n// Create a new Context Registry (you can use a global too)\nContextRegistry registry = new ContextRegistry();\n// Register thread local accessors (you can use SPI too)\nregistry.registerThreadLocalAccessor(new ObservationThreadLocalAccessor());\n\n// When you set a thread local value...\nObservationThreadLocalHolder.setValue("hello");\n// ... we can capture it using ContextSnapshot\nContextSnapshot snapshot = ContextSnapshotFactory.builder().contextRegistry(registry).build().captureAll();\n\n// After capturing if you change the thread local value again ContextSnapshot will\n// not see it\nObservationThreadLocalHolder.setValue("hola");\ntry {\n // We\'re populating the thread local values with what we had in\n // ContextSnapshot\n try (Scope scope = snapshot.setThreadLocals()) {\n // Within this scope you will see the stored thread local values\n then(ObservationThreadLocalHolder.getValue()).isEqualTo("hello");\n }\n // After the scope is closed we will come back to the previously present\n // values in thread local\n then(ObservationThreadLocalHolder.getValue()).isEqualTo("hola");\n}\nfinally {\n // We\'re clearing the thread local values so that we don\'t pollute the thread\n ObservationThreadLocalHolder.reset();\n}\n-----\n\n:leveloffset!:\n'},function(e,t,n){var r={"./appOptics.adoc":88,"./atlas.adoc":91,"./azure-monitor.adoc":95,"./cloudwatch.adoc":96,"./datadog.adoc":97,"./dynatrace.adoc":98,"./elastic.adoc":99,"./ganglia.adoc":100,"./graphite.adoc":102,"./hierarchical-name-mapping.adoc":104,"./humio.adoc":105,"./influx.adoc":107,"./install.adoc":108,"./instana.adoc":109,"./jmx.adoc":110,"./kairos.adoc":112,"./new-relic.adoc":113,"./otlp.adoc":118,"./prometheus.adoc":119,"./signalFx.adoc":125,"./stackdriver.adoc":131,"./statsD.adoc":132,"./wavefront.adoc":133};function a(e){var t=i(e);return n(t)}function i(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=i,e.exports=a,a.id=87},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer AppOptics\n:toc:\n:sectnums:\n:system: appoptics\n\nAppOptics is a dimensional time-series SaaS with built-in dashboarding.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures an AppOptics instance:\n\n[source,java]\n----\nAppOpticsConfig appopticsConfig = new AppOpticsConfig() {\n @Override\n public String apiToken() {\n return MY_TOKEN;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null;\n }\n};\nMeterRegistry registry = new AppOpticsMeterRegistry(appopticsConfig, Clock.SYSTEM);\n----\n\n`AppOpticsConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.appoptics` directly to the `AppOpticsConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.appoptics:\n api-token: YOURKEY\n\n # You will probably want disable AppOptics publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to AppOptics. The default is 1 minute.\n step: 1m\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in AppOptics for metrics that originate in Micrometer.\n\n=== Timers\n\nThe AppOptics implementation of `Timer` produces three fields in AppOptics:\n\n* `sum`: Rate of calls per second.\n* `count`: Rate of total time per second.\n* `max`: A sliding window maximum amount recorded.\n\n.Dimensionally aggregable average in AppOptics.\nimage::"+n(89)+"[AppOptics timer average]\n\nAppOptics performs the `sum/count` division dimensionally to generate aggregable averages on your behalf.\n\n.Timer over a simulated service.\nimage::"+n(90)+"[AppOptics-rendered timer]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"0592f647d85fc092e402fdfb77234e24.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"6bf5cf6071710d129ffaadb50d2b0874.png"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Atlas\n:toc:\n:sectnums:\n:system: atlas\n\nAtlas is an in-memory dimensional time series database with built-in graphing, a custom stack-based query language, and advanced math operations. Atlas originated at Netflix, where it remains the operational metrics solution.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\n[source,java]\n----\nAtlasConfig atlasConfig = new AtlasConfig() {\n @Override\n public Duration step() {\n return Duration.ofSeconds(10);\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM);\n----\n\nMicrometer uses Netflix's https://github.com/netflix/spectator[Spectator] as the underlying instrumentation library when recording metrics destined for Atlas. `AtlasConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties prefixed with `management.metrics.export.atlas` directly to the `AtlasConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.atlas:\n # The location of your Atlas server\n uri: http://localhost:7101/api/v1/publish\n\n # You will probably want to conditionally disable Atlas publishing in local development.\n enabled: true\n\n # The interval at which metrics are sent to Atlas. The default is 1 minute.\n step: 1m\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Atlas for metrics originating in Micrometer. See the https://github.com/netflix/atlas/wiki[Atlas wiki] for a far more complete reference of what is possible in Atlas.\n\n=== Counters\n\nAtlas serves up graphs in the form of PNG images (and other https://github.com/Netflix/atlas/wiki/Output-Formats[output formats] as well).\n\nWe use the following query to visualize the counter from Atlas. Note that the value is rate-normalized over the step interval rather than monotonically increasing. Atlas always expects link:/docs/concepts#_client_side[rate-aggregated] data for counters from Micrometer.\n\n.Counter over a positive-biased random walk.\nimage::"+n(92)+'[Atlas-rendered counter]\n\n[source,http]\n----\nGET /api/v1/graph?\n q=\n name,counter,:eq,\n 2,:lw\n &tz=US/Central\n &s=e-15m <1>\n &w=400 <2>\n &l=0 <3>\nHost: localhost:7101\n----\n<1> The range of time we want to visualize along the x-axis. `e` represents the end time or "`now`". This graph\'s axis is from 15 minutes ago until now. Atlas automatically chooses the finest grained step interval available from the data that would render at least 1px wide on the resultant image.\n<2> The overall width of the PNG image returned should be 400px.\n<3> Set the y-axis lower limit to 0 so that random perturbation in the walk does not look so dramatic.\n\n=== Timers\n\nWhile reading directly from a `Timer` returns a `double`, the underlying value is\nstored in https://github.com/netflix/spectator[Spectator] as a nanosecond-precise `long`. What precision is lost by\nconverting to a `double` in the `Timer` interface does not affect a system like\nAtlas, because it has been configured to read measurements from the underlying\nSpectator `Timer` that Micrometer is hiding from you.\n\nThe Spectator Atlas `Timer` produces four time series, each with a different `statistic` tag:\n\n* `count`: Rate of calls per second.\n* `totalTime`: Rate of total time per second.\n* `totalOfSquares`: Rate of total time squared per second (useful for standard deviation).\n* `max`: The maximum amount recorded.\n\nTherefore, you can achieve a throughput (requests/second) line with the following query:\n\n```http\nname,timer,:eq,statistic,count,:eq,:and\n```\n\nNotice that `statistic` is just a dimension that can be drilled down and selected like any other.\n\nFurthermore, `totalTime/count` represents average latency and can be selected with a short-hand `:dist-avg` query, which selects the `totalTime` and `count` time series and performs the division for us:\n\n```http\nname,timer,:eq,:dist-avg\n```\n\nIn the preceding example, you can see these two lines plotted on a single dual-axis graph.\n\n.Timer over a simulated service.\nimage::'+n(93)+"[Atlas-rendered timer]\n\n\n=== Long task timers\n\nSuppose we had a task that took two minutes to complete when it was expected to complete in less than 70 seconds. A key benefit of long task timers is the ability to receive an alert at the first reporting interval after we have exceeded the threshold. With a regular timer, we would not receive an alert until the first reporting interval after the process completed. If we had a ten-second publishing interval, the regular timer alert would arrive almost a minute after the long task timer alert.\n\n.Simulated back-to-back long tasks.\nimage::"+n(94)+"[Atlas-rendered long task timer]\n\n[source, http]\n----\nGET /api/v1/graph?\n q=\n name,longTaskTimer,:eq,statistic,duration,:eq,:and, <1>\n :dup,\n 70,:gt,:vspan,f00,:color,40,:alpha,alerted,:legend, <2>\n 70,f00,:color,alert+threshold,:legend <3>\n &tz=US/Central\n &s=e-15m\n &w=400\n &l=0\n &title=Peaks+of+Long+Tasks\n &ylabel=time\nHost: localhost:7101\n----\n<1> A representation of long tasks that are happening back-to-back.\n<2> A vertical span that appears whenever the long task exceeds our threshold of 70 seconds. So that it does not overwhelm the graph, we also decrease the opacity of the vspan.\n<3> Plot the threshold of 70 seconds as a separate line.\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"b22351bea254b10bef1c26d3cc3ef397.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"4ce8699a76d05ed4311151acc1772b1f.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"1127e78d5d7524b0c0540de3d2a732b2.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Azure Monitor\n:toc:\n:sectnums:\n:system: azure-monitor\n\nAzure Monitor is a dimensional time-series SaaS with built-in dashboarding.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures a Micrometer Azure Monitor:\n\n[source,java]\n----\nAzureMonitorConfig azureMonitorConfig = new AzureMonitorConfig() {\n @Override\n public String instrumentationKey() {\n return MY_KEY;\n }\n\n @Override\n public String get(String key) {\n return null;\n }\n};\nMeterRegistry registry = new AzureMonitorMeterRegistry(azureMonitorConfig, Clock.SYSTEM);\n----\n\n`AzureMonitorConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.azure-monitor` directly to the `AzureMonitorConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.azure-monitor:\n instrumentation-key: YOURKEY\n\n # You will probably want disable Azure Monitor publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Azure Monitor. The default is 1 minute.\n step: 1m\n----\n"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer CloudWatch\nTommy Ludwig \n:toc:\n:sectnums:\n:system: cloudwatch2\n\nhttps://aws.amazon.com/cloudwatch/[Amazon CloudWatch] is a dimensional time-series SaaS on Amazon's cloud.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\nNOTE: The `micrometer-registry-cloudwatch2` module uses AWS SDK v2. `micrometer-registry-cloudwatch` is for AWS SDK v1.\n\n== Configuring\n\nThe following example configures Micrometer CloudWatch:\n\n[source,java]\n----\nCloudWatchConfig cloudWatchConfig = new CloudWatchConfig() {\n @Override\n public String get(String s) {\n return null;\n }\n\n @Override\n public String namespace() {\n return \"mynamespace\";\n }\n};\nMeterRegistry meterRegistry = new CloudWatchMeterRegistry(cloudWatchConfig, Clock.SYSTEM, CloudWatchAsyncClient.create());\n----\n\nYou can provide your own `CloudWatchAsyncClient` to the constructor of the registry.\n\n`CloudWatchConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, https://docs.awspring.io/spring-cloud-aws/docs/current/reference/html/index.html#cloudwatch-metrics[Micrometer support in Spring Cloud AWS] binds properties prefixed with `management.metrics.export.cloudwatch` directly to the `CloudWatchConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.cloudwatch:\n namespace: YOURNAMESPACE\n\n # You will probably want to disable publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to CloudWatch. The default is 1 minute.\n step: 1m\n----\n"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Datadog\n:toc:\n:sectnums:\n:system: datadog\n\nDatadog is a dimensional time-series SaaS with built-in dashboarding and alerting.\n\n== Installation and Configuration\n\nMicrometer supports shipping metrics to Datadog directly by using its HTTP API or by using DogStatsD through the link:/docs/registry/statsD[StatsD registry].\nThe API approach is far more efficient if you need to choose between the two.\n\n=== Direct to Datadog API Approach\n\nFor Gradle, add the following implementation:\n\n[source,groovy]\n----\nimplementation \'io.micrometer:micrometer-registry-datadog:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml]\n----\n\n io.micrometer\n micrometer-registry-datadog\n ${micrometer.version}\n\n----\n\nMetrics are rate-aggregated and pushed to `datadoghq` on a periodic interval. Rate aggregation performed by the registry yields datasets that are similar to those produced by `dogstatsd`.\n\n[source, java]\n----\nDatadogConfig config = new DatadogConfig() {\n @Override\n public Duration step() {\n return Duration.ofSeconds(10);\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new DatadogMeterRegistry(config, Clock.SYSTEM);\n----\n\n`DatadogConfig` is an interface with a set of default methods.\nIf, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration through properties.\nFor example, Spring Boot\'s Micrometer support binds properties directly to the `DatadogConfig`.\nSee the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.export.datadog[Datadog] section in the Spring Boot reference documentation.\n\n`DatadogConfig.hostTag()` specifies a tag key that is mapped to https://docs.datadoghq.com/api/v1/metrics/#submit-metrics[the `host` field] when shipping metrics to Datadog.\nFor example, if `DatadogConfig.hostTag()` returns `host`, the tag having `host` as its key is used.\nYou can set the tag by using common tags, as follows:\n\n[source,java]\n----\nregistry.config().commonTags("host", "my-host");\n----\n\n`uri` is an important property to configure.\nThe default value is `https://api.datadoghq.com`.\nDepending on the Datadog site (region), the api endpoint will be different.\nTo find your the correct `uri` for your account, do the following:\n\n1. Read about https://docs.datadoghq.com/getting_started/site/[Datadog site].\n2. Go to https://docs.datadoghq.com/api/latest/metrics/[Metrics API reference] and select your own option from the "DATADOG SITE" dropdown.\n3. Check any API request\'s endpoint.\n\nE.g. For `US5` site, the correct API endpoint is `https://api.us5.datadoghq.com` while for `US3` site, it is `https://api.us3.datadoghq.com/`.\n\n=== Through DogStatsD Approach\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-statsd:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-statsd\n ${micrometer.version}\n\n----\n\nMetrics are immediately shipped to DogStatsD using Datadog\'s flavor of the StatsD line protocol. `java-dogstatsd-client` is _not_ needed on the classpath for this to work, as Micrometer uses its own implementation.\n\n[source,java]\n----\nStatsdConfig config = new StatsdConfig() {\n @Override\n public String get(String k) {\n return null;\n }\n\n @Override\n public StatsdFlavor flavor() {\n return StatsdFlavor.DATADOG;\n }\n};\n\nMeterRegistry registry = new StatsdMeterRegistry(config, Clock.SYSTEM);\n----\n\nMicrometer supports DogStatsD\'s https://docs.datadoghq.com/developers/dogstatsd/?tab=kubernetes#origin-detection-over-udp[origin detection over UDP] feature on Kubernetes if the `DD_ENTITY_ID` environment variable is properly set.\n\nMicrometer, by default, publishes `Timer` meters to DogStatsD as the StatsD "timing" metric type `ms`,\nwhich are sent to Datadog as https://docs.datadoghq.com/metrics/types/?tab=histogram#metric-types[histogram] type metrics.\nMicrometer publishes `DistributionSummary` meters as histogram type metrics by default, also.\n\nWhen `percentileHistogram` is enabled for the meter, Micrometer sends `Timer` and `DistributionSummary` meters as Datadog https://docs.datadoghq.com/metrics/distributions[Distributions] to DogStatsD.\nYou can make a `DistributionSummary` with `percentileHistogram` enabled as follows:\n\n[source,java]\n----\nDistributionSummary responseSizeSummary = DistributionSummary.builder("http.server.response.size")\n .baseUnit("bytes")\n .publishPercentileHistogram()\n .register(registry);\n----\n'},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Dynatrace\n:toc:\n:sectnums:\n:system: dynatrace\n\nhttps://www.dynatrace.com/[*Dynatrace*] is a Software Intelligence Platform featuring application performance monitoring (APM), artificial intelligence for operations (AIOps), IT infrastructure monitoring, digital experience management (DEM), and digital business analytics capabilities.\nIt can ingest multi-purpose dimensional time-series data and has built-in dashboarding.\nBoth SaaS and self-hosted (Managed) deployments are offered.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-{system}:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nFor setting up new integrations with Dynatrace, it is recommended to use the latest version of the https://docs.dynatrace.com/docs/shortlink/api-metrics-v2[Dynatrace Metrics API] (v2).\nIf you are using Micrometer with Spring Boot, please also refer to the https://docs.dynatrace.com/docs/shortlink/micrometer-metrics-ingest[Dynatrace documentation] and/or the https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.metrics.export.dynatrace[Spring Boot documentation].\nDynatrace provides different ways of setting up integrations:\n\n=== Using Dynatrace auto-configuration (preferred) [[bookmark-auto-configuration]]\n\nDynatrace auto-configuration is available for hosts that are monitored by a OneAgent or by the Dynatrace Operator for Kubernetes.\n\nIf a Dynatrace OneAgent is installed on the host running Micrometer, metrics can be exported directly using the OneAgent without having to specify an endpoint URI or API token.\nIf running in Kubernetes with the Dynatrace operator installed, the registry will pick up your endpoint URI and API token from the operator instead.\nIn this case there is no need to configure anything, so you can use the following code in your project to export Micrometer metrics to Dynatrace:\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n @Nullable\n public String get(String k) {\n // This method of the interface is used by the other configuration methods and needs to be\n // implemented here. Returning null accepts the defaults for the other configuration items.\n return null;\n }\n};\nMeterRegistry registry = new DynatraceMeterRegistry(dynatraceConfig, Clock.SYSTEM);\n----\n\nIf you are using Micrometer 1.10.0 or above, you can also use the DEFAULT config to achieve the same with less code:\n\n[source,java]\n----\nMeterRegistry registry = new DynatraceMeterRegistry(DynatraceConfig.DEFAULT, Clock.SYSTEM);\n----\n\nIt is also possible to change other properties by creating an instance of the `DynatraceConfig` and overwriting the respective methods.\nFor example, you can specify the exporter version, which defaults to `v2` unless a deviceId is set:\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n public DynatraceApiVersion apiVersion() {\n return DynatraceApiVersion.V2;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new DynatraceMeterRegistry(dynatraceConfig, Clock.SYSTEM);\n----\n\n`DynatraceConfig` is an interface with a set of default methods.\nSpring Boot\'s Micrometer support binds properties prefixed with `management.dynatrace.metrics.export` directly to the `DynatraceConfig`.\n\nNOTE: Property names for binding attributes from Spring Boot have changed in Spring Boot version 3.0.0. If you use a Spring Boot version before 3.0.0, use `management.metrics.export.dynatrace` instead of `management.dynatrace.metrics.export`.\n\nUsing Spring Boot Micrometer support allows configuring the Dynatrace exporter by using <>.\nWhen using Micrometer with Spring Boot, you don\'t have to instantiate the `DynatraceMeterRegistry` manually as Spring Boot will do it automatically for you.\nAll configuration options that can be set by overwriting methods can also be set via Spring Boot properties, and adding a separate MeterRegistry can lead to metrics not being exported as auto-configuration might break.\n\nTo use the Dynatrace metrics exporter for Micrometer in your Spring Boot project, it is enough to include the `runtimeOnly \'io.micrometer:micrometer-registry-dynatrace\'` dependency.\nIn this default configuration, metrics will be exported to the local OneAgent or Kubernetes operator-provided endpoint.\n\n=== Using a custom endpoint\n\nIf auto-configuration is not available on the host, both the Dynatrace Metrics API v2 endpoint and an API token have to be specified.\nThe https://docs.dynatrace.com/docs/shortlink/api-authentication[Dynatrace API token documentation] contains more information on how to create an API token.\nThe \'Ingest metrics\' (`metrics.ingest`) permission is required on the token in order to ingest metrics.\nIt is recommended to limit scope to only this permission.\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n public DynatraceApiVersion apiVersion() {\n // not strictly required, but makes the code more clear/explicit\n return DynatraceApiVersion.V2;\n }\n\n @Override\n public String uri() {\n // The endpoint of the Dynatrace Metrics API v2 including path, e.g.:\n // "https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest"\n String endpoint = System.getenv("ENVVAR_METRICS_INGEST_URL");\n return endpoint != null ? endpoint : DynatraceConfig.super.uri();\n }\n\n @Override\n public String apiToken() {\n // should be read from a secure source\n String token = System.getenv("ENVVAR_METRICS_INGEST_TOKEN");\n return token != null ? token : "";\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new DynatraceMeterRegistry(dynatraceConfig, Clock.SYSTEM);\n----\n\nThese properties can also be set via Spring Boot, using property or yaml files.\nIt is also possible to reference environment variables using the Spring property placeholders (e.g.: `management.dynatrace.metrics.export.uri: ${DT_METRICS_INGEST_URL}`).\n\nNOTE: `v2` is used as the default API version unless a `deviceId` is set (<>).\n\n[source,yml]\n----\n# For Spring Boot 3.0.0 and above:\nmanagement.dynatrace.metrics.export:\n# For Spring Boot versions below 3.0.0, use the line below instead of the line above:\n# management.metrics.export.dynatrace:\n # for SaaS: https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest\n # for managed deployments: https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest\n uri: YOUR_METRICS_INGEST_URL\n\n # should be read from a secure source\n api-token: YOUR_METRICS_INGEST_TOKEN\n----\n\n=== Meter metadata\n\nStarting with Micrometer 1.12.0, the Dynatrace registry v2 exports meter metadata to Dynatrace.\nCurrently supported types of metadata are *unit* (called "base unit" in Micrometer) and *description*.\nNo changes are required to start exporting Metadata to Dynatrace - upgrading to version 1.12.0 or above is enough.\nFind more information about metrics metadata in the https://docs.dynatrace.com/docs/shortlink/metric-ingestion-protocol#metadata[Dynatrace documentation].\n\nThe export of metrics metadata can be disabled by setting the `exportMeterMetadata` property on the `DynatraceConfig` (see <> below) to `false`.\n\n== API Versions\n\n=== API v2 [[bookmark-apiv2]]\n\nWhen the API version is configured to `v2`, the registry will send data using the https://docs.dynatrace.com/docs/shortlink/api-metrics-v2[Metrics API v2].\nIn order to maintain backwards compatibility, when a `deviceId` is set (which is required for `v1` and not used in `v2`), `v1` is used as the default.\nOtherwise, the version defaults to `v2`, and does not have to be set explicitly.\nWith no endpoint URI and token set, metrics will be exported to the local OneAgent endpoint or, if running in Kubernetes with the Dynatrace operator installed, to the endpoint provided by the operator.\nIf no auto-configuration is desired, it is possible to specify endpoint and token explicitly, in order to export metrics to that specific endpoint.\nExplicitly specifying these will overwrite auto-configuration.\n\n*Minimal configuration with Dynatrace auto-configuration*\n\nIn the minimal configuration <> (no URI or API token), the v2 registry will attempt to retrieve the endpoint provided by the Dynatrace Kubernetes operator.\nIf the operator is not set up or does not provide this information, the exporter will attempt to send metrics to the https://docs.dynatrace.com/docs/shortlink/local-api[local OneAgent metrics ingest endpoint].\nNote that this only works if a OneAgent is running on the host and the https://docs.dynatrace.com/docs/shortlink/local-api#enable-the-oneagent-metric-api[local OneAgent Metric API] is available.\nIf the ingestion port for the local OneAgent was changed to a custom one, the full endpoint URI has to be provided for the URI property (with API token left empty).\n\n*Configuration with URI and API token*\n\nIf no auto-configuration is available or the metrics should be sent to a different endpoint (e.g. a different tenant), the Dynatrace v2 exporter can be configured with an explicit endpoint URI and an https://docs.dynatrace.com/docs/shortlink/api-authentication[API token].\nThe https://docs.dynatrace.com/docs/shortlink/api-authentication[API token] must have the https://docs.dynatrace.com/docs/dynatrace-api/basics/dynatrace-api-authentication#token-scopes["Ingest metrics"] (`metrics.ingest`) permission set.\nIt is recommended to limit scope to only this permission.\n\nThe entire Metrics v2 API endpoint URI has to be specified including its path, i.e.: with the path `/api/v2/metrics/ingest` on SaaS and managed deployments, or `/metrics/ingest` for OneAgent endpoints as mentioned in the https://docs.dynatrace.com/docs/shortlink/api-metrics-v2-post-datapoints[documentation].\n\n*Properties available in the v2 exporter* [[bookmark-available-properties]]\n\nWhen using the https://docs.dynatrace.com/docs/shortlink/api-metrics-v2[Dynatrace metrics API v2], the following properties can be set:\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n public DynatraceApiVersion apiVersion() {\n return DynatraceApiVersion.V2;\n }\n\n @Override\n public String uri() {\n // The endpoint of the Dynatrace Metrics API v2 including path, e.g.:\n // "https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest".\n String endpoint = System.getenv("ENVVAR_METRICS_INGEST_URL");\n return endpoint != null ? endpoint : DynatraceConfig.super.uri();\n }\n\n @Override\n public String apiToken() {\n // should be read from a secure source\n String token = System.getenv("ENVVAR_METRICS_INGEST_TOKEN");\n return token != null ? token : "";\n }\n\n @Override\n public String metricKeyPrefix() {\n // will be prepended to all metric keys\n return "your.desired.prefix";\n }\n\n @Override\n public boolean enrichWithDynatraceMetadata() {\n return true;\n }\n\n @Override\n public Map defaultDimensions() {\n // create and return a map containing the desired key-value pairs.\n Map dims = new HashMap<>();\n dims.put("dimensionKey", "dimensionValue");\n return dims;\n }\n\n // Only available in Micrometer 1.9.0 and above\n @Override\n public boolean useDynatraceSummaryInstruments() {\n return false;\n }\n\n // Only available in Micrometer 1.12.0 and above\n @Override\n public boolean exportMeterMetadata() {\n return true;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n----\n\nThese properties can also be set in Spring Boot configuration files:\n\n[source,yml]\n----\nmanagement.dynatrace.metrics.export:\n # Required only if not using the OneAgent endpoint\n # For SaaS: https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest\n # For managed deployments: https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest\n uri: YOUR_METRICS_INGEST_URL\n\n # should be read from a secure source\n api-token: YOUR_METRICS_INGEST_TOKEN\n\n # These properties can only be used with the v2 exporter.\n v2:\n # Sets a prefix that is prepended to each exported metric key.\n metric-key-prefix: my.metric.key.prefix\n\n # If set to true and a local OneAgent or operator is running, retrieves metadata\n # and adds it as additional dimensions to all data points (default: true)\n enrich-with-dynatrace-metadata: true\n\n # Sets an arbitrary number of key-value pairs as default dimensions.\n # Micrometer tags will overwrite these dimensions, if they have the same key.\n # Each exported metric will contain these dimensions.\n default-dimensions:\n key1: "value1"\n key2: "value2"\n\n # (since 1.9.0) Whether or not to use the Dynatrace-specific summary instruments. (default: true)\n # This should only be disabled if problems with existing instrumentation are discovered after upgrading to 1.9.0.\n # Set to false, this will restore the previous (1.8.x) behavior for Timers and DistributionSummaries.\n use-dynatrace-summary-instruments: true\n\n # (since 1.12.0) Determines whether meter metadata (unit, description) should be exported.\n export-meter-metadata: true\n\n # The export interval in which metrics are sent to Dynatrace (default: 60s).\n step: 60s\n----\n\nFor more information about the metadata picked up by the Dynatrace metadata enrichment feature, see https://docs.dynatrace.com/docs/shortlink/enrichment-files[the Dynatrace documentation].\n\nIn Micrometer 1.9.0, Dynatrace-specific summary instruments (`DynatraceTimer` and `DynatraceDistributionSummary`) were introduced.\nThese specialized instruments are tailored to the Dynatrace metrics ingest, and prevent the creation of invalid metrics.\nThey are available from version 1.9.0 and are used as a drop-in replacement by default.\nNo action is needed from users upgrading to 1.9.0. If there is a discrepancy in the observed metrics, it is possible to return to the previous behavior by setting the `useDynatraceSummaryInstruments` toggle to `false`.\n\n=== API v1 (Legacy) [[bookmark-apiv1]]\n\nWhen the apiVersion is configured to `v1`, the registry will send data using the https://docs.dynatrace.com/docs/shortlink/api-custom-metrics[Dynatrace Timeseries API v1 for custom metrics].\nIf a `deviceId` is specified, it will default to `v1` for backwards compatibility with earlier setups.\nThe `device-id` property is required for `v1` and not used in `v2`.\nExisting setups will continue to work when updating to newer versions of Micrometer.\nThe reported metrics will be assigned to https://docs.dynatrace.com/docs/shortlink/api-custom-device-report-metric[custom devices] in Dynatrace.\n\nFor the v1 API, do not specify the ingest path, but only the base URL of your environment, e.g.: `uri: https://{your-environment-id}.live.dynatrace.com`\n\n[source,java]\n----\nDynatraceConfig dynatraceConfig = new DynatraceConfig() {\n @Override\n public String uri() {\n // The Dynatrace environment URI without any path, e.g.:\n // https://{your-environment-id}.live.dynatrace.com\n return MY_DYNATRACE_URI;\n }\n\n @Override\n public String apiToken() {\n // should be read from a secure source\n return MY_TOKEN;\n }\n\n @Override\n public String deviceId() {\n return MY_DEVICE_ID;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new DynatraceMeterRegistry(dynatraceConfig, Clock.SYSTEM);\n----\n\n[source,yml]\n----\nmanagement.dynatrace.metrics.export:\n # For v1 export, do not append a path to the endpoint URL, e.g.:\n # For SaaS: https://{your-environment-id}.live.dynatrace.com\n # For managed deployments: https://{your-domain}/e/{your-environment-id}\n uri: https://{your-environment-id}.live.dynatrace.com\n\n # should be read from a secure source\n api-token: MY_TOKEN\n\n # When setting the device id, metrics will be exported to the v1 timeseries endpoint\n # Using just device-id (without the v1 prefix) is deprecated, but will work to maintain backwards compatibility.\n v1:\n device-id: sample\n\n # To disable Dynatrace publishing, e.g. in a local development profile, use:\n # enabled: false\n\n # The interval at which metrics are sent to Dynatrace. The default is 1 minute.\n step: 1m\n----\n'},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Elastic\n:toc:\n:sectnums:\n:system: elastic\n\nElasticsearch is an open source search and analytics platform. Metrics stored in Elasticsearch can be visualized in Kibana.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures an ElasticSearch instance:\n\n[source,java]\n----\nElasticConfig elasticConfig = new ElasticConfig() {\n @Override\n @Nullable\n public String get(String k) {\n return null;\n }\n};\nMeterRegistry registry = new ElasticMeterRegistry(elasticConfig, Clock.SYSTEM);\n----\n\n`ElasticConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.elastic` directly to the `ElasticConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.elastic:\n # You will probably want disable Elastic publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Elastic. The default is 1 minute.\n step: 1m\n\n # The index to store metrics in, defaults to \"micrometer-metrics\"\n index: micrometer-metrics\n----\n\n== Elastic APM agent integration\n\nIf you are using the Elastic APM agent, it can collect metrics from Micrometer `MeterRegistry` instances automatically. You can use the `SimpleMeterRegistry` if you only want metrics collected by the Elastic APM agent and not shipped anywhere else. See the https://www.elastic.co/guide/en/apm/agent/java/current/metrics.html#metrics-micrometer[Elastic docs] for more details.\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Ganglia\n:toc:\n:sectnums:\n:system: ganglia\n\nGanglia is an aging hierarchical metrics system which enjoyed wide popularity in Linux system monitoring and is still in place in many organizations. It originated in the early 2000s at the University of California, Berkeley.\n\nNOTE: The `micrometer-registry-ganglia` module uses the https://github.com/ganglia/gmetric4j[gmetric4j] library, which contains classes generated by the LGPL licensed https://sourceforge.net/projects/remotetea/[remotetea project].\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures a Ganglia instance:\n\n[source,java]\n----\nGangliaConfig gangliaConfig = new GangliaConfig() {\n @Override\n public String host() {\n return \"mygraphitehost\";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n\nMeterRegistry registry = new GangliaMeterRegistry(gangliaConfig, Clock.SYSTEM);\n----\n\nMicrometer uses Dropwizard Metrics as the underlying instrumentation library when recording metrics destined for Ganglia. `GangliaConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.ganglia` directly to the `GangliaConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.ganglia:\n # The location of your Ganglia server\n host: mygraphitehost\n\n # You will probably want to conditionally disable Ganglia publishing in local development.\n enabled: true\n\n # The interval at which metrics are sent to Ganglia. The default is 1 minute.\n step: 1m\n----\n\n== Hierarchical name mapping\n\nMicrometer provides a `HierarchicalNameMapper` interface that governs how a dimensional meter ID is mapped to flat hierarchical names.\n\nThe default (`HierarchicalNameMapper.DEFAULT`) sorts tags alphabetically by key and appends tag key/value pairs to the base meter name with '.' -- for example, `http_server_requests.method.GET.response.200`. The name and tag keys have the registry's naming convention applied to them first.\n\nIf there is something special about your naming scheme that you need to honor, you can provide your own `HierarchicalNameMapper` implementation. The most common cause of a custom mapper comes from a need to prefix something to the front of every metric (generally something like `app..http_server_requests.method.GET.response.200`).\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Ganglia for metrics originating in Micrometer.\n\n=== Counters\n\nGanglia counters measure mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughputs.\n\n.A Ganglia rendered graph of the random walk counter.\nimage::"+n(101)+"[Ganglia-rendered counter]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"80865a7f65d47863b3b264a9ed09629a.png"},function(e,t,n){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Graphite\n:toc:\n:sectnums:\n:system: graphite\n\nGraphite is one of the most popular current hierarchical metrics systems backed by a fixed-size database, similar in design and purpose to RRDtool. It originated at Orbitz in 2006 and was open sourced in 2008.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-{system}:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures a Graphite instance:\n\n[source,java]\n----\nGraphiteConfig graphiteConfig = new GraphiteConfig() {\n @Override\n public String host() {\n return "mygraphitehost";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n\nMeterRegistry registry = new GraphiteMeterRegistry(graphiteConfig, Clock.SYSTEM, HierarchicalNameMapper.DEFAULT);\n----\n\nMicrometer uses Dropwizard Metrics as the underlying instrumentation library when recording metrics destined for Graphite. `GraphiteConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer\'s Spring Boot support binds properties that are prefixed with `management.metrics.export.graphite` directly to the `GraphiteConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.graphite:\n # The location of your Graphite server\n host: mygraphitehost\n\n # You will probably want to conditionally disable Graphite publishing in local development.\n enabled: true\n\n # The interval at which metrics are sent to Graphite. The default is 1 minute.\n step: 1m\n----\n\n== Graphite Tag Support\n\nAs of Micrometer version 1.4.0, Micrometer supports exporting Graphite metrics by using tags instead of the traditional hierarchical format. By default, metrics are exported by using the tag format, unless any `tagsAsPrefix` values are configured.\nhttps://graphite.readthedocs.io/en/latest/tags.html[Tag support] was added to Graphite in the 1.1.0 Graphite release.\nIf you wish to revert to the traditional hierarchical format, ensure that the `graphiteTagsEnabled` config value is set to `false`.\nThe following documentation sections on hierarchical name mapping and metrics prefixing are only applicable if tag support is disabled.\n\n== Hierarchical name mapping\n\nMicrometer provides a `HierarchicalNameMapper` interface that governs how a dimensional meter ID is mapped to flat hierarchical names.\n\nThe default (`HierarchicalNameMapper.DEFAULT`) sorts tags alphabetically by key and appends tag key/value pairs to the base meter name with \'.\' -- for example, `http_server_requests.method.GET.response.200`. The name and tag keys have the registry\'s naming convention applied to them first.\n\nIf there is something special about your naming scheme that you need to honor, you can provide your own `HierarchicalNameMapper` implementation. The most common cause of a custom mapper comes from a need to prefix something to the front of every metric (generally something like `app..http_server_requests.method.GET.response.200`).\n\n== Prefixing your metrics\n\nTo add a prefix to all metrics that go to graphite, use the `GraphiteConfig#tagsAsPrefix` configuration option. This option applies the tag value of a set of common tags as a prefix. For example, if `tagsAsPrefix` contains `application`, and a meter named `myTimer` is created with a tag of `application=APPNAME`, it appears in Graphite as `APPNAME.myTimer`.\n\nGenerally, when you use `tagsAsPrefix`, you should add common tags to the registry so that the tags are present on all meters that belong to that registry:\n\n[source,java]\n----\n@Bean\npublic MeterRegistryCustomizer commonTags() {\n return r -> r.config().commonTags("application", "APPNAME");\n}\n----\n\nWe do it this way because, generally, a tag prefix in Graphite is correlated to a common tag elsewhere. Prefixes tend to be something like app name or host. By applying those values as common tags, you make your metrics more portable (that is, if you ever switch to a dimensional monitoring system, you are set).\n\nYou can use this when the order of the prefix matters. Micrometer always sorts tags, but the order of tag keys in `tagsAsPrefix` is preserved, so adding `host` and `application` to `tagsAsPrefix` results in a prefixed metric, such as `HOST.APP.myCounter`.\n\nTo meet your specific naming needs, you can also provide a custom hierarchical name mapper when creating `GraphiteMeterRegistry`, as follows:\n\n[source,java]\n----\nGraphiteMeterRegistry r = new GraphiteMeterRegistry(\n GraphiteConfig.DEFAULT,\n Clock.SYSTEM,\n (id, convention) -> "prefix." + HierarchicalNameMapper.DEFAULT.toHierarchicalName(id, convention));\n----\n\nNOTE: If you use a custom `HierarchicalNameMapper`, `tagsAsPrefix` is ignored.\n\n== Further Customizing the `GraphiteReporter`\n\nWe give you the option to configure `GraphiteReporter` yourself if you need further customization. To do so, use this constructor and provide your own `GraphiteReporter`:\n\n[source,java]\n----\nGraphiteMeterRegistry(GraphiteConfig config, Clock clock, HierarchicalNameMapper nameMapper,\n MetricRegistry metricRegistry, GraphiteReporter reporter)\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Graphite for metrics originating in Micrometer.\n\n=== Counters\n\nGraphite counters measure mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughputs.\n\n.A Graphite rendered graph of the random walk counter.\nimage::'+n(103)+"[Graphite-rendered counter]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"cd6b0314737fa94a81e42689517fd7fb.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n== Hierarchical name mapping\n\nMicrometer provides a `HierarchicalNameMapper` interface that governs how a dimensional meter ID is mapped to flat hierarchical names.\n\nThe default (`HierarchicalNameMapper.DEFAULT`) sorts tags alphabetically by key and appends tag key/value pairs to the base meter name with '.' -- for example, `http_server_requests.method.GET.response.200`. The name and tag keys have the registry's naming convention applied to them first.\n\nIf there is something special about your naming scheme that you need to honor, you can provide your own `HierarchicalNameMapper` implementation. The most common cause of a custom mapper comes from a need to prefix something to the front of every metric (generally something like `app..http_server_requests.method.GET.response.200`).\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Humio\n:toc:\n:sectnums:\n:system: humio\n\nHumio is a dimensional time-series SaaS with built-in dashboarding.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures a Humio instance:\n\n[source,java]\n----\nHumioConfig humioConfig = new HumioConfig() {\n @Override\n public String apiToken() {\n return MY_TOKEN;\n }\n\n @Override\n @Nullable\n public String get(String k) {\n return null;\n }\n};\nMeterRegistry registry = new HumioMeterRegistry(humioConfig, Clock.SYSTEM);\n----\n\n`HumioConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.humio` directly to the `HumioConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.humio:\n api-token: YOURKEY\n\n # You will probably want disable Humio publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Humio. The default is 1 minute.\n step: 1m\n\n # The cluster Micrometer will send metrics to. The default is \"https://cloud.humio.com\"\n uri: https://myhumiohost\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Humio for metrics originating in Micrometer.\n\n=== Timers\n\nThe Humio implementation of `Timer` produces four fields in Humio:\n\n* `sum`: Rate of calls per second.\n* `count`: Rate of total time per second.\n* `max`: A sliding window maximum amount recorded.\n* `avg`: A non-aggregable average for only this set of tag values.\n\nThe following query constructs a dimensionally aggregable average latency per URI:\n\n[source, text]\n----\nname = http_server_requests\n| timechart(uri, function=max(avg))\n----\n\n.Timer over a simulated service.\nimage::"+n(106)+"[Humio-rendered timer]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"6584fae5a94c66dd29f69056b031d7e3.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Influx\n:toc:\n:sectnums:\n:system: influx\n\nThe InfluxData suite of tools supports real-time stream processing and storage of time-series data. It supports downsampling, automatically expiring and deleting unwanted data, as well as backup and restore.\n\nThe InfluxMeterRegistry supports the 1.x InfluxDB API as well as the v2 API.\n\n== Configuring\n\nMicrometer supports shipping metrics to InfluxDB directly or through Telegraf through the StatsD registry.\n\n=== Direct to InfluxDB\n\nThe following example adds the required library in Gradle:\n\n[source,groovy]\n----\nimplementation 'io.micrometer:micrometer-registry-influx:latest.release'\n----\n\nThe following example adds the required library in Maven:\n\n[source,xml]\n----\n\n io.micrometer\n micrometer-registry-influx\n ${micrometer.version}\n\n----\n\nMetrics are rate-aggregated and pushed to InfluxDB on a periodic interval. Rate aggregation performed by the registry yields datasets that are quite similar to those produced by Telegraf. The following example configures a meter registry for InfluxDB:\n\n.InfluxDB 1.x configuration example\n[source, java]\n----\nInfluxConfig config = new InfluxConfig() {\n @Override\n public Duration step() {\n return Duration.ofSeconds(10);\n }\n\n @Override\n public String db() {\n return \"mydb\";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new InfluxMeterRegistry(config, Clock.SYSTEM);\n----\n\nTo ship metrics to InfluxDB 2.x, make sure to configure the `org` and `bucket` to which to write the metrics, as well as the authentication `token`.\n\n.InfluxDB v2 configuration example\n[source, java]\n----\nInfluxConfig config = new InfluxConfig() {\n\n @Override\n public String org() {\n return \"myorg\";\n }\n\n @Override\n public String bucket() {\n return \"app-metrics\";\n }\n\n @Override\n public String token() {\n return \"auth_token_here\"; // FIXME: This should be securely bound rather than hard-coded, of course.\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\nMeterRegistry registry = new InfluxMeterRegistry(config, Clock.SYSTEM);\n----\n\n`InfluxConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.influx` directly to the `InfluxConfig`:\n\n[source, yaml]\n----\nmanagement.metrics.export.influx:\n api-version: v2 # API version of InfluxDB to use. Defaults to 'v1' unless an org is configured. If an org is configured, defaults to 'v2'.\n auto-create-db: true # Whether to create the InfluxDB database if it does not exist before attempting to publish metrics to it. InfluxDB v1 only. (Default: true)\n batch-size: 10000 # Number of measurements per request to use for this backend. If more measurements are found, then multiple requests will be made. (Default: 10000)\n bucket: mybucket # Bucket for metrics. Use either the bucket name or ID. Defaults to the value of the db property if not set. InfluxDB v2 only.\n compressed: true # Whether to enable GZIP compression of metrics batches published to InfluxDB. (Default: true)\n connect-timeout: 1s # Connection timeout for requests to this backend. (Default: 1s)\n consistency: one # Write consistency for each point. (Default: one)\n db: mydb # Database to send metrics to. InfluxDB v1 only. (Default: mydb)\n enabled: true # Whether exporting of metrics to this backend is enabled. (Default: true)\n num-threads: 2 # Number of threads to use with the metrics publishing scheduler. (Default: 2)\n org: myorg # Org to write metrics to. InfluxDB v2 only.\n password: mysecret # Login password of the InfluxDB server. InfluxDB v1 only.\n read-timeout: 10s # Read timeout for requests to this backend. (Default: 10s)\n retention-policy: my_rp # Retention policy to use (InfluxDB writes to the DEFAULT retention policy if one is not specified). InfluxDB v1 only.\n step: 1m # Step size (i.e. reporting frequency) to use. (Default: 1m)\n token: AUTH_TOKEN_HERE # Authentication token to use with calls to the InfluxDB backend. For InfluxDB v1, the Bearer scheme is used. For v2, the Token scheme is used.\n uri: http://localhost:8086 # URI of the InfluxDB server. (Default: http://localhost:8086)\n user-name: myusername # Login user of the InfluxDB server. InfluxDB v1 only.\n----\n\n=== Through Telegraf\n\nTelegraf is a StatsD agent that expects a modified flavor of the StatsD line protocol.\n\nThe following listing adds the relevant library in Gradle:\n\n[source,groovy]\n----\nimplementation 'io.micrometer:micrometer-registry-statsd:latest.release'\n----\n\nThe following listing adds the relevant library in Maven:\n\n[source,xml]\n----\n\n io.micrometer\n micrometer-registry-statsd\n ${micrometer.version}\n\n----\n\nMetrics are shipped immediately over UDP to Telegraf by using Telegraf's flavor of the StatsD line protocol.\n\n[source,java]\n----\nStatsdConfig config = new StatsdConfig() {\n @Override\n public String get(String k) {\n return null;\n }\n\n @Override\n public StatsdFlavor flavor() {\n return StatsdFlavor.Telegraf;\n }\n};\n\nMeterRegistry registry = new StatsdMeterRegistry(config, Clock.SYSTEM);\n----\n"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Instana\nFabian Lange \n:toc:\n:sectnums:\n:system: instana\n\nInstana is an automatic application performance management and infrastructure monitoring system.\n\n== Installation and Configuration\n\nInstana automatically detects and reports all metrics without the need of any additional dependency or configuration.\nIt does so by detecting all instances of `io.micrometer.core.instrument.MeterRegistry` and collecting all registered `io.micrometer.core.instrument.Meter` instances from them.\n\nYou can run the Instana agent alongside your application by using Micrometer, and the Instana agent automatically monitors it.\n\n== Supported Metrics\n\n* **Timer**: The total time of recorded events, scaled to milliseconds.\n* **Counter**: The cumulative count since this counter was created.\n* **Gauge**: The current value.\n* **DistributionSummary**: The total number of all recorded events.\n* **LongTaskTimer**: The current number of tasks being executed.\n* **FunctionCounter**: The cumulative count since this counter was created.\n* **FunctionTimer**: The total time of all occurrences of the timed event.\n* **TimeGauge**: The current value, scaled to the appropriate base unit.\n\nThe metrics show up on the Java Virtual Machine dashboard in Instana. You can configure alerting based on these metrics.\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer JMX\n:toc:\n:sectnums:\n:system: jmx\n\nMicrometer provides a hierarchical mapping to JMX, primarily as a cheap and portable way to view metrics locally. Where JMX exporting is found in production, the same metrics are generally exported to another, more purpose-fit monitoring system.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\nMicrometer also sometimes scrapes data from JMX beans for use in reporting metrics. This registry implementation is not needed for these uses. Rather, this module is strictly used to _export_ data to JMX.\n\n== Hierarchical name mapping\n\nMicrometer provides a `HierarchicalNameMapper` interface that governs how a dimensional meter ID is mapped to flat hierarchical names.\n\nThe default (`HierarchicalNameMapper.DEFAULT`) sorts tags alphabetically by key and appends tag key/value pairs to the base meter name with '.' -- for example, `http_server_requests.method.GET.response.200`. The name and tag keys have the registry's naming convention applied to them first.\n\nIf there is something special about your naming scheme that you need to honor, you can provide your own `HierarchicalNameMapper` implementation. The most common cause of a custom mapper comes from a need to prefix something to the front of every metric (generally something like `app..http_server_requests.method.GET.response.200`).\n\n== Counters\n\nJMX counters measure mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughputs.\n\n.The JMX rendered values of the random walk counter.\nimage::"+n(111)+"[JMX-rendered counter]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"8077bd6e31e279e04a38e631b2d61a1e.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer KairosDB\n:toc:\n:sectnums:\n:system: kairos\n\nKairosDB is a dimensional time-series database built on top of Cassandra. Charting can be accomplished in Grafana by using a link:https://docs.grafana.org/v4.0/datasources/kairosdb/[Kairos datasource].\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures KairosDB:\n\n[source,java]\n----\nKairosConfig kairosConfig = new KairosConfig() {\n @Override\n @Nullable\n public String get(String k) {\n return null;\n }\n};\nMeterRegistry registry = new KairosMeterRegistry(kairosConfig, Clock.SYSTEM);\n----\n\n`KairosConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.kairos` directly to the `KairosConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.kairos:\n # You will probably want disable Kairos publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Kairos. The default is 1 minute.\n step: 1m\n\n # Authentication may be required, depending on how you have Kairos configured\n user-name: MYUSER\n password: MYPASSWORD\n----\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer New Relic\n:toc:\n:sectnums:\n:system: new-relic\n\nNew Relic offers a dimensional monitoring system product called Insights. It includes a full UI and a query language called NRQL. New Relic Insights operates on a push model. Some features of NRQL assume that Insights receives a distinct event payload for every timing, count, and so on. Micrometer instead ships aggregates at a prescribed interval, letting your app's throughput scale without concern for event propagation to Insights becoming a bottleneck.\n\nNOTE: New Relic provides its own Micrometer `MeterRegistry` implementation based on dimensional metrics.\nIt intends to supersede Micrometer's `NewRelicMeterRegistry` (which uses custom events in New Relic), because New Relic's dimensional metrics are a better fit for metrics than custom events.\nYou can find more details in https://github.com/newrelic/micrometer-registry-newrelic[its GitHub repository].\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures New Relic:\n\n[source,java]\n----\nNewRelicConfig newRelicConfig = new NewRelicConfig() {\n @Override\n public String accountId() {\n return \"MYACCOUNT\";\n }\n\n @Override\n public String apiKey() {\n return \"MY_INSIGHTS_API_KEY\";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n\nMeterRegistry registry = new NewRelicMeterRegistry(newRelicConfig, Clock.SYSTEM);\n----\n\nThere are two distinct sources of API keys in New Relic.\n\n`NewRelicConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.newrelic` directly to the `NewRelicConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.newrelic:\n account-id: MYACCOUNT\n api-key: MY_INSIGHTS_API_KEY\n\n # The interval at which metrics are sent to New Relic. See Duration.parse for the expected format.\n # The default is 1 minute.\n step: 1m\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in New Relic for metrics originating in Micrometer. See the https://docs.newrelic.com/docs/insights/nrql-new-relic-query-language/using-nrql/introduction-nrql[New Relic NRQL docs] for a far more complete reference of what is possible in New Relic.\n\n=== Timers\n\nAt each publishing interval, the New Relic `Timer` produces a single event with the timer's name and several attributes:\n\n* `avg`: A mean latency for the publishing interval.\n* `count`: Throughput per second over the publishing interval.\n* `totalTime`: Total time per second over the publishing interval (used with `count`) to create aggregable means.\n\nAdditionally, if any percentiles or SLO buckets are defined on the timer, additional events are produced:\n\n* `${name}.percentiles`: Micrometer calculated percentiles for the publishing interval. One event is produced for each percentile, with a tag of `phi` in the range of [0,1].\n* `${name}.histogram`: One event is produced for each SLO boundary with a tag of 'le', indicating that it represents a cumulative count of events less than or equal to SLO boundaries over the publishing interval.\n\nTo generate an aggregable view of latency in New Relic, divide `totalTime` by `count`:\n\n[source,sql]\n----\nSELECT sum(totalTime)/sum(count) as 'Average Latency', max(max) as 'Max' FROM timer since 30 minutes ago TIMESERIES auto\n----\n\n.Timer latency.\nimage::"+n(114)+"[New Relic-rendered timer]\n\nTo generate a throughput chart:\n\n[source,sql]\n----\nSELECT average(count) as 'Average Throughput' FROM timer since 30 minutes ago TIMESERIES auto\n----\n\n.Timer throughput.\nimage::"+n(115)+"[New Relic-rendered timer throughput]\n\nTo generate a plot of client-side percentiles:\n\n[source,sql]\n----\nSELECT latest(value) from timerPercentile FACET phi since 30 minutes ago TIMESERIES auto\n----\n\n.Timer Percentiles.\nimage::"+n(116)+"[New Relic-rendered percentiles]\n\nNote how these percentiles are _not aggregable_. We have selected the `latest(value)` function to display this chart (it isn't correct to `average(value)` on a percentile value). The more dimensions you add to a timer, the less useful these values become.\n\nFinally, if you define SLO boundaries with the fluent builder for `Timer`, you can view throughput below certain SLO boundaries. In this example, we set SLO boundaries at 275 (yellow), 300 (red), and 500 (blue) milliseconds for a simulated `Timer` that is recording samples normally distributed around 250 ms. These counts represent the rate/second of samples less than or equal to each SLO boundary.\n\n[source,sql]\n----\nSELECT sum(value) from timerHistogram FACET le since 30 minutes ago TIMESERIES auto\n----\n\n.Timer SLO boundaries.\nimage::"+n(117)+"[New Relic-rendered SLO boundaries]\n\nWhere the lines converge at various points, it is evident that no sample exceeded the 275 ms SLO boundary.\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"4d25b7833c033d6e2380ec4ecc988d66.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"02f1215f99df0a7a03ac1d5986ace874.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"03797aa9d08a216ed90efab2b59e0114.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"e1fdf4994baf8640b183e76694d53390.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer OTLP\n:toc:\n:sectnums:\n:system: otlp\n\nOpenTelemetry is a CNCF incubating project for providing standards for telemetry data. OpenTelemetry protocol (OTLP) is a vendor neutral protocol that can be used to send data to various backends which support it. You can read the corresponding docs on how the metrics are ingested and can be visualized in the respective vendor docs.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\nThe following example configures an OTLP registry:\n\n[source,java]\n----\nOtlpConfig otlpConfig = new OtlpConfig() {\n @Override\n public String get(final String key) {\n return null;\n }\n};\n\nMeterRegistry registry = new OtlpMeterRegistry(otlpConfig, Clock.SYSTEM);\n----\n\n`OtlpConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source (e.g.: a simple `Map` can work), you can override the default configuration through properties. For example, Micrometer's Spring Boot support binds properties prefixed with `management.otlp.metrics.export` directly to the `OtlpConfig`:\n\n[source, yaml]\n----\nmanagement:\n otlp:\n metrics:\n export:\n # Supported configs\n url: \"https://otlp.example.com:4318/v1/metrics\"\n aggregationTemporality: \"cumulative\"\n headers:\n header1: value1\n resourceAttributes:\n key1: value1\n----\n\n1. `url` - The URL to which data will be reported. Defaults to `http://localhost:4318/v1/metrics`\n2. `aggregationTemporality` - https://opentelemetry.io/docs/specs/otel/metrics/data-model/#temporality[Aggregation temporality, window=_blank] determines how the additive quantities are expressed, in relation to time. The supported values are `cumulative` or `delta`. Defaults to `cumulative`. +\n*Note*: This config was introduced in version 1.11.0.\n3. `headers` - Additional headers to send with exported metrics. This can be used for authorization headers. By default, headers will be loaded from the config. If that is not set, they can be taken from the environment variables `OTEL_EXPORTER_OTLP_HEADERS` and `OTEL_EXPORTER_OTLP_METRICS_HEADERS`. If a header is set in both the environmental variables, the header in the latter will override the former.\n4. `resourceAttributes` - https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service[Resource attributes, window=_blank] that will be used for all metrics published. By default, Micrometer adds the following resource attributes:\n\n[%autowidth]\n|===\n|Key | Default value\n\n|telemetry.sdk.name\n|io.micrometer\n\n|telemetry.sdk.language\n|java\n\n|telemetry.sdk.version\n| (e.g.: 1.11.0)\n\n|service.name\n|unknown_service\n|===\n\nIf this config is empty, then resource attributes will be loaded from the environmental variable `OTEL_RESOURCE_ATTRIBUTES`. `service.name` can be overridden by the environmental variable `OTEL_SERVICE_NAME` and this takes precedence over other configs.\n\n== Supported metrics\nhttps://opentelemetry.io/docs/specs/otel/metrics/data-model/#metric-points[Metric points, window=_blank] define the different data points that are supported in OTLP. Micrometer supports exporting the below data points in OTLP format,\n\n1. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#sums[Sums, window=_blank]\n2. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#gauge[Gauge, window=_blank]\n3. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#histogram[Histogram, window=_blank]\n4. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#summary-legacy[Summary, window=_blank]\n\nThe below table maps OTLP data points and the Micrometer meters:\n\n[%autowidth]\n|===\n|OTLP data point | Micrometer meter type\n\n|Sums\n|Counter, FunctionCounter\n\n|Gauge\n|Gauge, TimeGauge, MultiGauge\n\n|Histogram\n|Timer, DistributionSummary, LongTaskTimer, FunctionTimer (only sum and count are set)\n\n|Summary\n|Timer, DistributionSummary, LongTaskTimer\n|===\n\n*Note*:\n\n1. `max` on Histogram data point is only supported in delta aggregation temporality. This is because the values represented by cumulative min and max will stabilize as more events are recorded and are less useful when recorded over application's lifecycle.\n2. Currently, Micrometer only exports metadata for type `Meter` to OTLP.\n\n== Histograms and Percentiles\nMicrometer `Timer` and `DistributionSummary` support configuring link:/docs/concepts#_histograms_and_percentiles[client-side percentiles and percentile histograms]. OTLP specification terms Summary data point (client-side percentiles) as legacy and not recommended for new applications. Summary data point also cannot have min/max associated with it. Due to these reasons Micrometer prefers exporting Timers and DistributionSummary as Histogram data point. By default, a Timer/DistributionSummary without any additional percentile/histogram config will be exported as Histogram data point. However, by configuring the timer to generate only client-side percentiles using `publishPercentiles` this can be changed to a Summary data point exporting pre-calculated percentiles. When both `publishPercentiles` and (`publishPercentileHistogram` or `serviceLevelObjectives`) are configured, Histogram data point is preferred and pre-calculated percentiles *will not* be generated. See the below table on which data point will be used with different configurations:\n\n[%autowidth]\n|===\n|Configuration | OTLP data point\n\n| publishPercentiles\n| Summary\n\n| publishPercentileHistogram\n| Histogram\n\n| serviceLevelObjectives\n| Histogram\n\n| publishPercentiles and (publishPercentileHistogram/serviceLevelObjectives)\n| Histogram\n|===\n\nAlternatively, if you are using Spring Boot, you can use the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.customizing.per-meter-properties[per-meter properties, window=_blank] to configure this behaviour.\n\nIf you want to generate Histogram data point for a Timer with name `test.timer` and default buckets generated by Micrometer, use:\n\n[source,properties]\n----\nmanagement.metrics.distribution.percentiles-histogram.test.timer=true\n----\n\nand for buckets with customized SLO, use:\n\n[source,properties]\n----\nmanagement.metrics.distribution.slo.test.timer=10.0,100.0,500.0,1000.0\n----\n\nAlternatively, if you want to generate Summary data point for a timer with name `test.timer` and 90th and 99th percentiles, you can use the below config:\n\n[source,properties]\n----\nmanagement.metrics.distribution.percentiles.test.timer=0.9,0.99\n----\n"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Prometheus\n:toc:\n:sectnums:\n:system: prometheus\n\nPrometheus is a dimensional time series database with a built-in UI, a custom query language, and math operations.\nPrometheus is designed to operate on a pull model, periodically scraping metrics from application instances, based on service discovery.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nPrometheus expects to scrape or poll individual application instances for metrics.\nIn addition to creating a Prometheus registry, you also need to expose an HTTP endpoint to Prometheus' scraper.\nIn a Spring Boot application, a https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#prometheus[Prometheus actuator endpoint] is auto-configured in the presence of Spring Boot Actuator.\nOtherwise, you can use any JVM-based HTTP server implementation to expose scrape data to Prometheus.\n\nThe following example uses the JDK's `com.sun.net.httpserver.HttpServer` to expose a scrape endpoint:\n\n[source,java]\n----\nPrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);\n\ntry {\n HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);\n server.createContext(\"/prometheus\", httpExchange -> {\n String response = prometheusRegistry.scrape(); <1>\n httpExchange.sendResponseHeaders(200, response.getBytes().length);\n try (OutputStream os = httpExchange.getResponseBody()) {\n os.write(response.getBytes());\n }\n });\n\n new Thread(server::start).start();\n} catch (IOException e) {\n throw new RuntimeException(e);\n}\n----\n<1> The `PrometheusMeterRegistry` has a `scrape()` function that knows how to supply the String data necessary for the scrape. All you have to do is wire it to an endpoint.\n\nYou can alternatively use `io.prometheus.client.exporter.HTTPServer` that you can find in `io.prometheus:simpleclient_httpserver`:\n[source,java]\n----\nPrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);\n// you can set the daemon flag to false if you want the server to block\nnew HTTPServer(new InetSocketAddress(8080), prometheusRegistry.getPrometheusRegistry(), true);\n----\n\nAnother alternative can be `io.prometheus.client.exporter.MetricsServlet` that you can find in `io.prometheus:simpleclient_servlet` in case your app is running in a servlet container (e.g.: Tomcat):\n[source,java]\n----\nPrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);\nHttpServlet metricsServlet = new MetricsServlet(prometheusRegistry.getPrometheusRegistry());\n----\n\n=== Scrape format\n\nBy default, the https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format[Prometheus text format] is returned from the `PrometheusMeterRegistry` `scrape()` method.\n\nThe https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md[OpenMetrics] format can also be produced.\nTo specify the format to be returned, you can pass a content type to the `scrape` method.\nFor example, to get the OpenMetrics 1.0.0 format scrape, you could use the Prometheus Java client constant for it, as follows:\n\n[source,java]\n----\nString openMetricsScrape = registry.scrape(TextFormat.CONTENT_TYPE_OPENMETRICS_100);\n----\n\nIn Spring Boot applications, the https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#prometheus[Prometheus Actuator endpoint] supports scraping in either format, defaulting to the Prometheus text format in absence of a specific `Accept` header.\n\n=== The Prometheus Rename Filter\n\nIn some cases, Micrometer provides instrumentation that overlaps with commonly used Prometheus simple client modules but has chosen a different naming scheme for consistency and portability.\nIf you wish to use the Prometheus \"standard\" names, add the following filter:\n\n[source,java]\n----\nprometheusRegistry.config().meterFilter(new PrometheusRenameFilter());\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Prometheus for metrics originating in Micrometer.\nSee the https://prometheus.io/docs/querying/basics[Prometheus docs] for a far more complete reference of what is possible in Prometheus.\n\n=== Grafana Dashboard\n\nA publicly available Grafana dashboard for Micrometer-sourced JVM and Tomcat metrics is available https://grafana.com/grafana/dashboards/4701-jvm-micrometer/[here].\n\nimage::"+n(120)+'[Grafana dashboard for JVM and Tomcat binders]\n\nThe dashboard features:\n\n* JVM memory\n* Process memory (provided by https://github.com/mweirauch/micrometer-jvm-extras[micrometer-jvm-extras])\n* CPU-Usage, Load, Threads, File Descriptors, and Log Events\n* JVM Memory Pools (Heap, Non-Heap)\n* Garbage Collection\n* Classloading\n* Direct/Mapped buffer sizes\n\nInstead of using the `job` tag to distinguish different applications, this dashboard makes use of a common tag called `application`, which is applied to every metric.\nYou can apply the common tag as follows:\n\n[source,java]\n----\nregistry.config().commonTags("application", "MYAPPNAME");\n----\n\nIn Spring Boot applications, you can use the https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.metrics.customizing.common-tags[property support for common tags]:\n\n[source,properties]\n----\nmanagement.metrics.tags.application=MYAPPNAME\n----\n\n=== Counters\n\nThe query that generates a graph for the random-walk counter is\n`rate(counter[10s])`.\n\n.A Grafana rendered graph of the random walk counter.\nimage::'+n(121)+"[Grafana-rendered Prometheus counter]\n\nRepresenting a counter without rate normalization over some time window is rarely useful, as the representation is a function of both the rapidity with which the counter is incremented and the longevity of the service. It is generally most useful to rate-normalize these time series to reason about them. Since Prometheus keeps track of discrete events across all time, it has the advantage of allowing for the selection of an arbitrary time window across which to normalize at query time (for example, `rate(counter[10s])` provides a notion of requests per second over 10 second windows). The rate-normalized graph in the preceding image would return back to a value around 55 as soon as the new instance (say on a production deployment) was in service.\n\n.Counter over the same random walk, no rate normalization.\nimage::"+n(122)+"[Grafana-rendered Prometheus counter (no rate)]\n\nIn contrast, without rate normalization, the counter drops back to zero on service restart, and the count increases without bound for the duration of the service's uptime.\n\n=== Timers\n\nThe Prometheus `Timer` produces two counter time series with different names:\n\n* `${name}_count`: Total number of all calls.\n* `${name}_sum`: Total time of all calls.\n\nRepresenting a counter without rate normalization over some time window is rarely useful, as the representation is a function of both the rapidity with which the counter is incremented and the longevity of the service.\n\nUsing the following Prometheus queries, we can graph the most commonly used statistics about timers:\n\n* Average latency: `rate(timer_sum[10s])/rate(timer_count[10s])`\n* Throughput (requests per second): `rate(timer_count[10s])`\n\n.Timer over a simulated service.\nimage::"+n(123)+"[Grafana-rendered Prometheus timer]\n\n=== Long task timers\n\nThe Prometheus query to plot the duration of a long task timer for a serial task is `long_task_timer_sum`. In Grafana, we can set an alert threshold at some fixed point.\n\n.Simulated back-to-back long tasks with a fixed alert threshold.\nimage::"+n(124)+"[Grafana-rendered Prometheus long task timer]\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"39fa40459993be394ab40488bf8c6a8f.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"594dce115710525d7771ebe3ab82db19.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"439c0eea1918ef143002cf62a9393e2a.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"fc8d290e3f5356fad7d93f76c6abe188.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"fb6c59cc88b85301c5bbb890afc71593.png"},function(e,t,n){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer SignalFx\n:toc:\n:sectnums:\n:system: signalfx\n\nSignalFx is a dimensional monitoring system SaaS with a full UI that operates on a push model. It has a rich set of alert \"`detectors`\".\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures SignalFx:\n\n[source,java]\n----\nSignalFxConfig signalFxConfig = new SignalFxConfig() {\n @Override\n public String accessToken() {\n return \"MYTOKEN\";\n }\n\n @Override\n public String get(String k) {\n return null; // accept the rest of the defaults\n }\n};\n\nMeterRegistry registry = new SignalFxMeterRegistry(signalFxConfig, Clock.SYSTEM);\n----\n\nThere are two distinct sources of API keys in SignalFx.\n\n`SignalFxConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.signalfx` directly to the `SignalFxConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.signalfx:\n access-token: MYTOKEN\n\n # The interval at which metrics are sent to Ganglia. See Duration.parse for the expected format.\n # The default is 1 minute.\n step: 1m\n----\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in SignalFx for metrics originating in Micrometer. See the https://docs.signalfx.com/en/latest/charts/index.html[SignalFx docs] for a far more complete reference of what is possible in SignalFx.\n\n=== Timers\n\nAt each publishing interval, the SignalFx `Timer` produces several time series in SignalFx:\n\n* `${name}.avg`: A mean latency for the publishing interval.\n* `${name}.count`: Throughput per second over the publishing interval.\n* `${name}.totalTime`: Total time per second over the publishing interval (used with `count`) to create aggregable means.\n* `${name}.percentiles`: Micrometer calculated percentiles for the publishing interval. One time series is produced for each percentile, with a tag of `phi` in the range of [0,1].\n* `${name}.histogram`: One event is produced for each SLO boundary with a tag of 'le', indicating that it represents a cumulative count of events less than or equal to SLO boundaries over the publishing interval.\n\nTo generate an aggregable view of latency in SignalFx, divide `totalTime` by `count`:\n\nimage::"+n(126)+"[SignalFx-aggregable latency query]\n\nThis is accomplished by adding signals for `${name}.totalTime` and `${name}.count`, adding a formula that divides them, and hiding the inputs to the formula.\n\n.Timer latency.\nimage::"+n(127)+"[SignalFx-rendered timer]\n\nTo generate a throughput chart, use the `${name}.count` signal:\n\n.Timer throughput.\nimage::"+n(128)+"[SignalFx-rendered timer throughput]\n\nTo generate a plot of client-side percentiles, use the `${name}.percentiles` signal:\n\n.Timer Percentiles.\nimage::"+n(129)+"[SignalFx-rendered percentiles]\n\nNote that these percentiles are _not aggregable_. The more dimensions you add to a timer, the less useful these values become.\n\nFinally, if you define SLO boundaries with the fluent builder for `Timer`, you can view throughput below certain SLO boundaries by using the `${name}.histogram` signal. In this example, we set SLO boundaries at 275 (green), 300 (blue), and 500 (purple) milliseconds for a simulated `Timer` that is recording samples normally distributed around 250 ms. These counts represent the rate/second of samples less than or equal to each SLO boundary.\n\n.Timer SLO boundaries.\nimage::"+n(130)+"[SignalFx-rendered SLO boundaries]\n\nWhere the lines converge at various points it is evident that no sample exceeded the 275 ms SLO boundary.\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"d77cd7a4fea8c37d7dc37e7bbe069b5e.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"beec842accba1a667e0e288745191070.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"02141ef2139e0de5d180b5b76dff1982.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"a165f31f4506fa9b07f0f2319dd58aa0.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"b19ccbd241a3bfd9377d4066961b1620.png"},function(e,t){e.exports="////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Stackdriver Monitoring\nRay Tsang \n:toc:\n:sectnums:\n:system: stackdriver\n\nStackdriver Monitoring is a dimensional time-series SaaS with built-in dashboarding.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation 'io.micrometer:micrometer-registry-{system}:latest.release'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThe following example configures Stackdriver:\n\n[source,java]\n----\nStackdriverConfig stackdriverConfig = new StackdriverConfig() {\n @Override\n public String projectId() {\n return MY_PROJECT_ID;\n }\n\n @Override\n public String get(String key) {\n return null;\n }\n}\n\nMeterRegistry registry = StackdriverMeterRegistry.builder(stackdriverConfig).build();\n----\n\n`StackdriverConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer's Spring Boot support binds properties that are prefixed with `management.metrics.export.stackdriver` directly to the `StackdriverConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.stackdriver:\n project-id: MY_PROJECT_ID\n resource-type: global\n\n # You will probably want to disable Stackdriver Monitoring publishing in a local development profile.\n enabled: true\n\n # The interval at which metrics are sent to Stackdriver Monitoring. The default is 1 minute.\n step: 1m\n----\n\nFor most environments, you need to create and configure credentials to push metrics to Stackdriver Monitoring.\nIn most cases, you should create a service account with Stackdriver Monitoring permissions and configure a\n`GOOGLE_APPLICATION_CREDENTIALS` environmental variable to the path of the service account key file.\nThe following example shows how to do so:\n\n[source]\n----\nexport PROJECT_ID=MY_PROJECT_ID\nexport APP_NAME=MY_APP\n\n# Create a service account\ngcloud iam service-accounts create $APP_NAME\n\n# Grant the service account Stackdriver Monitoring writer permission\ngcloud projects add-iam-policy-binding $PROJECT_ID \\\n --member serviceAccount:$APP_NAME@$PROJECT_ID.iam.gserviceaccount.com \\\n --role roles/monitoring.metricWriter\n\n# Create a key JSON file\ngcloud iam service-accounts keys create $HOME/$APP_NAME-key.json \\\n --iam-account $APP_NAME@$PROJECT_ID.iam.gserviceaccount.com\n\n# Configure GOOGLE_APPLICATION_CREDENTIALS env var\nexport GOOGLE_APPLICATION_CREDENTIALS=$HOME/$APP_NAME-key.json\n----\n\nWhen running in managed environments (such as Google App Engine, Google Cloud Run, Google Cloud Function)\nyou need not configure this environmental variable. In those environments, a service account is\nautomatically associated with the application instance. The underlying Stackdriver Monitoring client\nlibrary can automatically detect and use those credentials.\n\n== Stackdriver Labels\n\nMicrometer metrics tags are mapped to https://cloud.google.com/monitoring/api/v3/metrics-details#intro-time-series[Stackdriver metrics labels]. With tags and labels, you can further filter or group\nby the tag or label. See link:/docs/concepts#_tag_naming[Micrometer Concepts] for more information on tags.\nThe following example filters by tags:\n\n[source,java]\n----\nMeterRegistry registry = StackdriverMeterRegistry.builder(stackdriverConfig).build();\nregistry.config().commonTags(\"application\", \"my-application\");\n----\n\nYou can also configure resource labels with the `StackdriverConfig` method `resourceLabels`. Depending on the configured `resourceType`, there will be required resource labels. See the documentation on https://cloud.google.com/monitoring/custom-metrics/creating-metrics#which-resource[choosing a monitored resource type].\n\nIMPORTANT: When using Micrometer across multiple applications/instances, it is necessary that Stackdriver labels are unique per application/instance. Otherwise, you will see errors like `One or more TimeSeries could not be written: One or more points were written more frequently than the maximum sampling period configured`. If using a resource type other than `global`, the resource labels may already make metrics unique per application instance. If not, a common tag with the hostname or platform-provided instance ID may be a good candidate for achieving this.\n\n== Spring Boot\n\nSpring Boot provides auto-configuration for Micrometer's StackdriverMeterRegistry. For more information, see the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-metrics-export-stackdriver[Spring Boot documentation].\n\nYou can manually configure or register the `StackdriverMeterRegistry`.\nIn addition to using Spring Boot Actuator, make sure you create the `StackdriverMeterRegistry` bean:\n\n[source,java]\n----\n@Bean\nStackdriverConfig stackdriverConfig() {\n return new StackdriverConfig() {\n @Override\n public String projectId() {\n return MY_PROJECT_ID;\n }\n\n @Override\n public String get(String key) {\n return null;\n }\n }\n}\n\n@Bean\nStackdriverMeterRegistry meterRegistry(StackdriverConfig stackdriverConfig) {\n return StackdriverMeterRegistry.builder(stackdriverConfig).build();\n}\n----\n\nYou can also use https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics-common-tags[Spring Boot Actuator Common Tags configuration] to configure common tags:\n\n[source]\n----\nspring.application.name=my-application\nmanagement.metrics.tags.application=${spring.application.name}\n----\n\n== GraalVM native image compilation\n\nTo compile an application using `micrometer-registry-stackdriver` to a https://www.graalvm.org/reference-manual/native-image/[native image using GraalVM], add the https://github.com/GoogleCloudPlatform/native-image-support-java[native-image-support-java] library as a dependency. This will ensure the correct native image configuration is available and avoid errors like `Classes that should be initialized at run time got initialized during image building`.\n"},function(e,t){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer StatsD\n:toc:\n:sectnums:\n:system: statsd\n\nStatsD is a UDP-based sidecar-driven metrics collection system. The maintainer of the original StatsD line protocol specification is Etsy. Datadog\'s DogStatsD and Influx\'s Telegraf each accept a modified version of the line protocol, having each enriched the original specification with dimensionality in different ways.\n\nIf you intend to use the Datadog or Telegraf flavors, see the documentation for Micrometer\'s link:/docs/registry/datadog[Datadog] or link:/docs/registry/influx[Influx] support.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-{system}:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThis configuration is used to ship metrics to a StatsD agent that is compatible with the original Etsy protocol. Metrics are shipped immediately over UDP to the agent.\n\n[source,java]\n----\nStatsdConfig config = new StatsdConfig() {\n @Override\n public String get(String k) {\n return null;\n }\n\n\n @Override\n public StatsdFlavor flavor() {\n return StatsdFlavor.Etsy;\n }\n};\n\nMeterRegistry registry = new StatsdMeterRegistry(config, Clock.SYSTEM);\n----\n\nNOTE: You can also configure Telegraf to accept the dogstatsd format. If you use Telegraf, configuring Micrometer to ship Telegraf-formatted StatsD lines eases the requirements of your Telegraf configuration.\n\n`StatsdConfig` is an interface with a set of default methods. If, in the implementation of `get(String k)`, rather than returning `null`, you instead bind it to a property source, you can override the default configuration. For example, Micrometer\'s Spring Boot support binds properties that are prefixed with `management.metrics.export.statsd` directly to the `StatsdConfig`:\n\n[source,yml]\n----\nmanagement.metrics.export.statsd:\n flavor: etsy\n\n # You will probably want to conditionally disable StatsD publishing in local development.\n enabled: true\n\n # The interval at which metrics are sent to StatsD. The default is 1 minute.\n step: 1m\n----\n\n== Customizing the Metrics Sink\n\nBy default, Micrometer publishes StatsD line protocol over UDP, as the vast majority of existing StatsD agents are UDP servers. It is possible to fully customize how the line protocol is shipped by modifying the builder for `StatsdMeterRegistry`:\n\n[source,java]\n----\nConsumer lineLogger = line -> logger.info(line); <1>\n\nMeterRegistry registry = StatsdMeterRegistry.builder(StatsdConfig.DEFAULT) <2>\n .clock(clock)\n .lineSink(lineLogger)\n .build();\n----\n<1> Define what to do with lines.\n<2> The flavor configuration option determines the structure of the line for the default line builder. It has no effect if you override the line builder with a customization.\n\n=== Using Apache Kafka for Line Sink\n\nYou can also use Apache Kafka for line sink, as follows:\n\n[source,java]\n----\nProperties properties = new Properties();\nproperties.setProperty(BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");\nproperties.setProperty(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());\nproperties.setProperty(VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());\n\nProducer producer = new KafkaProducer<>(properties);\n\nStatsdMeterRegistry.builder(statsdConfig)\n .lineSink((line) -> producer.send(new ProducerRecord<>("my-metrics", line)))\n .build();\n----\n\nNow Micrometer produces lines for metrics to the `my-metrics` topic and you can consume the lines on the topic.\n\n== Customizing the Line Format\n\nThe built-in Etsy, dogstatsd, and Telegraf flavors cover most known public StatsD agents, but you can completely customize the line format to satisfy closed, proprietary agents. Again, we use the `StatsdMeterRegistry` builder to establish a line builder for each ID. Providing an instance of the builder _per ID_ offers you the opportunity to eagerly cache the serialization of the ID\'s name and tags to optimize the serialization of a StatsD line based on that ID as samples are recorded. The following listing defines a fictional format:\n\n[source,java]\n----\nFunction nameAndUnits = id -> new StatsdLineBuilder() {\n String name = id.getName() + "/" + (id.getBaseUnit() == null ? "unknown" : id.getBaseUnit());\n\n @Override\n public String count(long amount, Statistic stat) {\n return name + ":" + amount + "|c";\n }\n\n ... // implement gauge, histogram, and timing similarly\n}\n\nMeterRegistry registry = StatsdMeterRegistry.builder(StatsdConfig.DEFAULT) <1>\n .clock(clock)\n .lineBuilder(nameAndUnits)\n .build();\n----\n<1> Because you have taken control of line building, the flavor is ignored.\n'},function(e,t,n){e.exports='////\nDO NOT EDIT THIS FILE. IT WAS GENERATED.\nManual changes to this file will be lost when it is generated again.\nEdit the files in the src/docs/ directory instead.\n////\n\n\n= Micrometer Wavefront\n:toc:\n:sectnums:\n:system: wavefront\n\nWavefront is a dimensional monitoring system offered as a SaaS with a full UI, custom query language, and advanced math operations. Wavefront operates on a push model. Metrics may either be pushed through a sidecar process running on the same host (called the Wavefront proxy) or directly to the Wavefront API.\n\n== Installing\n\nFor Gradle, add the following implementation:\n\n[source,groovy,subs=+attributes]\n----\nimplementation \'io.micrometer:micrometer-registry-{system}:latest.release\'\n----\n\nFor Maven, add the following dependency:\n\n[source,xml,subs=+attributes]\n----\n\n io.micrometer\n micrometer-registry-{system}\n ${micrometer.version}\n\n----\n\n== Configuring\n\nThis section describes how to configure Wavefront when you send data:\n\n* <>\n* <>\n\n[[configuring-directly-to-wavefront]]\n=== Directly to Wavefront\n\nThe following example configures sending directly to Wavefront:\n\n[source,java]\n----\nWavefrontConfig config = new WavefrontConfig() {\n @Override\n public String uri() {\n return "https://longboard.wavefront.com"; <1>\n }\n\n @Override\n public String apiToken() {\n return "MYAPIKEY"; <2>\n }\n\n @Override\n public String get(String key) {\n return null; <3>\n }\n};\nMeterRegistry registry = new WavefrontMeterRegistry(config, Clock.SYSTEM);\n----\n<1> `longboard` is the name of the co-tenant instance on which most organizations start. Once you reach a sufficient scale, Wavefront may move you\nto a dedicated host.\n<2> This is required when pushing directly to Wavefront\'s API.\n<3> Accept the rest of the defaults.\n\n[[configuring-through-a-wavefront-proxy-sidecar]]\n=== Through a Wavefront Proxy Sidecar\n\nThe following example configures sending through a Wavefront proxy sidecar:\n\n[source,java]\n----\nMeterRegistry registry = new WavefrontMeterRegistry(WavefrontConfig.DEFAULT_PROXY, Clock.SYSTEM);\n----\n\nThe default proxy configuration pushs metrics and histogram distributions to a Wavefront proxy sitting on `localhost:2878`.\n\nNOTE: If publishing metrics to a Wavefront proxy, the URI must be expressed in the form of `proxy://HOST:PORT`.\n\n== Graphing\n\nThis section serves as a quick start to rendering useful representations in Wavefront for metrics originating in Micrometer. See the https://docs.wavefront.com/query_language_getting_started.html[Wavefront docs] for a far more complete reference of what is possible in Wavefront.\n\n=== Counters\n\nThe query that generates a graph for a random-walk counter is `rate(ts(counter))`.\n\n.A Wavefront rendered graph of the random walk counter.\nimage::'+n(134)+"[Wavefront-rendered counter]\n\nRepresenting a counter without rate normalization over some time window is rarely useful, as the\nrepresentation is a function of both the rapidity with which the counter is incremented and the\nlongevity of the service. It is generally most useful to rate-normalize these time series to\nreason about them.\n\nBecause Wavefront keeps track of cumulative counts across all time, it has the\nadvantage of allowing for the selection of a particular time function at query time (for example,\n`rate(ts(counter))` to compute the per-second rate of change).\n\n=== Timers\n\nThe Wavefront `Timer` produces different time series depending on whether or not\n`publishPercentileHistogram` is enabled.\n\nIf `publishPercentileHistogram` is enabled, the Wavefront `Timer` produces histogram distributions\nthat let you query for the latency at any percentile using `hs()` queries. For example, you can\nvisualize latency at the 95th percentile (`percentile(95, hs(timer.m))`) or the 99.9th percentile\n(`percentile(99.9, hs(timer.m))`). For more information on histogram distributions, see\n<>, later in this section.\n\nIf `publishPercentileHistogram` is disabled, the Wavefront `Timer` produces several\ntime series:\n\n* `${name}.avg`: Mean latency across all calls.\n* `${name}.count`: Total number of all calls.\n* `${name}.sum`: Total time of all calls.\n* `${name}.max`: Max latency over the publishing interval.\n* `${name}.percentiles`: Micrometer-calculated percentiles for the publishing interval. These\ncannot be aggregated across dimensions.\n\nYou can use these time series to generate a quick view of latency in Wavefront:\n\n.Timer latency.\nimage::"+n(135)+'[Wavefront-rendered timer]\n\nThe preceding chart shows the average latency (`rate(ts(timer.sum))/rate(ts(timer.count))` in\ngreen), 95th percentile (`ts(timer.percentile, phi="0.95")` in orange), and max (`ts(timer.max)`\nin blue).\n\nAdditionally, `rate(ts(timer.count))` represents a rate/second throughput of events being timed:\n\n.Timer throughput.\nimage::'+n(136)+"[Wavefront-rendered timer throughput]\n\n[[wavefront-histograms]]\n=== Wavefront Histograms\n\nWavefront's histogram implementation stores an actual distribution of metrics, as opposed to single metrics. This lets you apply any percentile and aggregation function on the distribution at query time without having to specify specific percentiles and metrics to keep during metric collection.\n\nWavefront histogram distributions are collected and reported for any `Timer` or `DistributionSummary` that has `publishPercentileHistogram` enabled.\n\nBy default, distributions that are reported to Wavefront get aggregated by the minute, providing you with a histogram distribution for each minute. You also have the option of aggregating by hour or day. You can customize this with the following configuration options:\n\n* `reportMinuteDistribution`: Boolean specifying whether to aggregate by minute. Enabled by default. Metric name in Wavefront has `.m` suffix.\n* `reportHourDistribution`: Boolean specifying whether to aggregate by hour. Disabled by default. Metric name in Wavefront has `.h` suffix.\n* `reportDayDistribution`: Boolean specifying whether to aggregate by day. Disabled by default. Metric name in Wavefront has `.d` suffix.\n\nIf you are sending to a Wavefront proxy, by default, both metrics and histogram distributions are published to the same port: 2878 in the default proxy configuration. If your proxy is configured to listen for histogram distributions on a different port, you can specify the port to which to publish by using the `distributionPort` configuration option.\n\nYou can query histogram distributions in Wavefront by using `hs()` queries. For example, `percentile(98, hs(${name}.m))` returns the 98th percentile for a particular histogram aggregated over each minute. Each histogram metric name has a suffix (`.m`, `.h`, or `.d`), depending on the histogram's aggregation interval.\n\nSee the https://docs.wavefront.com/proxies_histograms.html[Wavefront Histograms documentation] for more information.\n"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"105c9d351e5cabcc4504a9b863032ca3.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"10ee7658d93746e240978c7ca9b3467b.png"},function(e,t,n){"use strict";n.r(t),t.default=n.p+"9cc263d6b012a89bad0374b418540a31.png"},,function(e,t,n){},function(e,t,n){"use strict";n.r(t);var r=n(1),a=n(13),i=n.n(a),o=n(148),s=n(147),c=n(30),l=n(146),u=n(22),d=n.p+"static/media/logo.e2f91fdb.svg",m=(n(36),n(37),n(144)),g=n(145),h=n(0);function p(){return Object(h.jsxs)(m.a,{collapseOnSelect:!0,expand:"lg",bg:"dark",variant:"dark",style:{borderTop:"8px solid #1ba89c"},children:[Object(h.jsx)(m.a.Brand,{as:u.a,to:"/",children:Object(h.jsx)("img",{src:d,className:"img-fluid",style:{maxHeight:80},alt:"Micrometer"})}),Object(h.jsx)(m.a.Toggle,{"aria-controls":"responsive-navbar-nav"}),Object(h.jsx)(m.a.Collapse,{id:"responsive-navbar-nav",children:Object(h.jsxs)(g.a,{className:"ml-auto mt-2 mt-lg-0",children:[Object(h.jsx)(g.a.Item,{href:"/docs",children:Object(h.jsxs)(g.a.Link,{as:u.a,to:"/docs",children:[Object(h.jsx)("i",{className:"fa fa-lg fa-book"})," Documentation"]})}),Object(h.jsx)(g.a.Item,{children:Object(h.jsxs)(g.a.Link,{href:"https://github.com/micrometer-metrics/micrometer",children:[Object(h.jsx)("i",{className:"fa fa-lg fa-github-alt"})," GitHub"]})}),Object(h.jsx)(g.a.Item,{children:Object(h.jsxs)(g.a.Link,{href:"https://twitter.com/micrometerio",children:[Object(h.jsx)("i",{className:"fa fa-lg fa-twitter"})," Twitter"]})}),Object(h.jsx)(g.a.Item,{children:Object(h.jsxs)(g.a.Link,{href:"https://slack.micrometer.io",children:[Object(h.jsx)("i",{className:"fa fa-lg fa-slack"})," Slack"]})})]})})]})}function f(){return Object(h.jsx)("div",{className:"bg-dark text-center",children:Object(h.jsx)("div",{className:"col-12",style:{padding:10,color:"white"},children:Object(h.jsxs)("p",{children:['Copyright \xa9 2005 - 2023 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. See ',Object(h.jsx)("a",{href:"https://www.vmware.com/help/legal.html",children:"Terms of Use"})," and ",Object(h.jsx)("a",{href:"https://www.vmware.com/help/privacy.html",children:"Privacy Policy"}),"."]})})})}var b=n.p+"static/media/logo-no-title.c52ef844.svg",y=n.p+"static/media/playback-latency.b7cc768a.png";function v(){return Object(h.jsxs)("div",{children:[Object(h.jsx)("div",{className:"jumbotron text-center",style:{background:"url(".concat(y,") no-repeat center center")},children:Object(h.jsxs)("div",{className:"container",children:[Object(h.jsx)("img",{src:b,className:"img-fluid",alt:""}),Object(h.jsx)("h1",{className:"jumbotron-heading mt-3",style:{color:"white",background:"rgba(52, 48, 45, 0.8)"},children:"Vendor-neutral application observability facade"}),Object(h.jsx)("p",{className:"lead",style:{padding:8,color:"white",background:"rgba(52, 48, 45, 0.8)"},children:"Micrometer provides a simple facade over the instrumentation clients for the most popular observability systems, allowing you to instrument your JVM-based application code without vendor lock-in. Think SLF4J, but for observability."})]})}),Object(h.jsxs)("div",{className:"container-fluid",children:[Object(h.jsxs)("div",{className:"row",style:{background:"rgba(17, 122, 113, 0.8)",color:"white",padding:30},children:[Object(h.jsxs)("div",{className:"col-lg-4 text-center",children:[Object(h.jsx)("i",{className:"fa fa-4x fa-database","aria-hidden":"true"}),Object(h.jsx)("h2",{children:"Dimensional Metrics"}),Object(h.jsxs)("p",{children:["Micrometer provides vendor-neutral interfaces for ",Object(h.jsx)("strong",{children:"timers"}),", ",Object(h.jsx)("strong",{children:"gauges"}),", ",Object(h.jsx)("strong",{children:"counters"}),", ",Object(h.jsx)("strong",{children:"distribution summaries"}),", and ",Object(h.jsx)("strong",{children:"long task timers"})," with a dimensional data model that, when paired with a dimensional monitoring system, allows for efficient access to a particular named metric with the ability to drill down across its dimensions."]})]}),Object(h.jsxs)("div",{className:"col-lg-4 text-center",children:[Object(h.jsx)("i",{className:"fa fa-4x fa-area-chart","aria-hidden":"true"}),Object(h.jsx)("h2",{children:"Pre-configured Bindings"}),Object(h.jsx)("p",{children:"Out-of-the-box instrumentation of caches, the class loader, garbage collection, processor utilization, thread pools, and more tailored to actionable insight."})]}),Object(h.jsxs)("div",{className:"col-lg-4 text-center",children:[Object(h.jsx)("i",{className:"fa fa-4x fa-leaf","aria-hidden":"true"}),Object(h.jsx)("h2",{children:"Integrated into Spring"}),Object(h.jsx)("p",{children:"Micrometer is the instrumentation library powering the delivery of application observability from Spring Boot applications."})]})]}),Object(h.jsx)("div",{className:"row justify-content-center",style:{padding:30},children:Object(h.jsxs)("div",{className:"col-lg-6 col-md-12",children:[Object(h.jsx)("h2",{children:"Support for popular observability systems"}),Object(h.jsx)("p",{children:"As an instrumentation facade, Micrometer allows you to instrument your code with dimensional metrics, spans with a vendor-neutral interface and decide on the observability system as a last step. Instrumenting your core library code with Micrometer allows the libraries to be included in applications that ship data to different backends."}),Object(h.jsxs)("p",{children:["Contains built-in support for ",Object(h.jsx)("strong",{children:"AppOptics"}),", ",Object(h.jsx)("strong",{children:"Azure Monitor"}),", Netflix ",Object(h.jsx)("strong",{children:"Atlas"}),", ",Object(h.jsx)("strong",{children:"CloudWatch"}),", ",Object(h.jsx)("strong",{children:"Datadog"}),", ",Object(h.jsx)("strong",{children:"Dynatrace"}),", ",Object(h.jsx)("strong",{children:"Elastic"}),", ",Object(h.jsx)("strong",{children:"Ganglia"}),", ",Object(h.jsx)("strong",{children:"Graphite"}),", ",Object(h.jsx)("strong",{children:"Humio"}),", ",Object(h.jsx)("strong",{children:"Influx/Telegraf"}),", ",Object(h.jsx)("strong",{children:"JMX"}),", ",Object(h.jsx)("strong",{children:"KairosDB"}),", ",Object(h.jsx)("strong",{children:"New Relic"}),", ",Object(h.jsx)("strong",{children:"OpenTelemetry"})," Protocol (OTLP), ",Object(h.jsx)("strong",{children:"Prometheus"}),", ",Object(h.jsx)("strong",{children:"SignalFx"}),", Google ",Object(h.jsx)("strong",{children:"Stackdriver"}),", ",Object(h.jsx)("strong",{children:"StatsD"}),", and ",Object(h.jsx)("strong",{children:"Wavefront"}),"."]}),Object(h.jsxs)("p",{children:["Through ",Object(h.jsx)("strong",{children:"Micrometer Observation"})," and ",Object(h.jsx)("strong",{children:"Micrometer Tracing"})," you can ship your spans via ",Object(h.jsx)("strong",{children:"OpenZipkin Brave"})," or ",Object(h.jsx)("strong",{children:"OpenTelemetry"})," tracers to different backends (e.g. ",Object(h.jsx)("strong",{children:"OpenZipkin"})," or ",Object(h.jsx)("strong",{children:"Wavefront"}),")."]})]})})]})]})}var w=n(143);n(43);function T(){return Object(h.jsxs)("div",{className:"container-fluid mt-4",style:{paddingRight:30,paddingLeft:30},children:[Object(h.jsx)("h1",{children:"Micrometer Documentation"}),Object(h.jsx)("p",{children:"Micrometer provides a simple facade over the instrumentation clients for the most popular observability systems, allowing you to instrument your JVM-based application code without vendor lock-in. Think SLF4J, but for application observability! Data recorded by Micrometer are intended to be used to observe, alert, and react to the current/recent operational state of your environment."}),Object(h.jsxs)("p",{children:["Join the discussion with any comments and feature requests on Micrometer's public ",Object(h.jsxs)("a",{href:"https://slack.micrometer.io",children:[Object(h.jsx)("i",{className:"fa fa-lg fa-slack"})," Slack Workspace"]}),"."]}),Object(h.jsxs)("ol",{children:[Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/installing",children:"Installing"}),". Where to get the latest release and snapshot builds."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/concepts",children:"Concepts"}),". An introduction to the abstraction provided by Micrometer."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)("span",{className:"doc-section",children:"Setup"}),". Instructions for how to configure Micrometer for use with different monitoring systems. As a facade over multiple monitoring systems, the point of Micrometer is to allow you to instrument your code in the same way and be able to visualize the results in your monitoring system of choice.",Object(h.jsxs)("ul",{children:[Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/appOptics",children:"AppOptics"}),". AppOptics is a dimensional time-series SAAS with built-in dashboarding. Micrometer supports shipping metrics to AppOptics directly via its API."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/atlas",children:"Atlas"}),". An in-memory dimensional time series database with built-in graphing, a custom stack-based query language, and advanced math operations. Atlas originated at Netflix, where it remains the operational metrics solution."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/cloudwatch",children:"CloudWatch"}),". CloudWatch is a dimensional time-series SaaS on Amazon's cloud."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/datadog",children:"Datadog"}),". Datadog is a dimensional time-series SAAS with built-in dashboarding and alerting. Micrometer supports shipping metrics to Datadog directly via its API or through Dogstatsd via the StatsD registry."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/dynatrace",children:"Dynatrace"}),". Dynatrace is a Software Intelligence Platform featuring application performance monitoring (APM), artificial intelligence for operations (AIOps), IT infrastructure monitoring, digital experience management (DEM), and digital business analytics capabilities. Micrometer supports shipping metrics to Dynatrace directly via its API."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/elastic",children:"Elastic"}),". Elasticsearch is an open source search and analytics platform. Metrics stored in Elasticsearch can be visualized in Kibana."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/ganglia",children:"Ganglia"}),". An aging hierarchical metrics system which enjoyed wide popularity in Linux system monitoring and is still in place in many organizations. It originated in the early 2000s at the University of California, Berkeley."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/graphite",children:"Graphite"}),". One of the most popular current hierarchical metrics systems backed by a fixed-size database, similar in design and purpose to RRD. It originated at Orbitz in 2006 and was open sourced in 2008."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/humio",children:"Humio"}),". Humio is a dimensional time-series SAAS with built-in dashboarding. Micrometer supports shipping metrics to Humio directly via its API."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/influx",children:"Influx"}),". The InfluxData suite of tools supports real-time stream processing and storage of time-series data. It supports downsampling, automatically expiring and deleting unwanted data, as well as backup and restore. Analysis of data is done via a SQL-like query language."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/instana",children:"Instana"}),". Instana is an automatic application performance management and infrastructure monitoring system."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/jmx",children:"JMX"}),". Micrometer provides a hierarchical mapping to JMX, primarily as a cheap and portable way to view metrics locally. Where JMX exporting is found in production, the same metrics are generally exported to another, more purpose-fit monitoring system."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/kairos",children:"KairosDB"}),". KairosDB is a dimensional time-series database built on top of Cassandra. Charting can be accomplished in Grafana."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/new-relic",children:"New Relic"}),". Micrometer publishes to New Relic Insights, a SaaS offering with a full UI and a query language called NRQL. New Relic Insights operates on a push model."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/otlp",children:"OpenTelemetry Protocol (OTLP)"}),". OpenTelemetry is a CNCF incubating project for providing standards for telemetry data. Micrometer can publish metrics using the OpenTelemetry protocol (OTLP) to the backends that support it."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/prometheus",children:"Prometheus"}),". An in-memory dimensional time series database with a simple built-in UI, a custom query language, and math operations. Prometheus is designed to operate on a pull model, scraping metrics from application instances periodically based on service discovery."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/signalFx",children:"SignalFx"}),'. SignalFx is a dimensional monitoring system SaaS with a full UI operating on a push model. It has a rich set of alert "detectors".']}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/stackdriver",children:"Stackdriver"}),". Stackdriver Monitoring is a dimensional time-series SAAS with built-in dashboarding and alerting. Micrometer supports shipping metrics to Stackdriver directly via its API using a push model. Alternatively, you can export Micrometer metrics via Prometheus and use a Prometheus to Stackdriver sidecar."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/statsD",children:"StatsD"}),". Micrometer supports three flavors of StatsD: the original Etsy format plus the Datadog and Telegraf (Influx) extensions of StatsD that add dimensional support. Use this registry if you prefer to publish metrics to a StatsD agent. Also use this registry with Datadog flavor to publish metrics to Splunk."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/registry/wavefront",children:"Wavefront"}),". Wavefront is a SaaS-based metrics monitoring and analytics platform that lets you visualize, query, and alert over data from across your entire stack (infrastructure, network, custom app metrics, business KPIs, etc.)"]})]})]}),Object(h.jsxs)("li",{children:[Object(h.jsx)("span",{className:"doc-section",children:"Reference"}),". Detailed list of out-of-the-box instrumentation provided by Micrometer.",Object(h.jsxs)("ul",{children:[Object(h.jsxs)("li",{children:[Object(h.jsx)("a",{className:"doc-section",href:"https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-metrics",children:"Spring Boot"}),". Micrometer is the instrumentation library powering the delivery of application metrics from Spring."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/ref/jvm",children:"JVM"}),". Metrics on classloaders, memory, garbage collection, threads, etc."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/ref/cache",children:"Cache"}),". Instrumentation for the most popular caching frameworks."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/ref/okhttpclient",children:"OkHttpClient"}),". Instrumentation for OkHttpClient."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/ref/jetty",children:"Jetty and Jersey"}),". Instrumentation for Jetty and Jersey."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/ref/netty",children:"Netty"}),". Instrumentation for Netty."]})]})]}),Object(h.jsxs)("li",{children:[Object(h.jsx)("span",{className:"doc-section",children:"Guides"}),".",Object(h.jsx)("ul",{children:Object(h.jsx)("li",{children:Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/guide/consoleReporter",children:"Passing through to Dropwizard's console reporter"})})}),Object(h.jsx)("ul",{children:Object(h.jsx)("li",{children:Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/guide/httpSenderResilience4jRetry",children:"HttpSender with Resilience4j retry"})})}),Object(h.jsx)("ul",{children:Object(h.jsx)("li",{children:Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/guide/customMeterRegistry",children:"Custom meter registry"})})})]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/observation",children:"Micrometer Observation"}),". An introduction to the Micrometer Observation API."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/tracing",children:"Micrometer Tracing"}),". An introduction to the abstraction provided by Micrometer Tracing."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/contextPropagation",children:"Micrometer Context Propagation"}),". An introduction to the abstraction provided by Micrometer Context Propagation."]}),Object(h.jsxs)("li",{children:[Object(h.jsx)(w.a,{className:"doc-section",to:"/docs/support",children:"Support policy"}),". Micrometer's support policy for releases."]})]})]})}var x=n(26),S=n(27),O=n(17),M=n(31),C=n(29),I=(n(45),n(28)),k=n.n(I),E=(n(54),n(55)());E.Extensions.register((function(){this.treeProcessor((function(){this.process((function(e){return e.findBy({context:"image"}).forEach((function(e){var t=e.getAttribute("alt");e.setAttribute("alt",t+'" class="img-fluid',!0)})),e}))}))}));var R=function(e){Object(M.a)(n,e);var t=Object(C.a)(n);function n(e){var r;return Object(x.a)(this,n),(r=t.call(this,e)).highlightCode=r.highlightCode.bind(Object(O.a)(r)),r}return Object(S.a)(n,[{key:"componentDidMount",value:function(){this.highlightCode()}},{key:"componentDidUpdate",value:function(){this.highlightCode()}},{key:"highlightCode",value:function(){this.root.querySelectorAll("pre code").forEach((function(e){return k.a.highlightBlock(e)}))}},{key:"render",value:function(){var e=this,t=E.convert(this.props.source,{attributes:this.props.attrs,safe:"safe"});return Object(h.jsx)("div",{ref:function(t){e.root=t},dangerouslySetInnerHTML:{__html:t}})}}]),n}(r.Component);n(63);function A(e){var t=e.title,n=e.content,r=e.attrs;return Object(h.jsxs)("div",{className:"container-fluid mt-4 ml-3 mr-3",children:[Object(h.jsx)("h1",{children:t}),Object(h.jsx)("hr",{}),Object(h.jsx)(R,{source:n,attrs:r})]})}A.defaultProps={attrs:{}};var j=n(64),D=n(65),N=n(69),P=n(70),_=n(74),L=n(75),B=n(76),H=n(77),F=n(78),G=n(79),q=n(80),W=n(81),U=n(82),V=n(86),z=["appOptics","atlas","azure-monitor","cloudwatch","datadog","dynatrace","elastic","ganglia","graphite","humio","influx","instana","jmx","kairos","new-relic","otlp","prometheus","signalFx","stackdriver","statsD","wavefront"],Y={};function K(){return Object(h.jsxs)("div",{children:[Object(h.jsx)(c.a,{exact:!0,path:"/docs",component:T}),Object(h.jsx)(c.a,{path:"/docs/installing",render:function(){return Object(h.jsx)(A,{title:"Installing",content:j})}}),Object(h.jsx)(c.a,{exact:!0,path:"/docs/concepts",render:function(){return Object(h.jsx)(A,{title:"Concepts",content:D})}}),Object(h.jsx)(c.a,{path:"/docs/registry/:system",render:function(e){var t=e.match.params.system;return z.includes(t)?Object(h.jsx)(A,{title:"Micrometer ".concat(t.split("-").map((function(e){return e[0].toUpperCase()+e.slice(1)})).join(" ")),content:Y[t]}):Object(h.jsx)(l.a,{to:"/docs"})}}),Object(h.jsx)(c.a,{path:"/docs/ref/jvm",render:function(){return Object(h.jsx)(A,{title:"JVM and System Metrics",content:N})}}),Object(h.jsx)(c.a,{path:"/docs/ref/cache",render:function(){return Object(h.jsx)(A,{title:"Cache Metrics",content:P})}}),Object(h.jsx)(c.a,{path:"/docs/ref/okhttpclient",render:function(){return Object(h.jsx)(A,{title:"OkHttpClient Metrics",content:_})}}),Object(h.jsx)(c.a,{path:"/docs/ref/jetty",render:function(){return Object(h.jsx)(A,{title:"Jetty and Jersey Metrics",content:L})}}),Object(h.jsx)(c.a,{path:"/docs/ref/netty",render:function(){return Object(h.jsx)(A,{title:"Netty Metrics",content:B})}}),Object(h.jsx)(c.a,{path:"/docs/guide/consoleReporter",render:function(){return Object(h.jsx)(A,{title:"Passing through to Dropwizard's console reporter",content:H})}}),Object(h.jsx)(c.a,{path:"/docs/guide/httpSenderResilience4jRetry",render:function(){return Object(h.jsx)(A,{title:"HttpSender with Resilience4j retry",content:F})}}),Object(h.jsx)(c.a,{path:"/docs/guide/customMeterRegistry",render:function(){return Object(h.jsx)(A,{title:"Custom meter registry",content:G})}}),Object(h.jsx)(c.a,{path:"/docs/support",render:function(){return Object(h.jsx)(A,{title:"Micrometer Support Policy",content:q})}}),Object(h.jsx)(c.a,{path:"/docs/observation",render:function(){return Object(h.jsx)(A,{title:"Observation",content:W})}}),Object(h.jsx)(c.a,{path:"/docs/tracing",render:function(){return Object(h.jsx)(A,{title:"Tracing",content:U})}}),Object(h.jsx)(c.a,{path:"/docs/contextPropagation",render:function(){return Object(h.jsx)(A,{title:"Context Propagation",content:V})}})]})}function J(){return Object(h.jsxs)("div",{children:[Object(h.jsx)(p,{}),Object(h.jsxs)(s.a,{children:[Object(h.jsx)(c.a,{exact:!0,path:"/",component:v}),Object(h.jsx)(c.a,{path:"/security-policy",component:function(){return window.location.href="https://tanzu.vmware.com/security",null}}),Object(h.jsx)(K,{}),Object(h.jsx)(c.a,{path:"**",render:function(){return Object(h.jsx)(l.a,{to:"/"})}})]}),Object(h.jsx)("div",{className:"container-fluid",style:{paddingRight:0,paddingLeft:0},children:Object(h.jsx)(f,{className:"row"})})]})}z.forEach((function(e){return Y[e]=n(87)("./".concat(e,".adoc"))}));Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));var $=n(11),Z=(n(137),n(138),Object($.a)());i.a.render(Object(h.jsx)(o.a,{history:Z,children:Object(h.jsx)(J,{})}),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((function(e){e.unregister()})).catch((function(e){console.error(e.message)}))}]),[[139,1,2]]]); +//# sourceMappingURL=main.38af4cc0.chunk.js.map \ No newline at end of file diff --git a/static/js/main.5d59054d.chunk.js.map b/static/js/main.38af4cc0.chunk.js.map similarity index 71% rename from static/js/main.5d59054d.chunk.js.map rename to static/js/main.38af4cc0.chunk.js.map index 5a217fd..9b02bd7 100644 --- a/static/js/main.5d59054d.chunk.js.map +++ b/static/js/main.38af4cc0.chunk.js.map @@ -1 +1 @@ -{"version":3,"sources":["components/Asciidoc/highlight/index.js","components/Asciidoc/highlight/highlight.js","components/Asciidoc/highlight/languages/gradle.js","components/Asciidoc/highlight/languages/groovy.js","components/Asciidoc/highlight/languages/http.js","components/Asciidoc/highlight/languages/java.js","components/Asciidoc/highlight/languages/xml.js","components/Asciidoc/highlight/languages/yaml.js","components/Asciidoc/highlight/languages/json.js","generated-docs/installing/index.adoc","generated-docs/concepts/index.adoc","generated-docs/concepts/img/prometheus-counter-norate.png","generated-docs/concepts/img/prometheus-counter.png","generated-docs/concepts/img/rate-normalizing.png","generated-docs/jvm/index.adoc","generated-docs/cache/index.adoc","generated-docs/cache/img/prometheus-guava-cache.png","generated-docs/cache/img/prometheus-guava-cache-ratio.png","generated-docs/cache/img/grafana-guava-miss-ratio.png","generated-docs/okhttpclient/index.adoc","generated-docs/jetty/index.adoc","generated-docs/netty/index.adoc","generated-docs/guide/console-reporter.adoc","generated-docs/guide/http-sender-resilience4j-retry.adoc","generated-docs/guide/custom-meter-registry.adoc","generated-docs/support/index.adoc","generated-docs/observation/index.adoc","generated-docs/tracing/index.adoc","generated-docs/tracing/img/trace-id.jpg","generated-docs/tracing/img/parents.jpg","generated-docs/tracing/img/zipkin.jpg","generated-docs/contextpropagation/index.adoc","../ /^/.//.*/.adoc$","generated-docs/implementations/appOptics.adoc","generated-docs/implementations/img/appoptics-timer-average.png","generated-docs/implementations/img/appoptics-timer.png","generated-docs/implementations/atlas.adoc","generated-docs/implementations/img/atlas-counter.png","generated-docs/implementations/img/atlas-timer.png","generated-docs/implementations/img/atlas-long-task-timer.png","generated-docs/implementations/azure-monitor.adoc","generated-docs/implementations/cloudwatch.adoc","generated-docs/implementations/datadog.adoc","generated-docs/implementations/dynatrace.adoc","generated-docs/implementations/elastic.adoc","generated-docs/implementations/ganglia.adoc","generated-docs/implementations/img/ganglia-counter.png","generated-docs/implementations/graphite.adoc","generated-docs/implementations/img/graphite-counter.png","generated-docs/implementations/hierarchical-name-mapping.adoc","generated-docs/implementations/humio.adoc","generated-docs/implementations/img/humio-timer.png","generated-docs/implementations/influx.adoc","generated-docs/implementations/install.adoc","generated-docs/implementations/instana.adoc","generated-docs/implementations/jmx.adoc","generated-docs/implementations/img/jmx-counter.png","generated-docs/implementations/kairos.adoc","generated-docs/implementations/new-relic.adoc","generated-docs/implementations/img/new-relic-timer-latency.png","generated-docs/implementations/img/new-relic-timer-throughput.png","generated-docs/implementations/img/new-relic-timer-percentiles.png","generated-docs/implementations/img/new-relic-timer-sla.png","generated-docs/implementations/otlp.adoc","generated-docs/implementations/prometheus.adoc","generated-docs/implementations/img/prometheus-dashboard.png","generated-docs/implementations/img/prometheus-counter.png","generated-docs/implementations/img/prometheus-counter-norate.png","generated-docs/implementations/img/prometheus-timer.png","generated-docs/implementations/img/prometheus-long-task-timer.png","generated-docs/implementations/signalFx.adoc","generated-docs/implementations/img/signalfx-timer-latency-query.png","generated-docs/implementations/img/signalfx-timer-latency.png","generated-docs/implementations/img/signalfx-timer-throughput.png","generated-docs/implementations/img/signalfx-timer-percentiles.png","generated-docs/implementations/img/signalfx-timer-sla.png","generated-docs/implementations/stackdriver.adoc","generated-docs/implementations/statsD.adoc","generated-docs/implementations/wavefront.adoc","generated-docs/implementations/img/wavefront-counter-rate.png","generated-docs/implementations/img/wavefront-timer-latency.png","generated-docs/implementations/img/wavefront-timer-throughput.png","img/logo.svg","components/MyNavbar/index.js","components/Footer/index.js","img/logo-no-title.svg","img/playback-latency.png","components/Home/index.js","components/DocRoot/index.js","components/Asciidoc/index.js","components/DocSection/index.js","components/DocRoutes/index.js","components/App/App.js","registerServiceWorker.js","index.js"],"names":["hljs","require","registerLanguage","module","exports","factory","window","self","ArrayProto","objectKeys","Object","keys","languages","aliases","noHighlightRe","languagePrefixRe","fixMarkupRe","spanEndTag","options","classPrefix","tabReplace","useBR","undefined","escape","value","replace","tag","node","nodeName","toLowerCase","testRe","re","lexeme","match","exec","index","isNotHighlighted","language","test","blockLanguage","block","i","length","_class","classes","className","parentNode","getLanguage","split","inherit","parent","key","result","objects","Array","prototype","slice","call","arguments","forEach","obj","nodeStream","_nodeStream","offset","child","firstChild","nextSibling","nodeType","nodeValue","push","event","mergeStreams","original","highlighted","processed","nodeStack","selectStream","open","attr_str","a","map","attributes","join","close","render","stream","substring","reverse","splice","pop","substr","expand_mode","mode","variants","cached_variants","variant","endsWithParent","compileLanguage","reStr","source","langRe","global","RegExp","case_insensitive","compileMode","compiled","keywords","beginKeywords","compiled_keywords","flatten","str","kw","pair","Number","lexemesRe","lexemes","begin","beginRe","end","endRe","terminator_end","illegal","illegalRe","relevance","contains","concat","apply","c","starts","terminators","filter","Boolean","highlight","name","ignore_illegals","continuation","subMode","endOfMode","endsParent","isIllegal","keywordMatch","match_str","hasOwnProperty","buildSpan","classname","insideSpan","leaveOpen","noPrefix","openSpan","processKeywords","keyword_match","last_index","top","mode_buffer","lastIndex","processSubLanguage","explicit","subLanguage","continuations","highlightAuto","processBuffer","startNewMode","create","processLexeme","buffer","new_mode","skip","excludeBegin","returnBegin","end_mode","origin","returnEnd","excludeEnd","Error","current","count","e","message","indexOf","text","languageSubset","second_best","fixMarkup","p1","buildClassName","prevClassName","currentLang","resultLang","trim","highlightBlock","originalStream","resultNode","document","createElementNS","innerHTML","textContent","configure","user_options","initHighlighting","called","blocks","querySelectorAll","initHighlightingOnLoad","addEventListener","lang","alias","listLanguages","IDENT_RE","UNDERSCORE_IDENT_RE","NUMBER_RE","C_NUMBER_RE","BINARY_NUMBER_RE","RE_STARTERS_RE","BACKSLASH_ESCAPE","APOS_STRING_MODE","QUOTE_STRING_MODE","PHRASAL_WORDS_MODE","COMMENT","inherits","C_LINE_COMMENT_MODE","C_BLOCK_COMMENT_MODE","HASH_COMMENT_MODE","NUMBER_MODE","C_NUMBER_MODE","BINARY_NUMBER_MODE","CSS_NUMBER_MODE","REGEXP_MODE","TITLE_MODE","UNDERSCORE_TITLE_MODE","METHOD_GUARD","keyword","literal","VERSION","KEYWORDS","JAVA_NUMBER_MODE","TAG_INTERNALS","LITERALS","keyPrefix","keyName","KEY","STRING","TYPES","VALUE_CONTAINER","OBJECT","ARRAY","webpackContext","req","id","webpackContextResolve","__webpack_require__","o","code","resolve","MyNavbar","Navbar","collapseOnSelect","expand","bg","style","borderTop","Brand","as","Link","to","src","logo","maxHeight","alt","Toggle","Collapse","Nav","Item","href","Footer","padding","color","Home","background","logoNoTitle","DocRoot","paddingRight","paddingLeft","NavLink","asciidoctor","Extensions","register","this","treeProcessor","process","doc","findBy","img","getAttribute","setAttribute","Asciidoc","props","highlightCode","bind","root","converted","convert","attrs","safe","ref","dangerouslySetInnerHTML","__html","Component","DocSection","title","content","defaultProps","docsInstalling","docsConcepts","docsJvm","docsCache","docsOkHttpClient","docsJetty","docsNetty","docsConsoleReporter","docsHttpSenderResilience4jRetry","docsCustomMeterRegistry","docsSupport","docsObservation","docsTracing","docsContextPropagation","systems","docsBySystem","DocRoutes","Route","exact","path","component","system","params","includes","part","toUpperCase","Redirect","App","Switch","location","sys","hostname","history","createBrowserHistory","ReactDOM","Router","getElementById","navigator","serviceWorker","ready","then","registration","unregister","catch","error","console"],"mappings":"0HAAA,IAAIA,EAAOC,EAAQ,IAEnBD,EAAKE,iBAAiB,SAAUD,EAAQ,KACxCD,EAAKE,iBAAiB,SAAUD,EAAQ,KACxCD,EAAKE,iBAAiB,OAAQD,EAAQ,KACtCD,EAAKE,iBAAiB,OAAQD,EAAQ,KACtCD,EAAKE,iBAAiB,MAAOD,EAAQ,KACrCD,EAAKE,iBAAiB,OAAQD,EAAQ,KACtCD,EAAKE,iBAAiB,OAAQD,EAAQ,KAEtCE,EAAOC,QAAUJ,G,oECAhB,SAASK,GAG6B,kBAAXC,QAAuBA,QACd,kBAATC,MAAqBA,MAmB/C,SAASP,GAET,IAAIQ,EAAa,GACbC,EAAaC,OAAOC,KAGpBC,EAAY,GACZC,EAAY,GAGZC,EAAmB,gCACnBC,EAAmB,8BACnBC,EAAmB,+BAEnBC,EAAa,UAIbC,EAAU,CACZC,YAAa,QACbC,WAAY,KACZC,OAAO,EACPT,eAAWU,GAMb,SAASC,EAAOC,GACd,OAAOA,EAAMC,QAAQ,KAAM,SAASA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,QAG1E,SAASC,EAAIC,GACX,OAAOA,EAAKC,SAASC,cAGvB,SAASC,EAAOC,EAAIC,GAClB,IAAIC,EAAQF,GAAMA,EAAGG,KAAKF,GAC1B,OAAOC,GAAyB,IAAhBA,EAAME,MAGxB,SAASC,EAAiBC,GACxB,OAAOvB,EAAcwB,KAAKD,GAG5B,SAASE,EAAcC,GACrB,IAAIC,EAAGR,EAAOS,EAAQC,EAClBC,EAAUJ,EAAMK,UAAY,IAMhC,GAJAD,GAAWJ,EAAMM,WAAaN,EAAMM,WAAWD,UAAY,GAG3DZ,EAAQlB,EAAiBmB,KAAKU,GAE5B,OAAOG,EAAYd,EAAM,IAAMA,EAAM,GAAK,eAK5C,IAAKQ,EAAI,EAAGC,GAFZE,EAAUA,EAAQI,MAAM,QAEKN,OAAQD,EAAIC,EAAQD,IAG/C,GAAIL,EAFJO,EAASC,EAAQH,KAEeM,EAAYJ,GAC1C,OAAOA,EAKb,SAASM,EAAQC,GACf,IAAIC,EACAC,EAAS,GACTC,EAAUC,MAAMC,UAAUC,MAAMC,KAAKC,UAAW,GAEpD,IAAKP,KAAOD,EACVE,EAAOD,GAAOD,EAAOC,GAKvB,OAJAE,EAAQM,SAAQ,SAASC,GACvB,IAAKT,KAAOS,EACVR,EAAOD,GAAOS,EAAIT,MAEfC,EAKT,SAASS,EAAWlC,GAClB,IAAIyB,EAAS,GA0Bb,OAzBA,SAAUU,EAAYnC,EAAMoC,GAC1B,IAAK,IAAIC,EAAQrC,EAAKsC,WAAYD,EAAOA,EAAQA,EAAME,YAC9B,IAAnBF,EAAMG,SACRJ,GAAUC,EAAMI,UAAU1B,OACA,IAAnBsB,EAAMG,WACbf,EAAOiB,KAAK,CACVC,MAAO,QACPP,OAAQA,EACRpC,KAAMqC,IAERD,EAASD,EAAYE,EAAOD,GAIvBrC,EAAIsC,GAAO/B,MAAM,oBACpBmB,EAAOiB,KAAK,CACVC,MAAO,OACPP,OAAQA,EACRpC,KAAMqC,KAKd,OAAOD,EAvBT,CAwBGpC,EAAM,GACFyB,EAGT,SAASmB,EAAaC,EAAUC,EAAajD,GAC3C,IAAIkD,EAAY,EACZtB,EAAS,GACTuB,EAAY,GAEhB,SAASC,IACP,OAAKJ,EAAS9B,QAAW+B,EAAY/B,OAGjC8B,EAAS,GAAGT,SAAWU,EAAY,GAAGV,OAChCS,EAAS,GAAGT,OAASU,EAAY,GAAGV,OAAUS,EAAWC,EAkBnC,UAAzBA,EAAY,GAAGH,MAAoBE,EAAWC,EArB5CD,EAAS9B,OAAS8B,EAAWC,EAwBxC,SAASI,EAAKlD,GACZ,SAASmD,EAASC,GAAI,MAAO,IAAMA,EAAEnD,SAAW,KAAOL,EAAOwD,EAAEvD,OAAOC,QAAQ,IAAK,UAAY,IAChG2B,GAAU,IAAM1B,EAAIC,GAAQnB,EAAWwE,IAAIvB,KAAK9B,EAAKsD,WAAYH,GAAUI,KAAK,IAAM,IAGxF,SAASC,EAAMxD,GACbyB,GAAU,KAAO1B,EAAIC,GAAQ,IAG/B,SAASyD,EAAOd,IACG,UAAhBA,EAAMA,MAAoBO,EAAOM,GAAOb,EAAM3C,MAGjD,KAAO6C,EAAS9B,QAAU+B,EAAY/B,QAAQ,CAC5C,IAAI2C,EAAST,IAGb,GAFAxB,GAAU7B,EAAOC,EAAM8D,UAAUZ,EAAWW,EAAO,GAAGtB,SACtDW,EAAYW,EAAO,GAAGtB,OAClBsB,IAAWb,EAAU,CAOvBG,EAAUY,UAAU5B,QAAQwB,GAC5B,GACEC,EAAOC,EAAOG,OAAO,EAAG,GAAG,IAC3BH,EAAST,UACFS,IAAWb,GAAYa,EAAO3C,QAAU2C,EAAO,GAAGtB,SAAWW,GACtEC,EAAUY,UAAU5B,QAAQkB,OAEJ,UAApBQ,EAAO,GAAGf,MACZK,EAAUN,KAAKgB,EAAO,GAAG1D,MAEzBgD,EAAUc,MAEZL,EAAOC,EAAOG,OAAO,EAAG,GAAG,IAG/B,OAAOpC,EAAS7B,EAAOC,EAAMkE,OAAOhB,IAKtC,SAASiB,EAAYC,GAMnB,OALIA,EAAKC,WAAaD,EAAKE,kBACzBF,EAAKE,gBAAkBF,EAAKC,SAASb,KAAI,SAASe,GAChD,OAAO9C,EAAQ2C,EAAM,CAACC,SAAU,MAAOE,OAGpCH,EAAKE,iBAAoBF,EAAKI,gBAAkB,CAAC/C,EAAQ2C,KAAW,CAACA,GAG9E,SAASK,EAAgB5D,GAEvB,SAAS6D,EAAMnE,GACX,OAAQA,GAAMA,EAAGoE,QAAWpE,EAGhC,SAASqE,EAAO5E,EAAO6E,GACrB,OAAO,IAAIC,OACTJ,EAAM1E,GACN,KAAOa,EAASkE,iBAAmB,IAAM,KAAOF,EAAS,IAAM,KAInE,SAASG,EAAYZ,EAAM1C,GACzB,IAAI0C,EAAKa,SAAT,CAKA,GAHAb,EAAKa,UAAW,EAEhBb,EAAKc,SAAWd,EAAKc,UAAYd,EAAKe,cAClCf,EAAKc,SAAU,CACjB,IAAIE,EAAoB,GAEpBC,EAAU,SAAShE,EAAWiE,GAC5BzE,EAASkE,mBACXO,EAAMA,EAAIjF,eAEZiF,EAAI9D,MAAM,KAAKW,SAAQ,SAASoD,GAC9B,IAAIC,EAAOD,EAAG/D,MAAM,KACpB4D,EAAkBI,EAAK,IAAM,CAACnE,EAAWmE,EAAK,GAAKC,OAAOD,EAAK,IAAM,OAI5C,kBAAlBpB,EAAKc,SACdG,EAAQ,UAAWjB,EAAKc,UAExBjG,EAAWmF,EAAKc,UAAU/C,SAAQ,SAAUd,GAC1CgE,EAAQhE,EAAW+C,EAAKc,SAAS7D,OAGrC+C,EAAKc,SAAWE,EAElBhB,EAAKsB,UAAYd,EAAOR,EAAKuB,SAAW,OAAO,GAE3CjE,IACE0C,EAAKe,gBACPf,EAAKwB,MAAQ,OAASxB,EAAKe,cAAc3D,MAAM,KAAKkC,KAAK,KAAO,QAE7DU,EAAKwB,QACRxB,EAAKwB,MAAQ,SACfxB,EAAKyB,QAAUjB,EAAOR,EAAKwB,OACtBxB,EAAK0B,KAAQ1B,EAAKI,iBACrBJ,EAAK0B,IAAM,SACT1B,EAAK0B,MACP1B,EAAK2B,MAAQnB,EAAOR,EAAK0B,MAC3B1B,EAAK4B,eAAiBtB,EAAMN,EAAK0B,MAAQ,GACrC1B,EAAKI,gBAAkB9C,EAAOsE,iBAChC5B,EAAK4B,iBAAmB5B,EAAK0B,IAAM,IAAM,IAAMpE,EAAOsE,iBAEtD5B,EAAK6B,UACP7B,EAAK8B,UAAYtB,EAAOR,EAAK6B,UACT,MAAlB7B,EAAK+B,YACP/B,EAAK+B,UAAY,GACd/B,EAAKgC,WACRhC,EAAKgC,SAAW,IAElBhC,EAAKgC,SAAWtE,MAAMC,UAAUsE,OAAOC,MAAM,GAAIlC,EAAKgC,SAAS5C,KAAI,SAAS+C,GAC1E,OAAOpC,EAAkB,SAANoC,EAAenC,EAAOmC,OAE3CnC,EAAKgC,SAASjE,SAAQ,SAASoE,GAAIvB,EAAYuB,EAAGnC,MAE9CA,EAAKoC,QACPxB,EAAYZ,EAAKoC,OAAQ9E,GAG3B,IAAI+E,EACFrC,EAAKgC,SAAS5C,KAAI,SAAS+C,GACzB,OAAOA,EAAEpB,cAAgB,QAAUoB,EAAEX,MAAQ,QAAUW,EAAEX,SAE1DS,OAAO,CAACjC,EAAK4B,eAAgB5B,EAAK6B,UAClCzC,IAAIkB,GACJgC,OAAOC,SACVvC,EAAKqC,YAAcA,EAAYvF,OAAS0D,EAAO6B,EAAY/C,KAAK,MAAM,GAAQ,CAAChD,KAAM,WAAiB,OAAO,QAG/GsE,EAAYnE,GAYd,SAAS+F,EAAUC,EAAM7G,EAAO8G,EAAiBC,GAE/C,SAASC,EAAQxG,EAAQ4D,GACvB,IAAInD,EAAGC,EAEP,IAAKD,EAAI,EAAGC,EAASkD,EAAKgC,SAASlF,OAAQD,EAAIC,EAAQD,IACrD,GAAIX,EAAO8D,EAAKgC,SAASnF,GAAG4E,QAASrF,GACnC,OAAO4D,EAAKgC,SAASnF,GAK3B,SAASgG,EAAU7C,EAAM5D,GACvB,GAAIF,EAAO8D,EAAK2B,MAAOvF,GAAS,CAC9B,KAAO4D,EAAK8C,YAAc9C,EAAK1C,QAC7B0C,EAAOA,EAAK1C,OAEd,OAAO0C,EAET,GAAIA,EAAKI,eACP,OAAOyC,EAAU7C,EAAK1C,OAAQlB,GAIlC,SAAS2G,EAAU3G,EAAQ4D,GACzB,OAAQ0C,GAAmBxG,EAAO8D,EAAK8B,UAAW1F,GAGpD,SAAS4G,EAAahD,EAAM3D,GAC1B,IAAI4G,EAAYxG,EAASkE,iBAAmBtE,EAAM,GAAGJ,cAAgBI,EAAM,GAC3E,OAAO2D,EAAKc,SAASoC,eAAeD,IAAcjD,EAAKc,SAASmC,GAGlE,SAASE,EAAUC,EAAWC,EAAYC,EAAWC,GACnD,IACIC,EAAc,iBADAD,EAAW,GAAKjI,EAAQC,aAM1C,OAFAiI,GAAYJ,EAAY,MAENC,GAJAC,EAAY,GAAKjI,GAOrC,SAASoI,IACP,IAAIC,EAAeC,EAAYtH,EAAOmB,EAEtC,IAAKoG,EAAI9C,SACP,OAAOnF,EAAOkI,GAOhB,IALArG,EAAS,GACTmG,EAAa,EACbC,EAAItC,UAAUwC,UAAY,EAC1BzH,EAAQuH,EAAItC,UAAUhF,KAAKuH,GAEpBxH,GACLmB,GAAU7B,EAAOkI,EAAYnE,UAAUiE,EAAYtH,EAAME,SACzDmH,EAAgBV,EAAaY,EAAKvH,KAEhC0F,GAAa2B,EAAc,GAC3BlG,GAAU2F,EAAUO,EAAc,GAAI/H,EAAOU,EAAM,MAEnDmB,GAAU7B,EAAOU,EAAM,IAEzBsH,EAAaC,EAAItC,UAAUwC,UAC3BzH,EAAQuH,EAAItC,UAAUhF,KAAKuH,GAE7B,OAAOrG,EAAS7B,EAAOkI,EAAY/D,OAAO6D,IAG5C,SAASI,IACP,IAAIC,EAAsC,kBAApBJ,EAAIK,YAC1B,GAAID,IAAahJ,EAAU4I,EAAIK,aAC7B,OAAOtI,EAAOkI,GAGhB,IAAIrG,EAASwG,EACAxB,EAAUoB,EAAIK,YAAaJ,GAAa,EAAMK,EAAcN,EAAIK,cAChEE,EAAcN,EAAaD,EAAIK,YAAYnH,OAAS8G,EAAIK,iBAAcvI,GAYnF,OANIkI,EAAI7B,UAAY,IAClBA,GAAavE,EAAOuE,WAElBiC,IACFE,EAAcN,EAAIK,aAAezG,EAAOoG,KAEnCT,EAAU3F,EAAOf,SAAUe,EAAO5B,OAAO,GAAO,GAGzD,SAASwI,IACP5G,GAA8B,MAAnBoG,EAAIK,YAAsBF,IAAuBN,IAC5DI,EAAc,GAGhB,SAASQ,EAAarE,GACpBxC,GAAUwC,EAAK/C,UAAWkG,EAAUnD,EAAK/C,UAAW,IAAI,GAAO,GAC/D2G,EAAM9I,OAAOwJ,OAAOtE,EAAM,CAAC1C,OAAQ,CAAC1B,MAAOgI,KAG7C,SAASW,EAAcC,EAAQpI,GAI7B,GAFAyH,GAAeW,EAED,MAAVpI,EAEF,OADAgI,IACO,EAGT,IAAIK,EAAW7B,EAAQxG,EAAQwH,GAC/B,GAAIa,EAaF,OAZIA,EAASC,KACXb,GAAezH,GAEXqI,EAASE,eACXd,GAAezH,GAEjBgI,IACKK,EAASG,aAAgBH,EAASE,eACrCd,EAAczH,IAGlBiI,EAAaI,EAAUrI,GAChBqI,EAASG,YAAc,EAAIxI,EAAOU,OAG3C,IAAI+H,EAAWhC,EAAUe,EAAKxH,GAC9B,GAAIyI,EAAU,CACZ,IAAIC,EAASlB,EACTkB,EAAOJ,KACTb,GAAezH,GAET0I,EAAOC,WAAaD,EAAOE,aAC/BnB,GAAezH,GAEjBgI,IACIU,EAAOE,aACTnB,EAAczH,IAGlB,GACMwH,EAAI3G,YACNO,GAAUnC,GAEPuI,EAAIc,MAASd,EAAIK,cACpBlC,GAAa6B,EAAI7B,WAEnB6B,EAAMA,EAAItG,aACHsG,IAAQiB,EAASvH,QAI1B,OAHIuH,EAASzC,QACXiC,EAAaQ,EAASzC,OAAQ,IAEzB0C,EAAOC,UAAY,EAAI3I,EAAOU,OAGvC,GAAIiG,EAAU3G,EAAQwH,GACpB,MAAM,IAAIqB,MAAM,mBAAqB7I,EAAS,gBAAkBwH,EAAI3G,WAAa,aAAe,KAQlG,OADA4G,GAAezH,EACRA,EAAOU,QAAU,EAG1B,IAAIL,EAAWU,EAAYsF,GAC3B,IAAKhG,EACH,MAAM,IAAIwI,MAAM,sBAAwBxC,EAAO,KAGjDpC,EAAgB5D,GAChB,IAEiByI,EAFbtB,EAAMjB,GAAgBlG,EACtByH,EAAgB,GAChB1G,EAAS,GACb,IAAI0H,EAAUtB,EAAKsB,IAAYzI,EAAUyI,EAAUA,EAAQ5H,OACrD4H,EAAQjI,YACVO,EAAS2F,EAAU+B,EAAQjI,UAAW,IAAI,GAAQO,GAGtD,IAAIqG,EAAc,GACd9B,EAAY,EAChB,IAEE,IADA,IAAI1F,EAAO8I,EAAO5I,EAAQ,EAExBqH,EAAIvB,YAAYyB,UAAYvH,EAC5BF,EAAQuH,EAAIvB,YAAY/F,KAAKV,IAG7BuJ,EAAQZ,EAAc3I,EAAM8D,UAAUnD,EAAOF,EAAME,OAAQF,EAAM,IACjEE,EAAQF,EAAME,MAAQ4I,EAGxB,IADAZ,EAAc3I,EAAMkE,OAAOvD,IACvB2I,EAAUtB,EAAKsB,EAAQ5H,OAAQ4H,EAAUA,EAAQ5H,OAC/C4H,EAAQjI,YACVO,GAAUnC,GAGd,MAAO,CACL0G,UAAWA,EACXnG,MAAO4B,EACPf,SAAUgG,EACVmB,IAAKA,GAEP,MAAOwB,GACP,GAAIA,EAAEC,UAA6C,IAAlCD,EAAEC,QAAQC,QAAQ,WACjC,MAAO,CACLvD,UAAW,EACXnG,MAAOD,EAAOC,IAGhB,MAAMwJ,GAgBZ,SAASjB,EAAcoB,EAAMC,GAC3BA,EAAiBA,GAAkBlK,EAAQN,WAAaH,EAAWG,GACnE,IAAIwC,EAAS,CACXuE,UAAW,EACXnG,MAAOD,EAAO4J,IAEZE,EAAcjI,EAelB,OAdAgI,EAAelD,OAAOnF,GAAaY,SAAQ,SAAS0E,GAClD,IAAIyC,EAAU1C,EAAUC,EAAM8C,GAAM,GACpCL,EAAQzI,SAAWgG,EACfyC,EAAQnD,UAAY0D,EAAY1D,YAClC0D,EAAcP,GAEZA,EAAQnD,UAAYvE,EAAOuE,YAC7B0D,EAAcjI,EACdA,EAAS0H,MAGTO,EAAYhJ,WACde,EAAOiI,YAAcA,GAEhBjI,EAUT,SAASkI,EAAU9J,GACjB,OAASN,EAAQE,YAAcF,EAAQG,MAEnCG,EAAMC,QAAQT,GAAa,SAASiB,EAAOsJ,GACzC,OAAIrK,EAAQG,OAAmB,OAAVY,EACZ,OACEf,EAAQE,WACVmK,EAAG9J,QAAQ,MAAOP,EAAQE,YAE5B,MAPTI,EAWN,SAASgK,EAAeC,EAAeC,EAAaC,GAClD,IAAItJ,EAAWqJ,EAAc7K,EAAQ6K,GAAeC,EAChDvI,EAAW,CAACqI,EAAcG,QAU9B,OARKH,EAAcxJ,MAAM,aACvBmB,EAAOiB,KAAK,SAG2B,IAArCoH,EAAcP,QAAQ7I,IACxBe,EAAOiB,KAAKhC,GAGPe,EAAO8B,KAAK,KAAK0G,OAO1B,SAASC,EAAerJ,GACtB,IAAIb,EAAMmK,EAAgB1I,EAAQ2I,EAAYZ,EAC1C9I,EAAWE,EAAcC,GAEzBJ,EAAiBC,KAGjBnB,EAAQG,OACVM,EAAOqK,SAASC,gBAAgB,+BAAgC,QAC3DC,UAAY1J,EAAM0J,UAAUzK,QAAQ,MAAO,IAAIA,QAAQ,cAAe,MAE3EE,EAAOa,EAET2I,EAAOxJ,EAAKwK,YACZ/I,EAASf,EAAW+F,EAAU/F,EAAU8I,GAAM,GAAQpB,EAAcoB,IAEpEW,EAAiBjI,EAAWlC,IACTe,UACjBqJ,EAAaC,SAASC,gBAAgB,+BAAgC,QAC3DC,UAAY9I,EAAO5B,MAC9B4B,EAAO5B,MAAQ+C,EAAauH,EAAgBjI,EAAWkI,GAAaZ,IAEtE/H,EAAO5B,MAAQ8J,EAAUlI,EAAO5B,OAEhCgB,EAAM0J,UAAY9I,EAAO5B,MACzBgB,EAAMK,UAAY2I,EAAehJ,EAAMK,UAAWR,EAAUe,EAAOf,UACnEG,EAAMY,OAAS,CACbf,SAAUe,EAAOf,SACjBN,GAAIqB,EAAOuE,WAETvE,EAAOiI,cACT7I,EAAM6I,YAAc,CAClBhJ,SAAUe,EAAOiI,YAAYhJ,SAC7BN,GAAIqB,EAAOiI,YAAY1D,aAQ7B,SAASyE,EAAUC,GACjBnL,EAAU+B,EAAQ/B,EAASmL,GAM7B,SAASC,IACP,IAAIA,EAAiBC,OAArB,CAEAD,EAAiBC,QAAS,EAE1B,IAAIC,EAASR,SAASS,iBAAiB,YACvCjM,EAAWmD,QAAQF,KAAK+I,EAAQX,IAMlC,SAASa,IACPC,iBAAiB,mBAAoBL,GAAkB,GACvDK,iBAAiB,OAAQL,GAAkB,GAG7C,SAASpM,EAAiBmI,EAAMhG,GAC9B,IAAIuK,EAAOhM,EAAUyH,GAAQhG,EAASrC,GAClC4M,EAAK/L,SACP+L,EAAK/L,QAAQ8C,SAAQ,SAASkJ,GAAQhM,EAAQgM,GAASxE,KAI3D,SAASyE,IACP,OAAOrM,EAAWG,GAGpB,SAASmC,EAAYsF,GAEnB,OADAA,GAAQA,GAAQ,IAAIxG,cACbjB,EAAUyH,IAASzH,EAAUC,EAAQwH,IAK9CrI,EAAKoI,UAAYA,EACjBpI,EAAK+J,cAAgBA,EACrB/J,EAAKsL,UAAYA,EACjBtL,EAAK6L,eAAiBA,EACtB7L,EAAKoM,UAAYA,EACjBpM,EAAKsM,iBAAmBA,EACxBtM,EAAK0M,uBAAyBA,EAC9B1M,EAAKE,iBAAmBA,EACxBF,EAAK8M,cAAgBA,EACrB9M,EAAK+C,YAAcA,EACnB/C,EAAKiD,QAAUA,EAGfjD,EAAK+M,SAAW,eAChB/M,EAAKgN,oBAAsB,gBAC3BhN,EAAKiN,UAAY,oBACjBjN,EAAKkN,YAAc,yEACnBlN,EAAKmN,iBAAmB,eACxBnN,EAAKoN,eAAiB,+IAGtBpN,EAAKqN,iBAAmB,CACtBjG,MAAO,eAAgBO,UAAW,GAEpC3H,EAAKsN,iBAAmB,CACtBzK,UAAW,SACXuE,MAAO,IAAME,IAAK,IAClBG,QAAS,MACTG,SAAU,CAAC5H,EAAKqN,mBAElBrN,EAAKuN,kBAAoB,CACvB1K,UAAW,SACXuE,MAAO,IAAKE,IAAK,IACjBG,QAAS,MACTG,SAAU,CAAC5H,EAAKqN,mBAElBrN,EAAKwN,mBAAqB,CACxBpG,MAAO,8IAETpH,EAAKyN,QAAU,SAAUrG,EAAOE,EAAKoG,GACnC,IAAI9H,EAAO5F,EAAKiD,QACd,CACEJ,UAAW,UACXuE,MAAOA,EAAOE,IAAKA,EACnBM,SAAU,IAEZ8F,GAAY,IAQd,OANA9H,EAAKgC,SAASvD,KAAKrE,EAAKwN,oBACxB5H,EAAKgC,SAASvD,KAAK,CACjBxB,UAAW,SACXuE,MAAO,+BACPO,UAAW,IAEN/B,GAET5F,EAAK2N,oBAAsB3N,EAAKyN,QAAQ,KAAM,KAC9CzN,EAAK4N,qBAAuB5N,EAAKyN,QAAQ,OAAQ,QACjDzN,EAAK6N,kBAAoB7N,EAAKyN,QAAQ,IAAK,KAC3CzN,EAAK8N,YAAc,CACjBjL,UAAW,SACXuE,MAAOpH,EAAKiN,UACZtF,UAAW,GAEb3H,EAAK+N,cAAgB,CACnBlL,UAAW,SACXuE,MAAOpH,EAAKkN,YACZvF,UAAW,GAEb3H,EAAKgO,mBAAqB,CACxBnL,UAAW,SACXuE,MAAOpH,EAAKmN,iBACZxF,UAAW,GAEb3H,EAAKiO,gBAAkB,CACrBpL,UAAW,SACXuE,MAAOpH,EAAKiN,UAALjN,kGASP2H,UAAW,GAEb3H,EAAKkO,YAAc,CACjBrL,UAAW,SACXuE,MAAO,KAAME,IAAK,aAClBG,QAAS,KACTG,SAAU,CACR5H,EAAKqN,iBACL,CACEjG,MAAO,KAAME,IAAK,KAClBK,UAAW,EACXC,SAAU,CAAC5H,EAAKqN,qBAItBrN,EAAKmO,WAAa,CAChBtL,UAAW,QACXuE,MAAOpH,EAAK+M,SACZpF,UAAW,GAEb3H,EAAKoO,sBAAwB,CAC3BvL,UAAW,QACXuE,MAAOpH,EAAKgN,oBACZrF,UAAW,GAEb3H,EAAKqO,aAAe,CAElBjH,MAAO,UAAYpH,EAAKgN,oBACxBrF,UAAW,IA7xBXtH,CAAQD,GATX,I,cCVDD,EAAOC,QAAU,SAASJ,GACxB,MAAO,CACLuG,kBAAkB,EAClBG,SAAU,CACR4H,QACE,mxCAkBJ1G,SAAU,CACR5H,EAAK2N,oBACL3N,EAAK4N,qBACL5N,EAAKsN,iBACLtN,EAAKuN,kBACLvN,EAAK8N,YACL9N,EAAKkO,gB,cC3BX/N,EAAOC,QAAU,SAASJ,GACtB,MAAO,CACH0G,SAAU,CACN6H,QAAU,kBACVD,QACA,wUASJ1G,SAAU,CACN5H,EAAKyN,QACD,UACA,OACA,CACI9F,UAAY,EACZC,SAAW,CACT,CAEIR,MAAO,OAAQO,UAAW,GAE9B,CACI9E,UAAY,SACZuE,MAAQ,iBAKtBpH,EAAK2N,oBACL3N,EAAK4N,qBACL,CACI/K,UAAW,SACXuE,MAAO,MAAOE,IAAK,OAEvB,CACIzE,UAAW,SACXuE,MAAO,MAAOE,IAAK,OAEvB,CACIzE,UAAW,SACXuE,MAAO,OAAQE,IAAK,OACpBK,UAAW,IAEf3H,EAAKsN,iBACL,CACIzK,UAAW,SACXuE,MAAO,iBACPQ,SAAU,CACN5H,EAAKqN,mBAGbrN,EAAKuN,kBACL,CACI1K,UAAW,OACXuE,MAAO,kBAAmBE,IAAK,IAC/BG,QAAS,MAEbzH,EAAKgO,mBACL,CACInL,UAAW,QACX8D,cAAe,6BAA8BW,IAAK,IAClDG,QAAS,IACTG,SAAU,CACN,CAACjB,cAAe,sBAChB3G,EAAKoO,wBAGbpO,EAAK+N,cACL,CACIlL,UAAW,OAAQuE,MAAO,cAE9B,CAEIvE,UAAW,SAAUuE,MAAO,6BAEhC,CAGIA,MAAO,KAAME,IAAK,MAEtB,CAEIzE,UAAW,SAAUuE,MAAO,uBAC5BO,UAAW,IAGnBF,QAAS,W,cC5FjBtH,EAAOC,QAAU,SAASJ,GACxB,IAAIwO,EAAU,iBACd,MAAO,CACL3N,QAAS,CAAC,SACV4G,QAAS,MACTG,SAAU,CACR,CACER,MAAO,IAAMoH,EAASlH,IAAK,IAC3BM,SAAU,CAAC,CAAC/E,UAAW,SAAUuE,MAAO,kBAE1C,CACEA,MAAO,iBAAmBoH,EAAU,IAAKhE,aAAa,EAAMlD,IAAK,IACjEM,SAAU,CACR,CACE/E,UAAW,SACXuE,MAAO,IAAKE,IAAK,IACjBiD,cAAc,EAAMK,YAAY,GAElC,CACExD,MAAOoH,GAET,CACE3L,UAAW,UACXuE,MAAO,YAIb,CACEvE,UAAW,YACXuE,MAAO,OAAQE,IAAK,KAAMsD,YAAY,EACtCnD,QAAS,YACTO,OAAQ,CAACV,IAAK,IAAKK,UAAW,IAEhC,CACEP,MAAO,SACPY,OAAQ,CAAC6B,YAAa,GAAI7D,gBAAgB,Q,cCjClD7F,EAAOC,QAAU,SAASJ,GACxB,IAEIyO,EACF,6VAqBEC,EAAmB,CACrB7L,UAAW,SACXuE,MAhBmB,uNAiBnBO,UAAW,GAGb,MAAO,CACL9G,QAAS,CAAC,OACV6F,SAAU+H,EACVhH,QAAS,QACTG,SAAU,CACR5H,EAAKyN,QACH,UACA,OACA,CACE9F,UAAY,EACZC,SAAW,CACT,CAEER,MAAO,OAAQO,UAAW,GAE5B,CACE9E,UAAY,SACZuE,MAAQ,iBAKhBpH,EAAK2N,oBACL3N,EAAK4N,qBACL5N,EAAKsN,iBACLtN,EAAKuN,kBACL,CACE1K,UAAW,QACX8D,cAAe,kBAAmBW,IAAK,QAASsD,YAAY,EAC5DlE,SAAU,kBACVe,QAAS,WACTG,SAAU,CACR,CAACjB,cAAe,sBAChB3G,EAAKoO,wBAGT,CAGEzH,cAAe,wBACfgB,UAAW,GAEb,CACE9E,UAAW,WACXuE,MAAO,qKAAoCpH,EAAKgN,oBAAsB,UAAWxC,aAAa,EAAMlD,IAAK,QACzGsD,YAAY,EACZlE,SAAU+H,EACV7G,SAAU,CACR,CACER,MAAOpH,EAAKgN,oBAAsB,UAAWxC,aAAa,EAC1D7C,UAAW,EACXC,SAAU,CAAC5H,EAAKoO,wBAElB,CACEvL,UAAW,SACXuE,MAAO,KAAME,IAAK,KAClBZ,SAAU+H,EACV9G,UAAW,EACXC,SAAU,CACR5H,EAAKsN,iBACLtN,EAAKuN,kBACLvN,EAAK+N,cACL/N,EAAK4N,uBAGT5N,EAAK2N,oBACL3N,EAAK4N,uBAGTc,EACA,CACE7L,UAAW,OAAQuE,MAAO,kB,cCtGlCjH,EAAOC,QAAU,SAASJ,GACxB,IACI2O,EAAgB,CAClB3I,gBAAgB,EAChByB,QAAS,IACTE,UAAW,EACXC,SAAU,CACR,CACE/E,UAAW,OACXuE,MARa,qBASbO,UAAW,GAEb,CACEP,MAAO,OACPO,UAAW,EACXC,SAAU,CACR,CACE/E,UAAW,SACX6F,YAAY,EACZ7C,SAAU,CACR,CAACuB,MAAO,IAAKE,IAAK,KAClB,CAACF,MAAO,IAAKE,IAAK,KAClB,CAACF,MAAO,sBAOpB,MAAO,CACLvG,QAAS,CAAC,OAAQ,QAAS,MAAO,OAAQ,MAAO,MAAO,MAAO,SAC/D0F,kBAAkB,EAClBqB,SAAU,CACR,CACE/E,UAAW,OACXuE,MAAO,YAAaE,IAAK,IACzBK,UAAW,GACXC,SAAU,CAAC,CAACR,MAAO,MAAOE,IAAK,SAEjCtH,EAAKyN,QACH,UACA,SACA,CACE9F,UAAW,KAGf,CACEP,MAAO,kBAAmBE,IAAK,UAC/BK,UAAW,IAEb,CACE9E,UAAW,OACXuE,MAAO,SAAUE,IAAK,MAAOK,UAAW,IAE1C,CACEP,MAAO,YAAaE,IAAK,MACzBuC,YAAa,MACbjC,SAAU,CAAC,CAACR,MAAO,OAAQE,IAAK,OAAQgD,MAAM,KAEhD,CACEzH,UAAW,MAOXuE,MAAO,oBAAqBE,IAAK,IACjCZ,SAAU,CAAC2B,KAAM,SACjBT,SAAU,CAAC+G,GACX3G,OAAQ,CACNV,IAAK,WAAYqD,WAAW,EAC5Bd,YAAa,CAAC,MAAO,SAGzB,CACEhH,UAAW,MAEXuE,MAAO,qBAAsBE,IAAK,IAClCZ,SAAU,CAAC2B,KAAM,UACjBT,SAAU,CAAC+G,GACX3G,OAAQ,CACNV,IAAK,aAAgBqD,WAAW,EAChCd,YAAa,CAAC,eAAgB,aAAc,aAAc,SAG9D,CACEhH,UAAW,MACXuE,MAAO,MAAOE,IAAK,MACnBM,SAAU,CACR,CACE/E,UAAW,OAAQuE,MAAO,aAAcO,UAAW,GAErDgH,Q,cC5FVxO,EAAOC,QAAU,SAASJ,GACxB,IAAI4O,EAAW,yBAEXC,EAAY,WACZC,EAAW,qBACXC,EAAM,CACRlM,UAAW,OACXgD,SAAU,CACR,CAAEuB,MAAOyH,EAAYC,EAAU,KAC/B,CAAE1H,MAAOyH,YAAkBC,EAAlBD,MACT,CAAEzH,MAAOyH,YAAkBC,EAAlBD,QAWTG,EAAS,CACXnM,UAAW,SACX8E,UAAW,EACX9B,SAAU,CACR,CAACuB,MAAO,IAAKE,IAAK,KAClB,CAACF,MAAO,IAAKE,IAAK,KAClB,CAACF,MAAO,QAEVQ,SAAU,CACR5H,EAAKqN,iBAhBgB,CACvBxK,UAAW,oBACXgD,SAAU,CACR,CAAEuB,MAAO,KAAQE,IAAK,MACtB,CAAEF,MAAO,KAAOE,IAAK,SAiBzB,MAAO,CACLf,kBAAkB,EAClB1F,QAAS,CAAC,MAAO,OAAQ,QACzB+G,SAAU,CACRmH,EACA,CACElM,UAAW,OACXuE,MAAO,UACPO,UAAW,IAEb,CACE9E,UAAW,SACXuE,MAAO,YACPuD,WAAW,EACX/C,SAAUoH,EAAOpH,SAEjBN,IAAKyH,EAAIlJ,SAAS,GAAGuB,OAEvB,CACEvE,UAAW,OACXuE,MAAO,KAAOpH,EAAKgN,qBAErB,CACEnK,UAAW,OACXuE,MAAO,IAAMpH,EAAKgN,oBAAsB,KAE1C,CACEnK,UAAW,OACXuE,MAAO,MAAQpH,EAAKgN,oBAAsB,KAE5C,CACEnK,UAAW,SACXuE,MAAO,OACPO,UAAW,GAEb3H,EAAK6N,kBACL,CACElH,cAAeiI,EACflI,SAAU,CAAC6H,QAASK,IAEtB5O,EAAK+N,cACLiB,M,cC/EN7O,EAAOC,QAAU,SAASJ,GACxB,IAAI4O,EAAW,CAACL,QAAS,mBACrBU,EAAQ,CACVjP,EAAKuN,kBACLvN,EAAK+N,eAEHmB,EAAkB,CACpB5H,IAAK,IAAKtB,gBAAgB,EAAM4E,YAAY,EAC5ChD,SAAUqH,EACVvI,SAAUkI,GAERO,EAAS,CACX/H,MAAO,IAAKE,IAAK,IACjBM,SAAU,CACR,CACE/E,UAAW,OACXuE,MAAO,IAAKE,IAAK,IACjBM,SAAU,CAAC5H,EAAKqN,kBAChB5F,QAAS,OAEXzH,EAAKiD,QAAQiM,EAAiB,CAAC9H,MAAO,OAExCK,QAAS,OAEP2H,EAAQ,CACVhI,MAAO,MAAOE,IAAK,MACnBM,SAAU,CAAC5H,EAAKiD,QAAQiM,IACxBzH,QAAS,OAGX,OADAwH,EAAMzJ,OAAOyJ,EAAMvM,OAAQ,EAAGyM,EAAQC,GAC/B,CACLxH,SAAUqH,EACVvI,SAAUkI,EACVnH,QAAS,S,0DCjCbtH,EAAOC,QAAU,qrE,gBCAjBD,EAAOC,QAAU,w0pBAA05pB,EAAQ,IAAsD,6dAA+d,EAAQ,IAA+C,26CAA+6C,EAAQ,IAA6C,29uC,6BCAn+tB,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,swE,gBCAjBD,EAAOC,QAAU,yiDAA6iD,EAAQ,IAAmD,qWAAuW,EAAQ,IAAyD,uoBAA2oB,EAAQ,IAAqD,sC,6BCAzuF,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,s0C,cCAjBD,EAAOC,QAAU,6oC,cCAjBD,EAAOC,QAAU,gtD,cCAjBD,EAAOC,QAAU,+8F,cCAjBD,EAAOC,QAAU,4iD,cCAjBD,EAAOC,QAAU,k0E,cCAjBD,EAAOC,QAAU,yvF,cCAjBD,EAAOC,QAAU,m+3D,gBCAjBD,EAAOC,QAAU,6wJAA+wJ,EAAQ,IAAqC,2vBAA6vB,EAAQ,IAAoC,s22BAA6j3B,EAAQ,IAAmC,0pT,6BCA9tiC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,kuM,gBCAjB,IAAI4E,EAAM,CACT,mBAAoB,GACpB,eAAgB,GAChB,uBAAwB,GACxB,oBAAqB,GACrB,iBAAkB,GAClB,mBAAoB,GACpB,iBAAkB,GAClB,iBAAkB,IAClB,kBAAmB,IACnB,mCAAoC,IACpC,eAAgB,IAChB,gBAAiB,IACjB,iBAAkB,IAClB,iBAAkB,IAClB,aAAc,IACd,gBAAiB,IACjB,mBAAoB,IACpB,cAAe,IACf,oBAAqB,IACrB,kBAAmB,IACnB,qBAAsB,IACtB,gBAAiB,IACjB,mBAAoB,KAIrB,SAASqK,EAAeC,GACvB,IAAIC,EAAKC,EAAsBF,GAC/B,OAAOG,EAAoBF,GAE5B,SAASC,EAAsBF,GAC9B,IAAIG,EAAoBC,EAAE1K,EAAKsK,GAAM,CACpC,IAAItE,EAAI,IAAIH,MAAM,uBAAyByE,EAAM,KAEjD,MADAtE,EAAE2E,KAAO,mBACH3E,EAEP,OAAOhG,EAAIsK,GAEZD,EAAe1O,KAAO,WACrB,OAAOD,OAAOC,KAAKqE,IAEpBqK,EAAeO,QAAUJ,EACzBrP,EAAOC,QAAUiP,EACjBA,EAAeE,GAAK,I,gBC5CpBpP,EAAOC,QAAU,8wEAAgxE,EAAQ,IAAoD,sLAAwL,EAAQ,IAA4C,gC,6BCAzkF,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,gBCAzCD,EAAOC,QAAU,m3FAAq3F,EAAQ,IAA0C,miEAAsiE,EAAQ,IAAwC,ylBAA2lB,EAAQ,IAAkD,suB,6BCAnqL,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,45D,cCAjBD,EAAOC,QAAU,swE,cCAjBD,EAAOC,QAAU,soK,cCAjBD,EAAOC,QAAU,89jB,cCAjBD,EAAOC,QAAU,4xE,gBCAjBD,EAAOC,QAAU,whHAA0hH,EAAQ,KAA4C,gC,6BCA/lH,OAAe,cAA0B,wC,gBCAzCD,EAAOC,QAAU,6oMAAipM,EAAQ,KAA6C,iC,6BCAvtM,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,49B,gBCAjBD,EAAOC,QAAU,2iFAA6iF,EAAQ,KAAwC,4B,6BCA9mF,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,q4L,cCAjBD,EAAOC,QAAU,mnB,cCAjBD,EAAOC,QAAU,ggD,gBCAjBD,EAAOC,QAAU,mmEAAqmE,EAAQ,KAAwC,4B,6BCAtqE,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,28D,gBCAjBD,EAAOC,QAAU,+sIAAitI,EAAQ,KAAoD,0NAA4N,EAAQ,KAAuD,gPAAkP,EAAQ,KAAwD,80BAAg1B,EAAQ,KAAgD,uJ,6BCAnvL,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,6pO,gBCAjBD,EAAOC,QAAU,g/JAAk/J,EAAQ,KAAiD,gmCAAsmC,EAAQ,KAA+C,q2BAAu2B,EAAQ,KAAsD,i5BAAm5B,EAAQ,KAA6C,wTAA0T,EAAQ,KAAuD,mD,6BCA/7Q,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,gBCAzCD,EAAOC,QAAU,kiGAAoiG,EAAQ,KAAyD,mOAAqO,EAAQ,KAAmD,8HAAgI,EAAQ,KAAsD,+JAAiK,EAAQ,KAAuD,qoBAAuoB,EAAQ,KAA+C,qJ,6BCAl/I,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,cCAzCD,EAAOC,QAAU,ohN,cCAjBD,EAAOC,QAAU,4qK,gBCAjBD,EAAOC,QAAU,u+FAA0+F,EAAQ,KAAmD,irDAAmrD,EAAQ,KAAoD,8WAAkX,EAAQ,KAAuD,4/D,6BCAttK,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,6BCAzC,OAAe,cAA0B,wC,8HCA1B,MAA0B,iC,uCCO1B,SAASyP,IACtB,OACE,eAACC,EAAA,EAAD,CAAQC,kBAAgB,EAACC,OAAO,KAAKC,GAAG,OAAOlK,QAAQ,OAAOmK,MAAO,CAAEC,UAAW,qBAAlF,UACE,cAACL,EAAA,EAAOM,MAAR,CAAcC,GAAIC,IAAMC,GAAG,IAA3B,SACE,qBAAKC,IAAKC,EAAM5N,UAAU,YAAYqN,MAAO,CAAEQ,UAAW,IAAMC,IAAI,iBAEtE,cAACb,EAAA,EAAOc,OAAR,CAAe,gBAAc,0BAC7B,cAACd,EAAA,EAAOe,SAAR,CAAiBtB,GAAG,wBAApB,SACE,eAACuB,EAAA,EAAD,CAAKjO,UAAU,uBAAf,UACE,cAACiO,EAAA,EAAIC,KAAL,CAAUC,KAAK,QAAf,SACE,eAACF,EAAA,EAAIR,KAAL,CAAUD,GAAIC,IAAMC,GAAG,QAAvB,UAA+B,mBAAG1N,UAAU,qBAA5C,sBAEF,cAACiO,EAAA,EAAIC,KAAL,UACE,eAACD,EAAA,EAAIR,KAAL,CAAUU,KAAK,mDAAf,UAAkE,mBAAGnO,UAAU,2BAA/E,eAEF,cAACiO,EAAA,EAAIC,KAAL,UACE,eAACD,EAAA,EAAIR,KAAL,CAAUU,KAAK,mCAAf,UAAkD,mBAAGnO,UAAU,wBAA/D,gBAEF,cAACiO,EAAA,EAAIC,KAAL,UACE,eAACD,EAAA,EAAIR,KAAL,CAAUU,KAAK,8BAAf,UAA6C,mBAAGnO,UAAU,sBAA1D,sBCxBG,SAASoO,IACtB,OACE,qBAAKpO,UAAU,sBAAf,SACE,qBAAKA,UAAU,SAASqN,MAAO,CAACgB,QAAS,GAAIC,MAAO,SAApD,SACE,oKAAmI,mBAAGH,KAAK,yCAAR,0BAAnI,QAAyM,mBAAGA,KAAK,2CAAR,4BAAzM,WCNO,UAA0B,0CCA1B,MAA0B,6CCI1B,SAASI,IACtB,OACE,gCACE,qBAAKvO,UAAU,wBAAwBqN,MAAO,CAAEmB,WAAW,OAAD,OAASA,EAAT,8BAA1D,SACE,sBAAKxO,UAAU,YAAf,UACE,qBAAK2N,IAAKc,EAAazO,UAAU,YAAY8N,IAAI,KACjD,oBAAI9N,UAAU,yBAAyBqN,MAAO,CAAEiB,MAAO,QAASE,WAAY,yBAA5E,6DAGA,mBAAGxO,UAAU,OAAOqN,MAAO,CAAEgB,QAAS,EAAGC,MAAO,QAASE,WAAY,yBAArE,0PAOJ,sBAAKxO,UAAU,kBAAf,UACE,sBAAKA,UAAU,MAAMqN,MAAO,CAAEmB,WAAY,0BAA2BF,MAAO,QAASD,QAAS,IAA9F,UACE,sBAAKrO,UAAU,uBAAf,UACE,mBAAGA,UAAU,uBAAuB,cAAY,SAChD,qDACA,mFAAqD,4CAArD,KAA8E,4CAA9E,KAAuG,8CAAvG,KAAkI,4DAAlI,SAA+K,sDAA/K,8MAEF,sBAAKA,UAAU,uBAAf,UACE,mBAAGA,UAAU,yBAAyB,cAAY,SAClD,yDACA,iMAIF,sBAAKA,UAAU,uBAAf,UACE,mBAAGA,UAAU,mBAAmB,cAAY,SAC5C,wDACA,kKAIJ,qBAAKA,UAAU,6BAA6BqN,MAAO,CAAEgB,QAAS,IAA9D,SACE,sBAAKrO,UAAU,qBAAf,UACE,2EACA,kXAIA,+DACgC,+CADhC,KAC4D,mDAD5D,aACoG,2CADpG,KAC4H,gDAD5H,KACyJ,6CADzJ,KACmL,+CADnL,KAC+M,6CAD/M,KACyO,6CADzO,KACmQ,8CADnQ,KAC8R,2CAD9R,KACsT,qDADtT,KACwV,yCADxV,KAC8W,8CAD9W,KACyY,+CADzY,KACqa,mDADra,qBACqd,gDADrd,KACkf,8CADlf,YACohB,iDADphB,KACkjB,4CADljB,SAC+kB,+CAD/kB,OAGA,yCACU,4DADV,QACsD,wDADtD,gCACsH,sDADtH,OAC2J,mDAD3J,wCAC8N,gDAD9N,OAC6P,+CAD7P,mB,mBC9CG,SAAS0O,IACtB,OACE,sBAAK1O,UAAU,uBAAuBqN,MAAO,CAAEsB,aAAc,GAAIC,YAAa,IAA9E,UACE,0DAEA,kaAOA,mHAES,oBAAGT,KAAK,8BAAR,UAAsC,mBAAGnO,UAAU,sBAAnD,sBAFT,OAKA,+BACE,+BAAI,cAAC6O,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,mBAApC,wBAAJ,4DAGA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,iBAApC,sBAAJ,kEAGA,+BAAI,sBAAM1N,UAAU,cAAhB,mBAAJ,oSAIE,+BACE,+BAAI,cAAC6O,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,2BAApC,uBAAJ,uJAKA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,uBAApC,mBAAJ,gOAMA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,4BAApC,wBAAJ,uEAIA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,yBAApC,qBAAJ,6MAKA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,2BAApC,uBAAJ,iVAOA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,yBAApC,qBAAJ,mIAIA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,yBAApC,qBAAJ,8NAKA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,0BAApC,sBAAJ,yMAKA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,uBAApC,mBAAJ,+IAKA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,wBAApC,oBAAJ,+QAMA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,yBAApC,qBAAJ,wGAIA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,qBAApC,iBAAJ,6PAMA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,wBAApC,sBAAJ,0HAIA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,2BAApC,uBAAJ,iKAKA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,sBAApC,2CAAJ,sMAIA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,4BAApC,wBAAJ,sQAMA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,0BAApC,sBAAJ,0IAKA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,6BAApC,yBAAJ,mTAMA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,wBAApC,oBAAJ,sTAMA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,2BAApC,uBAAJ,sOAOJ,+BAAI,sBAAM1N,UAAU,cAAhB,uBAAJ,4EAEE,+BACE,+BAAI,mBAAGA,UAAU,cACVmO,KAAK,iGADR,yBAAJ,2GAIA,+BAAI,cAACU,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,gBAApC,iBAAJ,0EAGA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,kBAApC,mBAAJ,gEAGA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,yBAApC,0BAAJ,yCAEA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,kBAApC,8BAAJ,6CAEA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,kBAApC,mBAAJ,wCAIJ,+BAAI,sBAAM1N,UAAU,cAAhB,oBAAJ,IACE,6BACE,6BAAI,cAAC6O,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,8BAApC,kEAEN,6BACE,6BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,0CAApC,oDAEN,6BACE,6BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,kCAApC,0CAGR,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,oBAApC,oCAAJ,0DAGA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,gBAApC,gCAAJ,0EAGA,+BAAI,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,2BAApC,4CAAJ,sFAGA,+BACE,cAACmB,EAAA,EAAD,CAAS7O,UAAU,cAAc0N,GAAG,gBAApC,4BADF,uD,qECzKJoB,G,MAAc1R,EAAQ,GAARA,IAElB0R,EAAYC,WAAWC,UAAS,WAC9BC,KAAKC,eAAc,WACND,KACNE,SAAQ,SAAUC,GAMrB,OALAA,EAAIC,OAAO,CAAC,QAAW,UAAUvO,SAAQ,SAASwO,GAEhD,IAAIxB,EAAMwB,EAAIC,aAAa,OAC3BD,EAAIE,aAAa,MAAO1B,EAAM,sBAAsB,MAE/CsB,W,IAKQK,E,kDACnB,WAAYC,GAAQ,IAAD,8BACjB,cAAMA,IACDC,cAAgB,EAAKA,cAAcC,KAAnB,gBAFJ,E,qDAKnB,WACEX,KAAKU,kB,gCAGP,WACEV,KAAKU,kB,2BAGP,WACEV,KAAKY,KAAKjG,iBAAiB,YAAY9I,SAAQ,SAAAhC,GAAI,OAAI3B,IAAK6L,eAAelK,Q,oBAG7E,WAAU,IAAD,OACHgR,EAAYhB,EAAYiB,QAAQd,KAAKS,MAAMpM,OAAQ,CAAElB,WAAY6M,KAAKS,MAAMM,MAAOC,KAAM,SAE7F,OACE,qBACEC,IAAK,SAACL,GAAW,EAAKA,KAAOA,GAC7BM,wBAAyB,CAAEC,OAAQN,S,GAxBLO,a,MCrBvB,SAASC,EAAT,GAAgD,IAA1BC,EAAyB,EAAzBA,MAAOC,EAAkB,EAAlBA,QAASR,EAAS,EAATA,MACnD,OACE,sBAAKhQ,UAAU,iCAAf,UACE,6BAAKuQ,IACL,uBACA,cAAC,EAAD,CACEjN,OAAQkN,EACRR,MAAOA,OAKfM,EAAWG,aAAe,CACxBT,MAAO,ICZT,IAAIU,EAAiBtT,EAAQ,IACzBuT,EAAevT,EAAQ,IACvBwT,EAAUxT,EAAQ,IAClByT,EAAYzT,EAAQ,IACpB0T,EAAmB1T,EAAQ,IAC3B2T,EAAY3T,EAAQ,IACpB4T,EAAY5T,EAAQ,IACpB6T,EAAsB7T,EAAQ,IAC9B8T,EAAkC9T,EAAQ,IAC1C+T,EAA0B/T,EAAQ,IAClCgU,EAAchU,EAAQ,IACtBiU,EAAkBjU,EAAQ,IAC1BkU,EAAclU,EAAQ,IACtBmU,EAAyBnU,EAAQ,IAE/BoU,EAAU,CAAC,YAAa,QAAS,gBAAiB,aAAc,UAAW,YAAa,UAAW,UAAW,WAAY,QAAS,SAAU,UAAW,MAAO,SAAU,YAAa,OAAQ,aAAc,WAAY,cAAe,SAAU,aAEnPC,EAAe,GAGJ,SAASC,IACtB,OACE,gCACE,cAACC,EAAA,EAAD,CAAOC,OAAK,EAACC,KAAK,QAAQC,UAAWpD,IAErC,cAACiD,EAAA,EAAD,CAAOE,KAAK,mBAAmBtP,OAAQ,kBACrC,cAAC+N,EAAD,CAAYC,MAAM,aAAaC,QAASE,OAG1C,cAACiB,EAAA,EAAD,CAAOC,OAAK,EAACC,KAAK,iBAAiBtP,OAAQ,kBACzC,cAAC+N,EAAD,CAAYC,MAAM,WAAWC,QAASG,OAExC,cAACgB,EAAA,EAAD,CAAOE,KAAK,yBAAyBtP,OAAQ,YAAgB,IACvDwP,EADsD,EAAZ3S,MAC3B4S,OAAOD,OAC1B,OAAOP,EAAQS,SAASF,GACtB,cAACzB,EAAD,CAAYC,MAAK,qBAAgBwB,EAAO5R,MAAM,KAAKgC,KAAI,SAAA+P,GAAI,OAAIA,EAAK,GAAGC,cAAgBD,EAAKvR,MAAM,MAAI0B,KAAK,MAC/FmO,QAASiB,EAAaM,KAClC,cAACK,EAAA,EAAD,CAAU1E,GAAG,aAGjB,cAACiE,EAAA,EAAD,CAAOE,KAAK,gBAAgBtP,OAAQ,kBAClC,cAAC+N,EAAD,CAAYC,MAAM,yBAAyBC,QAASI,OAGtD,cAACe,EAAA,EAAD,CAAOE,KAAK,kBAAkBtP,OAAQ,kBACpC,cAAC+N,EAAD,CAAYC,MAAM,gBAAgBC,QAASK,OAG7C,cAACc,EAAA,EAAD,CAAOE,KAAK,yBAAyBtP,OAAQ,kBAC3C,cAAC+N,EAAD,CAAYC,MAAM,uBAAuBC,QAASM,OAGpD,cAACa,EAAA,EAAD,CAAOE,KAAK,kBAAkBtP,OAAQ,kBACpC,cAAC+N,EAAD,CAAYC,MAAM,2BAA2BC,QAASO,OAGxD,cAACY,EAAA,EAAD,CAAOE,KAAK,kBAAkBtP,OAAQ,kBACpC,cAAC+N,EAAD,CAAYC,MAAM,gBAAgBC,QAASQ,OAG7C,cAACW,EAAA,EAAD,CAAOE,KAAK,8BAA8BtP,OAAQ,kBAChD,cAAC+N,EAAD,CAAYC,MAAM,mDAAmDC,QAASS,OAGhF,cAACU,EAAA,EAAD,CAAOE,KAAK,0CAA0CtP,OAAQ,kBAC5D,cAAC+N,EAAD,CAAYC,MAAM,qCAAqCC,QAASU,OAGlE,cAACS,EAAA,EAAD,CAAOE,KAAK,kCAAkCtP,OAAQ,kBACpD,cAAC+N,EAAD,CAAYC,MAAM,wBAAwBC,QAASW,OAGrD,cAACQ,EAAA,EAAD,CAAOE,KAAK,gBAAgBtP,OAAQ,kBAClC,cAAC+N,EAAD,CAAYC,MAAM,4BAA4BC,QAASY,OAGzD,cAACO,EAAA,EAAD,CAAOE,KAAK,oBAAoBtP,OAAQ,kBACtC,cAAC+N,EAAD,CAAYC,MAAM,cAAcC,QAASa,OAG3C,cAACM,EAAA,EAAD,CAAOE,KAAK,gBAAgBtP,OAAQ,kBAClC,cAAC+N,EAAD,CAAYC,MAAM,UAAUC,QAASc,OAGvC,cAACK,EAAA,EAAD,CAAOE,KAAK,2BAA2BtP,OAAQ,kBAC7C,cAAC+N,EAAD,CAAYC,MAAM,sBAAsBC,QAASe,UCrF1C,SAASc,IACtB,OACE,gCACE,cAACrF,EAAD,IAEA,eAACsF,EAAA,EAAD,WACE,cAACX,EAAA,EAAD,CAAOC,OAAK,EAACC,KAAK,IAAIC,UAAWvD,IAEjC,cAACoD,EAAA,EAAD,CAAOE,KAAK,mBAAmBC,UAAW,WAExC,OADArU,OAAO8U,SAASpE,KAAK,oCACd,QAIT,cAACuD,EAAD,IAEA,cAACC,EAAA,EAAD,CAAOE,KAAK,KAAKtP,OAAQ,kBAAM,cAAC6P,EAAA,EAAD,CAAU1E,GAAG,YAG9C,qBAAK1N,UAAU,kBAAkBqN,MAAO,CAAEsB,aAAc,EAAGC,YAAa,GAAxE,SACE,cAACR,EAAD,CAAQpO,UAAU,aDF1BwR,EAAQ1Q,SAAQ,SAAA0R,GAAG,OAAIf,EAAae,GAAOpV,MAAQ,YAAyDoV,EAA1D,aEb9BlN,QACW,cAA7B7H,OAAO8U,SAASE,UAEe,UAA7BhV,OAAO8U,SAASE,UAEhBhV,OAAO8U,SAASE,SAASrT,MAAM,2D,YCH7BsT,G,cAAUC,eAEhBC,IAASrQ,OAAO,cAACsQ,EAAA,EAAD,CAAQH,QAASA,EAAjB,SAA0B,cAACL,EAAD,MAAiBlJ,SAAS2J,eAAe,SD+G7E,kBAAmBC,WACrBA,UAAUC,cAAcC,MACrBC,MAAK,SAACC,GACLA,EAAaC,gBAEdC,OAAM,SAACC,GACNC,QAAQD,MAAMA,EAAMlL,e","file":"static/js/main.5d59054d.chunk.js","sourcesContent":["var hljs = require('./highlight');\n\nhljs.registerLanguage('gradle', require('./languages/gradle'));\nhljs.registerLanguage('groovy', require('./languages/groovy'));\nhljs.registerLanguage('http', require('./languages/http'));\nhljs.registerLanguage('java', require('./languages/java'));\nhljs.registerLanguage('xml', require('./languages/xml'));\nhljs.registerLanguage('yaml', require('./languages/yaml'));\nhljs.registerLanguage('json', require('./languages/json'));\n\nmodule.exports = hljs;","/*\nSyntax highlighting with language autodetection.\nhttps://highlightjs.org/\n*/\n\n/* eslint no-restricted-globals: off */\n/* eslint no-undef: off */\n/* eslint no-mixed-operators: off */\n/* eslint no-useless-escape: off */\n\n(function(factory) {\n\n // Find the global object for export to both the browser and web workers.\n var globalObject = typeof window === 'object' && window ||\n typeof self === 'object' && self;\n\n // Setup highlight.js for different environments. First is Node.js or\n // CommonJS.\n if(typeof exports !== 'undefined') {\n factory(exports);\n } else if(globalObject) {\n // Export hljs globally even when using AMD for cases when this script\n // is loaded with others that may still expect a global hljs.\n globalObject.hljs = factory({});\n\n // Finally register the global hljs with AMD.\n if(typeof define === 'function' && define.amd) {\n define([], function() {\n return globalObject.hljs;\n });\n }\n }\n\n}(function(hljs) {\n // Convenience variables for build-in objects\n var ArrayProto = [],\n objectKeys = Object.keys;\n\n // Global internal variables used within the highlight.js library.\n var languages = {},\n aliases = {};\n\n // Regular expressions used throughout the highlight.js library.\n var noHighlightRe = /^(no-?highlight|plain|text)$/i,\n languagePrefixRe = /\\blang(?:uage)?-([\\w-]+)\\b/i,\n fixMarkupRe = /((^(<[^>]+>|\\t|)+|(?:\\n)))/gm;\n\n var spanEndTag = '';\n\n // Global options used when within external APIs. This is modified when\n // calling the `hljs.configure` function.\n var options = {\n classPrefix: 'hljs-',\n tabReplace: null,\n useBR: false,\n languages: undefined\n };\n\n\n /* Utility functions */\n\n function escape(value) {\n return value.replace(/&/g, '&').replace(//g, '>');\n }\n\n function tag(node) {\n return node.nodeName.toLowerCase();\n }\n\n function testRe(re, lexeme) {\n var match = re && re.exec(lexeme);\n return match && match.index === 0;\n }\n\n function isNotHighlighted(language) {\n return noHighlightRe.test(language);\n }\n\n function blockLanguage(block) {\n var i, match, length, _class;\n var classes = block.className + ' ';\n\n classes += block.parentNode ? block.parentNode.className : '';\n\n // language-* takes precedence over non-prefixed class names.\n match = languagePrefixRe.exec(classes);\n if (match) {\n return getLanguage(match[1]) ? match[1] : 'no-highlight';\n }\n\n classes = classes.split(/\\s+/);\n\n for (i = 0, length = classes.length; i < length; i++) {\n _class = classes[i]\n\n if (isNotHighlighted(_class) || getLanguage(_class)) {\n return _class;\n }\n }\n }\n\n function inherit(parent) { // inherit(parent, override_obj, override_obj, ...)\n var key;\n var result = {};\n var objects = Array.prototype.slice.call(arguments, 1);\n\n for (key in parent)\n result[key] = parent[key];\n objects.forEach(function(obj) {\n for (key in obj)\n result[key] = obj[key];\n });\n return result;\n }\n\n /* Stream merging */\n\n function nodeStream(node) {\n var result = [];\n (function _nodeStream(node, offset) {\n for (var child = node.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 3)\n offset += child.nodeValue.length;\n else if (child.nodeType === 1) {\n result.push({\n event: 'start',\n offset: offset,\n node: child\n });\n offset = _nodeStream(child, offset);\n // Prevent void elements from having an end tag that would actually\n // double them in the output. There are more void elements in HTML\n // but we list only those realistically expected in code display.\n if (!tag(child).match(/br|hr|img|input/)) {\n result.push({\n event: 'stop',\n offset: offset,\n node: child\n });\n }\n }\n }\n return offset;\n })(node, 0);\n return result;\n }\n\n function mergeStreams(original, highlighted, value) {\n var processed = 0;\n var result = '';\n var nodeStack = [];\n\n function selectStream() {\n if (!original.length || !highlighted.length) {\n return original.length ? original : highlighted;\n }\n if (original[0].offset !== highlighted[0].offset) {\n return (original[0].offset < highlighted[0].offset) ? original : highlighted;\n }\n\n /*\n To avoid starting the stream just before it should stop the order is\n ensured that original always starts first and closes last:\n\n if (event1 == 'start' && event2 == 'start')\n return original;\n if (event1 == 'start' && event2 == 'stop')\n return highlighted;\n if (event1 == 'stop' && event2 == 'start')\n return original;\n if (event1 == 'stop' && event2 == 'stop')\n return highlighted;\n\n ... which is collapsed to:\n */\n return highlighted[0].event === 'start' ? original : highlighted;\n }\n\n function open(node) {\n function attr_str(a) {return ' ' + a.nodeName + '=\"' + escape(a.value).replace('\"', '"') + '\"';}\n result += '<' + tag(node) + ArrayProto.map.call(node.attributes, attr_str).join('') + '>';\n }\n\n function close(node) {\n result += '';\n }\n\n function render(event) {\n (event.event === 'start' ? open : close)(event.node);\n }\n\n while (original.length || highlighted.length) {\n var stream = selectStream();\n result += escape(value.substring(processed, stream[0].offset));\n processed = stream[0].offset;\n if (stream === original) {\n /*\n On any opening or closing tag of the original markup we first close\n the entire highlighted node stack, then render the original tag along\n with all the following original tags at the same offset and then\n reopen all the tags on the highlighted stack.\n */\n nodeStack.reverse().forEach(close);\n do {\n render(stream.splice(0, 1)[0]);\n stream = selectStream();\n } while (stream === original && stream.length && stream[0].offset === processed);\n nodeStack.reverse().forEach(open);\n } else {\n if (stream[0].event === 'start') {\n nodeStack.push(stream[0].node);\n } else {\n nodeStack.pop();\n }\n render(stream.splice(0, 1)[0]);\n }\n }\n return result + escape(value.substr(processed));\n }\n\n /* Initialization */\n\n function expand_mode(mode) {\n if (mode.variants && !mode.cached_variants) {\n mode.cached_variants = mode.variants.map(function(variant) {\n return inherit(mode, {variants: null}, variant);\n });\n }\n return mode.cached_variants || (mode.endsWithParent && [inherit(mode)]) || [mode];\n }\n\n function compileLanguage(language) {\n\n function reStr(re) {\n return (re && re.source) || re;\n }\n\n function langRe(value, global) {\n return new RegExp(\n reStr(value),\n 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '')\n );\n }\n\n function compileMode(mode, parent) {\n if (mode.compiled)\n return;\n mode.compiled = true;\n\n mode.keywords = mode.keywords || mode.beginKeywords;\n if (mode.keywords) {\n var compiled_keywords = {};\n\n var flatten = function(className, str) {\n if (language.case_insensitive) {\n str = str.toLowerCase();\n }\n str.split(' ').forEach(function(kw) {\n var pair = kw.split('|');\n compiled_keywords[pair[0]] = [className, pair[1] ? Number(pair[1]) : 1];\n });\n };\n\n if (typeof mode.keywords === 'string') { // string\n flatten('keyword', mode.keywords);\n } else {\n objectKeys(mode.keywords).forEach(function (className) {\n flatten(className, mode.keywords[className]);\n });\n }\n mode.keywords = compiled_keywords;\n }\n mode.lexemesRe = langRe(mode.lexemes || /\\w+/, true);\n\n if (parent) {\n if (mode.beginKeywords) {\n mode.begin = '\\\\b(' + mode.beginKeywords.split(' ').join('|') + ')\\\\b';\n }\n if (!mode.begin)\n mode.begin = /\\B|\\b/;\n mode.beginRe = langRe(mode.begin);\n if (!mode.end && !mode.endsWithParent)\n mode.end = /\\B|\\b/;\n if (mode.end)\n mode.endRe = langRe(mode.end);\n mode.terminator_end = reStr(mode.end) || '';\n if (mode.endsWithParent && parent.terminator_end)\n mode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;\n }\n if (mode.illegal)\n mode.illegalRe = langRe(mode.illegal);\n if (mode.relevance == null)\n mode.relevance = 1;\n if (!mode.contains) {\n mode.contains = [];\n }\n mode.contains = Array.prototype.concat.apply([], mode.contains.map(function(c) {\n return expand_mode(c === 'self' ? mode : c)\n }));\n mode.contains.forEach(function(c) {compileMode(c, mode);});\n\n if (mode.starts) {\n compileMode(mode.starts, parent);\n }\n\n var terminators =\n mode.contains.map(function(c) {\n return c.beginKeywords ? '\\\\.?(' + c.begin + ')\\\\.?' : c.begin;\n })\n .concat([mode.terminator_end, mode.illegal])\n .map(reStr)\n .filter(Boolean);\n mode.terminators = terminators.length ? langRe(terminators.join('|'), true) : {exec: function(/*s*/) {return null;}};\n }\n\n compileMode(language);\n }\n\n /*\n Core highlighting function. Accepts a language name, or an alias, and a\n string with the code to highlight. Returns an object with the following\n properties:\n\n - relevance (int)\n - value (an HTML string with highlighting markup)\n\n */\n function highlight(name, value, ignore_illegals, continuation) {\n\n function subMode(lexeme, mode) {\n var i, length;\n\n for (i = 0, length = mode.contains.length; i < length; i++) {\n if (testRe(mode.contains[i].beginRe, lexeme)) {\n return mode.contains[i];\n }\n }\n }\n\n function endOfMode(mode, lexeme) {\n if (testRe(mode.endRe, lexeme)) {\n while (mode.endsParent && mode.parent) {\n mode = mode.parent;\n }\n return mode;\n }\n if (mode.endsWithParent) {\n return endOfMode(mode.parent, lexeme);\n }\n }\n\n function isIllegal(lexeme, mode) {\n return !ignore_illegals && testRe(mode.illegalRe, lexeme);\n }\n\n function keywordMatch(mode, match) {\n var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0];\n return mode.keywords.hasOwnProperty(match_str) && mode.keywords[match_str];\n }\n\n function buildSpan(classname, insideSpan, leaveOpen, noPrefix) {\n var classPrefix = noPrefix ? '' : options.classPrefix,\n openSpan = '';\n\n return openSpan + insideSpan + closeSpan;\n }\n\n function processKeywords() {\n var keyword_match, last_index, match, result;\n\n if (!top.keywords)\n return escape(mode_buffer);\n\n result = '';\n last_index = 0;\n top.lexemesRe.lastIndex = 0;\n match = top.lexemesRe.exec(mode_buffer);\n\n while (match) {\n result += escape(mode_buffer.substring(last_index, match.index));\n keyword_match = keywordMatch(top, match);\n if (keyword_match) {\n relevance += keyword_match[1];\n result += buildSpan(keyword_match[0], escape(match[0]));\n } else {\n result += escape(match[0]);\n }\n last_index = top.lexemesRe.lastIndex;\n match = top.lexemesRe.exec(mode_buffer);\n }\n return result + escape(mode_buffer.substr(last_index));\n }\n\n function processSubLanguage() {\n var explicit = typeof top.subLanguage === 'string';\n if (explicit && !languages[top.subLanguage]) {\n return escape(mode_buffer);\n }\n\n var result = explicit ?\n highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]) :\n highlightAuto(mode_buffer, top.subLanguage.length ? top.subLanguage : undefined);\n\n // Counting embedded language score towards the host language may be disabled\n // with zeroing the containing mode relevance. Usecase in point is Markdown that\n // allows XML everywhere and makes every XML snippet to have a much larger Markdown\n // score.\n if (top.relevance > 0) {\n relevance += result.relevance;\n }\n if (explicit) {\n continuations[top.subLanguage] = result.top;\n }\n return buildSpan(result.language, result.value, false, true);\n }\n\n function processBuffer() {\n result += (top.subLanguage != null ? processSubLanguage() : processKeywords());\n mode_buffer = '';\n }\n\n function startNewMode(mode) {\n result += mode.className? buildSpan(mode.className, '', true): '';\n top = Object.create(mode, {parent: {value: top}});\n }\n\n function processLexeme(buffer, lexeme) {\n\n mode_buffer += buffer;\n\n if (lexeme == null) {\n processBuffer();\n return 0;\n }\n\n var new_mode = subMode(lexeme, top);\n if (new_mode) {\n if (new_mode.skip) {\n mode_buffer += lexeme;\n } else {\n if (new_mode.excludeBegin) {\n mode_buffer += lexeme;\n }\n processBuffer();\n if (!new_mode.returnBegin && !new_mode.excludeBegin) {\n mode_buffer = lexeme;\n }\n }\n startNewMode(new_mode, lexeme);\n return new_mode.returnBegin ? 0 : lexeme.length;\n }\n\n var end_mode = endOfMode(top, lexeme);\n if (end_mode) {\n var origin = top;\n if (origin.skip) {\n mode_buffer += lexeme;\n } else {\n if (!(origin.returnEnd || origin.excludeEnd)) {\n mode_buffer += lexeme;\n }\n processBuffer();\n if (origin.excludeEnd) {\n mode_buffer = lexeme;\n }\n }\n do {\n if (top.className) {\n result += spanEndTag;\n }\n if (!top.skip && !top.subLanguage) {\n relevance += top.relevance;\n }\n top = top.parent;\n } while (top !== end_mode.parent);\n if (end_mode.starts) {\n startNewMode(end_mode.starts, '');\n }\n return origin.returnEnd ? 0 : lexeme.length;\n }\n\n if (isIllegal(lexeme, top))\n throw new Error('Illegal lexeme \"' + lexeme + '\" for mode \"' + (top.className || '') + '\"');\n\n /*\n Parser should not reach this point as all types of lexemes should be caught\n earlier, but if it does due to some bug make sure it advances at least one\n character forward to prevent infinite looping.\n */\n mode_buffer += lexeme;\n return lexeme.length || 1;\n }\n\n var language = getLanguage(name);\n if (!language) {\n throw new Error('Unknown language: \"' + name + '\"');\n }\n\n compileLanguage(language);\n var top = continuation || language;\n var continuations = {}; // keep continuations for sub-languages\n var result = '', current;\n for(current = top; current !== language; current = current.parent) {\n if (current.className) {\n result = buildSpan(current.className, '', true) + result;\n }\n }\n var mode_buffer = '';\n var relevance = 0;\n try {\n var match, count, index = 0;\n while (true) {\n top.terminators.lastIndex = index;\n match = top.terminators.exec(value);\n if (!match)\n break;\n count = processLexeme(value.substring(index, match.index), match[0]);\n index = match.index + count;\n }\n processLexeme(value.substr(index));\n for(current = top; current.parent; current = current.parent) { // close dangling modes\n if (current.className) {\n result += spanEndTag;\n }\n }\n return {\n relevance: relevance,\n value: result,\n language: name,\n top: top\n };\n } catch (e) {\n if (e.message && e.message.indexOf('Illegal') !== -1) {\n return {\n relevance: 0,\n value: escape(value)\n };\n } else {\n throw e;\n }\n }\n }\n\n /*\n Highlighting with language detection. Accepts a string with the code to\n highlight. Returns an object with the following properties:\n\n - language (detected language)\n - relevance (int)\n - value (an HTML string with highlighting markup)\n - second_best (object with the same structure for second-best heuristically\n detected language, may be absent)\n\n */\n function highlightAuto(text, languageSubset) {\n languageSubset = languageSubset || options.languages || objectKeys(languages);\n var result = {\n relevance: 0,\n value: escape(text)\n };\n var second_best = result;\n languageSubset.filter(getLanguage).forEach(function(name) {\n var current = highlight(name, text, false);\n current.language = name;\n if (current.relevance > second_best.relevance) {\n second_best = current;\n }\n if (current.relevance > result.relevance) {\n second_best = result;\n result = current;\n }\n });\n if (second_best.language) {\n result.second_best = second_best;\n }\n return result;\n }\n\n /*\n Post-processing of the highlighted markup:\n\n - replace TABs with something more useful\n - replace real line-breaks with '
' for non-pre containers\n\n */\n function fixMarkup(value) {\n return !(options.tabReplace || options.useBR)\n ? value\n : value.replace(fixMarkupRe, function(match, p1) {\n if (options.useBR && match === '\\n') {\n return '
';\n } else if (options.tabReplace) {\n return p1.replace(/\\t/g, options.tabReplace);\n }\n return '';\n });\n }\n\n function buildClassName(prevClassName, currentLang, resultLang) {\n var language = currentLang ? aliases[currentLang] : resultLang,\n result = [prevClassName.trim()];\n\n if (!prevClassName.match(/\\bhljs\\b/)) {\n result.push('hljs');\n }\n\n if (prevClassName.indexOf(language) === -1) {\n result.push(language);\n }\n\n return result.join(' ').trim();\n }\n\n /*\n Applies highlighting to a DOM node containing code. Accepts a DOM node and\n two optional parameters for fixMarkup.\n */\n function highlightBlock(block) {\n var node, originalStream, result, resultNode, text;\n var language = blockLanguage(block);\n\n if (isNotHighlighted(language))\n return;\n\n if (options.useBR) {\n node = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');\n node.innerHTML = block.innerHTML.replace(/\\n/g, '').replace(//g, '\\n');\n } else {\n node = block;\n }\n text = node.textContent;\n result = language ? highlight(language, text, true) : highlightAuto(text);\n\n originalStream = nodeStream(node);\n if (originalStream.length) {\n resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');\n resultNode.innerHTML = result.value;\n result.value = mergeStreams(originalStream, nodeStream(resultNode), text);\n }\n result.value = fixMarkup(result.value);\n\n block.innerHTML = result.value;\n block.className = buildClassName(block.className, language, result.language);\n block.result = {\n language: result.language,\n re: result.relevance\n };\n if (result.second_best) {\n block.second_best = {\n language: result.second_best.language,\n re: result.second_best.relevance\n };\n }\n }\n\n /*\n Updates highlight.js global options with values passed in the form of an object.\n */\n function configure(user_options) {\n options = inherit(options, user_options);\n }\n\n /*\n Applies highlighting to all
..
blocks on a page.\n */\n function initHighlighting() {\n if (initHighlighting.called)\n return;\n initHighlighting.called = true;\n\n var blocks = document.querySelectorAll('pre code');\n ArrayProto.forEach.call(blocks, highlightBlock);\n }\n\n /*\n Attaches highlighting to the page load event.\n */\n function initHighlightingOnLoad() {\n addEventListener('DOMContentLoaded', initHighlighting, false);\n addEventListener('load', initHighlighting, false);\n }\n\n function registerLanguage(name, language) {\n var lang = languages[name] = language(hljs);\n if (lang.aliases) {\n lang.aliases.forEach(function(alias) {aliases[alias] = name;});\n }\n }\n\n function listLanguages() {\n return objectKeys(languages);\n }\n\n function getLanguage(name) {\n name = (name || '').toLowerCase();\n return languages[name] || languages[aliases[name]];\n }\n\n /* Interface definition */\n\n hljs.highlight = highlight;\n hljs.highlightAuto = highlightAuto;\n hljs.fixMarkup = fixMarkup;\n hljs.highlightBlock = highlightBlock;\n hljs.configure = configure;\n hljs.initHighlighting = initHighlighting;\n hljs.initHighlightingOnLoad = initHighlightingOnLoad;\n hljs.registerLanguage = registerLanguage;\n hljs.listLanguages = listLanguages;\n hljs.getLanguage = getLanguage;\n hljs.inherit = inherit;\n\n // Common regexps\n hljs.IDENT_RE = '[a-zA-Z]\\\\w*';\n hljs.UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\\\w*';\n hljs.NUMBER_RE = '\\\\b\\\\d+(\\\\.\\\\d+)?';\n hljs.C_NUMBER_RE = '(-?)(\\\\b0[xX][a-fA-F0-9]+|(\\\\b\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?)'; // 0x..., 0..., decimal, float\n hljs.BINARY_NUMBER_RE = '\\\\b(0b[01]+)'; // 0b...\n hljs.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\\\*|\\\\*=|\\\\+|\\\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\\\?|\\\\[|\\\\{|\\\\(|\\\\^|\\\\^=|\\\\||\\\\|=|\\\\|\\\\||~';\n\n // Common modes\n hljs.BACKSLASH_ESCAPE = {\n begin: '\\\\\\\\[\\\\s\\\\S]', relevance: 0\n };\n hljs.APOS_STRING_MODE = {\n className: 'string',\n begin: '\\'', end: '\\'',\n illegal: '\\\\n',\n contains: [hljs.BACKSLASH_ESCAPE]\n };\n hljs.QUOTE_STRING_MODE = {\n className: 'string',\n begin: '\"', end: '\"',\n illegal: '\\\\n',\n contains: [hljs.BACKSLASH_ESCAPE]\n };\n hljs.PHRASAL_WORDS_MODE = {\n begin: /\\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\\b/\n };\n hljs.COMMENT = function (begin, end, inherits) {\n var mode = hljs.inherit(\n {\n className: 'comment',\n begin: begin, end: end,\n contains: []\n },\n inherits || {}\n );\n mode.contains.push(hljs.PHRASAL_WORDS_MODE);\n mode.contains.push({\n className: 'doctag',\n begin: '(?:TODO|FIXME|NOTE|BUG|XXX):',\n relevance: 0\n });\n return mode;\n };\n hljs.C_LINE_COMMENT_MODE = hljs.COMMENT('//', '$');\n hljs.C_BLOCK_COMMENT_MODE = hljs.COMMENT('/\\\\*', '\\\\*/');\n hljs.HASH_COMMENT_MODE = hljs.COMMENT('#', '$');\n hljs.NUMBER_MODE = {\n className: 'number',\n begin: hljs.NUMBER_RE,\n relevance: 0\n };\n hljs.C_NUMBER_MODE = {\n className: 'number',\n begin: hljs.C_NUMBER_RE,\n relevance: 0\n };\n hljs.BINARY_NUMBER_MODE = {\n className: 'number',\n begin: hljs.BINARY_NUMBER_RE,\n relevance: 0\n };\n hljs.CSS_NUMBER_MODE = {\n className: 'number',\n begin: hljs.NUMBER_RE + '(' +\n '%|em|ex|ch|rem' +\n '|vw|vh|vmin|vmax' +\n '|cm|mm|in|pt|pc|px' +\n '|deg|grad|rad|turn' +\n '|s|ms' +\n '|Hz|kHz' +\n '|dpi|dpcm|dppx' +\n ')?',\n relevance: 0\n };\n hljs.REGEXP_MODE = {\n className: 'regexp',\n begin: /\\//, end: /\\/[gimuy]*/,\n illegal: /\\n/,\n contains: [\n hljs.BACKSLASH_ESCAPE,\n {\n begin: /\\[/, end: /\\]/,\n relevance: 0,\n contains: [hljs.BACKSLASH_ESCAPE]\n }\n ]\n };\n hljs.TITLE_MODE = {\n className: 'title',\n begin: hljs.IDENT_RE,\n relevance: 0\n };\n hljs.UNDERSCORE_TITLE_MODE = {\n className: 'title',\n begin: hljs.UNDERSCORE_IDENT_RE,\n relevance: 0\n };\n hljs.METHOD_GUARD = {\n // excludes method names from keyword processing\n begin: '\\\\.\\\\s*' + hljs.UNDERSCORE_IDENT_RE,\n relevance: 0\n };\n\n return hljs;\n}));\n\n","module.exports = function(hljs) {\n return {\n case_insensitive: true,\n keywords: {\n keyword:\n 'task project allprojects subprojects artifacts buildscript configurations ' +\n 'dependencies repositories sourceSets description delete from into include ' +\n 'exclude source classpath destinationDir includes options sourceCompatibility ' +\n 'targetCompatibility group flatDir doLast doFirst flatten todir fromdir ant ' +\n 'def abstract break case catch continue default do else extends final finally ' +\n 'for if implements instanceof native new private protected public return static ' +\n 'switch synchronized throw throws transient try volatile while strictfp package ' +\n 'import false null super this true antlrtask checkstyle codenarc copy boolean ' +\n 'byte char class double float int interface long short void compile runTime ' +\n 'file fileTree abs any append asList asWritable call collect compareTo count ' +\n 'div dump each eachByte eachFile eachLine every find findAll flatten getAt ' +\n 'getErr getIn getOut getText grep immutable inject inspect intersect invokeMethods ' +\n 'isCase join leftShift minus multiply newInputStream newOutputStream newPrintWriter ' +\n 'newReader newWriter next plus pop power previous print println push putAt read ' +\n 'readBytes readLines reverse reverseEach round size sort splitEachLine step subMap ' +\n 'times toInteger toList tokenize upto waitForOrKill withPrintWriter withReader ' +\n 'withStream withWriter withWriterAppend write writeLine'\n },\n contains: [\n hljs.C_LINE_COMMENT_MODE,\n hljs.C_BLOCK_COMMENT_MODE,\n hljs.APOS_STRING_MODE,\n hljs.QUOTE_STRING_MODE,\n hljs.NUMBER_MODE,\n hljs.REGEXP_MODE\n\n ]\n }\n};","/* eslint no-useless-escape: off */\n\nmodule.exports = function(hljs) {\n return {\n keywords: {\n literal : 'true false null',\n keyword:\n 'byte short char int long boolean float double void ' +\n // groovy specific keywords\n 'def as in assert trait ' +\n // common keywords with Java\n 'super this abstract static volatile transient public private protected synchronized final ' +\n 'class interface enum if else for while switch case break default continue ' +\n 'throw throws try catch finally implements extends new import package return instanceof'\n },\n\n contains: [\n hljs.COMMENT(\n '/\\\\*\\\\*',\n '\\\\*/',\n {\n relevance : 0,\n contains : [\n {\n // eat up @'s in emails to prevent them to be recognized as doctags\n begin: /\\w+@/, relevance: 0\n },\n {\n className : 'doctag',\n begin : '@[A-Za-z]+'\n }\n ]\n }\n ),\n hljs.C_LINE_COMMENT_MODE,\n hljs.C_BLOCK_COMMENT_MODE,\n {\n className: 'string',\n begin: '\"\"\"', end: '\"\"\"'\n },\n {\n className: 'string',\n begin: \"'''\", end: \"'''\"\n },\n {\n className: 'string',\n begin: \"\\\\$/\", end: \"/\\\\$\",\n relevance: 10\n },\n hljs.APOS_STRING_MODE,\n {\n className: 'regexp',\n begin: /~?\\/[^\\/\\n]+\\//,\n contains: [\n hljs.BACKSLASH_ESCAPE\n ]\n },\n hljs.QUOTE_STRING_MODE,\n {\n className: 'meta',\n begin: \"^#!/usr/bin/env\", end: '$',\n illegal: '\\n'\n },\n hljs.BINARY_NUMBER_MODE,\n {\n className: 'class',\n beginKeywords: 'class interface trait enum', end: '{',\n illegal: ':',\n contains: [\n {beginKeywords: 'extends implements'},\n hljs.UNDERSCORE_TITLE_MODE\n ]\n },\n hljs.C_NUMBER_MODE,\n {\n className: 'meta', begin: '@[A-Za-z]+'\n },\n {\n // highlight map keys and named parameters as strings\n className: 'string', begin: /[^\\?]{0}[A-Za-z0-9_$]+ *:/\n },\n {\n // catch middle element of the ternary operator\n // to avoid highlight it as a label, named parameter, or map key\n begin: /\\?/, end: /\\:/\n },\n {\n // highlight labeled statements\n className: 'symbol', begin: '^\\\\s*[A-Za-z0-9_$]+:',\n relevance: 0\n }\n ],\n illegal: /#|<\\//\n }\n};","module.exports = function(hljs) {\n var VERSION = 'HTTP/[0-9\\\\.]+';\n return {\n aliases: ['https'],\n illegal: '\\\\S',\n contains: [\n {\n begin: '^' + VERSION, end: '$',\n contains: [{className: 'number', begin: '\\\\b\\\\d{3}\\\\b'}]\n },\n {\n begin: '^[A-Z]+ (.*?) ' + VERSION + '$', returnBegin: true, end: '$',\n contains: [\n {\n className: 'string',\n begin: ' ', end: ' ',\n excludeBegin: true, excludeEnd: true\n },\n {\n begin: VERSION\n },\n {\n className: 'keyword',\n begin: '[A-Z]+'\n }\n ]\n },\n {\n className: 'attribute',\n begin: '^\\\\w', end: ': ', excludeEnd: true,\n illegal: '\\\\n|\\\\s|=',\n starts: {end: '$', relevance: 0}\n },\n {\n begin: '\\\\n\\\\n',\n starts: {subLanguage: [], endsWithParent: true}\n }\n ]\n };\n};","/* eslint no-useless-escape: off */\n\nmodule.exports = function(hljs) {\n var JAVA_IDENT_RE = '[\\u00C0-\\u02B8a-zA-Z_$][\\u00C0-\\u02B8a-zA-Z_$0-9]*';\n var GENERIC_IDENT_RE = JAVA_IDENT_RE + '(<' + JAVA_IDENT_RE + '(\\\\s*,\\\\s*' + JAVA_IDENT_RE + ')*>)?';\n var KEYWORDS =\n 'false synchronized int abstract float private char boolean static null if const ' +\n 'for true while long strictfp finally protected import native final void ' +\n 'enum else break transient catch instanceof byte super volatile case assert short ' +\n 'package default double public try this switch continue throws protected public private ' +\n 'module requires exports do';\n\n // https://docs.oracle.com/javase/7/docs/technotes/guides/language/underscores-literals.html\n var JAVA_NUMBER_RE = '\\\\b' +\n '(' +\n '0[bB]([01]+[01_]+[01]+|[01]+)' + // 0b...\n '|' +\n '0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)' + // 0x...\n '|' +\n '(' +\n '([\\\\d]+[\\\\d_]+[\\\\d]+|[\\\\d]+)(\\\\.([\\\\d]+[\\\\d_]+[\\\\d]+|[\\\\d]+))?' +\n '|' +\n '\\\\.([\\\\d]+[\\\\d_]+[\\\\d]+|[\\\\d]+)' +\n ')' +\n '([eE][-+]?\\\\d+)?' + // octal, decimal, float\n ')' +\n '[lLfF]?';\n var JAVA_NUMBER_MODE = {\n className: 'number',\n begin: JAVA_NUMBER_RE,\n relevance: 0\n };\n\n return {\n aliases: ['jsp'],\n keywords: KEYWORDS,\n illegal: /<\\/|#/,\n contains: [\n hljs.COMMENT(\n '/\\\\*\\\\*',\n '\\\\*/',\n {\n relevance : 0,\n contains : [\n {\n // eat up @'s in emails to prevent them to be recognized as doctags\n begin: /\\w+@/, relevance: 0\n },\n {\n className : 'doctag',\n begin : '@[A-Za-z]+'\n }\n ]\n }\n ),\n hljs.C_LINE_COMMENT_MODE,\n hljs.C_BLOCK_COMMENT_MODE,\n hljs.APOS_STRING_MODE,\n hljs.QUOTE_STRING_MODE,\n {\n className: 'class',\n beginKeywords: 'class interface', end: /[{;=]/, excludeEnd: true,\n keywords: 'class interface',\n illegal: /[:\"\\[\\]]/,\n contains: [\n {beginKeywords: 'extends implements'},\n hljs.UNDERSCORE_TITLE_MODE\n ]\n },\n {\n // Expression keywords prevent 'keyword Name(...)' from being\n // recognized as a function definition\n beginKeywords: 'new throw return else',\n relevance: 0\n },\n {\n className: 'function',\n begin: '(' + GENERIC_IDENT_RE + '\\\\s+)+' + hljs.UNDERSCORE_IDENT_RE + '\\\\s*\\\\(', returnBegin: true, end: /[{;=]/,\n excludeEnd: true,\n keywords: KEYWORDS,\n contains: [\n {\n begin: hljs.UNDERSCORE_IDENT_RE + '\\\\s*\\\\(', returnBegin: true,\n relevance: 0,\n contains: [hljs.UNDERSCORE_TITLE_MODE]\n },\n {\n className: 'params',\n begin: /\\(/, end: /\\)/,\n keywords: KEYWORDS,\n relevance: 0,\n contains: [\n hljs.APOS_STRING_MODE,\n hljs.QUOTE_STRING_MODE,\n hljs.C_NUMBER_MODE,\n hljs.C_BLOCK_COMMENT_MODE\n ]\n },\n hljs.C_LINE_COMMENT_MODE,\n hljs.C_BLOCK_COMMENT_MODE\n ]\n },\n JAVA_NUMBER_MODE,\n {\n className: 'meta', begin: '@[A-Za-z]+'\n }\n ]\n };\n};","/* eslint no-useless-escape: off */\n\nmodule.exports = function(hljs) {\n var XML_IDENT_RE = '[A-Za-z0-9\\\\._:-]+';\n var TAG_INTERNALS = {\n endsWithParent: true,\n illegal: /`]+/}\n ]\n }\n ]\n }\n ]\n };\n return {\n aliases: ['html', 'xhtml', 'rss', 'atom', 'xjb', 'xsd', 'xsl', 'plist'],\n case_insensitive: true,\n contains: [\n {\n className: 'meta',\n begin: '',\n relevance: 10,\n contains: [{begin: '\\\\[', end: '\\\\]'}]\n },\n hljs.COMMENT(\n '',\n {\n relevance: 10\n }\n ),\n {\n begin: '<\\\\!\\\\[CDATA\\\\[', end: '\\\\]\\\\]>',\n relevance: 10\n },\n {\n className: 'meta',\n begin: /<\\?xml/, end: /\\?>/, relevance: 10\n },\n {\n begin: /<\\?(php)?/, end: /\\?>/,\n subLanguage: 'php',\n contains: [{begin: '/\\\\*', end: '\\\\*/', skip: true}]\n },\n {\n className: 'tag',\n /*\n The lookahead pattern (?=...) ensures that 'begin' only matches\n '|$)', end: '>',\n keywords: {name: 'style'},\n contains: [TAG_INTERNALS],\n starts: {\n end: '', returnEnd: true,\n subLanguage: ['css', 'xml']\n }\n },\n {\n className: 'tag',\n // See the comment in the ', returnEnd: true,\n subLanguage: ['css', 'xml']\n }\n },\n {\n className: 'tag',\n // See the comment in the