Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/plugins-jdk17-test.1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ jobs:
- caffeine-3.x-scenario
- lettuce-webflux-6x-scenario
- elasticsearch-java-9.x-scenario
- webflux-6.x-7.x-scenario
steps:
- uses: actions/checkout@v2
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/plugins-jdk21-test.0.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ jobs:
case:
- spring-6.x-scenario
- jdk-virtual-thread-executor-scenario
- gateway-5.x-scenario
steps:
- uses: actions/checkout@v2
with:
Expand Down
16 changes: 16 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ Release Notes.
------------------

* Add Spring LDAP 3.3.x-4.x plugin.
* Fix `NoSuchMethodError: org.apache.skywalking.apm.plugin.spring.webflux.v6.DispatcherHandlerHandleMethodInterceptor`
on Spring Framework 7 (Spring Boot 4). `HttpHeaders` no longer implements `MultiValueMap`, so
`List get(Object)` was removed; the entry span was never created and the service produced no
traces. Affects `spring-webflux-6.x` and, through its shaded copy, `spring-cloud-gateway-4.x`
(apache/skywalking#14047).
* Extend `spring-webflux-6.x`, `spring-webflux-6.x-webclient`, `springmvc-annotation-6.x`,
`spring-resttemplate-6.x` and `spring-cloud-gateway-4.x` plugins to support Spring Framework 7,
Spring Boot 4 and Spring Cloud Gateway 5.x.
* Add Spring version witnesses to the `spring-webflux-5.x` and `spring-webflux-6.x` plugins, so they
no longer both match `DispatcherHandler#handle` on Reactor 3.4 (Spring Boot 2.4-2.7).
* **Breaking:** rename `apm-toolkit-webflux` to `apm-toolkit-webflux-5.x` and move its package to
`org.apache.skywalking.apm.toolkit.webflux.v5`; add `apm-toolkit-webflux-6.x`
(`org.apache.skywalking.apm.toolkit.webflux.v6`) for Reactor 3.5+ / Spring Boot 3.x and 4.x, where
`WebFluxSkyWalkingOperators#continueTracing` previously threw `NoSuchMethodError` because Reactor
removed `Signal#getContext()` in 3.5.0. Existing `apm-toolkit-webflux` jars (9.7.0 and earlier)
remain instrumented by the agent, so upgrading the agent alone does not force a change.
* Fix the Log4j2 plugin descriptor (`Log4j2Plugins.dat`) missing from the `apm-toolkit-log4j-2.x` jar since 9.5.0, which broke `%traceId` and `%sw_ctx` resolution in Log4j2 `PatternLayout` (apache/skywalking#14006).

All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/263?closed=1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>apm-toolkit-webflux</artifactId>
<artifactId>apm-toolkit-webflux-5.x</artifactId>

<properties>
<spring-webflux.version>5.1.0.RELEASE</spring-webflux.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*
*/

package org.apache.skywalking.apm.toolkit.webflux;
package org.apache.skywalking.apm.toolkit.webflux.v5;

import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Signal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*
*/

package org.apache.skywalking.apm.toolkit.webflux;
package org.apache.skywalking.apm.toolkit.webflux.v5;

import org.springframework.web.server.ServerWebExchange;

Expand Down
58 changes: 58 additions & 0 deletions apm-application-toolkit/apm-toolkit-webflux-6.x/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>apm-application-toolkit</artifactId>
<groupId>org.apache.skywalking</groupId>
<version>9.8.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>apm-toolkit-webflux-6.x</artifactId>

<properties>
<spring-webflux.version>6.0.0</spring-webflux.version>
<!--
Compile against a Reactor that has already REMOVED Signal#getContext (removed in 3.5.0).
This is deliberate: the 5.x toolkit rotted silently for three years because it compiled
against the oldest Reactor (3.2 via spring-webflux 5.1), so javac never flagged the
removal. Pinning forward makes any future Reactor removal a compile error instead of a
runtime NoSuchMethodError. Reactor 3.5.x is still Java 8 bytecode, so the module keeps
the project-wide maven.compiler.release=8.
The emitted refs (Signal#getContextView, Context#of(ContextView)) exist from 3.4.0, so
the jar links on Reactor 3.4 -> 3.8.
-->
<reactor-core.version>3.5.0</reactor-core.version>
</properties>

<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>${reactor-core.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<version>${spring-webflux.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.apache.skywalking.apm.toolkit.webflux.v6;

import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Signal;
import reactor.core.publisher.SignalType;
import reactor.util.context.Context;

import java.util.concurrent.Callable;
import java.util.function.Consumer;

/**
* WebFlux operators that are capable to reuse tracing context from Reactor's Context.
*/
public final class WebFluxSkyWalkingOperators {

private WebFluxSkyWalkingOperators() {
throw new IllegalStateException("You can't instantiate a utility class");
}

/**
* Wraps a runnable with a local span and continue tracing context.
*
* @param signalType - Reactor's signal type
* @param runnable - lambda to execute within the tracing context
* @return consumer of a signal
*/
public static Consumer<Signal<?>> continueTracing(SignalType signalType, Runnable runnable) {
return signal -> {
if (signalType != signal.getType()) {
return;
}
continueTracing(runnable).accept(signal);
};
}

/**
* Wraps a consumer with a local span and continue tracing context.
*
* @param signalType - Reactor's signal type
* @param consumer - lambda to execute within the tracing context
* @return consumer of a signal
*/
public static Consumer<Signal> continueTracing(SignalType signalType, Consumer<Signal> consumer) {
return signal -> {
if (signalType != signal.getType()) {
return;
}
continueTracing(Context.of(signal.getContextView()), () -> consumer.accept(signal));
};
}

/**
* Wraps a runnable with a local span and continue tracing context.
*
* @param runnable - lambda to execute within the tracing context
* @return consumer of a signal
*/
public static Consumer<Signal> continueTracing(Runnable runnable) {
return signal -> {
Context context = Context.of(signal.getContextView());
continueTracing(context, runnable);
};
}

/**
* Wraps a runnable with a local span and continue tracing context.
*
* @param context - Reactor context that contains the tracing context
* @param runnable - lambda to execute within the tracing context
*/
public static void continueTracing(Context context, Runnable runnable) {
runnable.run();
}

/**
* Wraps a callable with a local span and continue tracing context.
*
* @param context - Reactor context that contains the tracing context
* @param callable - lambda to execute within the tracing context
* @param <T> callable's return type
* @return value from the callable
*/
public static <T> T continueTracing(Context context, Callable<T> callable) {
try {
return callable.call();
} catch (Exception e) {
return sneakyThrow(e);
}
}

/**
* Wraps a callable with a local span and continue tracing context.
*
* @param serverWebExchange - EnhancedInstance that contains the tracing context
* @param callable - lambda to execute within the tracing context
* @param <T> callable's return type
* @return value from the callable
*/
public static <T> T continueTracing(ServerWebExchange serverWebExchange, Callable<T> callable) {
try {
return callable.call();
} catch (Exception e) {
return sneakyThrow(e);
}
}

/**
* Wraps a runnable with a local span and continue tracing context.
*
* @param serverWebExchange - EnhancedInstance that contains the tracing context
* @param runnable - lambda to execute within the tracing context
*/
public static void continueTracing(ServerWebExchange serverWebExchange, Runnable runnable) {
runnable.run();
}

private static <T extends Throwable, R> R sneakyThrow(Throwable t) throws T {
throw (T) t;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.apache.skywalking.apm.toolkit.webflux.v6;

import org.springframework.web.server.ServerWebExchange;

import java.util.Optional;

/**
* TraceContext for WebFlux.
*/
public class WebFluxSkyWalkingTraceContext {
/**
* Try to get the traceId of current trace context.
*
* @param serverWebExchange - EnhancedInstance that contains the tracing context
* @return traceId, if it exists, or empty {@link String}.
*/
public static String traceId(ServerWebExchange serverWebExchange) {
return "";
}

/**
* Try to get the segmentId of current trace context.
*
* @param serverWebExchange - EnhancedInstance that contains the tracing context
* @return segmentId, if it exists, or empty {@link String}.
*/
public static String segmentId(ServerWebExchange serverWebExchange) {
return "";
}

/**
* Try to get the spanId of current trace context. The spanId is a negative number when the trace context is
* missing.
*
* @param serverWebExchange - EnhancedInstance that contains the tracing context
* @return spanId, if it exists, or empty {@link String}.
*/
public static int spanId(ServerWebExchange serverWebExchange) {
return -1;
}

/**
* Try to get the custom value from trace context.
*
* @param serverWebExchange - EnhancedInstance that contains the tracing context
* @return custom data value.
*/
public static Optional<String> getCorrelation(ServerWebExchange serverWebExchange, String key) {
return Optional.empty();
}

/**
* Put the custom key/value into trace context.
*
* @param serverWebExchange - EnhancedInstance that contains the tracing context
* @return previous value if it exists.
*/
public static Optional<String> putCorrelation(ServerWebExchange serverWebExchange, String key, String value) {
return Optional.empty();
}
}
3 changes: 2 additions & 1 deletion apm-application-toolkit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<module>apm-toolkit-micrometer-registry</module>
<module>apm-toolkit-micrometer-1.10</module>
<module>apm-toolkit-kafka</module>
<module>apm-toolkit-webflux</module>
<module>apm-toolkit-webflux-5.x</module>
<module>apm-toolkit-webflux-6.x</module>
</modules>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ private static void collectHttpHeaders(final List<String> headersList, final Abs
}

public static Enumeration<String> getHeaders(final ServerHttpRequest request, final String headerName) {
List<String> values = request.getHeaders().get(headerName);
// Use getValuesAsList(String) rather than get(Object): Spring Framework 7 dropped the
// MultiValueMap contract from HttpHeaders, removing List get(Object). getValuesAsList(String)
// is declared on HttpHeaders itself in Spring 5, 6 and 7 with an identical descriptor.
// Note it additionally splits comma-delimited values, which is the more correct reading
// for header collection.
List<String> values = request.getHeaders().getValuesAsList(headerName);
if (values == null) {
return Collections.enumeration(Collections.emptyList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,6 @@ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allA
if (httpServletResponse != null) {
statusCode = httpServletResponse.getStatus();
} else if (ServerHttpResponse.class.isAssignableFrom(response.getClass())) {
if (HttpResponseWrappers.servletStatusSupported()) {
statusCode = ((ServerHttpResponse) response).getRawStatusCode();
}
Object context = runtimeContext.get(REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT);
if (context != null) {
((AbstractSpan[]) context)[0] = span.prepareForAsync();
Expand Down
Loading
Loading