All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.nike.wingtips.springboot2.webflux.WingtipsSpringBoot2WebfluxConfiguration Maven / Gradle / Ivy

package com.nike.wingtips.springboot2.webflux;

import com.nike.internal.util.StringUtils;
import com.nike.wingtips.Tracer;
import com.nike.wingtips.spring.webflux.server.WingtipsSpringWebfluxWebFilter;
import com.nike.wingtips.tags.HttpTagAndSpanNamingAdapter;
import com.nike.wingtips.tags.HttpTagAndSpanNamingStrategy;
import com.nike.wingtips.tags.NoOpHttpTagStrategy;
import com.nike.wingtips.tags.OpenTracingHttpTagStrategy;
import com.nike.wingtips.tags.ZipkinHttpTagStrategy;

import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Wingtips Spring Boot 2 WebFlux configuration - this class enables Wingtips tracing on incoming requests by exposing
 * a {@link WingtipsSpringWebfluxWebFilter} as a {@link Bean}, and provides a few other Wingtips configuration
 * features. You can enable the features of this class by registering it with your Spring Boot application's {@link
 * org.springframework.context.ApplicationContext} via {@link org.springframework.context.annotation.Import}, {@link
 * org.springframework.context.annotation.ComponentScan}, or through any of the other mechanisms that register Spring
 * beans. Web app specific beans (like {@link #wingtipsSpringWebfluxWebFilter()}) are marked with {@link
 * ConditionalOnWebApplication} with type {@link ConditionalOnWebApplication.Type#REACTIVE} so that they will be
 * ignored if you are not in a WebFlux application environment.
 *
 * 

You can override the default {@link WingtipsSpringWebfluxWebFilter} by exposing one of your own in your * application Spring config - if you don't specify one yourself then a new {@link WingtipsSpringWebfluxWebFilter} * will be used. * *

This class uses {@link WingtipsSpringBoot2WebfluxProperties} to control some behavior options via your * application's properties file(s). See that class for full details, but for example you could set the following * properties in your {@code application.properties}: *

 *     wingtips.wingtips-disabled=false
 *     wingtips.user-id-header-keys=userid,altuserid
 *     wingtips.span-logging-format=KEY_VALUE
 *     wingtips.server-side-span-tagging-strategy=ZIPKIN
 *     wingtips.server-side-span-tagging-adapter=com.nike.wingtips.spring.webflux.server.SpringWebfluxServerRequestTagAdapter
 * 
* None of these properties are required - if they are missing then {@link WingtipsSpringWebfluxWebFilter} will be * registered, it will not look for any user ID headers, the span logging format will not be changed (defaults to * JSON), and the default tag and span naming strategy and adapter will be used (defaults to {@link * ZipkinHttpTagStrategy} and {@link com.nike.wingtips.spring.webflux.server.SpringWebfluxServerRequestTagAdapter}). * *

If you want Zipkin support in your Wingtips Spring Boot 2 WebFlux application for exporting span data to a * Zipkin server, please see {@code WingtipsWithZipkinSpringBoot2WebfluxConfiguration} from the * {@code wingtips-zipkin2-spring-boot2-webflux} Wingtips module. * * @author Nic Munroe */ @Configuration @EnableConfigurationProperties(WingtipsSpringBoot2WebfluxProperties.class) public class WingtipsSpringBoot2WebfluxConfiguration { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * The project-specific override {@link WingtipsSpringWebfluxWebFilter} that should be used. If a {@link * WingtipsSpringWebfluxWebFilter} is not found in the project's Spring app context then this will be initially * injected as null and a default {@link WingtipsSpringWebfluxWebFilter} will be created and used. * *

This field injection with {@link Autowired} and {@code required = false} is necessary to allow individual * projects the option to override the default without causing an exception in the case that the project does * not specify an override. */ @Autowired(required = false) protected WingtipsSpringWebfluxWebFilter customSpringWebfluxWebFilter; protected WingtipsSpringBoot2WebfluxProperties wingtipsProperties; @Autowired public WingtipsSpringBoot2WebfluxConfiguration(WingtipsSpringBoot2WebfluxProperties wingtipsProperties) { this.wingtipsProperties = wingtipsProperties; // Set the span logging representation if specified in the wingtips properties. if (wingtipsProperties.getSpanLoggingFormat() != null) { Tracer.getInstance().setSpanLoggingRepresentation(wingtipsProperties.getSpanLoggingFormat()); } } /** * Create and return a {@link WingtipsSpringWebfluxWebFilter}, which will auto-register itself with the * Spring Boot 2 WebFlux app as a {@link org.springframework.web.server.WebFilter} and enable Wingtips tracing * for incoming requests. * *

NOTE: This will return null (and essentially be a no-op for Spring) in the following cases: *

    *
  • * When {@link WingtipsSpringBoot2WebfluxProperties#isWingtipsDisabled()} is true. In this case the * application specifically does *not* want a {@link WingtipsSpringWebfluxWebFilter} registered. *
  • *
  • * When {@link #customSpringWebfluxWebFilter} is non-null. In this case the application has a custom * implementation of {@link WingtipsSpringWebfluxWebFilter} that they want to use instead of whatever * default one this method would provide, so we should return null here to allow the custom application * impl to be used. *
  • *
* * @return The {@link WingtipsSpringWebfluxWebFilter} that should be used, or null in the case that * {@link WingtipsSpringBoot2WebfluxProperties#isWingtipsDisabled()} is true or if the application already * defined a custom override. */ @Bean @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) public WingtipsSpringWebfluxWebFilter wingtipsSpringWebfluxWebFilter() { if (wingtipsProperties.isWingtipsDisabled()) { return null; } // Allow projects to completely override the filter that gets used if desired. If not overridden then create // a new one. if (customSpringWebfluxWebFilter != null) { return null; } // We need to create a new one. return WingtipsSpringWebfluxWebFilter .newBuilder() .withUserIdHeaderKeys(extractUserIdHeaderKeysAsList(wingtipsProperties)) .withTagAndNamingStrategy(extractTagAndNamingStrategy(wingtipsProperties)) .withTagAndNamingAdapter(extractTagAndNamingAdapter(wingtipsProperties)) .build(); } /** * This bean registers a Project Reactor {@link reactor.core.scheduler.Scheduler} * hook, which ensures that Wingtips tracing state is propagated to {@link reactor.core.publisher.Mono} * and {@link reactor.core.publisher.Flux} across async boundaries based on the tracing state at the time * of subscription to the Mono/Flux. * *

NOTE: The {@link WingtipsSpringBoot2WebfluxProperties#isReactorEnabled()} property will control whether * this hook actually registers itself or not, so you can enable or disable this integration from your * application properties. */ @Bean public WingtipsReactorInitializer reactorInitializer() { return new WingtipsReactorInitializer(wingtipsProperties.isReactorEnabled()); } protected @Nullable List extractUserIdHeaderKeysAsList(WingtipsSpringBoot2WebfluxProperties props) { if (props.getUserIdHeaderKeys() == null) { return null; } return Stream .of(props.getUserIdHeaderKeys().split(",")) .filter(StringUtils::isNotBlank) .map(String::trim) .collect(Collectors.toList()); } protected @Nullable HttpTagAndSpanNamingStrategy extractTagAndNamingStrategy( WingtipsSpringBoot2WebfluxProperties props ) { String strategyName = props.getServerSideSpanTaggingStrategy(); if (StringUtils.isBlank(strategyName)) { // Nothing specified, so return null to use the default. return null; } // Check for a short-name match first. if ("zipkin".equalsIgnoreCase(strategyName)) { return ZipkinHttpTagStrategy.getDefaultInstance(); } if("opentracing".equalsIgnoreCase(strategyName)) { return OpenTracingHttpTagStrategy.getDefaultInstance(); } if("none".equalsIgnoreCase(strategyName) || "noop".equalsIgnoreCase(strategyName)) { return NoOpHttpTagStrategy.getDefaultInstance(); } // At this point there was no short-name match. Try instantiating it by classname. try { //noinspection unchecked return (HttpTagAndSpanNamingStrategy) Class.forName(strategyName).newInstance(); } catch (Exception ex) { // Couldn't instantiate by interpreting it as a class name. Return null so the default gets used. logger.warn("Unable to match tagging strategy \"{}\". Using the default strategy (Zipkin)", strategyName, ex); return null; } } protected @Nullable HttpTagAndSpanNamingAdapter extractTagAndNamingAdapter( WingtipsSpringBoot2WebfluxProperties props ) { String adapterName = props.getServerSideSpanTaggingAdapter(); if (StringUtils.isBlank(adapterName)) { // Nothing specified, so return null to use the default. return null; } // There are no shortnames for the adapter like there are for strategy. Try instantiating by classname try { //noinspection unchecked return (HttpTagAndSpanNamingAdapter) Class.forName(adapterName).newInstance(); } catch (Exception ex) { // Couldn't instantiate by interpreting it as a class name. Return null so the default gets used. logger.warn( "Unable to match tagging adapter \"{}\". " + "Using the default adapter (SpringWebfluxServerRequestTagAdapter)", adapterName, ex ); return null; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy