@@ -98,42 +98,42 @@ public NormalizedDataMetricsDataBuilder metric(String metricPrefix, String metri
9898
9999 public NormalizedDataMetricsDataBuilder resourceAttribute (String key , String value ) {
100100 if (hasText (key )) {
101- resourceAttributes .add (OtlpAttributeUtils . attribute (key , value ));
101+ resourceAttributes .add (attribute (key , value ));
102102 }
103103 return this ;
104104 }
105105
106106 public NormalizedDataMetricsDataBuilder resourceAttribute (String key , AnyValue value ) {
107107 if (hasText (key ) && value != null ) {
108- resourceAttributes .add (OtlpAttributeUtils . attribute (key , value ));
108+ resourceAttributes .add (attribute (key , value ));
109109 }
110110 return this ;
111111 }
112112
113113 public NormalizedDataMetricsDataBuilder resourceAttributes (List <KeyValue > attributes ) {
114114 if (attributes != null ) {
115- resourceAttributes .addAll (attributes );
115+ resourceAttributes .addAll (normalizeAttributeKeys ( attributes ) );
116116 }
117117 return this ;
118118 }
119119
120120 public NormalizedDataMetricsDataBuilder scopeAttribute (String key , String value ) {
121121 if (hasText (key )) {
122- scopeAttributes .add (OtlpAttributeUtils . attribute (key , value ));
122+ scopeAttributes .add (attribute (key , value ));
123123 }
124124 return this ;
125125 }
126126
127127 public NormalizedDataMetricsDataBuilder scopeAttribute (String key , AnyValue value ) {
128128 if (hasText (key ) && value != null ) {
129- scopeAttributes .add (OtlpAttributeUtils . attribute (key , value ));
129+ scopeAttributes .add (attribute (key , value ));
130130 }
131131 return this ;
132132 }
133133
134134 public NormalizedDataMetricsDataBuilder scopeAttributes (List <KeyValue > attributes ) {
135135 if (attributes != null ) {
136- scopeAttributes .addAll (attributes );
136+ scopeAttributes .addAll (normalizeAttributeKeys ( attributes ) );
137137 }
138138 return this ;
139139 }
@@ -148,6 +148,16 @@ public NormalizedDataMetricsDataBuilder gaugeMetric(NormalizedData normalizedDat
148148 }
149149
150150 public MetricsData build () {
151+ for (ResourceMetrics resourceMetrics : metricsData .getResourceMetrics ()) {
152+ if (resourceMetrics .getResource () != null ) {
153+ addMissingAttributes (resourceMetrics .getResource ().getAttributes (), resourceAttributes );
154+ }
155+ for (ScopeMetrics scopeMetrics : resourceMetrics .getScopeMetrics ()) {
156+ if (scopeMetrics .getScope () != null ) {
157+ addMissingAttributes (scopeMetrics .getScope ().getAttributes (), scopeAttributes );
158+ }
159+ }
160+ }
151161 return metricsData ;
152162 }
153163
@@ -226,9 +236,9 @@ private NumberDataPoint dataPoint(NormalizedData normalizedData, String contentP
226236 NumberDataPoint dataPoint = new NumberDataPoint ();
227237 dataPoint .setTimeUnixNano (OtlpTimeUtils .unixNanoAsString (normalizedData .getTimeObserved ()));
228238 dataPoint .setAsDouble (metricValue (content , jsonValue , jsonType ).doubleValue ());
229- dataPoint .getAttributes ().add (OtlpAttributeUtils . attribute ("iot.content.path" , contentPath ));
239+ dataPoint .getAttributes ().add (attribute ("iot.content.path" , contentPath ));
230240 dataPoint .getAttributes ()
231- .add (OtlpAttributeUtils . attribute ("iot.content.type" , normalizedData .getContentType ()));
241+ .add (attribute ("iot.content.type" , normalizedData .getContentType ()));
232242 return dataPoint ;
233243 }
234244
@@ -238,14 +248,25 @@ public String metricName(NormalizedData normalizedData) {
238248
239249 public String metricName (String contentPath ) {
240250 if (!hasText (contentPath )) {
241- return metricPrefix + ".value" ;
251+ return toDotSeparatedSnakeCase ( metricPrefix ) + ".value" ;
242252 }
243- String sanitizedPath = contentPath .strip ().replace ('\\' , '/' ).replaceAll ("^/+" , "" ). replace ( '/' , '.' )
244- .replaceAll ("[^A-Za-z0-9_.-]+" , "_" ).replaceAll ("\\ .+" , "." );
253+ String sanitizedPath = toDotSeparatedSnakeCase ( contentPath .strip ().replace ('\\' , '/' ).replaceAll ("^/+" , "" )
254+ .replace ( '/' , '.' ). replaceAll ("[^A-Za-z0-9_.-]+" , "_" ).replaceAll ("\\ .+" , "." ) );
245255 if (!hasText (sanitizedPath )) {
246256 sanitizedPath = "value" ;
247257 }
248- return metricPrefix + "." + sanitizedPath ;
258+ return toDotSeparatedSnakeCase (metricPrefix ) + "." + sanitizedPath ;
259+ }
260+
261+ public static String toDotSeparatedSnakeCase (String input ) {
262+ if (!hasText (input )) {
263+ return input ;
264+ }
265+ String [] parts = input .strip ().replace ('\\' , '/' ).replace ('/' , '.' ).split ("\\ ." );
266+ for (int i = 0 ; i < parts .length ; i ++) {
267+ parts [i ] = toSnakeCase (parts [i ]);
268+ }
269+ return String .join ("." , parts ).replaceAll ("\\ .+" , "." );
249270 }
250271
251272 public BigDecimal metricValue (NormalizedData normalizedData ) {
@@ -283,6 +304,19 @@ private BigDecimal metricValue(String content, OracleJsonValue jsonValue, Oracle
283304 }
284305 }
285306
307+ private static String toSnakeCase (String value ) {
308+ if (!hasText (value )) {
309+ return value ;
310+ }
311+ return value .strip ()
312+ .replaceAll ("([A-Z]+)([A-Z][a-z])" , "$1_$2" )
313+ .replaceAll ("([a-z0-9])([A-Z])" , "$1_$2" )
314+ .replaceAll ("[^A-Za-z0-9]+" , "_" )
315+ .replaceAll ("_+" , "_" )
316+ .replaceAll ("^_|_$" , "" )
317+ .toLowerCase (java .util .Locale .ROOT );
318+ }
319+
286320 private static String appendPath (String contentPath , String fieldName ) {
287321 if (!hasText (contentPath )) {
288322 return fieldName ;
@@ -300,10 +334,8 @@ private static boolean isNumeric(OracleJsonType jsonType) {
300334
301335 private ResourceMetrics resourceMetrics (NormalizedData normalizedData ) {
302336 Resource resource = new Resource ();
303- resource .getAttributes ().add (OtlpAttributeUtils .attribute ("service.name" , serviceName ));
304- resource .getAttributes ().add (OtlpAttributeUtils .attribute ("iot.digital_twin.instance_id" ,
305- normalizedData .getDigitalTwinInstanceId ()));
306- resource .getAttributes ().addAll (resourceAttributes );
337+ resource .getAttributes ().add (attribute ("service.name" , serviceName ));
338+ resource .getAttributes ().add (attribute ("iot.digital_twin.instance_id" , normalizedData .getDigitalTwinInstanceId ()));
307339
308340 ResourceMetrics resourceMetrics = new ResourceMetrics ();
309341 resourceMetrics .setResource (resource );
@@ -312,15 +344,40 @@ private ResourceMetrics resourceMetrics(NormalizedData normalizedData) {
312344
313345 private ScopeMetrics scopeMetrics () {
314346 InstrumentationScope scope = new InstrumentationScope ();
315- scope .setName (scopeName );
347+ scope .setName (toDotSeparatedSnakeCase ( scopeName ) );
316348 scope .setVersion (scopeVersion );
317- scope .getAttributes ().addAll (scopeAttributes );
318349
319350 ScopeMetrics scopeMetrics = new ScopeMetrics ();
320351 scopeMetrics .setScope (scope );
321352 return scopeMetrics ;
322353 }
323354
355+ private static void addMissingAttributes (List <KeyValue > target , List <KeyValue > attributes ) {
356+ for (KeyValue attribute : attributes ) {
357+ if (!target .contains (attribute )) {
358+ target .add (attribute );
359+ }
360+ }
361+ }
362+
363+ private static List <KeyValue > normalizeAttributeKeys (List <KeyValue > attributes ) {
364+ List <KeyValue > normalizedAttributes = new ArrayList <>();
365+ for (KeyValue attribute : attributes ) {
366+ if (attribute != null ) {
367+ normalizedAttributes .add (attribute (attribute .getKey (), attribute .getValue ()));
368+ }
369+ }
370+ return normalizedAttributes ;
371+ }
372+
373+ private static KeyValue attribute (String key , String value ) {
374+ return OtlpAttributeUtils .attribute (toDotSeparatedSnakeCase (key ), value );
375+ }
376+
377+ private static KeyValue attribute (String key , AnyValue value ) {
378+ return OtlpAttributeUtils .attribute (toDotSeparatedSnakeCase (key ), value );
379+ }
380+
324381 private static boolean hasText (String value ) {
325382 return value != null && !value .isBlank ();
326383 }
0 commit comments