@@ -30,6 +30,7 @@ pub use process::ProcessResourceDetector;
30
30
#[ cfg( feature = "metrics" ) ]
31
31
use opentelemetry_api:: attributes;
32
32
use opentelemetry_api:: { Key , KeyValue , Value } ;
33
+ use std:: borrow:: Cow ;
33
34
use std:: collections:: { btree_map, BTreeMap } ;
34
35
use std:: ops:: Deref ;
35
36
use std:: time:: Duration ;
@@ -38,6 +39,7 @@ use std::time::Duration;
38
39
#[ derive( Clone , Debug , PartialEq ) ]
39
40
pub struct Resource {
40
41
attrs : BTreeMap < Key , Value > ,
42
+ schema_url : Option < Cow < ' static , str > > ,
41
43
}
42
44
43
45
impl Default for Resource {
@@ -54,6 +56,7 @@ impl Resource {
54
56
pub fn empty ( ) -> Self {
55
57
Self {
56
58
attrs : Default :: default ( ) ,
59
+ schema_url : None ,
57
60
}
58
61
}
59
62
@@ -71,6 +74,24 @@ impl Resource {
71
74
resource
72
75
}
73
76
77
+ /// Create a new `Resource` from a key value pairs and [schema url].
78
+ ///
79
+ /// Values are de-duplicated by key, and the first key-value pair with a non-empty string value
80
+ /// will be retained.
81
+ ///
82
+ /// schema_url must be a valid URL using HTTP or HTTPS protocol.
83
+ ///
84
+ /// [schema url]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/schemas/overview.md#schema-url
85
+ pub fn from_schema_url < KV , S > ( kvs : KV , schema_url : S ) -> Self
86
+ where
87
+ KV : IntoIterator < Item = KeyValue > ,
88
+ S : Into < Cow < ' static , str > > ,
89
+ {
90
+ let mut resource = Self :: new ( kvs) ;
91
+ resource. schema_url = Some ( schema_url. into ( ) ) ;
92
+ resource
93
+ }
94
+
74
95
/// Create a new `Resource` from resource detectors.
75
96
///
76
97
/// timeout will be applied to each detector.
@@ -89,8 +110,19 @@ impl Resource {
89
110
90
111
/// Create a new `Resource` by combining two resources.
91
112
///
113
+ /// ### Key value pairs
92
114
/// Keys from the `other` resource have priority over keys from this resource, even if the
93
115
/// updated value is empty.
116
+ ///
117
+ /// ### [Schema url]
118
+ /// If both of the resource are not empty. Schema url is determined by the following rules, in order:
119
+ /// 1. If this resource has a schema url, it will be used.
120
+ /// 2. If this resource does not have a schema url, and the other resource has a schema url, it will be used.
121
+ /// 3. If both resources have a schema url and it's the same, it will be used.
122
+ /// 4. If both resources have a schema url and it's different, the schema url will be empty.
123
+ /// 5. If both resources do not have a schema url, the schema url will be empty.
124
+ ///
125
+ /// [Schema url]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/schemas/overview.md#schema-url
94
126
pub fn merge < T : Deref < Target = Self > > ( & self , other : T ) -> Self {
95
127
if self . attrs . is_empty ( ) {
96
128
return other. clone ( ) ;
@@ -109,9 +141,31 @@ impl Resource {
109
141
resource. attrs . insert ( k. clone ( ) , v. clone ( ) ) ;
110
142
}
111
143
144
+ if self . schema_url == other. schema_url {
145
+ resource. schema_url = self . schema_url . clone ( ) ;
146
+ } else if self . schema_url . is_none ( ) {
147
+ // if the other resource has schema url, use it.
148
+ if other. schema_url . is_some ( ) {
149
+ resource. schema_url = other. schema_url . clone ( ) ;
150
+ }
151
+ // else empty schema url.
152
+ } else {
153
+ // if self has schema url, use it.
154
+ if other. schema_url . is_none ( ) {
155
+ resource. schema_url = self . schema_url . clone ( ) ;
156
+ }
157
+ }
158
+
112
159
resource
113
160
}
114
161
162
+ /// Return the [schema url] of the resource. If the resource does not have a schema url, return `None`.
163
+ ///
164
+ /// [schema url]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/schemas/overview.md#schema-url
165
+ pub fn schema_url ( & self ) -> Option < & str > {
166
+ self . schema_url . as_ref ( ) . map ( |s| s. as_ref ( ) )
167
+ }
168
+
115
169
/// Returns the number of attributes for this resource
116
170
pub fn len ( & self ) -> usize {
117
171
self . attrs . len ( )
@@ -215,13 +269,14 @@ mod tests {
215
269
assert_eq ! (
216
270
Resource :: new( args_with_dupe_keys) ,
217
271
Resource {
218
- attrs: expected_attrs
272
+ attrs: expected_attrs,
273
+ schema_url: None ,
219
274
}
220
275
) ;
221
276
}
222
277
223
278
#[ test]
224
- fn merge_resource ( ) {
279
+ fn merge_resource_key_value_pairs ( ) {
225
280
let resource_a = Resource :: new ( vec ! [
226
281
KeyValue :: new( "a" , "" ) ,
227
282
KeyValue :: new( "b" , "b-value" ) ,
@@ -243,11 +298,47 @@ mod tests {
243
298
assert_eq ! (
244
299
resource_a. merge( & resource_b) ,
245
300
Resource {
246
- attrs: expected_attrs
301
+ attrs: expected_attrs,
302
+ schema_url: None ,
247
303
}
248
304
) ;
249
305
}
250
306
307
+ #[ test]
308
+ fn merge_resource_schema_url ( ) {
309
+ // if both resources contains key value pairs
310
+ let test_cases = vec ! [
311
+ ( Some ( "http://schema/a" ) , None , Some ( "http://schema/a" ) ) ,
312
+ ( Some ( "http://schema/a" ) , Some ( "http://schema/b" ) , None ) ,
313
+ ( None , Some ( "http://schema/b" ) , Some ( "http://schema/b" ) ) ,
314
+ (
315
+ Some ( "http://schema/a" ) ,
316
+ Some ( "http://schema/a" ) ,
317
+ Some ( "http://schema/a" ) ,
318
+ ) ,
319
+ ( None , None , None ) ,
320
+ ] ;
321
+
322
+ for ( schema_url, other_schema_url, expect_schema_url) in test_cases. into_iter ( ) {
323
+ let mut resource = Resource :: new ( vec ! [ KeyValue :: new( "key" , "" ) ] ) ;
324
+ resource. schema_url = schema_url. map ( Into :: into) ;
325
+
326
+ let mut other_resource = Resource :: new ( vec ! [ KeyValue :: new( "key" , "" ) ] ) ;
327
+ other_resource. schema_url = other_schema_url. map ( Into :: into) ;
328
+
329
+ assert_eq ! (
330
+ resource. merge( & other_resource) . schema_url,
331
+ expect_schema_url. map( Into :: into)
332
+ ) ;
333
+ }
334
+
335
+ // if only one resource contains key value pairs
336
+ let resource = Resource :: from_schema_url ( vec ! [ ] , "http://schema/a" ) ;
337
+ let other_resource = Resource :: new ( vec ! [ KeyValue :: new( "key" , "" ) ] ) ;
338
+
339
+ assert_eq ! ( resource. merge( & other_resource) . schema_url, None ) ;
340
+ }
341
+
251
342
#[ test]
252
343
fn detect_resource ( ) {
253
344
env:: set_var ( "OTEL_RESOURCE_ATTRIBUTES" , "key=value, k = v , a= x, a=z" ) ;
@@ -262,7 +353,7 @@ mod tests {
262
353
KeyValue :: new( "key" , "value" ) ,
263
354
KeyValue :: new( "k" , "v" ) ,
264
355
KeyValue :: new( "a" , "x" ) ,
265
- KeyValue :: new( "a" , "z" )
356
+ KeyValue :: new( "a" , "z" ) ,
266
357
] )
267
358
)
268
359
}
0 commit comments