@@ -2,10 +2,11 @@ use super::{Cas, SwapError};
2
2
use anyhow:: { Context , Result } ;
3
3
use spin_core:: { async_trait, wasmtime:: component:: Resource } ;
4
4
use spin_resource_table:: Table ;
5
+ use spin_telemetry:: OpenTelemetrySpanExt as _;
5
6
use spin_world:: v2:: key_value;
6
7
use spin_world:: wasi:: keyvalue as wasi_keyvalue;
7
8
use std:: { collections:: HashSet , sync:: Arc } ;
8
- use tracing:: { instrument, Level } ;
9
+ use tracing:: instrument;
9
10
10
11
const DEFAULT_STORE_TABLE_CAPACITY : u32 = 256 ;
11
12
@@ -69,7 +70,14 @@ impl KeyValueDispatch {
69
70
}
70
71
71
72
pub fn get_store < T : ' static > ( & self , store : Resource < T > ) -> anyhow:: Result < & Arc < dyn Store > > {
72
- self . stores . get ( store. rep ( ) ) . context ( "invalid store" )
73
+ let res = self . stores . get ( store. rep ( ) ) . context ( "invalid store" ) ;
74
+ if let Err ( err) = & res {
75
+ let current_span = tracing:: Span :: current ( ) ;
76
+ current_span. set_status ( spin_telemetry:: opentelemetry:: trace:: Status :: error (
77
+ err. to_string ( ) ,
78
+ ) ) ;
79
+ }
80
+ res
73
81
}
74
82
75
83
pub fn get_cas < T : ' static > ( & self , cas : Resource < T > ) -> Result < & Arc < dyn Cas > > {
@@ -106,7 +114,7 @@ impl KeyValueDispatch {
106
114
impl key_value:: Host for KeyValueDispatch { }
107
115
108
116
impl key_value:: HostStore for KeyValueDispatch {
109
- #[ instrument( name = "spin_key_value.open" , skip( self ) , err( level = Level :: INFO ) , fields( otel. kind = "client" , kv. backend=self . manager. summary( & name) . unwrap_or( "unknown" . to_string( ) ) ) ) ]
117
+ #[ instrument( name = "spin_key_value.open" , skip( self ) , err, fields( otel. kind = "client" , kv. backend=self . manager. summary( & name) . unwrap_or( "unknown" . to_string( ) ) ) ) ]
110
118
async fn open ( & mut self , name : String ) -> Result < Result < Resource < key_value:: Store > , Error > > {
111
119
Ok ( async {
112
120
if self . allowed_stores . contains ( & name) {
@@ -124,54 +132,54 @@ impl key_value::HostStore for KeyValueDispatch {
124
132
. await )
125
133
}
126
134
127
- #[ instrument( name = "spin_key_value.get" , skip( self , store, key) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
135
+ #[ instrument( name = "spin_key_value.get" , skip( self , store, key) , fields( otel. kind = "client" ) ) ]
128
136
async fn get (
129
137
& mut self ,
130
138
store : Resource < key_value:: Store > ,
131
139
key : String ,
132
140
) -> Result < Result < Option < Vec < u8 > > , Error > > {
133
141
let store = self . get_store ( store) ?;
134
- Ok ( store. get ( & key) . await )
142
+ Ok ( store. get ( & key) . await . map_err ( track_error_on_span ) )
135
143
}
136
144
137
- #[ instrument( name = "spin_key_value.set" , skip( self , store, key, value) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
145
+ #[ instrument( name = "spin_key_value.set" , skip( self , store, key, value) , fields( otel. kind = "client" ) ) ]
138
146
async fn set (
139
147
& mut self ,
140
148
store : Resource < key_value:: Store > ,
141
149
key : String ,
142
150
value : Vec < u8 > ,
143
151
) -> Result < Result < ( ) , Error > > {
144
152
let store = self . get_store ( store) ?;
145
- Ok ( store. set ( & key, & value) . await )
153
+ Ok ( store. set ( & key, & value) . await . map_err ( track_error_on_span ) )
146
154
}
147
155
148
- #[ instrument( name = "spin_key_value.delete" , skip( self , store, key) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
156
+ #[ instrument( name = "spin_key_value.delete" , skip( self , store, key) , fields( otel. kind = "client" ) ) ]
149
157
async fn delete (
150
158
& mut self ,
151
159
store : Resource < key_value:: Store > ,
152
160
key : String ,
153
161
) -> Result < Result < ( ) , Error > > {
154
162
let store = self . get_store ( store) ?;
155
- Ok ( store. delete ( & key) . await )
163
+ Ok ( store. delete ( & key) . await . map_err ( track_error_on_span ) )
156
164
}
157
165
158
- #[ instrument( name = "spin_key_value.exists" , skip( self , store, key) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
166
+ #[ instrument( name = "spin_key_value.exists" , skip( self , store, key) , fields( otel. kind = "client" ) ) ]
159
167
async fn exists (
160
168
& mut self ,
161
169
store : Resource < key_value:: Store > ,
162
170
key : String ,
163
171
) -> Result < Result < bool , Error > > {
164
172
let store = self . get_store ( store) ?;
165
- Ok ( store. exists ( & key) . await )
173
+ Ok ( store. exists ( & key) . await . map_err ( track_error_on_span ) )
166
174
}
167
175
168
- #[ instrument( name = "spin_key_value.get_keys" , skip( self , store) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
176
+ #[ instrument( name = "spin_key_value.get_keys" , skip( self , store) , fields( otel. kind = "client" ) ) ]
169
177
async fn get_keys (
170
178
& mut self ,
171
179
store : Resource < key_value:: Store > ,
172
180
) -> Result < Result < Vec < String > , Error > > {
173
181
let store = self . get_store ( store) ?;
174
- Ok ( store. get_keys ( ) . await )
182
+ Ok ( store. get_keys ( ) . await . map_err ( track_error_on_span ) )
175
183
}
176
184
177
185
async fn drop ( & mut self , store : Resource < key_value:: Store > ) -> Result < ( ) > {
@@ -180,8 +188,19 @@ impl key_value::HostStore for KeyValueDispatch {
180
188
}
181
189
}
182
190
191
+ /// Make sure that infrastructure related errors are tracked in the current span.
192
+ fn track_error_on_span ( err : Error ) -> Error {
193
+ if let Error :: Other ( _) | Error :: StoreTableFull = & err {
194
+ let current_span = tracing:: Span :: current ( ) ;
195
+ current_span. set_status ( spin_telemetry:: opentelemetry:: trace:: Status :: error (
196
+ err. to_string ( ) ,
197
+ ) ) ;
198
+ }
199
+ err
200
+ }
201
+
183
202
fn to_wasi_err ( e : Error ) -> wasi_keyvalue:: store:: Error {
184
- match e {
203
+ match track_error_on_span ( e ) {
185
204
Error :: AccessDenied => wasi_keyvalue:: store:: Error :: AccessDenied ,
186
205
Error :: NoSuchStore => wasi_keyvalue:: store:: Error :: NoSuchStore ,
187
206
Error :: StoreTableFull => wasi_keyvalue:: store:: Error :: Other ( "store table full" . to_string ( ) ) ,
@@ -190,6 +209,7 @@ fn to_wasi_err(e: Error) -> wasi_keyvalue::store::Error {
190
209
}
191
210
192
211
impl wasi_keyvalue:: store:: Host for KeyValueDispatch {
212
+ #[ instrument( name = "wasi_key_value.open" , skip( self , identifier) , fields( otel. kind = "client" ) ) ]
193
213
async fn open (
194
214
& mut self ,
195
215
identifier : String ,
@@ -217,6 +237,7 @@ impl wasi_keyvalue::store::Host for KeyValueDispatch {
217
237
218
238
use wasi_keyvalue:: store:: Bucket ;
219
239
impl wasi_keyvalue:: store:: HostBucket for KeyValueDispatch {
240
+ #[ instrument( name = "wasi_key_value.get" , skip( self , self_, key) , fields( otel. kind = "client" ) ) ]
220
241
async fn get (
221
242
& mut self ,
222
243
self_ : Resource < Bucket > ,
@@ -226,6 +247,7 @@ impl wasi_keyvalue::store::HostBucket for KeyValueDispatch {
226
247
store. get ( & key) . await . map_err ( to_wasi_err)
227
248
}
228
249
250
+ #[ instrument( name = "wasi_key_value.set" , skip( self , self_, key, value) , fields( otel. kind = "client" ) ) ]
229
251
async fn set (
230
252
& mut self ,
231
253
self_ : Resource < Bucket > ,
@@ -236,6 +258,7 @@ impl wasi_keyvalue::store::HostBucket for KeyValueDispatch {
236
258
store. set ( & key, & value) . await . map_err ( to_wasi_err)
237
259
}
238
260
261
+ #[ instrument( name = "wasi_key_value.delete" , skip( self , self_, key) , fields( otel. kind = "client" ) ) ]
239
262
async fn delete (
240
263
& mut self ,
241
264
self_ : Resource < Bucket > ,
@@ -245,6 +268,7 @@ impl wasi_keyvalue::store::HostBucket for KeyValueDispatch {
245
268
store. delete ( & key) . await . map_err ( to_wasi_err)
246
269
}
247
270
271
+ #[ instrument( name = "wasi_key_value.exists" , skip( self , self_, key) , fields( otel. kind = "client" ) ) ]
248
272
async fn exists (
249
273
& mut self ,
250
274
self_ : Resource < Bucket > ,
@@ -254,6 +278,7 @@ impl wasi_keyvalue::store::HostBucket for KeyValueDispatch {
254
278
store. exists ( & key) . await . map_err ( to_wasi_err)
255
279
}
256
280
281
+ #[ instrument( name = "wasi_key_value.list_keys" , skip( self , self_, cursor) , fields( otel. kind = "client" ) ) ]
257
282
async fn list_keys (
258
283
& mut self ,
259
284
self_ : Resource < Bucket > ,
@@ -278,7 +303,7 @@ impl wasi_keyvalue::store::HostBucket for KeyValueDispatch {
278
303
}
279
304
280
305
impl wasi_keyvalue:: batch:: Host for KeyValueDispatch {
281
- #[ instrument( name = "spin_key_value.get_many" , skip( self , bucket, keys) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
306
+ #[ instrument( name = "spin_key_value.get_many" , skip( self , bucket, keys) , fields( otel. kind = "client" ) ) ]
282
307
#[ allow( clippy:: type_complexity) ]
283
308
async fn get_many (
284
309
& mut self ,
@@ -292,7 +317,7 @@ impl wasi_keyvalue::batch::Host for KeyValueDispatch {
292
317
store. get_many ( keys) . await . map_err ( to_wasi_err)
293
318
}
294
319
295
- #[ instrument( name = "spin_key_value.set_many" , skip( self , bucket, key_values) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
320
+ #[ instrument( name = "spin_key_value.set_many" , skip( self , bucket, key_values) , fields( otel. kind = "client" ) ) ]
296
321
async fn set_many (
297
322
& mut self ,
298
323
bucket : Resource < wasi_keyvalue:: batch:: Bucket > ,
@@ -305,7 +330,7 @@ impl wasi_keyvalue::batch::Host for KeyValueDispatch {
305
330
store. set_many ( key_values) . await . map_err ( to_wasi_err)
306
331
}
307
332
308
- #[ instrument( name = "spin_key_value.delete_many" , skip( self , bucket, keys) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
333
+ #[ instrument( name = "spin_key_value.delete_many" , skip( self , bucket, keys) , fields( otel. kind = "client" ) ) ]
309
334
async fn delete_many (
310
335
& mut self ,
311
336
bucket : Resource < wasi_keyvalue:: batch:: Bucket > ,
@@ -320,6 +345,7 @@ impl wasi_keyvalue::batch::Host for KeyValueDispatch {
320
345
}
321
346
322
347
impl wasi_keyvalue:: atomics:: HostCas for KeyValueDispatch {
348
+ #[ instrument( name = "wasi_key_value_cas.new" , skip( self , bucket, key) , fields( otel. kind = "client" ) ) ]
323
349
async fn new (
324
350
& mut self ,
325
351
bucket : Resource < wasi_keyvalue:: atomics:: Bucket > ,
@@ -342,6 +368,7 @@ impl wasi_keyvalue::atomics::HostCas for KeyValueDispatch {
342
368
. map ( Resource :: new_own)
343
369
}
344
370
371
+ #[ instrument( name = "wasi_key_value_cas.current" , skip( self , cas) , fields( otel. kind = "client" ) ) ]
345
372
async fn current (
346
373
& mut self ,
347
374
cas : Resource < wasi_keyvalue:: atomics:: Cas > ,
@@ -366,7 +393,7 @@ impl wasi_keyvalue::atomics::Host for KeyValueDispatch {
366
393
Ok ( error)
367
394
}
368
395
369
- #[ instrument( name = "spin_key_value.increment" , skip( self , bucket, key, delta) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
396
+ #[ instrument( name = "spin_key_value.increment" , skip( self , bucket, key, delta) , fields( otel. kind = "client" ) ) ]
370
397
async fn increment (
371
398
& mut self ,
372
399
bucket : Resource < wasi_keyvalue:: atomics:: Bucket > ,
@@ -377,7 +404,7 @@ impl wasi_keyvalue::atomics::Host for KeyValueDispatch {
377
404
store. increment ( key, delta) . await . map_err ( to_wasi_err)
378
405
}
379
406
380
- #[ instrument( name = "spin_key_value.swap" , skip( self , cas_res, value) , err ( level = Level :: INFO ) , fields( otel. kind = "client" ) ) ]
407
+ #[ instrument( name = "spin_key_value.swap" , skip( self , cas_res, value) , fields( otel. kind = "client" ) ) ]
381
408
async fn swap (
382
409
& mut self ,
383
410
cas_res : Resource < atomics:: Cas > ,
@@ -424,8 +451,8 @@ use spin_world::v1::key_value::Error as LegacyError;
424
451
use spin_world:: wasi:: keyvalue:: atomics;
425
452
use spin_world:: wasi:: keyvalue:: atomics:: { CasError , HostCas } ;
426
453
427
- fn to_legacy_error ( value : key_value :: Error ) -> LegacyError {
428
- match value {
454
+ fn to_legacy_error ( err : Error ) -> LegacyError {
455
+ match err {
429
456
Error :: StoreTableFull => LegacyError :: StoreTableFull ,
430
457
Error :: NoSuchStore => LegacyError :: NoSuchStore ,
431
458
Error :: AccessDenied => LegacyError :: AccessDenied ,
0 commit comments