Add application startup metrics support

This commit adds a new `StartupStep` interface and its factory
`ApplicationStartup`. Such steps are created, tagged with metadata and
thir execution time can be recorded - in order to collect metrics about
the application startup.

The default implementation is a "no-op" variant and has no side-effect.
Other implementations can record and collect events in a dedicated
metrics system or profiling tools. We provide here an implementation for
recording and storing steps with Java Flight Recorder.

This commit also instruments the Spring application context to gather
metrics about various phases of the application context, such as:

* context refresh phase
* bean definition registry post-processing
* bean factory post-processing
* beans instantiation and post-processing

Third part libraries involved in the Spring application context can
reuse the same infrastructure to record similar metrics.

Closes gh-24878
This commit is contained in:
Brian Clozel
2020-07-27 15:08:01 +02:00
parent 4252b7fd7d
commit 9301d7a294
22 changed files with 890 additions and 16 deletions

View File

@@ -1610,3 +1610,66 @@ http\://www.foo.example/schema/jcache=com.foo.JCacheNamespaceHandler
# in 'META-INF/spring.schemas'
http\://www.foo.example/schema/jcache/jcache.xsd=com/foo/jcache.xsd
----
[[application-startup-steps]]
== Application Startup Steps
This part of the appendix lists the existing `StartupSteps` that the core container is instrumented with.
WARNING: The name and detailed information about each startup step is not part of the public contract and
is subject to change; this is considered as an implementation detail of the core container and will follow
its behavior changes.
.Application startup steps defined in the core container
|===
| Name| Description| Tags
| `spring.beans.instantiate`
| Instantiation of a bean and its dependencies.
| `beanName` the name of the bean, `beanType` the type required at the injection point.
| `spring.beans.smart-initialize`
| Initialization of `SmartInitializingSingleton` beans.
| `beanName` the name of the bean.
| `spring.context.annotated-bean-reader.create`
| Creation of the `AnnotatedBeanDefinitionReader`.
|
| `spring.context.base-packages.scan`
| Scanning of base packages.
| `packages` array of base packages for scanning.
| `spring.context.beans.post-process`
| Beans post-processing phase.
|
| `spring.context.bean-factory.post-process`
| Invocation of the `BeanFactoryPostProcessor` beans.
| `postProcessor` the current post-processor.
| `spring.context.beandef-registry.post-process`
| Invocation of the `BeanDefinitionRegistryPostProcessor` beans.
| `postProcessor` the current post-processor.
| `spring.context.component-classes.register`
| Registration of component classes through `AnnotationConfigApplicationContext#register`.
| `classes` array of given classes for registration.
| `spring.context.config-classes.enhance`
| Enhancement of configuration classes with CGLIB proxies.
| `classCount` count of enhanced classes.
| `spring.context.config-classes.parse`
| Configuration classes parsing phase with the `ConfigurationClassPostProcessor`.
| `classCount` count of processed classes.
| `spring.context.refresh`
| Application context refresh phase.
|
| `spring.event.invoke-listener`
| Invocation of event listeners, if done in the main thread.
| `event` the current application event, `eventType` its type and `listener` the listener processing this event.
|===

View File

@@ -11027,7 +11027,75 @@ location path as a classpath location. You can also use location paths (resource
with special prefixes to force loading of definitions from the classpath or a URL,
regardless of the actual context type.
[[context-functionality-startup]]
=== Application Startup tracking
The `ApplicationContext` manages the lifecycle of Spring applications and provides a rich
programming model around components. As a result, complex applications can have equally
complex component graphs and startup phases.
Tracking the application startup steps with specific metrics can help understand where
time is being spent during the startup phase, but it can also be used as a way to better
understand the context lifecycle as a whole.
The `AbstractApplicationContext` (and its subclasses) is instrumented with an
`ApplicationStartup`, which collects `StartupStep` data about various startup phases:
* application context lifecycle (base packages scanning, config classes management)
* beans lifecycle (instantiation, smart initialization, post processing)
* application events processing
Here is an example of instrumentation in the `AnnotationConfigApplicationContext`:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// create a startup step and start recording
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// create a startup step and start recording
val scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();
----
The application context is already instrumented with multiple steps.
Once recorded, these startup steps can be collected, displayed and analyzed with specific tools.
For a complete list of existing startup steps, you can check out the
<<application-startup-steps, dedicated appendix section>>.
The default `ApplicationStartup` implementation is a no-op variant, for minimal overhead.
This means no metrics will be collected during application startup by default.
Spring Framework ships with an implementation for tracking startup steps with Java Flight Recorder:
`FlightRecorderApplicationStartup`. To use this variant, you must configure an instance of it
to the `ApplicationContext` as soon as it's been created.
Developers can also use the `ApplicationStartup` infrastructure if they're providing their own
`AbstractApplicationContext` subclass, or if they wish to collect more precise data.
WARNING: `ApplicationStartup` is meant to be only used during application startup and for
the core container; this is by no means a replacement for Java profilers or
metrics libraries like https://micrometer.io[Micrometer].
To start collecting custom `StartupStep`, components can either get the `ApplicationStartup`
instance from the application context directly, make their component implement `ApplicationStartupAware`,
or ask for the `ApplicationStartup` type on any injection point.
NOTE: Developers should not use the `"spring.*"` namespace when creating custom startup steps.
This namespace is reserved for internal Spring usage and is subject to change.
[[context-create]]
=== Convenient ApplicationContext Instantiation for Web Applications