1
1
// Copyright 2024 Oxide Computer Company
2
2
//! Opentelemetry tracing support
3
3
//!
4
+ // XXX Not sure if we want to just mimic reqwest-tracing
5
+ // or not...
4
6
//! Fields that we want to produce to provide comparable
5
- //! functionality to reqwest-tracing[1 ]:
7
+ //! functionality to [ reqwest-tracing]:
6
8
//!
7
- //! - http.request.method
8
- //! - url.scheme
9
- //! - server.address
10
- //! - server.port
11
- //! - otel.kind
12
- //! - otel.name
13
- //! - otel.status_code
14
- //! - user_agent.original
15
- //! - http.response.status_code
16
- //! - error.message
17
- //! - error.cause_chain
9
+ //! - [ ] error.cause_chain
10
+ //! - [ ] error.message
11
+ //! - [x] http.request.method
12
+ //! - [x] http.response.status_code
13
+ //! - [x] otel.kind (showing up as span.kind?)
14
+ //! - [x] otel.name
15
+ //! - [ ] otel.status_code (needs investigation)
16
+ //! - [x] server.address
17
+ //! - [x] server.port
18
+ //! - [ ] url.scheme
19
+ //! - [x] user_agent.original
18
20
//!
19
- //! [1] <https://docs.rs/reqwest-tracing/0.5.4/reqwest_tracing/macro.reqwest_otel_span.html>
21
+ //! Where possible we use the [opentelemetry-semantic-conventions] crate for naming.
22
+ //!
23
+ //! [reqwest-tracing]: https://docs.rs/reqwest-tracing/0.5.4/reqwest_tracing/macro.reqwest_otel_span.html
24
+ //! [opentelemetry-semantic-conventions]:
25
+ //! <https://docs.rs/opentelemetry-semantic-conventions/latest/opentelemetry_semantic_conventions/trace/index.html>
20
26
21
27
use opentelemetry:: {
22
28
global, trace:: Span , trace:: Tracer , trace:: TracerProvider ,
23
29
} ;
24
30
use opentelemetry_http:: HeaderExtractor ;
25
31
use opentelemetry_semantic_conventions:: trace;
26
32
27
- // - url.scheme
28
- // - server.address
29
- // - server.port
30
- // - user_agent.original
31
33
#[ derive( Debug , Clone , serde:: Serialize ) ]
32
34
pub ( crate ) struct RequestInfo {
33
35
pub id : String ,
@@ -39,16 +41,14 @@ pub(crate) struct RequestInfo {
39
41
pub user_agent : String ,
40
42
}
41
43
42
- // - http.response.status_code
43
- // - error.message
44
- // - error.cause_chain
45
- // - otel.status_code
46
44
pub ( crate ) struct ResponseInfo < ' a > {
47
45
pub status_code : u16 ,
48
46
pub message : String ,
49
47
pub error : Option < & ' a crate :: handler:: HandlerError > ,
50
48
}
51
49
50
+ /// Generate an opentelementry Context based on the headers
51
+ /// in the incoming hyper request.
52
52
fn extract_context_from_request (
53
53
request : & hyper:: Request < hyper:: body:: Incoming > ,
54
54
) -> opentelemetry:: Context {
@@ -57,6 +57,7 @@ fn extract_context_from_request(
57
57
} )
58
58
}
59
59
60
+ /// Create an opentelemetry Span to represent the handling of the incoming request.
60
61
pub fn create_request_span (
61
62
request : & hyper:: Request < hyper:: body:: Incoming > ,
62
63
) -> opentelemetry:: global:: BoxedSpan {
@@ -74,8 +75,14 @@ pub fn create_request_span(
74
75
. start_with_context ( & tracer, & parent_cx)
75
76
}
76
77
78
+ /// This trait contains all functionality needed for dropshot to annotate
79
+ /// opentelemetry spans with information about a request and the corresponding response.
77
80
pub trait TraceDropshot {
81
+ /// Attach attributes to the span based on the provided
82
+ /// RequestInfo
78
83
fn trace_request ( & mut self , request : RequestInfo ) ;
84
+ /// Attach the information from the ResponseInfo to the span
85
+ /// including marking the span as an error if an error occurred.
79
86
fn trace_response ( & mut self , response : ResponseInfo ) ;
80
87
}
81
88
@@ -84,30 +91,28 @@ impl TraceDropshot for opentelemetry::global::BoxedSpan {
84
91
self . set_attributes ( vec ! [
85
92
// Rename to dropshot.id ????
86
93
opentelemetry:: KeyValue :: new( "http.id" . to_string( ) , request. id) ,
94
+ opentelemetry:: KeyValue :: new( trace:: URL_PATH , request. path) ,
87
95
opentelemetry:: KeyValue :: new(
88
- "http.method" . to_string ( ) ,
96
+ trace :: HTTP_REQUEST_METHOD ,
89
97
request. method,
90
98
) ,
91
- opentelemetry:: KeyValue :: new( "http.path" . to_string( ) , request. path) ,
92
99
opentelemetry:: KeyValue :: new(
93
- "server.address" . to_string ( ) ,
100
+ trace :: SERVER_ADDRESS ,
94
101
request. local_addr. ip( ) . to_string( ) ,
95
102
) ,
96
103
opentelemetry:: KeyValue :: new(
97
- "server.port" . to_string ( ) ,
104
+ trace :: SERVER_PORT ,
98
105
request. local_addr. port( ) . to_string( ) ,
99
106
) ,
100
107
opentelemetry:: KeyValue :: new(
101
- "user_agent.original" . to_string ( ) ,
108
+ trace :: USER_AGENT_ORIGINAL ,
102
109
request. user_agent,
103
110
) ,
104
111
] ) ;
105
112
}
106
113
107
- // - [x] http.response.status_code
108
- // - [x] error.message
109
- // - [ ] error.cause_chain
110
- // - [ ] otel.status_code
114
+ /// TODO: Do we want to differentiate between 4xx vs 5xx errors
115
+ /// and not mark 4xx spans as error spans?
111
116
fn trace_response ( & mut self , response : ResponseInfo ) {
112
117
self . set_attributes ( vec ! [
113
118
opentelemetry:: KeyValue :: new(
0 commit comments