Skip to content

WIP: Auto-config for Extensions API tracer wrapper, metrics (with promethe… - #15

Closed
objectiser wants to merge 3 commits into
opentracing-contrib:masterfrom
objectiser:metricsprototype
Closed

WIP: Auto-config for Extensions API tracer wrapper, metrics (with promethe…#15
objectiser wants to merge 3 commits into
opentracing-contrib:masterfrom
objectiser:metricsprototype

Conversation

@objectiser

Copy link
Copy Markdown
Contributor

…us metrics reporter) and tracer resolver.

Prototype to investigate how we can add metrics (with prometheus reporter) support to an OpenTracing instrumented SB app with zero or minimal additional configuration.

This branch shows an updated accountmgr service that now only requires the maven dependencies, and additional metrics labels defined as beans (although these could be defined in a shared module to be completely outside scope of app):
https://github.com/objectiser/opentracing-prometheus-example/tree/autoconfig/simple/accountmgr

Currently contains a number of changes that could be separated out into their own PRs:

  • auto config for TracerResolver - so if tracer impl supports the resolver, then the app does not need to do anything regarding obtaining the tracer
  • extensions API tracer - only used to decorate the actual tracer if atleast one TracerObserver is found
  • metrics tracer observer
  • prometheus support - making the metrics reporter available (with any additional metric labels configured with the app) and if necessary establishing the servlet for the metrics exporter endpoint

@Configuration
public class TracerBeanPostProcessor implements BeanPostProcessor {

private static final Logger log = Logger.getLogger(TracerBeanPostProcessor.class.getName());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not slf4j logger?

@jpkrohling jpkrohling Jul 28, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All logging frameworks provide a logback for JUL, and JUL is the only logging framework that doesn't require an external dependency. So, it's usually a better choice for a library.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way: my point is moot if slf4j is already a dependency (I'm not sure that's the case)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use what is default/preferred in spring boot, and I guess it's not JUL

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jpkrohling jpkrohling left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, although I can't judge on the Spring semantics.

private String metricsName;

@Autowired(required=false)
private Set<MetricLabel> metricLabels;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this coming from? Spring I guess, but what happens when required = false?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - the app provides the MetricLabel beans. If none are provided, then metricLabels is null.

import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(value = {io.prometheus.client.exporter.MetricsServlet.class,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be more readable if the classes are imported.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check - but examples I have seen did not import the class, possibly because it may affect class loading of this class?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just found some examples that use the annotation on imported classes, so I guess it isn't a problem - so will change.


tracerObserver.onStart(spanData).onFinish(spanData, System.currentTimeMillis());

Mockito.verify(metricsReporter).reportSpan(spanData);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a while since I last used mockito, so, I'm missing something here... You are asserting that reportSpan has been called and that the mocked spanData was used as parameter, but what actually called the reporter?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially the resolved tracerObserver is a metrics observer configured with the mock metricsReporter - so when the onFinish is called with the mock spanData it gets passed through to the metrics reporter via the metrics observer.


@Bean
public io.opentracing.Tracer tracer() {
return io.opentracing.contrib.tracerresolver.TracerResolver.resolveTracer();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be imported?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reason as above - depends on whether this affects the ConditionalOnClass annotation.

@pavolloffay

Copy link
Copy Markdown
Contributor

auto config for TracerResolver - so if tracer impl supports the resolver, then the app does not need to do anything regarding obtaining the tracer

Could we make changes in TracerAutoConfiguration in spring-web module to get the tracer from GlobalTracer? If that works I think we can avoid tracer resolver bindings in this repo.

@malafeev

Copy link
Copy Markdown
@Bean 
public Tracer tracer() {
  return ...
}

couldn't work to avoid tracer resolver and GlobalTracer ?

@objectiser

Copy link
Copy Markdown
Contributor Author

There are two issues issue - resolving the Tracer, and registering it with the GlobalTracer.

Dealing with GlobalTracer first - this will be necessary (I believe) as some of the framework instrumentations will not be spring based, so would not inject the tracer, but instead obtain it from GlobalTracer - so whatever tracer resolution mechanism is used, we need to make sure it is registered - which is not currently dealt with by this PR.

Now, in terms of resolving the Tracer, there are two options - either the application takes responsibility or we use the TracerResolver. The aim of the tracerresolver code in this PR is to only use the TracerResolver if it is in the classpath - and therefore the app developer has decided to use that approach.

Not saying that the current approach is ideal - it needs more work to understand how best to deal with resolving the Tracer bean - but I think it is important to support the mechanism as an option available to users.

@objectiser

Copy link
Copy Markdown
Contributor Author

As a suggestion for dealing with registering with GlobalTracer - perhaps we just need a config that injects the Tracer and uses it to register on the GlobalTracer - so then no other code needs to worry about the registration? It will just use whatever Tracer has been resolved (through whatever mechanism) and it injected.

@pavolloffay

Copy link
Copy Markdown
Contributor

How does the TracerResolver work? IIRC it registers the resolved tracer to GlobalTracer and the app obtains the tracer from it.

As I propose we can simply change the current TracerAutoConfiguration for fallback to GlobalTracer if there is no Tracer bean available in the context. Or does this PR has other benefits?

@objectiser

Copy link
Copy Markdown
Contributor Author

Let me split out the tracer resolver part into a separate PR, as it needs to be discussed separately.


@Override
public void contextInitialized(javax.servlet.ServletContextEvent sce) {
sce.getServletContext().setAttribute(TracingFilter.SKIP_PATTERN, Pattern.compile(metricsPath));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skip pattern param is used only when the filter is registered via web.xml. In spring-web it is registered directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn't intended as a real solution, hence starting discussion here opentracing-contrib/java-spring-web#28.

import io.prometheus.client.exporter.MetricsServlet;

@Configuration
@ConditionalOnClass(value = {MetricsServlet.class, TracingFilter.class})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should not depend on TracingFilter

@pavolloffay

Copy link
Copy Markdown
Contributor

I have been thinking about this for a while and we should define boundaries of this project. The purpose of this repository is to instrument spring cloud related frameworks.

This tracer/prometheus metrics integration is not spring/cloud related. Btw there is also spring boot metrics project https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html and https://github.com/micrometer-metrics/micrometer (not sure how they overlap).

So far tracer instance has been obtained from spring context which is a standard way in spring I am not sure if tracer resolver autoconfig adds a benefit here. If users what to use it they can simple drop traver resolver dependency on classpath and write:

@Bean
public io.opentracing.tracer tracer() {
   return TracerResolver.resolveTracer();
}

and it is not necessary to have auto-config and starter artefact for it.

@objectiser

Copy link
Copy Markdown
Contributor Author

I would view its scope slightly differently - I believe it is about making OpenTracing based instrumentation as easy as possible to consume within spring boot/cloud.

The metrics references you mentioned do not provide the same level of application metric granularity as java-metrics instrumentation based on OpenTracing.

Regarding TracerResolver autoconfig (which is now in a different PR) - the Tracer needs to get into the spring context somehow, what you are suggesting is that this must be done by the application developer, while I am suggesting that having the option to include it via auto-config may mean the app developer doesn't have to change any code. Isn't it better to give the developer a choice?

@objectiser

objectiser commented Aug 1, 2017

Copy link
Copy Markdown
Contributor Author

As I am away for the rest of this week, what I would propose is that if no one has expressed an interest in auto-config for metrics being part of this repo by start of next week, then I'll move it to the java-metrics repo. Same for tracer resolver auto-config.

cc @malafeev @tedsuo @alesj

@pavolloffay

Copy link
Copy Markdown
Contributor

what you are suggesting is that this must be done by the application developer, while I am suggesting that having the option to include it via auto-config may mean the app developer doesn't have to change any code. Isn't it better to give the developer a choice?

It's true, I would vote for a little explicit code rather than maintaining (in my opinion unnecessary) wrapper around trace resolver.

So if you want to integrate tracing you have to include certain dependencies in your class path. In other words change project source code e.g (pom, gradle). This would be all if we supported trace resolver starter. However, what I propose is to add one self-contained class:

@Configuration
public class TracingConfiguration {
   @Bean
   public io.opentracing.tracer tracer() {
      return TracerResolver.resolveTracer();
   }
}

You don't have to reference this class from anywhere. If you want to remove tracing you just remove it. I think it's better to stick with spring conventions and keep the configuration concise.

@objectiser

Copy link
Copy Markdown
Contributor Author

Closing, moving autoconfigure code to:
opentracing-contrib/java-api-extensions#11
opentracing-contrib/java-metrics#16

@objectiser objectiser closed this Aug 8, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants