Skip to content

Commit 5ebaf8b

Browse files
committed
Add rfc0046_server_request_metrics.md
1 parent f7ec13c commit 5ebaf8b

1 file changed

Lines changed: 214 additions & 0 deletions

File tree

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<!-- Give your RFC a descriptive name saying what it would accomplish or what feature it defines -->
2+
RFC: Server request metrics
3+
=============
4+
5+
<!-- RFCs start with the "RFC" status and are then either "Implemented" or "Rejected". -->
6+
> Status: RFC
7+
>
8+
> Applies to: server
9+
10+
<!-- A great RFC will include a list of changes at the bottom so that the implementor can be sure they haven't missed anything -->
11+
For a summarized list of proposed changes, see the [Changes Checklist](#changes-checklist) section.
12+
13+
<!-- Insert a short paragraph explaining, at a high level, what this RFC is for -->
14+
This RFC defines the spec for metrique metrics integration for smithy generated servers using middleware. It will enable users to have an out-of-the-box experience with a set of default request and response metrics, as well as exposing the ability to define and integrate their own additional metrics using metrique in a turnkey fashion. The design ensures to maintain support for folding metrics from all layers of middleware, from custom outer-level middleware to model-level middleware and operation handlers. It also ensures that configuration options are available and extensible.
15+
16+
<!-- The "Terminology" section is optional but is really useful for defining the technical terms you're using in the RFC -->
17+
Terminology
18+
-----------
19+
20+
- **metrique**: https://crates.io/crates/metrique
21+
22+
<!-- Explain how users will use this new feature and, if necessary, how this compares to the current user experience -->
23+
The user experience if this RFC is implemented
24+
----------------------------------------------
25+
26+
### In the current version of the SDK
27+
28+
Users need to define their own metrics middleware. This involves creating a metrics HTTP plugin to support operation-level metrics, as well as a metrics layer if there is a need to fold outer and route level metrics.
29+
30+
To avoid bloating this RFC, see below for an example of the code the user would need to write to integrate metrics into their smithy-rs service with compatibility with all middleware layers. This is with a barebones example with no imports, a few placeholder metrics, and no further logic for injection of customizations such as initialization settings.
31+
32+
https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=9c40172125518215d2b4482526d1e306
33+
34+
### Once this RFC is implemented
35+
36+
Users will be able to add default metrics to their service like this:
37+
38+
```rust
39+
fn main() {
40+
let metrics_layer = MetricsLayer::new();
41+
42+
let app = PokemonService::builder(config)
43+
.get_pokemon_species(get_pokemon_species)
44+
.build()
45+
.expect("failed to build an instance of PokemonService");
46+
47+
let service = metrics_layer.layer(app);
48+
}
49+
```
50+
51+
For configuration of things like initialization, opting out, overriding how certain default metrics are set, etc, a builder will be provided:
52+
53+
```rust
54+
fn main() {
55+
let config = MetricsLayerConfig::builder()
56+
.without_start_metric()
57+
.build();
58+
let metrics_layer = MetricsLayer::builder(config)
59+
.init_metrics(|| { DefaultMetrics::default().append_on_drop(custom_sink) })
60+
.build();
61+
}
62+
```
63+
64+
To define additional metrics or add to the request extensions on top of the defaults:
65+
66+
```rust
67+
fn main() {
68+
let metrics_layer: MetricsLayer<MyMetrics> = MetricsLayer::builder(MetricsLayerConfig::default())
69+
.set_request_metrics(set_request_metrics)
70+
.build();
71+
}
72+
73+
#[smithy_metrics]
74+
#[metrics]
75+
struct MyMetrics {
76+
#[smithy_metrics(extension)]
77+
#[metrics(flatten)]
78+
operation_metrics: OperationMetrics,
79+
custom_metric: Option<String>
80+
}
81+
82+
#[metrics]
83+
struct OperationMetrics {
84+
get_pokemon_species_metrics: Option<String>
85+
}
86+
87+
fn set_request_metrics(req: Request<Body>, metrics: MyMetricsGuard) {
88+
req.custom_metric = ...;
89+
}
90+
```
91+
92+
<!-- Explain the implementation of this new feature -->
93+
How to actually implement this RFC
94+
----------------------------------
95+
96+
There will be two new rust-runtime crates `aws-smithy-http-server-metrics` and `aws-smithy-http-server-metrics-macro`, with types re-exported in the generated server.
97+
98+
### `MetricsLayer` struct
99+
100+
The focal struct that users can add to their service as a tower `Layer` for metrics, containing `new` and `builder` methods to get a `MetricsLayer` with the default configuration or a `MetricsLayerBuilder`, respectively. Because the `MetricsLayer` is specific to the struct provided in the type parameter, the `MetricsLayerBuilder` will be a product of the `#[smithy_metrics]` proc macro expansion, responsible for providing methods to customize things like the metrics initialization and setting custom request/response metrics. Contrarily, the `MetricsLayerConfig` along with its builder will be explicitly defined for general configuration not bound to any specific type parameter, such as enabling/disabling default metrics.
101+
102+
Contains a generic type parameter bound by a marker trait with a default of `DefaultMetrics`, which will be a type defined in the library that uses the `#[smithy_metrics]` expansion.
103+
104+
This will allow users the following construction experiences:
105+
106+
- `MetricsLayer::new()` for the default metrics and extensions
107+
108+
- `MetricsLayer::<CustomMetrics>::new()` for the default metrics with potential renaming or additional extensions from attribute proc macros
109+
110+
- `MetricsLayer::builder(config)` for a builder to configure things like metrics initialization, how default metrics are set from the request/response objects, etc
111+
112+
- `MetricsLayer::<CustomMetrics>::builder` for a builder with the ability to set their custom-defined metrics as well
113+
114+
### `MetricsLayerConfig` struct
115+
116+
Along with `MetricsLayerConfigBuilder`, structs for the general configuration of constructing a `MetricsLayer` not bound to any specific metrics type. This will include things like enabling/disabling default metrics.
117+
118+
### `MetricsLayerService` struct
119+
120+
A tower service for metrics that will contain the core logic for setting the metrics from the request/response objects.
121+
122+
### `MetricsPlugin` struct
123+
124+
An HTTP plugin metrics struct to allow for default operation-level metrics.
125+
126+
The plugin system will mitigate the need to define a layer per operation for metrics like the operation name. A Metrics Plugin will be implemented to set the operation and service metrics from the `Operation` and `Service` type parameters. It will be applied to the service in the operation setters, which will require a change in the server codegen.
127+
128+
### `MetricsPluginService` struct
129+
130+
A tower service for metrics that will contain the logic for setting the default operation-specific metrics.
131+
132+
### `DefaultMetrics` struct
133+
134+
A struct for default metrics. This will use the `#[smithy_metrics]` and be used as the default type parameter for `MetricsLayer`.
135+
136+
### `DefaultRequestMetrics` struct
137+
138+
A struct that will contain fields for the default request metrics, a field for which will be added to a struct containing the `#[smithy_metrics]` annotation.
139+
140+
### `DefaultResponseMetrics` struct
141+
142+
A struct that will contain fields for the default response metrics, a field for which will be added to a struct containing the `#[smithy_metrics]` annotation.
143+
144+
### `#[smithy_metrics]` attribute proc macro
145+
146+
A proc macro that can be placed on a metrique metrics struct for purposes such as the addition of default request/response metrics fields, the implementation of a marker trait, and the expansion of a `MetricsLayerBuilder` with concrete type being the annotated struct.
147+
148+
This will also come with `#[smithy_metrics(rename(x = "y"))]` to rename default fields and `#[smithy_metrics(extension)]` to mark struct fields for insertion to the request extensions to be used in custom middleware or operation handlers.
149+
150+
<!-- Include a checklist of all the things that need to happen for this RFC's implementation to be considered complete -->
151+
Changes checklist
152+
-----------------
153+
154+
- [] Create `rust-runtime` crates `aws-smithy-http-server-metrics` and `aws-smithy-http-server-metrics-macro`
155+
156+
- [] Implement struct `MetricsLayer<T>`
157+
158+
- [] Implement `new` and `builder` methods
159+
160+
- [] Implement struct `MetricsLayerBuilder<T>`
161+
162+
- [] Implement method for custom metrics initialization
163+
164+
- [] Implement methods for setting custom metrics from request and response objects
165+
166+
- [] Implement struct `MetricsLayerConfig`
167+
168+
- [] Implement default with out-of-the-box metrics enabled
169+
170+
- [] Implement struct `MetricsLayerConfigBuilder`
171+
172+
- [] Implement methods to opt out of all or individual default request/response metrics
173+
174+
- [] Implement struct `MetricsPlugin`
175+
176+
- [] Implement HTTPMarker
177+
178+
- [] Implement `Plugin` to map to `MetricsService`
179+
180+
- [] Implement struct `MetricsService` to contain the tower service logic for invoking the passed functions for setting request/response metrics and adding to the request extensions
181+
182+
- [] Implement `Clone`
183+
184+
- [] Implement tower `Service` to contain logic for:
185+
186+
- [] Initializing the metrics using the initialization function if passed, `Default` and a global entry sink, or emitting a compiler error saying one of these two must be satisfied
187+
188+
- [] Calling the request/response metrics handlers
189+
190+
- [] Implement struct `DefaultMetrics` to be a unit struct with the `#[smithy_metrics]` attribute to expand a builder with just the default metrics
191+
192+
- [] Implement struct `DefaultRequestMetrics` to be the type of a field that gets added to a `#[smithy_metrics]`-annotated struct to add the default request metrics to
193+
194+
- [] Implement struct `DefaultResponseMetrics`to be the type of a field that gets added to a `#[smithy_metrics]`-annotated struct to add the default response metrics to
195+
196+
- [] Implement proc macro attribute `#[smithy_metrics]`
197+
198+
- [] Implement expansion for implementing a marker struct
199+
200+
- [] Implement expansion to wrap the types of fields annotated with `#[smithy_metrics(extension)]` with `Slotguard` if the user hasn't explicitly done so, or show an error message telling them to
201+
202+
- [] Implement expansion to define and implement a `MetricsLayerBuilder` with the concrete type parameter being the annotated struct with methods to:
203+
204+
- [] Pass a custom metrics initialization function
205+
206+
- [] Pass custom request/response setter functions
207+
208+
- [] Create proc macro attribute `#[smithy_metrics(extension)]` on fields of a metrics struct to give users the ability to annotate the fields they want to be accessible from the request extensions down the line, such as in custom middleware or operation handlers
209+
210+
- [] Create proc macro attribute `#[smithy_metrics(rename(default_metric_name = "custom_name"))]` to give users the ability to rename default metrics
211+
212+
- [] Create a type alias for extension type containing the slotguards of the types from the annotated fields of the metrics struct, e.g. `type OperationMetricsExtension = Extension<SlotGuard<OperationMetrics>>`
213+
214+
- [] In the server codegen, apply the metrics plugin in all operation setters

0 commit comments

Comments
 (0)