2020import static java .util .logging .Level .FINE ;
2121import static java .util .logging .Level .SEVERE ;
2222
23+ import io .prometheus .jmx .MatchedRulesCache .CacheKey ;
2324import io .prometheus .jmx .logger .Logger ;
2425import io .prometheus .jmx .logger .LoggerFactory ;
2526import io .prometheus .metrics .core .metrics .Counter ;
@@ -475,7 +476,15 @@ private Config loadConfig(Map<String, Object> yamlConfig) throws MalformedObject
475476 cfg .rules .add (new Rule ());
476477 }
477478
478- cfg .rulesCache = new MatchedRulesCache (cfg .rules );
479+ boolean hasCachedRules = false ;
480+ for (Rule rule : cfg .rules ) {
481+ hasCachedRules |= rule .cache ;
482+ }
483+
484+ // Avoid all costs related to maintaining the cache if there are no cached rules
485+ if (hasCachedRules ) {
486+ cfg .rulesCache = new MatchedRulesCache ();
487+ }
479488 cfg .objectNameAttributeFilter = ObjectNameAttributeFilter .create (yamlConfig );
480489
481490 return cfg ;
@@ -563,12 +572,10 @@ private String angleBrackets(String s) {
563572 }
564573
565574 // Add the matched rule to the cached rules and tag it as not stale
566- // if the rule is configured to be cached
567- private void addToCache (
568- final Rule rule , final String cacheKey , final MatchedRule matchedRule ) {
569- if (rule .cache ) {
570- config .rulesCache .put (rule , cacheKey , matchedRule );
571- stalenessTracker .add (rule , cacheKey );
575+ private void addToCache (final CacheKey cacheKey , final MatchedRule matchedRule ) {
576+ if (config .rulesCache != null && cacheKey != null ) {
577+ config .rulesCache .put (cacheKey , matchedRule );
578+ stalenessTracker .markAsFresh (cacheKey );
572579 }
573580 }
574581
@@ -634,161 +641,173 @@ public void recordBean(
634641 String attrDescription ,
635642 Object beanValue ) {
636643
637- String beanName =
638- domain
639- + angleBrackets (beanProperties .toString ())
640- + angleBrackets (attrKeys .toString ());
641-
642- // Build the HELP string from the bean metadata.
643- String help =
644- domain
645- + ":name="
646- + beanProperties .get ("name" )
647- + ",type="
648- + beanProperties .get ("type" )
649- + ",attribute="
650- + attrName ;
651- // Add the attrDescription to the HELP if it exists and is useful.
652- if (attrDescription != null && !attrDescription .equals (attrName )) {
653- help = attrDescription + " " + help ;
654- }
655-
656644 MatchedRule matchedRule = MatchedRule .unmatched ();
657645
658- for (Rule rule : config .rules ) {
659- // Rules with bean values cannot be properly cached (only the value from the first
660- // scrape will be cached).
661- // If caching for the rule is enabled, replace the value with a dummy <cache> to
662- // avoid caching different values at different times.
663- Object matchBeanValue = rule .cache ? "<cache>" : beanValue ;
646+ CacheKey cacheKey = null ;
647+ MatchedRule cachedRule = null ;
664648
665- String attributeName ;
666- if (rule .attrNameSnakeCase ) {
667- attributeName = toSnakeAndLowerCase (attrName );
668- } else {
669- attributeName = attrName ;
649+ if (config .rulesCache != null ) {
650+ cacheKey = new CacheKey (domain , beanProperties , attrKeys , attrName );
651+ cachedRule = config .rulesCache .get (cacheKey );
652+ if (cachedRule != null ) {
653+ stalenessTracker .markAsFresh (cacheKey );
654+ matchedRule = cachedRule ;
670655 }
656+ }
671657
672- String matchName = ( beanName + attributeName + ": " + matchBeanValue ). intern ();
658+ if ( matchedRule . isUnmatched ()) {
673659
674- if (rule .cache ) {
675- MatchedRule cachedRule = config .rulesCache .get (rule , matchName );
676- if (cachedRule != null ) {
677- stalenessTracker .add (rule , matchName );
678- if (cachedRule .isMatched ()) {
679- matchedRule = cachedRule ;
680- break ;
681- }
660+ String beanName =
661+ domain
662+ + angleBrackets (beanProperties .toString ())
663+ + angleBrackets (attrKeys .toString ());
664+
665+ // Build the HELP string from the bean metadata.
666+ String help =
667+ domain
668+ + ":name="
669+ + beanProperties .get ("name" )
670+ + ",type="
671+ + beanProperties .get ("type" )
672+ + ",attribute="
673+ + attrName ;
674+ // Add the attrDescription to the HELP if it exists and is useful.
675+ if (attrDescription != null && !attrDescription .equals (attrName )) {
676+ help = attrDescription + " " + help ;
677+ }
682678
683- // The bean was cached earlier, but did not match the current rule.
684- // Skip it to avoid matching against the same pattern again
679+ for (Rule rule : config .rules ) {
680+
681+ // If we cache that rule, and we found a cache entry for this bean/attribute,
682+ // then what's left to do is to check all uncached rules
683+ if (rule .cache && cachedRule != null ) {
685684 continue ;
686685 }
687- }
688686
689- Matcher matcher = null ;
690- if (rule .pattern != null ) {
691- matcher = rule .pattern .matcher (matchName );
692- if (!matcher .matches ()) {
693- addToCache (rule , matchName , MatchedRule .unmatched ());
694- continue ;
687+ // Rules with bean values cannot be properly cached (only the value from the
688+ // first
689+ // scrape will be cached).
690+ // If caching for the rule is enabled, replace the value with a dummy <cache> to
691+ // avoid caching different values at different times.
692+ Object matchBeanValue = rule .cache ? "<cache>" : beanValue ;
693+
694+ String attributeName ;
695+ if (rule .attrNameSnakeCase ) {
696+ attributeName = toSnakeAndLowerCase (attrName );
697+ } else {
698+ attributeName = attrName ;
695699 }
696- }
697700
698- Double value = null ;
699- if (rule .value != null && !rule .value .isEmpty ()) {
700- String val = matcher .replaceAll (rule .value );
701- try {
702- value = Double .valueOf (val );
703- } catch (NumberFormatException e ) {
704- LOGGER .log (
705- FINE ,
706- "Unable to parse configured value '%s' to number for bean: %s%s:"
707- + " %s" ,
708- val ,
709- beanName ,
710- attrName ,
711- beanValue );
712- return ;
701+ String matchName = beanName + attributeName + ": " + matchBeanValue ;
702+
703+ Matcher matcher = null ;
704+ if (rule .pattern != null ) {
705+ matcher = rule .pattern .matcher (matchName );
706+ if (!matcher .matches ()) {
707+ continue ;
708+ }
713709 }
714- }
715710
716- // If there's no name provided, use default export format.
717- if (rule .name == null ) {
718- matchedRule =
719- defaultExport (
720- matchName ,
721- domain ,
722- beanProperties ,
723- attrKeys ,
724- attributeName ,
725- help ,
726- value ,
727- rule . valueFactor ,
728- rule . type ,
729- attributesAsLabelsWithValues );
730- addToCache ( rule , matchName , matchedRule ) ;
731- break ;
732- }
711+ Double value = null ;
712+ if (rule .value != null && ! rule . value . isEmpty () ) {
713+ String val = matcher . replaceAll ( rule . value );
714+ try {
715+ value = Double . valueOf ( val );
716+ } catch ( NumberFormatException e ) {
717+ LOGGER . log (
718+ FINE ,
719+ "Unable to parse configured value '%s' to number for bean:"
720+ + " %s%s: %s" ,
721+ val ,
722+ beanName ,
723+ attrName ,
724+ beanValue );
725+ return ;
726+ }
727+ }
733728
734- // Matcher is set below here due to validation in the constructor.
735- String name = safeName (matcher .replaceAll (rule .name ));
736- if (name .isEmpty ()) {
737- return ;
738- }
739- if (config .lowercaseOutputName ) {
740- name = name .toLowerCase ();
741- }
729+ // If there's no name provided, use default export format.
730+ if (rule .name == null ) {
731+ matchedRule =
732+ defaultExport (
733+ matchName ,
734+ domain ,
735+ beanProperties ,
736+ attrKeys ,
737+ attributeName ,
738+ help ,
739+ value ,
740+ rule .valueFactor ,
741+ rule .type ,
742+ attributesAsLabelsWithValues );
743+ if (rule .cache ) {
744+ addToCache (cacheKey , matchedRule );
745+ }
746+ break ;
747+ }
742748
743- // Set the help.
744- if (rule .help != null ) {
745- help = matcher .replaceAll (rule .help );
746- }
749+ // Matcher is set below here due to validation in the constructor.
750+ String name = safeName (matcher .replaceAll (rule .name ));
751+ if (name .isEmpty ()) {
752+ return ;
753+ }
754+ if (config .lowercaseOutputName ) {
755+ name = name .toLowerCase ();
756+ }
747757
748- // Set the labels.
749- ArrayList <String > labelNames = new ArrayList <>();
750- ArrayList <String > labelValues = new ArrayList <>();
751- addAttributesAsLabelsWithValuesToLabels (
752- config , attributesAsLabelsWithValues , labelNames , labelValues );
753- if (rule .labelNames != null ) {
754- for (int i = 0 ; i < rule .labelNames .size (); i ++) {
755- final String unsafeLabelName = rule .labelNames .get (i );
756- final String labelValReplacement = rule .labelValues .get (i );
757- try {
758- String labelName = safeName (matcher .replaceAll (unsafeLabelName ));
759- String labelValue = matcher .replaceAll (labelValReplacement );
760- if (config .lowercaseOutputLabelNames ) {
761- labelName = labelName .toLowerCase ();
762- }
763- if (!labelName .isEmpty () && !labelValue .isEmpty ()) {
764- labelNames .add (labelName );
765- labelValues .add (labelValue );
758+ // Set the help.
759+ if (rule .help != null ) {
760+ help = matcher .replaceAll (rule .help );
761+ }
762+
763+ // Set the labels.
764+ ArrayList <String > labelNames = new ArrayList <>();
765+ ArrayList <String > labelValues = new ArrayList <>();
766+ addAttributesAsLabelsWithValuesToLabels (
767+ config , attributesAsLabelsWithValues , labelNames , labelValues );
768+ if (rule .labelNames != null ) {
769+ for (int i = 0 ; i < rule .labelNames .size (); i ++) {
770+ final String unsafeLabelName = rule .labelNames .get (i );
771+ final String labelValReplacement = rule .labelValues .get (i );
772+ try {
773+ String labelName = safeName (matcher .replaceAll (unsafeLabelName ));
774+ String labelValue = matcher .replaceAll (labelValReplacement );
775+ if (config .lowercaseOutputLabelNames ) {
776+ labelName = labelName .toLowerCase ();
777+ }
778+ if (!labelName .isEmpty () && !labelValue .isEmpty ()) {
779+ labelNames .add (labelName );
780+ labelValues .add (labelValue );
781+ }
782+ } catch (Exception e ) {
783+ throw new RuntimeException (
784+ format (
785+ "Matcher '%s' unable to use: '%s' value: '%s'" ,
786+ matcher , unsafeLabelName , labelValReplacement ),
787+ e );
766788 }
767- } catch (Exception e ) {
768- throw new RuntimeException (
769- format (
770- "Matcher '%s' unable to use: '%s' value: '%s'" ,
771- matcher , unsafeLabelName , labelValReplacement ),
772- e );
773789 }
774790 }
775- }
776791
777- matchedRule =
778- new MatchedRule (
779- name ,
780- matchName ,
781- rule .type ,
782- help ,
783- labelNames ,
784- labelValues ,
785- value ,
786- rule .valueFactor );
787- addToCache (rule , matchName , matchedRule );
788- break ;
792+ matchedRule =
793+ new MatchedRule (
794+ name ,
795+ matchName ,
796+ rule .type ,
797+ help ,
798+ labelNames ,
799+ labelValues ,
800+ value ,
801+ rule .valueFactor );
802+ if (rule .cache ) {
803+ addToCache (cacheKey , matchedRule );
804+ }
805+ break ;
806+ }
789807 }
790808
791809 if (matchedRule .isUnmatched ()) {
810+ addToCache (cacheKey , matchedRule );
792811 return ;
793812 }
794813
@@ -804,8 +823,10 @@ public void recordBean(
804823 } else {
805824 LOGGER .log (
806825 FINE ,
807- "Ignoring unsupported bean: %s%s: %s " ,
808- beanName ,
826+ "Ignoring unsupported bean: %s%s%s%s: %s " ,
827+ domain ,
828+ angleBrackets (beanProperties .toString ()),
829+ angleBrackets (attrKeys .toString ()),
809830 attrName ,
810831 beanValue );
811832 return ;
@@ -880,11 +901,13 @@ public MetricSnapshots collect() {
880901 LOGGER .log (SEVERE , "JMX scrape failed: %s" , sw );
881902 }
882903
883- config .rulesCache .evictStaleEntries (stalenessTracker );
904+ if (config .rulesCache != null ) {
905+ config .rulesCache .evictStaleEntries (stalenessTracker );
906+ }
884907
885908 jmxScrapeDurationSeconds .set ((System .nanoTime () - start ) / 1.0E9 );
886909 jmxScrapeError .set (error );
887- jmxScrapeCachedBeans .set (stalenessTracker .cachedCount ());
910+ jmxScrapeCachedBeans .set (stalenessTracker .freshCount ());
888911
889912 return MatchedRuleToMetricSnapshotsConverter .convert (receiver .matchedRules );
890913 }
0 commit comments