Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
93b330a
feat(opentelemetry): add opentelemetry support
sandy2008 Jan 6, 2026
6cc6dac
Merge branch 'openrewrite:main' into main
sandy2008 Jan 6, 2026
223c623
Merge branch 'main' into main
timtebeek Jan 6, 2026
f85a8dc
parserClasspath entries were not used at all
timtebeek Jan 6, 2026
fe4f1f5
feat(opentelemetry): simplify migration recipes for Sleuth and OpenTr…
sandy2008 Jan 6, 2026
064e24d
Merge branch 'main' into main
timtebeek Jan 7, 2026
91c93ce
Regenerate recipes.csv
timtebeek Jan 7, 2026
134b836
chore(): Replaces Java migration recipes with YAML equivalents
sandy2008 Jan 8, 2026
4c11784
Inline two recipes and delete package-info.java
timtebeek Jan 8, 2026
f954a0a
Change package to spring.opentelemetry
timtebeek Jan 8, 2026
9b23386
Restore `ReplaceRestTemplateBuilderMethods` in recipes.csv
timtebeek Jan 8, 2026
960713e
Minimize modifiers in tests
timtebeek Jan 8, 2026
400c718
Remove duplicate entries from recipes.csv
timtebeek Jan 8, 2026
4f2e19d
Use wildcard
timtebeek Jan 8, 2026
c793351
Merge branch 'main' into main
timtebeek Jan 8, 2026
228db0e
Inline small recipes only used once
timtebeek Jan 8, 2026
cf22ecf
Remove obsolete `AddOpenTelemetryOtlpExporter`
timtebeek Jan 9, 2026
f32cdb2
Remove unused `UpgradeOpenTelemetry_2_23`
timtebeek Jan 9, 2026
b191d17
Restructure recipes top down
timtebeek Jan 9, 2026
c389446
Fix test references
timtebeek Jan 9, 2026
259f3b2
Merge branch 'main' into main
timtebeek Jan 9, 2026
317c72e
Add versions to added dependencies
timtebeek Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ recipeDependencies {

parserClasspath("org.springframework.cloud:spring-cloud-sleuth-api:3.1.+")

// OpenTelemetry dependencies
parserClasspath("io.opentelemetry:opentelemetry-api:1.+")
parserClasspath("io.opentelemetry:opentelemetry-context:1.+")
parserClasspath("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.+")
parserClasspath("io.micrometer:micrometer-tracing:1.+")
parserClasspath("io.opentracing:opentracing-api:0.33.0")

parserClasspath("org.springdoc:springdoc-openapi-starter-common:2.+")

parserClasspath("com.nimbusds:nimbus-jose-jwt:9.13")
Expand Down Expand Up @@ -177,6 +184,14 @@ recipeDependencies {
testParserClasspath("org.springframework:spring-web:6.0.+")
testParserClasspath("org.springframework:spring-web:6.1.8")
testParserClasspath("org.springframework:spring-webflux:6.1.16")

// OpenTelemetry test dependencies
testParserClasspath("io.opentelemetry:opentelemetry-api:1.+")
testParserClasspath("io.opentelemetry:opentelemetry-context:1.+")
testParserClasspath("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.+")
testParserClasspath("io.micrometer:micrometer-tracing:1.+")
testParserClasspath("io.opentracing:opentracing-api:0.33.0")
testParserClasspath("org.springframework.cloud:spring-cloud-sleuth-api:3.1.+")
}

val rewriteVersion = rewriteRecipe.rewriteVersion.get()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.spring.opentelemetry;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.ChangeType;

import java.util.Arrays;
import java.util.List;

/**
* Migrates Datadog tracing API types to OpenTelemetry API equivalents.
* <p>
* This recipe handles the migration of:
* <ul>
* <li>Datadog Tracer → OpenTelemetry Tracer</li>
* <li>Datadog Span → OpenTelemetry Span</li>
* <li>Datadog SpanContext → OpenTelemetry SpanContext</li>
* <li>Datadog Scope → OpenTelemetry Scope</li>
* <li>Datadog @Trace annotation → OpenTelemetry @WithSpan</li>
* </ul>
*/
public class MigrateDatadogToOpenTelemetry extends Recipe {

@Override
public String getDisplayName() {
return "Migrate Datadog tracing to OpenTelemetry";
}

@Override
public String getDescription() {
return "Migrates Datadog tracing API types and annotations to OpenTelemetry API equivalents. " +
"This includes migrating the Datadog Tracer, Span, SpanContext, Scope, and @Trace annotation.";
}

@Override
public List<Recipe> getRecipeList() {
return Arrays.asList(
// Datadog core tracing types to OpenTelemetry
new ChangeType("datadog.trace.api.Tracer", "io.opentelemetry.api.trace.Tracer", true),
new ChangeType("io.opentracing.Tracer", "io.opentelemetry.api.trace.Tracer", true),
new ChangeType("datadog.trace.api.DDTracer", "io.opentelemetry.api.trace.Tracer", true),

// Span types
new ChangeType("datadog.trace.api.Span", "io.opentelemetry.api.trace.Span", true),
new ChangeType("io.opentracing.Span", "io.opentelemetry.api.trace.Span", true),
new ChangeType("datadog.trace.api.DDSpan", "io.opentelemetry.api.trace.Span", true),

// SpanContext
new ChangeType("datadog.trace.api.SpanContext", "io.opentelemetry.api.trace.SpanContext", true),
new ChangeType("io.opentracing.SpanContext", "io.opentelemetry.api.trace.SpanContext", true),
new ChangeType("datadog.trace.api.DDSpanContext", "io.opentelemetry.api.trace.SpanContext", true),

// Scope
new ChangeType("datadog.trace.context.TraceScope", "io.opentelemetry.context.Scope", true),
new ChangeType("io.opentracing.Scope", "io.opentelemetry.context.Scope", true),

// @Trace annotation to @WithSpan
new ChangeType("datadog.trace.api.Trace", "io.opentelemetry.instrumentation.annotations.WithSpan", true)
);
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
// Use the composite recipe approach via getRecipeList()
return TreeVisitor.noop();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.spring.opentelemetry;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.ChangeType;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.search.UsesType;

import java.util.Arrays;
import java.util.List;

/**
* Migrates OpenTracing API to OpenTelemetry API.
* <p>
* OpenTracing has been superseded by OpenTelemetry and is no longer actively maintained.
* This recipe handles the migration of core OpenTracing types to their OpenTelemetry equivalents.
*
* @see <a href="https://opentelemetry.io/docs/migration/opentracing/">OpenTracing Migration Guide</a>
*/
public class MigrateOpenTracingToOpenTelemetry extends Recipe {

private static final String OPENTRACING_PACKAGE = "io.opentracing";
private static final String OTEL_API_PACKAGE = "io.opentelemetry.api";
private static final String OTEL_CONTEXT_PACKAGE = "io.opentelemetry.context";

@Override
public String getDisplayName() {
return "Migrate OpenTracing API to OpenTelemetry API";
}

@Override
public String getDescription() {
return "Migrate OpenTracing API to OpenTelemetry API. " +
"OpenTracing has been superseded by OpenTelemetry and is no longer actively maintained.";
}

@Override
public List<Recipe> getRecipeList() {
return Arrays.asList(
// Core types
new ChangeType(OPENTRACING_PACKAGE + ".Tracer", OTEL_API_PACKAGE + ".trace.Tracer", true),
new ChangeType(OPENTRACING_PACKAGE + ".Span", OTEL_API_PACKAGE + ".trace.Span", true),
new ChangeType(OPENTRACING_PACKAGE + ".SpanContext", OTEL_API_PACKAGE + ".trace.SpanContext", true),
new ChangeType(OPENTRACING_PACKAGE + ".Scope", OTEL_CONTEXT_PACKAGE + ".Scope", true),
new ChangeType(OPENTRACING_PACKAGE + ".ScopeManager", OTEL_CONTEXT_PACKAGE + ".Context", true),

// Propagation types
new ChangeType(OPENTRACING_PACKAGE + ".propagation.Format", OTEL_CONTEXT_PACKAGE + ".propagation.TextMapPropagator", true),
new ChangeType(OPENTRACING_PACKAGE + ".propagation.TextMapExtract", OTEL_CONTEXT_PACKAGE + ".propagation.TextMapGetter", true),
new ChangeType(OPENTRACING_PACKAGE + ".propagation.TextMapInject", OTEL_CONTEXT_PACKAGE + ".propagation.TextMapSetter", true)
);
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
new UsesType<>(OPENTRACING_PACKAGE + ".*", false),
new JavaIsoVisitor<ExecutionContext>() {
// The actual migration is done by the recipe list above
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.spring.opentelemetry;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.ChangeType;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.search.UsesType;

import java.util.Arrays;
import java.util.List;

/**
* Migrates Spring Cloud Sleuth annotations to OpenTelemetry annotations.
* <p>
* This recipe handles the migration of:
* <ul>
* <li>{@code @NewSpan} → {@code @WithSpan}</li>
* <li>{@code @SpanTag} → {@code @SpanAttribute}</li>
* <li>{@code @ContinueSpan} → {@code @WithSpan}</li>
* </ul>
*
* @see <a href="https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/annotations/">OpenTelemetry Annotations</a>
*/
public class MigrateSleuthAnnotationsToOpenTelemetry extends Recipe {

private static final String SLEUTH_ANNOTATION_PACKAGE = "org.springframework.cloud.sleuth.annotation";
private static final String OTEL_ANNOTATION_PACKAGE = "io.opentelemetry.instrumentation.annotations";

@Override
public String getDisplayName() {
return "Migrate Sleuth annotations to OpenTelemetry annotations";
}

@Override
public String getDescription() {
return "Migrate Spring Cloud Sleuth annotations (@NewSpan, @SpanTag, @ContinueSpan) " +
"to OpenTelemetry annotations (@WithSpan, @SpanAttribute).";
}

@Override
public List<Recipe> getRecipeList() {
return Arrays.asList(
// @NewSpan -> @WithSpan
new ChangeType(SLEUTH_ANNOTATION_PACKAGE + ".NewSpan", OTEL_ANNOTATION_PACKAGE + ".WithSpan", true),
// @SpanTag -> @SpanAttribute
new ChangeType(SLEUTH_ANNOTATION_PACKAGE + ".SpanTag", OTEL_ANNOTATION_PACKAGE + ".SpanAttribute", true),
// @ContinueSpan -> @WithSpan (note: semantics differ slightly)
new ChangeType(SLEUTH_ANNOTATION_PACKAGE + ".ContinueSpan", OTEL_ANNOTATION_PACKAGE + ".WithSpan", true)
);
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
new UsesType<>(SLEUTH_ANNOTATION_PACKAGE + ".*", false),
new JavaIsoVisitor<ExecutionContext>() {
// The actual migration is done by the recipe list above
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.spring.opentelemetry;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.ChangePackage;
import org.openrewrite.java.ChangeType;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;

import java.util.Arrays;
import java.util.List;

/**
* Migrates Spring Cloud Sleuth API to Micrometer Tracing API.
* <p>
* Spring Cloud Sleuth has been deprecated and is replaced by Micrometer Tracing
* with OpenTelemetry as the recommended backend.
*
* @see <a href="https://github.com/spring-cloud/spring-cloud-sleuth#spring-cloud-sleuth">Spring Cloud Sleuth README</a>
* @see <a href="https://micrometer.io/docs/tracing">Micrometer Tracing Documentation</a>
*/
public class MigrateSleuthToMicrometerTracing extends Recipe {

private static final String SLEUTH_PACKAGE = "org.springframework.cloud.sleuth";
private static final String MICROMETER_TRACING_PACKAGE = "io.micrometer.tracing";

@Override
public String getDisplayName() {
return "Migrate Spring Cloud Sleuth to Micrometer Tracing";
}

@Override
public String getDescription() {
return "Migrate Spring Cloud Sleuth API to Micrometer Tracing API. " +
"Spring Cloud Sleuth has been deprecated and is replaced by Micrometer Tracing " +
"with OpenTelemetry as the recommended backend.";
}

@Override
public List<Recipe> getRecipeList() {
return Arrays.asList(
// Core types migration
new ChangeType(SLEUTH_PACKAGE + ".Tracer", MICROMETER_TRACING_PACKAGE + ".Tracer", true),
new ChangeType(SLEUTH_PACKAGE + ".Span", MICROMETER_TRACING_PACKAGE + ".Span", true),
new ChangeType(SLEUTH_PACKAGE + ".Span$Kind", MICROMETER_TRACING_PACKAGE + ".Span$Kind", true),
new ChangeType(SLEUTH_PACKAGE + ".SpanCustomizer", MICROMETER_TRACING_PACKAGE + ".SpanCustomizer", true),
new ChangeType(SLEUTH_PACKAGE + ".CurrentTraceContext", MICROMETER_TRACING_PACKAGE + ".CurrentTraceContext", true),
new ChangeType(SLEUTH_PACKAGE + ".TraceContext", MICROMETER_TRACING_PACKAGE + ".TraceContext", true),
new ChangeType(SLEUTH_PACKAGE + ".ScopedSpan", MICROMETER_TRACING_PACKAGE + ".ScopedSpan", true),
new ChangeType(SLEUTH_PACKAGE + ".SpanAndScope", MICROMETER_TRACING_PACKAGE + ".SpanAndScope", true),

// Baggage types
new ChangeType(SLEUTH_PACKAGE + ".Baggage", MICROMETER_TRACING_PACKAGE + ".Baggage", true),
new ChangeType(SLEUTH_PACKAGE + ".BaggageInScope", MICROMETER_TRACING_PACKAGE + ".BaggageInScope", true),

// Propagation types
new ChangeType(SLEUTH_PACKAGE + ".propagation.Propagator", MICROMETER_TRACING_PACKAGE + ".propagation.Propagator", true),

// Exporter types
new ChangeType(SLEUTH_PACKAGE + ".exporter.SpanFilter", MICROMETER_TRACING_PACKAGE + ".exporter.SpanExportingPredicate", true),
new ChangeType(SLEUTH_PACKAGE + ".exporter.SpanReporter", MICROMETER_TRACING_PACKAGE + ".exporter.SpanReporter", true)
);
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
new UsesType<>(SLEUTH_PACKAGE + ".*", false),
new JavaIsoVisitor<ExecutionContext>() {
// The actual migration is done by the recipe list above
// This visitor serves as a precondition check
}
);
}
}
Loading