Skip to content

Commit 9d3a3f0

Browse files
Laurens-Wtimtebeekgithub-actions[bot]
authored
Migrate WebMvcTags to ServerRequestObservationConvention (#586)
* Initial test case * WIP * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * WIP * Apply suggestions from code review * WIP * WIP * WIP * WIP * WIP * Suggestions * WIP * Working setup * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Format and apply suggestion * More progress * Naming and imports * Adopt classpathFromResources & add tomcat-embedded-core * Fix most warnings and issues * Fix type issues and warnings * More edge cases * More edge cases, extra test coverage * Remove jetbrains notnull * Reduce document example test case and add additional test cases for edge cases * Apply review feedback * Apply formatter to tests * Reduce imports and classpath Explicitly add micrometer-observation jar * Use method matcher * By default use high cardinality keys as low might cause issues with ingesting metrics/traces * Consistently use the same version suffix for classpath resources * Demonstrate failure on any empty method * Fix class cast issue --------- Co-authored-by: Tim te Beek <tim@moderne.io> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 0d5e2d8 commit 9d3a3f0

File tree

8 files changed

+618
-0
lines changed

8 files changed

+618
-0
lines changed

build.gradle.kts

+7
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ recipeDependencies {
114114

115115
parserClasspath("org.apache.httpcomponents.core5:httpcore5:5.1.+")
116116
parserClasspath("org.apache.httpcomponents.client5:httpclient5:5.1.+")
117+
118+
parserClasspath("jakarta.servlet:jakarta.servlet-api:6.1.+")
119+
parserClasspath("io.micrometer:micrometer-commons:1.11.+")
120+
parserClasspath("io.micrometer:micrometer-core:1.11.+")
121+
parserClasspath("io.micrometer:micrometer-observation:1.11.+")
117122
}
118123

119124
val rewriteVersion = rewriteRecipe.rewriteVersion.get()
@@ -237,6 +242,7 @@ dependencies {
237242
"testWithSpringBoot_2_7RuntimeOnly"("jakarta.xml.bind:jakarta.xml.bind-api:2.3.3")
238243

239244
"testWithSpringBoot_3_0RuntimeOnly"("org.springframework.boot:spring-boot-starter:3.0.+")
245+
"testWithSpringBoot_3_0RuntimeOnly"("org.springframework.boot:spring-boot-starter-actuator:3.0.+")
240246
"testWithSpringBoot_3_0RuntimeOnly"("org.springframework.boot:spring-boot-starter-test:3.0.+")
241247
"testWithSpringBoot_3_0RuntimeOnly"("org.springframework:spring-context:6.0.+")
242248
"testWithSpringBoot_3_0RuntimeOnly"("org.springframework:spring-web:6.0.+")
@@ -249,6 +255,7 @@ dependencies {
249255
"testWithSpringBoot_3_0RuntimeOnly"("org.springframework.security:spring-security-config:6.0.+")
250256
"testWithSpringBoot_3_0RuntimeOnly"("org.springframework.security:spring-security-web:6.0.+")
251257
"testWithSpringBoot_3_0RuntimeOnly"("org.springframework.security:spring-security-ldap:6.0.+")
258+
"testWithSpringBoot_3_0RuntimeOnly"("jakarta.servlet:jakarta.servlet-api:6.1.+")
252259

253260
"testWithSpringBoot_3_2RuntimeOnly"("org.springframework.boot:spring-boot-starter:3.2.+")
254261
"testWithSpringBoot_3_2RuntimeOnly"("org.springframework.boot:spring-boot-starter-test:3.2.+")

src/main/java/org/openrewrite/java/spring/boot3/MigrateWebMvcTagsToObservationConvention.java

+296
Large diffs are not rendered by default.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

src/main/resources/META-INF/rewrite/spring-boot-30.yml

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ recipeList:
6969
- org.openrewrite.java.spring.boot3.MigrateThymeleafDependencies
7070
- org.openrewrite.java.spring.boot3.MigrateDropWizardDependencies
7171
- org.openrewrite.java.spring.boot3.RemoveSolrAutoConfigurationExclude
72+
- org.openrewrite.java.spring.boot3.MigrateWebMvcTagsToObservationConvention
7273
- org.openrewrite.java.spring.batch.SpringBatch4To5Migration
7374
- org.openrewrite.java.spring.framework.UpgradeSpringFramework_6_0
7475
- org.openrewrite.java.spring.kafka.UpgradeSpringKafka_3_0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.spring.boot3;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.DocumentExample;
20+
import org.openrewrite.java.JavaParser;
21+
import org.openrewrite.test.RecipeSpec;
22+
import org.openrewrite.test.RewriteTest;
23+
24+
import static org.openrewrite.java.Assertions.java;
25+
26+
class MigrateWebMvcTagsToObservationConventionTest implements RewriteTest {
27+
28+
@Override
29+
public void defaults(RecipeSpec spec) {
30+
spec.recipe(new MigrateWebMvcTagsToObservationConvention())
31+
.parser(JavaParser.fromJavaVersion().classpath(
32+
"micrometer-core",
33+
"spring-boot",
34+
"spring-context",
35+
"spring-beans",
36+
"spring-web",
37+
"jakarta.servlet-api"));
38+
}
39+
40+
@DocumentExample
41+
@Test
42+
void shouldMigrateWebMvcTagsProviderToDefaultServerRequestObservationConvention() {
43+
//language=java
44+
rewriteRun(
45+
java(
46+
"""
47+
import io.micrometer.core.instrument.Tag;
48+
import io.micrometer.core.instrument.Tags;
49+
import jakarta.servlet.http.HttpServletRequest;
50+
import jakarta.servlet.http.HttpServletResponse;
51+
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTags;
52+
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
53+
54+
class CustomWebMvcTagsProvider implements WebMvcTagsProvider {
55+
56+
@Override
57+
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
58+
Tags tags = Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.status(response), WebMvcTags.outcome(response));
59+
60+
String customHeader = request.getHeader("X-Custom-Header");
61+
if (customHeader != null) {
62+
tags = tags.and("custom.header", customHeader);
63+
}
64+
return tags;
65+
}
66+
}
67+
""",
68+
"""
69+
import io.micrometer.common.KeyValue;
70+
import io.micrometer.common.KeyValues;
71+
import jakarta.servlet.http.HttpServletRequest;
72+
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
73+
import org.springframework.http.server.observation.ServerRequestObservationContext;
74+
75+
class CustomWebMvcTagsProvider extends DefaultServerRequestObservationConvention {
76+
77+
@Override
78+
public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
79+
HttpServletRequest request = context.getCarrier();
80+
KeyValues values = super.getHighCardinalityKeyValues(context);
81+
82+
String customHeader = request.getHeader("X-Custom-Header");
83+
if (customHeader != null) {
84+
values.and(KeyValue.of("custom.header", customHeader));
85+
}
86+
return values;
87+
}
88+
}
89+
"""
90+
)
91+
);
92+
}
93+
94+
@Test
95+
void shouldMigrateTags_Of() {
96+
//language=java
97+
rewriteRun(
98+
java(
99+
"""
100+
import io.micrometer.core.instrument.Tag;
101+
import io.micrometer.core.instrument.Tags;
102+
import jakarta.servlet.http.HttpServletRequest;
103+
import jakarta.servlet.http.HttpServletResponse;
104+
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTags;
105+
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
106+
107+
class CustomWebMvcTagsProvider implements WebMvcTagsProvider {
108+
109+
Tags staticTags = Tags.of("a", "b", "c", "d");
110+
Tag staticTag = Tag.of("a", "b");
111+
112+
@Override
113+
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
114+
Tags tags = Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.status(response), WebMvcTags.outcome(response));
115+
tags = Tags.of("a", "b");
116+
tags = Tags.of("a", "b", "c", "d");
117+
tags = Tags.of(Tag.of("a", "b"), staticTag);
118+
tags = Tags.of(staticTags);
119+
return tags;
120+
}
121+
}
122+
""",
123+
"""
124+
import io.micrometer.common.KeyValue;
125+
import io.micrometer.common.KeyValues;
126+
import io.micrometer.core.instrument.Tag;
127+
import io.micrometer.core.instrument.Tags;
128+
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
129+
import org.springframework.http.server.observation.ServerRequestObservationContext;
130+
131+
class CustomWebMvcTagsProvider extends DefaultServerRequestObservationConvention {
132+
133+
Tags staticTags = Tags.of("a", "b", "c", "d");
134+
Tag staticTag = Tag.of("a", "b");
135+
136+
@Override
137+
public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
138+
KeyValues values = super.getHighCardinalityKeyValues(context);
139+
values.and(KeyValue.of("a", "b"));
140+
values.and(KeyValue.of("a", "b"), KeyValue.of("c", "d"));
141+
values.and(KeyValue.of("a", "b"), KeyValue.of(staticTag.getKey(), staticTag.getValue()));
142+
for (Tag tag : staticTags) {
143+
values.and(KeyValue.of(tag.getKey(), tag.getValue()));
144+
}
145+
return values;
146+
}
147+
}
148+
"""
149+
)
150+
);
151+
}
152+
153+
@Test
154+
void shouldMigrateTags_And() {
155+
//language=java
156+
rewriteRun(
157+
java(
158+
"""
159+
import io.micrometer.core.instrument.Tag;
160+
import io.micrometer.core.instrument.Tags;
161+
import jakarta.servlet.http.HttpServletRequest;
162+
import jakarta.servlet.http.HttpServletResponse;
163+
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTags;
164+
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
165+
166+
class CustomWebMvcTagsProvider implements WebMvcTagsProvider {
167+
168+
Tags staticTags = Tags.of("a", "b", "c", "d");
169+
Tag staticTag = Tag.of("a", "b");
170+
171+
@Override
172+
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
173+
Tags tags = Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.status(response), WebMvcTags.outcome(response));
174+
175+
String customHeader = request.getHeader("X-Custom-Header");
176+
if (customHeader != null) {
177+
tags = tags.and("custom.header", customHeader);
178+
}
179+
if (response.getStatus() >= 400) {
180+
tags = tags.and("error", "true");
181+
}
182+
tags = tags.and("a", "b", "c", "d");
183+
tags = Tags.and(Tag.of("a", "b"), staticTag);
184+
tags = tags.and(staticTags);
185+
return tags;
186+
}
187+
}
188+
""",
189+
"""
190+
import io.micrometer.common.KeyValue;
191+
import io.micrometer.common.KeyValues;
192+
import io.micrometer.core.instrument.Tag;
193+
import io.micrometer.core.instrument.Tags;
194+
import jakarta.servlet.http.HttpServletRequest;
195+
import jakarta.servlet.http.HttpServletResponse;
196+
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
197+
import org.springframework.http.server.observation.ServerRequestObservationContext;
198+
199+
class CustomWebMvcTagsProvider extends DefaultServerRequestObservationConvention {
200+
201+
Tags staticTags = Tags.of("a", "b", "c", "d");
202+
Tag staticTag = Tag.of("a", "b");
203+
204+
@Override
205+
public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
206+
HttpServletRequest request = context.getCarrier();
207+
HttpServletResponse response = context.getResponse();
208+
KeyValues values = super.getHighCardinalityKeyValues(context);
209+
210+
String customHeader = request.getHeader("X-Custom-Header");
211+
if (customHeader != null) {
212+
values.and(KeyValue.of("custom.header", customHeader));
213+
}
214+
if (response.getStatus() >= 400) {
215+
values.and(KeyValue.of("error", "true"));
216+
}
217+
values.and(KeyValue.of("a", "b"), KeyValue.of("c", "d"));
218+
values.and(KeyValue.of("a", "b"), KeyValue.of(staticTag.getKey(), staticTag.getValue()));
219+
for (Tag tag : staticTags) {
220+
values.and(KeyValue.of(tag.getKey(), tag.getValue()));
221+
}
222+
return values;
223+
}
224+
}
225+
"""
226+
)
227+
);
228+
}
229+
230+
@Test
231+
void shouldMigrateReturnTags_Of() {
232+
//language=java
233+
rewriteRun(
234+
java(
235+
"""
236+
import io.micrometer.core.instrument.Tag;
237+
import io.micrometer.core.instrument.Tags;
238+
import jakarta.servlet.http.HttpServletRequest;
239+
import jakarta.servlet.http.HttpServletResponse;
240+
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
241+
242+
class CustomWebMvcTagsProvider implements WebMvcTagsProvider {
243+
244+
@Override
245+
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
246+
return Tags.of(Tag.of("a", "b"));
247+
}
248+
}
249+
""",
250+
"""
251+
import io.micrometer.common.KeyValue;
252+
import io.micrometer.common.KeyValues;
253+
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
254+
import org.springframework.http.server.observation.ServerRequestObservationContext;
255+
256+
class CustomWebMvcTagsProvider extends DefaultServerRequestObservationConvention {
257+
258+
@Override
259+
public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
260+
KeyValues values = super.getHighCardinalityKeyValues(context);
261+
values.and(KeyValue.of("a", "b"));
262+
return values;
263+
}
264+
}
265+
"""
266+
)
267+
);
268+
}
269+
270+
@Test
271+
void shouldNotFailOnEmptyMethod() {
272+
//language=java
273+
rewriteRun(
274+
java(
275+
"""
276+
import io.micrometer.core.instrument.Tag;
277+
import jakarta.servlet.http.HttpServletRequest;
278+
import jakarta.servlet.http.HttpServletResponse;
279+
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
280+
281+
import java.util.Collections;
282+
283+
class CustomWebMvcTagsProvider implements WebMvcTagsProvider {
284+
285+
@Override
286+
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
287+
return Collections.emptyList();
288+
}
289+
290+
void shouldNotFailOnEmptyMethod() {}
291+
}
292+
""",
293+
"""
294+
import io.micrometer.common.KeyValues;
295+
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
296+
import org.springframework.http.server.observation.ServerRequestObservationContext;
297+
298+
import java.util.Collections;
299+
300+
class CustomWebMvcTagsProvider extends DefaultServerRequestObservationConvention {
301+
302+
@Override
303+
public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
304+
KeyValues values = super.getHighCardinalityKeyValues(context);
305+
return values;
306+
}
307+
308+
void shouldNotFailOnEmptyMethod() {}
309+
}
310+
"""
311+
)
312+
);
313+
}
314+
}

0 commit comments

Comments
 (0)