18
18
19
19
import java .io .ByteArrayOutputStream ;
20
20
import java .nio .charset .StandardCharsets ;
21
- import java .util .Collections ;
22
21
import java .util .LinkedHashMap ;
23
22
import java .util .Map ;
24
23
import java .util .function .Function ;
24
+ import java .util .function .Supplier ;
25
25
26
+ import org .springframework .util .CollectionUtils ;
26
27
import org .springframework .util .StringUtils ;
28
+ import org .springframework .util .function .SupplierUtils ;
27
29
28
30
/**
29
- * OpenTelemetryResourceAttributes retrieves information from the
30
- * {@code OTEL_RESOURCE_ATTRIBUTES} and {@code OTEL_SERVICE_NAME} environment variables
31
- * and merges it with the resource attributes provided by the user.
32
- * <p>
33
- * <b>User-provided resource attributes take precedence.</b>
31
+ * {@link OpenTelemetryResourceAttributes} for managing OpenTelemetry resource attributes
32
+ * and provides convenient API to construct, modify, and merge attributes from different
33
+ * sources.
34
34
* <p>
35
35
* <a href= "https://opentelemetry.io/docs/specs/otel/resource/sdk/">OpenTelemetry
36
36
* Resource Specification</a>
37
37
*
38
38
* @author Dmytro Nosan
39
39
* @since 3.5.0
40
+ * @see #fromEnv()
40
41
*/
41
42
public final class OpenTelemetryResourceAttributes {
42
43
43
- private final Map <String , String > resourceAttributes ;
44
-
45
- private final Function <String , String > getEnv ;
44
+ private final Map <String , String > attributes = new LinkedHashMap <>();
46
45
47
46
/**
48
- * Creates a new instance of {@link OpenTelemetryResourceAttributes} .
49
- * @param resourceAttributes user provided resource attributes to be used
47
+ * Return Resource attributes as a Map .
48
+ * @return the resource attributes as key-value pairs
50
49
*/
51
- public OpenTelemetryResourceAttributes ( Map <String , String > resourceAttributes ) {
52
- this ( resourceAttributes , null );
50
+ public Map <String , String > asMap ( ) {
51
+ return new LinkedHashMap <>( this . attributes );
53
52
}
54
53
55
54
/**
56
- * Creates a new {@link OpenTelemetryResourceAttributes} instance.
57
- * @param resourceAttributes user provided resource attributes to be used
58
- * @param getEnv a function to retrieve environment variables by name
55
+ * Adds a name-value pair to the resource attributes. Both the name and value will be
56
+ * trimmed.
57
+ * @param name the attribute name to add, must not be null or empty
58
+ * @param value the attribute value to add, must not be null
59
59
*/
60
- OpenTelemetryResourceAttributes (Map <String , String > resourceAttributes , Function <String , String > getEnv ) {
61
- this .resourceAttributes = (resourceAttributes != null ) ? resourceAttributes : Collections .emptyMap ();
62
- this .getEnv = (getEnv != null ) ? getEnv : System ::getenv ;
60
+ public void put (String name , String value ) {
61
+ if (StringUtils .hasText (name ) && value != null ) {
62
+ this .attributes .put (name .trim (), value .trim ());
63
+ }
63
64
}
64
65
65
66
/**
66
- * Returns resource attributes by combining attributes from environment variables and
67
- * user-defined resource attributes. The final resource contains all attributes from
68
- * both sources.
67
+ * Merge attributes with the provided resource attributes.
68
+ * <p>
69
+ * If a key exists in both, the value from provided resource takes precedence, even if
70
+ * it is empty.
69
71
* <p>
70
- * If a key exists in both environment variables and user-defined resources, the value
71
- * from the user-defined resource takes precedence, even if it is empty.
72
+ * <b>Keys that are null or empty will be ignored, and all keys will be trimmed.</b>
72
73
* <p>
73
- * <b>Null keys and values are ignored .</b>
74
- * @return the resource attributes
74
+ * <b>Values that are null will be ignored, and all values will be trimmed .</b>
75
+ * @param resourceAttributes resource attributes
75
76
*/
76
- public Map <String , String > asMap () {
77
- Map <String , String > attributes = getResourceAttributesFromEnv ();
78
- this .resourceAttributes .forEach ((name , value ) -> {
79
- if (name != null && value != null ) {
80
- attributes .put (name , value );
81
- }
82
- });
83
- return attributes ;
77
+ public void putAll (Map <String , String > resourceAttributes ) {
78
+ if (!CollectionUtils .isEmpty (resourceAttributes )) {
79
+ resourceAttributes .forEach (this ::put );
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Adds a name-value pair to the resource attributes. Both the name and supplied value
85
+ * will be trimmed.
86
+ * @param name the attribute name to add, must not be null or empty
87
+ * @param valueSupplier the attribute value supplier
88
+ */
89
+ public void putIfAbsent (String name , Supplier <String > valueSupplier ) {
90
+ if (!contains (name )) {
91
+ put (name , SupplierUtils .resolve (valueSupplier ));
92
+ }
93
+ }
94
+
95
+ @ Override
96
+ public boolean equals (Object obj ) {
97
+ if (obj == null || getClass () != obj .getClass ()) {
98
+ return false ;
99
+ }
100
+ OpenTelemetryResourceAttributes that = (OpenTelemetryResourceAttributes ) obj ;
101
+ return this .attributes .equals (that .attributes );
102
+ }
103
+
104
+ @ Override
105
+ public int hashCode () {
106
+ return this .attributes .hashCode ();
107
+ }
108
+
109
+ @ Override
110
+ public String toString () {
111
+ return this .attributes .toString ();
84
112
}
85
113
86
114
/**
87
- * Parses resource attributes from the {@link System#getenv()}. This method fetches
88
- * attributes defined in the {@code OTEL_RESOURCE_ATTRIBUTES} and
89
- * {@code OTEL_SERVICE_NAME} environment variables and provides them as key-value
90
- * pairs .
115
+ * Creates an {@link OpenTelemetryResourceAttributes} instance based on environment
116
+ * variables. This method fetches attributes defined in the
117
+ * {@code OTEL_RESOURCE_ATTRIBUTES} and {@code OTEL_SERVICE_NAME} environment
118
+ * variables .
91
119
* <p>
92
120
* If {@code service.name} is also provided in {@code OTEL_RESOURCE_ATTRIBUTES}, then
93
121
* {@code OTEL_SERVICE_NAME} takes precedence.
94
- * @return resource attributes
122
+ * @return an {@link OpenTelemetryResourceAttributes}
95
123
*/
96
- private Map <String , String > getResourceAttributesFromEnv () {
97
- Map <String , String > attributes = new LinkedHashMap <>();
98
- for (String attribute : StringUtils .tokenizeToStringArray (getEnv ("OTEL_RESOURCE_ATTRIBUTES" ), "," )) {
124
+ public static OpenTelemetryResourceAttributes fromEnv () {
125
+ return fromEnv (System ::getenv );
126
+ }
127
+
128
+ static OpenTelemetryResourceAttributes fromEnv (Function <String , String > getEnv ) {
129
+ OpenTelemetryResourceAttributes attributes = new OpenTelemetryResourceAttributes ();
130
+ for (String attribute : StringUtils .tokenizeToStringArray (getEnv .apply ("OTEL_RESOURCE_ATTRIBUTES" ), "," )) {
99
131
int index = attribute .indexOf ('=' );
100
132
if (index > 0 ) {
101
133
String key = attribute .substring (0 , index );
102
134
String value = attribute .substring (index + 1 );
103
- attributes .put (key . trim () , decode (value . trim () ));
135
+ attributes .put (key , decode (value ));
104
136
}
105
137
}
106
- String otelServiceName = getEnv ("OTEL_SERVICE_NAME" );
138
+ String otelServiceName = getEnv . apply ("OTEL_SERVICE_NAME" );
107
139
if (otelServiceName != null ) {
108
140
attributes .put ("service.name" , otelServiceName );
109
141
}
110
142
return attributes ;
111
143
}
112
144
113
- private String getEnv (String name ) {
114
- return this .getEnv .apply (name );
145
+ private boolean contains (String name ) {
146
+ if (!StringUtils .hasText (name )) {
147
+ return false ;
148
+ }
149
+ return this .attributes .containsKey (name .trim ());
115
150
}
116
151
117
152
/**
@@ -122,7 +157,7 @@ private String getEnv(String name) {
122
157
* @param value value to decode
123
158
* @return the decoded string
124
159
*/
125
- public static String decode (String value ) {
160
+ private static String decode (String value ) {
126
161
if (value .indexOf ('%' ) < 0 ) {
127
162
return value ;
128
163
}
0 commit comments