GithubHelp home page GithubHelp logo

wavefront-spring-boot's Introduction

Wavefront for Spring Boot 3

CI Status Released Version

This project provides a Spring Boot 3 starter for Wavefront. Add the starter to a project to send metrics, histograms, and traces to a Wavefront cluster. If you don't have a Wavefront account, the starter creates a freemium account for you and saves the API token in your home directory at ~/.wavefront_freemium.

Note: Spring Boot 2 users should refer to the README on the master branch.

Table of Contents

Prerequisites

  • Spring Boot 3.0 or above
  • Java 17 or above
  • Maven 3.5+ or Gradle 7.5+
    See System Requirements in the Spring Boot documentation.

Note: This starter reuses the existing Wavefront support for Metrics and Tracing in Spring Boot and provides the Actuator (i.e., spring-boot-starter-actuator).

Getting Started

There are several ways to start:

Create a New Project

The easiest way to get started is to:

  1. Create a new project on start.spring.io.
  2. Select Spring Boot 3.0.0 or later and define the other parameters for your project.
  3. Click "Add dependency" and select Wavefront from the dependency list.
  4. To include tracing support, add the Distributed Tracing dependency as well.

If you don't have a Wavefront account, the starter creates a freemium account for you and saves the API token in your home directory at ~/.wavefront_freemium.

Update an Existing Application

If you already have a Spring Boot application, you can use start.spring.io to explore a new project from your browser. This allows you to see the setup for the Spring Boot generation your project is using.

Next, follow the steps under Maven or Gradle to add the Wavefront Spring Boot BOM.

Maven

Follow these steps:

  1. Import the wavefront-spring-boot-bom Bill Of Materials (BOM):

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>com.wavefront</groupId>
          <artifactId>wavefront-spring-boot-bom</artifactId>
          <version>3.0.1</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>
    
  2. Add the wavefront-spring-boot-starter dependency to your project:

    <dependency>
      <groupId>com.wavefront</groupId>
      <artifactId>wavefront-spring-boot-starter</artifactId>
    </dependency>
    

Gradle

Follow these steps:

  1. Make sure your project uses the io.spring.dependency-management plugin and add the following to your build.gradle file:

      dependencyManagement {
        imports {
          mavenBom "com.wavefront:wavefront-spring-boot-bom:3.0.1"
        }
      }
    
  2. Add the wavefront-spring-boot-starter dependency to your project:

      dependencies {
        ...
        implementation 'com.wavefront:wavefront-spring-boot-starter'
      }
    

Configure the Application and Service Names

Set the Application and Service Name in application.properties. In Wavefront, the Application represents a logical grouping of individual Services. For example, a shopping application might be comprised of the cart and payment services. If your Spring Boot app is the payment service:

management.wavefront.application.name=shopping
management.wavefront.application.service-name=payment

Each time you restart your application, it either creates a new freemium account, or it restores from ~/.wavefront_freemium. At the end of the startup phase, the console displays a message with a login URL. Use it to log in to the Wavefront service and access the data that has been collected so far.

Here is an example message when an existing account is restored from ~/.wavefront_freemium:

Your existing Wavefront account information has been restored from disk.
To share this account, make sure the following is added to your configuration:

  management.wavefront.api-token=2c96d63a-abcd-efgh-ijhk-841611451e07
  management.wavefront.uri=https://wavefront.surf

Connect to your Wavefront dashboard using this one-time use link:
https://wavefront.surf/us/example

Tracing Support

If you'd like to send traces to Wavefront, you can do so using Micrometer Tracing. Follow these steps:

  1. Choose a Micrometer Tracer for your usecase. For instructions, see Micrometer's Supported Tracer documentation. For example, to use the OpenTelemetry Tracer:

    • Maven: Add the following dependency to the pom.xml file:

      <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-tracing-bridge-otel</artifactId>
      </dependency>
      
    • Gradle: Add the following dependency to the build.gradle file:

      dependencies {
        ...
        implementation 'io.micrometer:micrometer-tracing-bridge-otel'
      }
      
  2. For demo purposes, configure distributed tracing sampling to 100% in application.properties:

    management.tracing.sampling.probability=1.0
    

Building

To build the latest state of this project, invoke the following command from the root directory:

$ ./mvnw clean install

The project has a sample that showcases the basic usage of the starter. It starts a web app on localhost:8080. Invoke the following command from the root directory:

$ ./mvnw spring-boot:run -pl wavefront-spring-boot-sample

Documentation

License

Open Source License

Getting Support

  • If you run into any issues, let us know by creating a GitHub issue.
  • If you didn't find the information you are looking for in our Wavefront Documentation create a GitHub issue or PR in our docs repository.

wavefront-spring-boot's People

Contributors

adriancole avatar ajayj89 avatar akodali18 avatar dependabot[bot] avatar hanwavefront avatar haosong avatar howardyoo avatar jbau avatar jmoravec avatar jonatan-ivanov avatar marcingrzejszczak avatar oppegard avatar panghy avatar sdeleuze avatar shavidissa avatar snicoll avatar srujann avatar sushantdewan123 avatar thepeterstone avatar ttddyy avatar wf-jenkins avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wavefront-spring-boot's Issues

Application does not start with wavefront.properties token set

When I follow the instructions, I get following error:
Description:

apiToken must be set whenever publishing directly to the Wavefront API.

Steps:
Create a new project on start.spring.io. (version 2.2.6, Java 8, Maven build)
Select actuator and web as dependencies.

clone, build and add wavefront starter to the pom.xml:

		<dependency>
			<groupId>com.wavefront</groupId>
			<artifactId>wavefront-spring-boot-starter</artifactId>
			<version>2.0.0-SNAPSHOT</version>
		</dependency>

Add wavefront.properties into src/main/resources folder:
url=https://longboard.wavefront.com
token=SOME KEY
application.name=MetricsDemo

When I remove actuator from the dependencies, the application starts. However, I do not see the metrics in my dashboard. I assume, I should find them under MetricsDemo as prefix?

2020-03-30 21:24:59.321 WARN 78624 --- [ main] c.w.s.WavefrontConfigConditional : Cannot auto-negotiate Wavefront credentials, cannot configure Wavefront Observability for Spring Boot
2020-03-30 21:24:59.526 INFO 78624 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'io.opentracing.contrib.spring.cloud.async.DefaultAsyncAutoConfiguration$DefaultTracedAsyncConfigurerSupport' of type [io.opentracing.contrib.spring.cloud.async.DefaultAsyncAutoConfiguration$DefaultTracedAsyncConfigurerSupport$$EnhancerBySpringCGLIB$$8f74a0f9] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-03-30 21:24:59.594 INFO 78624 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'io.opentracing.contrib.spring.cloud.async.DefaultAsyncAutoConfiguration' of type [io.opentracing.contrib.spring.cloud.async.DefaultAsyncAutoConfiguration$$EnhancerBySpringCGLIB$$90927196] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

Autoconfiguration order issue in 2.1.0 using Sleuth

Howdy!

I've started seeing an issue after updating a project to use 2.1.0 and correspondingly Sleuth 3.0.x.

If you have an application with an auto-configuration class which has a dependency on Tracer (from Sleuth), then a noop Sampler is auto-configured, and no traces are sampled without manually configuring a Sampler.

I have a small project which demonstrates the issue here. The application adds a simple auto-configuration class as follows:

@Configuration
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class CustomAutoConfigUsingTracer {
	@Bean
	public String dependency(Tracer tracer) {
		return "test";
	}
}

When the application runs, the AutoConfigureAfter forces this component to be registered after Brave and correspondingly BraveSamplerConfiguration. Unfortunately, that seems to ignore the AutoConfigureBefore requirement that WavefrontTracingSleuthConfiguration should run before BraveAutoConfiguration. This causes issues because Sleuth has a SamplerCondition which configures a real Sampler if a custom TracingCustomizer has been configured (which WavefrontTracingSleuthConfiguration adds).

If you run the demo project, you would see this output indicating the order in which the auto-configuration classes are loaded, and correspondingly, the sampler that was configured:

  3 - org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration
  4 - com.wavefront.autoconfigissue.AutoConfigIssueDemo.CustomAutoConfigUsingTracer
  5 - org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration
  6 - org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration
  7 - com.wavefront.spring.autoconfigure.WavefrontAutoConfiguration
  ...
  Sampler is: NeverSample

Prior to 2.1.0 (and Sleuth 3.0.x) this same code worked as expected and a real Sampler is auto-configured by Sleuth/Brave. I believe this was possibly introduced by #92 here.

It's very unclear to me which component is at fault here, or where this issue could be addressed most effectively. The example project and corresponding auto-configuration are very similar to how a library I've been using instruments database queries via p6spy, which is how I ran into this issue.

Would greatly appreciate any input/feedback. I'm really unsure how to address this without a custom Sampler configuration, which isn't great experience given that the Sampler auto-configuration used to work.

Thanks!!!

WavefrontAutoConfiguration has reference to WavefrontMetricsExportAutoConfiguration that may not be on the classpath

The WavefrontAutoConfiguration is assuming that the actuator is present. When running this starter excluding the actuator this leads to:

java.lang.IllegalArgumentException: Could not find class [org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration]
	at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:334) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:446) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:369) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:284) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:193) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.core.type.AnnotatedTypeMetadata.getAnnotationAttributes(AnnotatedTypeMetadata.java:106) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.annotation.AnnotationConfigUtils.attributesFor(AnnotationConfigUtils.java:285) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:102) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:81) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.registerBeanDefinitionForImportedConfigurationClass(ConfigurationClassBeanDefinitionReader.java:160) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:141) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:280) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) [spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) [spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) [spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
	at sample.WavefrontSampleApplication.main(WavefrontSampleApplication.java:10) [classes/:na]

Although this setup is unusual, we should not fail if the actuator is not present.

Remote Server on port 9001 does not stop when application stops

Locally, with locally built starter (2.0.0-SNAPSHOT) using:

./mnvw spring-boot:start

When I stop the application (ctrl-c), and try to restart it, it says port 9001 already in use.

I would expect that server to be shut down when the application stops.

Make Micrometer optional

One of the team I work with have an application that does not use micrometer.

In configurations, there are places referencing WavefrontConfig from micrometer.
(here, here - this one may relate to #74 )

For opentracing, they seems to be retrieving source from WavefrontConfig.

Probably, it could be replaced with a supplier bean like this:

@FunctionalInterface
public interface WavefrontSourceProvider {

	String getSource();

}

Then, conditionally create one to use WavefrontConfig if micrometer is available.

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WavefrontConfig.class)
@ConditionalOnMissingBean(WavefrontSourceProvider.class)
static class MicrometerSourceConfiguration {

	@Bean
	WavefrontSourceProvider wavefrontSourceProvider(WavefrontConfig wavefrontConfig) {
		return wavefrontConfig::source;
	}

}

I didn't check other places, but would be nice to make the micrometer optional.

Reuse maxQueueSize from WavefrontSender for spans

There is a TODO in the code that we should fix somehow.

The Spring Boot auto-configuration of the WavefrontSender allows you to specify a custom queue size so perhaps we should reuse that. I didn't do it as reusing the key as is here feels a bit odd. Perhaps there is a way to retrieve that setting or apply it transparently?

Auto-configuration fails with an older Sleuth version

There were some changes in Sleuth to improve the integration here. One notable addition is the use of SpanHandler (see #55).

If this starter is used with an older Sleuth version, it currently fails with:

Caused by: java.lang.ClassNotFoundException: brave.handler.SpanHandler
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583) ~[na:na]
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
        ... 39 common frames omitted 

We should rather check for the presence of this class and no attempt to configure tracing with an older version.

Not seeing trace when using RestTemplate

I have a set of microservices following pattern described here: https://github.com/adamzwickey/todos. In wavefront tracing, i am seeing trace from my edge service to its downstream api and web. However, my api app uses RestTemplate to call downstream rest services. Wavefront is not making the connection. I've found the trace of a call that traversed the service, but the downstream service does not have a parent trace in the tags. It appears RestTemplate is not sending the appropriate trace information to the downstream services. What to do?

Publish Wavefront metadata early so it is available in the dashboard

Currently, when clicking on the link output to logs on startup, it takes you to the Spring Boot dashboard (good!), but there is no information about the application you just started available there until after the first metrics publish, which is after 1 minute by default. I think this experience can be approved by publishing at least the Wavefront-specific metadata used to populate the Source / Application / Service dropdown menus at the top of the dashboard. If those were populated, then users would be able to tell that Wavefront knows about the application they just started.

Micrometer doesn't provide an API to publish metrics early, but since this is specific to Wavefront and Micrometer is delegating to the Wavefront SDK to publish metrics, one idea is to use the Wavefront SDK to publish a purely informational metric with the expected metadata to populate the dropdown menus.

WavefrontJvmReporter is not properly disposed on context shutdown

An app that uses wavefront and devtools will emit the following logs after the first restart:

2021-04-09 15:17:17.426  WARN 64359 --- [rics-registry-0] c.w.s.c.m.WavefrontSdkMetricsRegistry    : Unable to send internal SDK metric

java.io.IOException: attempt to send using closed sender
	at com.wavefront.sdk.common.clients.WavefrontClient.sendMetric(WavefrontClient.java:323) ~[wavefront-sdk-java-2.6.2.jar:na]
	at com.wavefront.sdk.common.metrics.WavefrontSdkMetricsRegistry.run(WavefrontSdkMetricsRegistry.java:163) ~[wavefront-sdk-java-2.6.2.jar:na]
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[na:na]
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

2021-04-09 15:17:17.426  WARN 64359 --- [rics-registry-0] c.w.s.c.m.WavefrontSdkMetricsRegistry    : Unable to send internal SDK metric

java.io.IOException: attempt to send using closed sender
	at com.wavefront.sdk.common.clients.WavefrontClient.sendMetric(WavefrontClient.java:323) ~[wavefront-sdk-java-2.6.2.jar:na]
	at com.wavefront.sdk.common.metrics.WavefrontSdkMetricsRegistry.run(WavefrontSdkMetricsRegistry.java:163) ~[wavefront-sdk-java-2.6.2.jar:na]
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[na:na]
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

2021-04-09 15:17:17.427  WARN 64359 --- [rics-registry-0] c.w.s.c.m.WavefrontSdkMetricsRegistry    : Unable to send internal SDK metric

java.io.IOException: attempt to send using closed sender
	at com.wavefront.sdk.common.clients.WavefrontClient.sendMetric(WavefrontClient.java:323) ~[wavefront-sdk-java-2.6.2.jar:na]
	at com.wavefront.sdk.common.metrics.WavefrontSdkMetricsRegistry.run(WavefrontSdkMetricsRegistry.java:163) ~[wavefront-sdk-java-2.6.2.jar:na]
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[na:na]
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

2021-04-09 15:17:17.427  WARN 64359 --- [rics-registry-0] c.w.s.c.m.WavefrontSdkMetricsRegistry    : Unable to send internal SDK metric

java.io.IOException: attempt to send using closed sender
	at com.wavefront.sdk.common.clients.WavefrontClient.sendMetric(WavefrontClient.java:323) ~[wavefront-sdk-java-2.6.2.jar:na]
	at com.wavefront.sdk.common.metrics.WavefrontSdkMetricsRegistry.run(WavefrontSdkMetricsRegistry.java:163) ~[wavefront-sdk-java-2.6.2.jar:na]
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[na:na]
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

The exact same app with this extra configuration line will not emit those warnings:

wavefront.metrics.extract-jvm-metrics=false

Spring Boot devtools is a development feature that allows you to reduce the feedback loop by restarting a running process, taking into account any code change you've made in the IDE.

When the ApplicationContext is closed, any bean that is Closeable or has a close method will be disposed accordingly. It looks like that something WavefrontJvmReporter initiates is not disposed properly and continues to operate a sender that has been closed.

Here is a reproducer: https://github.com/snicoll-scratches/test-wavefront-devtools

  • Open the project and run the app from an IDE.
  • Change some code, for instance, change the string in HomeController.
  • Build the project (compile). This will restart the app automatically
  • After a little while the warnings show up.

Disable account negotiation if wavefront.freemium-account is false

Playing around with the starter a bit more I've noticed it also kicks in in integration tests which is not so bad if you're looking at doing an end to end test but a bit annoying if you've configured your integration test to disable metrics.

In such a case we should look at the wavefront.freemium-account since we set it to true if it's not present anyway. If the user has set it to false explicitly this is a reasonable sign that we should back off.

expose PCF VCAP_APPLICATION env variables as point tags

It would be great if wavefront spring boot autoconfigure can also automatically collect and expose important user env variables and also VCAP_APPLICATION variables, for example:
{ "VCAP_APPLICATION": { "application_id": "77e57641-29e8-4363-9e82-5f5aeb286a14", "application_name": "pcf-hello", "application_uris": [ "pcf-hello.cfapps.io" ], "application_version": "5e7a0053-7e5c-408a-88f8-5ca513e9f314", "cf_api": "https://api.run.pivotal.io", "limits": { "disk": 1024, "fds": 16384, "mem": 800 }, "name": "pcf-hello", "organization_id": "a78c4ee4-67ca-4105-bb8e-1286c9c2bbec", "organization_name": "yooh-org", "process_id": "77e57641-29e8-4363-9e82-5f5aeb286a14", "process_type": "web", "space_id": "23bdb750-4a4b-4d29-a1d5-07a4449afdda", "space_name": "development", "uris": [ "pcf-hello.cfapps.io" ], "users": null, "version": "5e7a0053-7e5c-408a-88f8-5ca513e9f314" } }

If we can expose these variables as point tags, that would be great, as these point tags are also exposed in PCF container metrics and in that way, we can related which applications are running on which container more easily.

Consider renaming project

For consistency with other repositories in this organization it would be nice if the name of this repo starts with wavefront.

I'd suggest wavefront-spring-boot as it opens up the possiblity to add more modules to this repo (for instance, the current autoconfigure module and a starter).

Thanks for the consideration.

Potential dependency for older code?

I had a failure when testing Pivotal's "fortune-teller" application whose last commit was back in 2016: https://github.com/spring-cloud-samples/fortune-teller

Steps to reproduce:

  1. install Wavefront for Spring Boot Autoconfigure
  1. install Fortune Teller
  • git clone https://github.com/spring-cloud-samples/fortune-teller.git

  • cd fortune-teller

  • mvn clean package [FAIL]

  • cd fortune-teller-ui

  • Edit pom.xml to add dependency:
    groupId: javax.xml.bind
    artifactId: jaxb-api
    version: 2.3.0

  • mvn clean package [SUCCESS]

  • Edit pom.xml for each service with version equal to 2.0.0-SNAPSHOT
    [NOTE: document does not state to add word SNAPSHOT

  • Add wavefront.properties for each service in src…main…resources.

  • mvn clean package [FAIL]
    KEY ERROR found in io.spring.cloud.samples.fortuneteller.ui.services.fortunes.FortuneServicePactTest: Caused by: java.lang.IllegalStateException: Could not evaluate condition on io.opentracing.contrib.spring.web.starter.ServerTracingAutoConfiguration#tracingHandlerInterceptor due to org/springframework/boot/web/servlet/FilterRegistrationBean not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @componentscan in the default package by mistake)
    at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:55)

Wavefront libraries not compatible with Spring boot 2.4.x

Hello,

I am working with spring boot 2.4.1 version and added the following dependencies for observability integration with wavefront. It did not work with version 2.4.1, I had to change the version to 2.3.x to get this working.

<dependency>
		  <groupId>com.wavefront</groupId>
		  <artifactId>wavefront-spring-boot-starter</artifactId>
		</dependency>		
		 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
            <version>2.2.2.RELEASE</version>
         </dependency>

Can you please confirm if spring boot 2.4.1 is not compatible. Also when we are planning to release the update if it's not compatible.

Thanks
Geeta

Make it more obvious what application id is used by an application

With a vanilla setup, we start emitting metrics (and potentially traces) for unnamed_service in the unnamed_application. I wonder if we shouldn't make it more obvious so that the user has a chance to understand that:

  • They should look for those IDs to find back metrics and traces for that app
  • This can (and should be) configured.

I should be not intrusive, a single line INFO message should do.

Use Wavefront proxy for URI when running on Tanzu Application Service

Tanzu Application Service has a tile for the Wavefront Proxy.

TAS developers are used to the idea of auto-configuring their Spring Boot application when a proper service binding exists (such as RabbitMQ or MySQL).

It would be a pleasant experience to have auto-configuration of the Wavefront proxy URI when a developer binds the Wavefront proxy to their application in TAS.
Today, developers can only dynamically get the value from the wavefront proxy if they use a Config server, because properties in manifest.yml cannot accept environment variables such as ${vcap.services...} which are injected to the container at a later stage.

Here's a possible code block that uses cfenv to find the information.

try {
	CfEnv cfEnv = new CfEnv();
	CfCredentials creds = cfEnv.findCredentialsByLabel("wavefront-proxy");
	String uri = creds.getHost().concat(":").concat(creds.getPort());
	// Set the wavefront URI = uri
	// Optional - remove/ignore a provided wavefront token if exists, since we should use the token set in the proxy in that case.
	
} catch (IllegalArgumentException e) {
	//Continue normal flow
}

Rename ~/.wavefront_token to ~/.wavefront_freemium

I think the current ~.wavefront_token name is confusing and we could use some clarification.

First and foremost, this is used by the account negotiation as a way to save the token. Whenever we read a token from that file, it is assumed it is a freemium account. If we don't want that, we need a different way of flagging the api token as being from a freemium account, see #28 for a concrete example.

The way the file is named makes it possible for someone to think they can put their existing token so that it is automatically configured for whatever app they are working on. Besides breaking the assumption it's a freemium account, it's also implying that this token is for wavefront.surf. Since there is no way to specify the cluster URI in that file, this can lead to more confusion and errors.

There are other mechanisms in Spring Boot to share properties for several applications (you could use environment variables or system properties with a common startup script). I don't think we should promote editing that file manually as it conflicts with the official configuration keys that Spring Boot already exposes.

A possible solution would be to rename the file to make it crystal clear it is about a freemium account, i.e. /.wavefront_freemium.

Adding spring-data-starter-data-jpa with ddl-auto=update causes block at startup

I've found that if I attempt to instrument a spring-boot app with Spring Data JPA and DDL-AUTO=Update, the application freezes during startup and never finishes initialization. I was able to reproduce in the sample app within this repository.

I forked the this repo added one commit with the updates to the sample app. See this link: doddatpivotal@2745ff9

When I run the application, I get the following logs. I faced this in my application, but wanted to isolate only the changes from the sample app that would cause the problem to occur.

2020-04-21 19:38:24.390  INFO 59069 --- [           main] sample.WavefrontSampleApplication        : Starting WavefrontSampleApplication on Dodds-MacBook-Pro.local with PID 59069 (/Users/dpfeffer/temp/wavefront-spring-boot/wavefront-spring-boot-sample/target/classes started by dpfeffer in /Users/dpfeffer/temp/wavefront-spring-boot)
2020-04-21 19:38:24.392  INFO 59069 --- [           main] sample.WavefrontSampleApplication        : No active profile set, falling back to default profiles: default
2020-04-21 19:38:25.097  INFO 59069 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFERRED mode.
2020-04-21 19:38:25.133  INFO 59069 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 30ms. Found 1 JPA repository interfaces.
2020-04-21 19:38:25.317  INFO 59069 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'io.opentracing.contrib.spring.cloud.async.DefaultAsyncAutoConfiguration$DefaultTracedAsyncConfigurerSupport' of type [io.opentracing.contrib.spring.cloud.async.DefaultAsyncAutoConfiguration$DefaultTracedAsyncConfigurerSupport$$EnhancerBySpringCGLIB$$d9274efb] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 19:38:25.490  INFO 59069 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'io.opentracing.contrib.spring.cloud.async.DefaultAsyncAutoConfiguration' of type [io.opentracing.contrib.spring.cloud.async.DefaultAsyncAutoConfiguration$$EnhancerBySpringCGLIB$$da451f98] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 19:38:25.696  INFO 59069 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-04-21 19:38:25.702  INFO 59069 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-04-21 19:38:25.702  INFO 59069 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-04-21 19:38:25.757  INFO 59069 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-04-21 19:38:25.758  INFO 59069 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1300 ms
2020-04-21 19:38:26.114  INFO 59069 --- [           main] i.m.c.instrument.push.PushMeterRegistry  : publishing metrics for WavefrontMeterRegistry every 1m
2020-04-21 19:38:26.273  INFO 59069 --- [           main] o.c.s.w.s.ServerTracingAutoConfiguration : Creating FilterRegistrationBean bean with TracingFilter mapped to [], skip pattern is "/api-docs.*|/swagger.*|.*\.png|.*\.css|.*\.js|.*\.html|/favicon.ico|/hystrix.stream|/actuator/(beans|beans/.*|caches|caches/.*|health|health/.*|info|info/.*|conditions|conditions/.*|configprops|configprops/.*|env|env/.*|loggers|loggers/.*|heapdump|heapdump/.*|threaddump|threaddump/.*|metrics|metrics/.*|scheduledtasks|scheduledtasks/.*|mappings|mappings/.*)"
2020-04-21 19:38:26.399  INFO 59069 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-21 19:38:26.442  INFO 59069 --- [         task-1] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-04-21 19:38:26.484  INFO 59069 --- [           main] o.c.s.w.s.ServerTracingAutoConfiguration : Creating WebMvcConfigurer bean with class io.opentracing.contrib.spring.web.interceptor.TracingHandlerInterceptor
2020-04-21 19:38:26.488  WARN 59069 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-04-21 19:38:26.501  INFO 59069 --- [         task-1] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.13.Final
2020-04-21 19:38:26.632  INFO 59069 --- [         task-1] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}

Add support for Spring Boot 2.4.x

Spring Boot 2.4.x is about to be GA with the Spring Cloud 2020.0.0 release train that brings a major release of Sleuth. We need to adapt the integration code to be compatible with those.

Text cannot be parsed to a Duration

Steps to reproduce:

  • Create SpringMVC+Thymleaf+Actuator, Java 11, Maven, Spring Boot 2.2.6 via start.spring.io
  • Add template and controller
  • Build and install starter
  • Add starter dependency to project
  • ./mvnw clean install

Notes:
I've also tested with Spring Boot 2.1.3 and Java 8

Repository is public:
https://github.com/dashaun/wavefront-spring-boot-starter-demo

/cc @seanbot2000

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tracingHandlerInterceptor' defined in class path resource [io/opentracing/contrib/spring/web/starter/ServerTracingAutoConfiguration.class]: Unsatisfied dependency expressed through method 'tracingHandlerInterceptor' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tracer' defined in class path resource [com/wavefront/springboot/WavefrontSpringBootAutoConfiguration.class]: Unsatisfied dependency expressed through method 'tracer' parameter 4; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wavefrontMeterRegistry' defined in class path resource [com/wavefront/springboot/WavefrontSpringBootAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.micrometer.wavefront.WavefrontMeterRegistry]: Factory method 'wavefrontMeterRegistry' threw exception; nested exception is java.time.format.DateTimeParseException: Text cannot be parsed to a Duration
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tracingHandlerInterceptor' defined in class path resource [io/opentracing/contrib/spring/web/starter/ServerTracingAutoConfiguration.class]: Unsatisfied dependency expressed through method 'tracingHandlerInterceptor' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tracer' defined in class path resource [com/wavefront/springboot/WavefrontSpringBootAutoConfiguration.class]: Unsatisfied dependency expressed through method 'tracer' parameter 4; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wavefrontMeterRegistry' defined in class path resource [com/wavefront/springboot/WavefrontSpringBootAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.micrometer.wavefront.WavefrontMeterRegistry]: Factory method 'wavefrontMeterRegistry' threw exception; nested exception is java.time.format.DateTimeParseException: Text cannot be parsed to a Duration
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tracer' defined in class path resource [com/wavefront/springboot/WavefrontSpringBootAutoConfiguration.class]: Unsatisfied dependency expressed through method 'tracer' parameter 4; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wavefrontMeterRegistry' defined in class path resource [com/wavefront/springboot/WavefrontSpringBootAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.micrometer.wavefront.WavefrontMeterRegistry]: Factory method 'wavefrontMeterRegistry' threw exception; nested exception is java.time.format.DateTimeParseException: Text cannot be parsed to a Duration
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wavefrontMeterRegistry' defined in class path resource [com/wavefront/springboot/WavefrontSpringBootAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.micrometer.wavefront.WavefrontMeterRegistry]: Factory method 'wavefrontMeterRegistry' threw exception; nested exception is java.time.format.DateTimeParseException: Text cannot be parsed to a Duration
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.micrometer.wavefront.WavefrontMeterRegistry]: Factory method 'wavefrontMeterRegistry' threw exception; nested exception is java.time.format.DateTimeParseException: Text cannot be parsed to a Duration
Caused by: java.time.format.DateTimeParseException: Text cannot be parsed to a Duration```

Ability for auto-configure to store auto-negotiated URL and make it available within the configuration bean

I've been testing the auto-config and found checking the standard output for auto-negotiated URL (the first URL that you must enter in order to enable the account and start using your Wavefront) to be very cumbersome if you are not running this locally on your dev / test environment.

So, was thinking maybe we can develop a bean that would tell us the following, once the application starts up:

  • Whether the application has any auto-negotiated URL so that user can get (without going through the standard output/log) to copy and open it.
  • Whether the application currently has well known file with URL and token already populated and user can check their values from a bean
  • Other information (like how long ago that happened, and whether it is expired, etc..?)

Just relying on the output messages when the application first starts can work, but is actually not so great if you want to somehow propagate that message automatically to somewhere else... as I can imagine if users would want to notify some third party notification whenever a new negotiation takes place.

Upgrade to Spring Boot 2.5.0

As part of the new 2.2.0 feature release, it would be nice to align with the next feature release of Spring Boot. A 2.5.0-RC1 is available for testing with the GA release scheduled mid-May.

Sleuth traces are exported with "default" service name rather than "unnamed_service"

We have some defaults here for wavefront.application.name (unnamed_application) and wavefront.application.service (unnamed_service).

If the service name is not set, this starter uses spring.application.name as this is a well-known property to configure the name of the service. We use this logic to emit metrics but, unfortunately, the Sleuth integration uses @LocalServiceName that always provides a default value if spring.application.name isn't set.

In the absence of any configuration, metrics are exported with service=unnamed_service and traces are exported with service=default.

Provide single-use login URL even if managed account configuration is set explicitly

On startup we provide a message to users to share the configuration that was auto-negotiated. When they do that we don't provide them a one-time link anymore. And the actuator endpoint does not ask for a one time link anymore as it doesn't know it is a managed account.

After having checked with @panghy, we can assume for now that all accounts from https://wavefront.surf are managed and we can request a one time link for them. We should probably add an option to stop requesting a one-time link on startup in case the user has turned that into a proper account.

Loose Configured URL and ApiToken Using Spring Cloud Starter Kubernetes Config

If you drop the following dependency in the samples application, then even if you configure the following properties, it always generates a new key and uses the developer URI. I found this behavior in my app and was able to reproduce in the sample app in this repo. I expect many people will want to use both spring cloud kubernetes and wavefront. If I remove the depenency and keep the properties in my sample app application.properties, then a new token is not generated. That is the expected result I want with the dependency.

management.metrics.export.wavefront.api-token=<YOUR_KEY>
management.metrics.export.wavefront.uri=https://surf.wavefront.com

	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
		<version>1.1.2.RELEASE</version>
	</dependency>

Do not negotiate account in integration tests

At the moment, the account negotiation EnvironmentPostProcessor kicks in in integration tests (i.e. @SpringBootTest). This has two unwanted side effects:

  • Accounts are provisioned by CI engine or wherever the test suite is invoked
  • Exporting metrics as part of testing an app does not make much sense unless the user has opt-in for that behaviour. The current arrangement makes it the default and it's not really obvious this is happening.

Changing that will lead a test to fail as we haven't provided a token. This should bring us back to what most other micrometer registries are doing. This can be easily disabled using management.metrics.export.wavefront.enabled.

API Token Still Required When Sending Data via Proxy

When management.metrics.export.wavefront.uri is pointed at a Wavefront Proxy, apps fail to start-up if the management.metrics.export.wavefront.apiToken property is not set:

Invalid Micrometer configuration detected:

  • management.metrics.export.wavefront.apiToken was 'null' but it must be set whenever publishing directly to the Wavefront API

Specifying a dummy value for the API token allows the app to start up fine.

ConfigMap cannot overwrite wavefront freemium account creation

I developed a spring boot application on my local laptop with the following application.properties

wavefront.application.name=demo
wavefront.application.service=HelloRest

As a natural use case, when I move to kubernetes env, i want to use configmap to override the auto freemium config.
So i created the following config map.

kind: ConfigMap
apiVersion: v1
metadata:
  name: hellorest
  namespace: demo8
data:
  wavefront.tracing.enabled: "false"
  wavefront.freemium-account: "false"
  management.metrics.export.wavefront.enabled: "false"
  spring.sleuth.enabled: "false"

However I still see the freemium account being configured.
When enabling debug log, i see that the configmap is getting loaded properly during start up

2020-07-10 01:44:33.302 DEBUG [hellorest,,,] 1 --- [           main] o.s.c.e.PropertySourcesPropertyResolver  : Found key 'management.metrics.export.wavefront.enabled' in PropertySource 'bootstrapProperties-configmap.hellorest.demo8' with value of type String
2020-07-10 01:44:33.330 DEBUG [hellorest,,,] 1 --- [           main] o.s.c.e.PropertySourcesPropertyResolver  : Found key 'wavefront.tracing.enabled' in PropertySource 'bootstrapProperties-configmap.hellorest.demo8' with value of type String

However the i don't see any wavefront.freemium-account being loaded.

root@ubuntu18:~/wavefront-demos/demo8/kube-demo# kubectl logs helloworld-86bfb659b9-g6m52 -n demo8 | grep freemium
root@ubuntu18:~/wavefront-demos/demo8/kube-demo#

By the way, adding the above parameters directly in application properties to application.properties, i see that freemium account creation get's disabled.

Consider using explicit dependencies rather than version ranges

Please consider using explicit dependency versions rather than version ranges. This makes the build non repeatable and expressing an opinion here does not prevent users to override to a more recent version, either via dependency management or by declaring the dependency with a specific version.

Use spring.application.name if no wavefront service is configured

spring.application.name is a property that is very often used to specify the identify of a particular app (service), especially in cloud-native architectures.

Right now we have ApplicationTags with application name and service.

@adriancole mentions that in his original PR. I was tempted to actually implement that logic directly in the auto-configuration of ApplicationTags.

However, I am confused whether we should use application name or service. Looking at the Wavefront dashboard, it seems like the name aggregates an number of "services". From the perspective, I think spring.application.name should be used as the default of wavefront.application.service.

Continually face metric point tag value cannot be blank.

I continually see this error in my logs. Is there some error checking that could be added.

java.lang.IllegalArgumentException: metric point tag value cannot be blank for tag key: uri (http.server.requests.max source=todos-api-855459587c-cs4xs exception=[None] method │
│ =[POST] application=[John's Todos Application] service=[todos-api] uri=[] outcome=[SUCCESS] status=[201])                                                                       │
│     at com.wavefront.sdk.common.Utils.metricToLineData(Utils.java:96)                                                                                                           │
│     at io.micrometer.wavefront.WavefrontMeterRegistry.addMetric(WavefrontMeterRegistry.java:367)                                                                                │
│     at io.micrometer.wavefront.WavefrontMeterRegistry.writeTimer(WavefrontMeterRegistry.java:311)                                                                               │
│     at io.micrometer.core.instrument.Meter.match(Meter.java:111)                                                                                                                │
│     at io.micrometer.wavefront.WavefrontMeterRegistry.lambda$publish$0(WavefrontMeterRegistry.java:197)                                                                         │
│     at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:267)                                                                                                │
│     at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)                                                                                           │
│     at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)                                                                                                    │
│     at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)                                                                                             │
│     at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)                                                                                            │
│     at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)                                                                                      │
│     at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)                                                                                                    │
│     at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)                                                                                                   │
│     at io.micrometer.wavefront.WavefrontMeterRegistry.publish(WavefrontMeterRegistry.java:207)                                                                                  │
│     at io.micrometer.core.instrument.push.PushMeterRegistry.publishSafely(PushMeterRegistry.java:48)                                                                            │
│     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)                                                                                                  │
│     at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)                                                                                                         │
│     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)                                                    │
│     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)                                                           │
│     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)                                                                                          │
│     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)                                                                                          │
│     at java.lang.Thread.run(Thread.java:748)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.