2121
2222package com .microsoft .applicationinsights .agent .internal .perfcounter ;
2323
24- import static java .util .Arrays .asList ;
25- import static java .util .Collections .emptyList ;
24+ import static java .util .Collections .singleton ;
2625import static java .util .Collections .singletonList ;
2726
2827import java .lang .management .ManagementFactory ;
2928import java .util .ArrayList ;
30- import java .util .Collections ;
3129import java .util .HashMap ;
3230import java .util .HashSet ;
3331import java .util .List ;
4745import org .slf4j .Logger ;
4846import org .slf4j .LoggerFactory ;
4947
48+ // TODO (trask) add tests
5049class AvailableJmxMetricLogger {
5150
5251 private static final Logger logger = LoggerFactory .getLogger (AvailableJmxMetricLogger .class );
5352
54- private static final Set <String > NUMERIC_ATTRIBUTE_TYPES =
55- new HashSet <>(
56- asList (
57- "long" ,
58- "int" ,
59- "double" ,
60- "float" ,
61- "java.lang.Long" ,
62- "java.lang.Integer" ,
63- "java.lang.Double" ,
64- "java.lang.Float" ));
65-
66- private static final Set <String > BOOLEAN_ATTRIBUTE_TYPES =
67- new HashSet <>(asList ("boolean" , "java.lang.Boolean" ));
68-
6953 @ GuardedBy ("lock" )
70- private Map <String , Set <String >> priorAvailableJmxAttributes = new HashMap <>();
54+ private Map <String , Set <String >> priorAttributeMap = new HashMap <>();
7155
7256 private final Object lock = new Object ();
7357
7458 void logAvailableJmxMetrics () {
7559 synchronized (lock ) {
76- Map <String , Set <String >> availableJmxAttributes = getAvailableJmxAttributes ();
77- logDifference (priorAvailableJmxAttributes , availableJmxAttributes );
78- priorAvailableJmxAttributes = availableJmxAttributes ;
60+ Map <String , Set <String >> attributeMap = getAttributeMap ();
61+ logDifference (priorAttributeMap , attributeMap );
62+ priorAttributeMap = attributeMap ;
7963 }
8064 }
8165
@@ -106,75 +90,64 @@ private static String toString(Map<String, Set<String>> jmxAttributes) {
10690 sb .append (" - object name: " )
10791 .append (entry .getKey ())
10892 .append ("\n " )
109- .append (" numeric attributes: " )
93+ .append (" attributes: " )
11094 .append (entry .getValue ().stream ().sorted ().collect (Collectors .joining (", " )))
11195 .append ("\n " );
11296 }
11397 return sb .toString ();
11498 }
11599
116- private static Map <String , Set <String >> getAvailableJmxAttributes () {
100+ private static Map <String , Set <String >> getAttributeMap () {
117101 MBeanServer server = ManagementFactory .getPlatformMBeanServer ();
118102 Set <ObjectName > objectNames = server .queryNames (null , null );
119- Map <String , Set <String >> availableJmxMetrics = new HashMap <>();
103+ Map <String , Set <String >> attributeMap = new HashMap <>();
120104 for (ObjectName objectName : objectNames ) {
121105 String name = objectName .toString ();
106+ Set <String > attributes ;
122107 try {
123- Set < String > attrs = getJmxAttributes (server , objectName );
124- if (! attrs .isEmpty ()) {
125- availableJmxMetrics . put ( name , attrs );
108+ attributes = getAttributes (server , objectName );
109+ if (attributes .isEmpty ()) {
110+ attributes . add ( "(no attributes found)" );
126111 }
127112 } catch (Exception e ) {
128113 // log exception at trace level since this is expected in several cases, e.g.
129114 // "java.lang.UnsupportedOperationException: CollectionUsage threshold is not supported"
130115 // and available jmx metrics are already only logged at debug
131116 logger .trace (e .getMessage (), e );
132- availableJmxMetrics . put ( name , Collections . singleton ("< error getting attributes: " + e ) );
117+ attributes = singleton ("( error getting attributes)" );
133118 }
119+ attributeMap .put (name , attributes );
134120 }
135- return availableJmxMetrics ;
121+ return attributeMap ;
136122 }
137123
138- private static Set <String > getJmxAttributes (MBeanServer server , ObjectName objectName )
124+ private static Set <String > getAttributes (MBeanServer server , ObjectName objectName )
139125 throws Exception {
140126 MBeanInfo mbeanInfo = server .getMBeanInfo (objectName );
141- Set <String > attributeNames = new HashSet <>();
127+ Set <String > attributes = new HashSet <>();
142128 for (MBeanAttributeInfo attribute : mbeanInfo .getAttributes ()) {
143- if (attribute .isReadable ()) {
144- try {
145- Object value = server .getAttribute (objectName , attribute .getName ());
146- attributeNames .addAll (getNumericAttributes (attribute , value ));
147- } catch (Exception e ) {
148- // log exception at trace level since this is expected in several cases, e.g.
149- // "java.lang.UnsupportedOperationException: CollectionUsage threshold is not supported"
150- // and available jmx metrics are already only logged at debug
151- logger .trace (e .getMessage (), e );
152- }
129+ if (!attribute .isReadable ()) {
130+ attributes .add (attribute .getName () + " (not readable)" );
131+ continue ;
132+ }
133+ try {
134+ Object value = server .getAttribute (objectName , attribute .getName ());
135+ attributes .addAll (getAttributes (attribute , value ));
136+ } catch (Exception e ) {
137+ // log exception at trace level since this is expected in several cases, e.g.
138+ // "java.lang.UnsupportedOperationException: CollectionUsage threshold is not supported"
139+ // and available jmx metrics are already only logged at debug
140+ logger .trace (e .getMessage (), e );
141+ attributes .add (attribute .getName () + " (exception)" );
153142 }
154143 }
155- return attributeNames ;
144+ return attributes ;
156145 }
157146
158- private static List <String > getNumericAttributes (MBeanAttributeInfo attribute , Object value ) {
147+ private static List <String > getAttributes (MBeanAttributeInfo attribute , @ Nullable Object value ) {
148+
159149 String attributeType = attribute .getType ();
160- if (NUMERIC_ATTRIBUTE_TYPES .contains (attributeType ) && value instanceof Number ) {
161- return singletonList (attribute .getName ());
162- }
163- if (BOOLEAN_ATTRIBUTE_TYPES .contains (attributeType ) && value instanceof Boolean ) {
164- return singletonList (attribute .getName ());
165- }
166- if (attributeType .equals ("java.lang.Object" ) && value instanceof Number ) {
167- return singletonList (attribute .getName ());
168- }
169- if (attributeType .equals ("java.lang.String" ) && value instanceof String ) {
170- try {
171- Double .parseDouble ((String ) value );
172- return singletonList (attribute .getName ());
173- } catch (NumberFormatException e ) {
174- // this is expected for non-numeric attributes
175- return emptyList ();
176- }
177- }
150+
178151 if (attributeType .equals (CompositeData .class .getName ())) {
179152 Object openType = attribute .getDescriptor ().getFieldValue ("openType" );
180153 CompositeType compositeType = null ;
@@ -184,43 +157,49 @@ private static List<String> getNumericAttributes(MBeanAttributeInfo attribute, O
184157 compositeType = ((CompositeDataSupport ) value ).getCompositeType ();
185158 }
186159 if (compositeType != null ) {
187- return getCompositeTypeAttributeNames (attribute , value , compositeType );
160+ return getCompositeTypeAttributes (attribute , value , compositeType );
188161 }
189162 }
190- return emptyList ();
163+
164+ return singletonList (attribute .getName () + " (" + valueType (value ) + ")" );
191165 }
192166
193- private static List <String > getCompositeTypeAttributeNames (
167+ private static List <String > getCompositeTypeAttributes (
194168 MBeanAttributeInfo attribute , Object compositeData , CompositeType compositeType ) {
195- List <String > attributeNames = new ArrayList <>();
169+ List <String > attributes = new ArrayList <>();
196170 for (String itemName : compositeType .keySet ()) {
171+ String attributeName = attribute .getName () + "." + itemName ;
197172 OpenType <?> itemType = compositeType .getType (itemName );
198173 if (itemType == null ) {
174+ attributes .add (attributeName + " (null)" );
199175 continue ;
200176 }
201- String className = itemType .getClassName ();
202- Class <?> clazz ;
203- try {
204- clazz = Class .forName (className );
205- } catch (ClassNotFoundException e ) {
206- logger .warn (e .getMessage (), e );
207- continue ;
177+ if (compositeData instanceof CompositeData ) {
178+ Object value = ((CompositeData ) compositeData ).get (itemName );
179+ attributes .add (attributeName + " (" + valueType (value ) + ")" );
180+ } else {
181+ attributes .add (attributeName + " (unexpected: " + compositeData .getClass ().getName () + ")" );
208182 }
209- if (Number .class .isAssignableFrom (clazz )) {
210- attributeNames .add (attribute .getName () + '.' + itemName );
211- } else if (clazz == String .class && compositeData instanceof CompositeData ) {
212- Object val = ((CompositeData ) compositeData ).get (itemName );
213- if (val instanceof String ) {
214- try {
215- Double .parseDouble ((String ) val );
216- attributeNames .add (attribute .getName () + '.' + itemName );
217- } catch (NumberFormatException e ) {
218- // this is expected for non-numeric attributes
219- }
220- }
183+ }
184+ return attributes ;
185+ }
186+
187+ private static String valueType (@ Nullable Object value ) {
188+ if (value instanceof Number ) {
189+ return "number" ;
190+ }
191+ if (value instanceof Boolean ) {
192+ return "boolean" ;
193+ }
194+ if (value instanceof String ) {
195+ try {
196+ Double .parseDouble ((String ) value );
197+ return "number" ;
198+ } catch (NumberFormatException e ) {
199+ return "string" ;
221200 }
222201 }
223- return attributeNames ;
202+ return "other" ;
224203 }
225204
226205 // visible for testing
0 commit comments