This guide shows how to deal with large AppMaps on an example of Spring PetClinic.
First, let's clone the application and open it in VSCode
$ git clone -b demo/large-appmaps https://github.com/getappmap/spring-petclinic.git
$ cd spring-petclinic
$ code .Select to trust the project on a pop-up.
Note: There is currently an issue with large AppMap handling in IntelliJ, so we'll be using VSCode in this guide.
Open appmap.yml and note it's configured to record the entirety of org.springframework.boot package:
name: "spring-petclinic"
language: "java"
packages:
- path: "org.springframework.samples.petclinic"
- path: "org.springframework.boot"
appmap_dir: "tmp/appmap"Now, create a launch configuration by running "Debug: Add configuration..." from the command palette (Ctrl+Shift+P) and choosing Java. In the launch.json, modify the CliReporter entry to say:
{
"type": "java",
"name": "CliReporter (with AppMap)",
"request": "launch",
"mainClass": "org.springframework.samples.petclinic.cli.CliReporter",
"projectName": "spring-petclinic",
"vmArgs": "-javaagent:${userHome}/.appmap/lib/java/appmap.jar -Dappmap.recording.auto=true"
}Note javaagent argument to attach the AppMap agent, and the appmap.recording.auto to enable process recording.
Now, run this launch configuration to create the recording.
In the console you should see the output of the script and a message about an AppMap being created. On my machine this map is over 1 GB in size:
Because of its size it won't be indexed or show up in the AppMap sidebar, but we can open it directly by ctrl+clicking its path in the console. Note it will take a while to process.
The stats screen indicates a lot of noise from org.springframework.boot.context.properties.source, so let's exclude that from the recording by modifying appmap.yml:
name: "spring-petclinic"
language: "java"
packages:
- path: "org.springframework.samples.petclinic"
- path: "org.springframework.boot"
exclude:
- "org.springframework.boot.context.properties.source"
appmap_dir: "tmp/appmap"Now let's remove the AppMap (it might not show up in the explorer, so use command line) and rerun the application:
The AppMap is now 80 MB — much better, but still a bit large. Let's open it.
We see the AppMap now, along with a message that it's been automatically pruned. The stats panel indicates there's still some noise from org.springframework.boot. We can continue exclusion process to carve out just what we need; we can also enable shallow recording which will only capture events that cross packages:
name: "spring-petclinic"
language: "java"
packages:
- path: "org.springframework.samples.petclinic"
- path: "org.springframework.boot"
shallow: true
exclude:
- "org.springframework.boot.context.properties"
- "org.springframework.boot.autoconfigure"
- "org.springframework.boot.logging"
- "org.springframework.boot.ansi"
- "org.springframework.boot.type"
- "org.springframework.boot.env"
appmap_dir: "tmp/appmap"
It's best to examine the AppMaps with the project structure in mind and decide on which functions and objects are usually important to capture and which are just noise. This will help create an appmap.yml that will provide a good starting point for whatever we want to do with the project. Such a config file can then be committed to the repository.
Note that even an AppMap that is a couple megabytes large can still be overwhelming. If we don't want to exclude code from recording, but just temporarily hide some modules from the view, it's easy to do by clicking the cross on the module in the sequence view:
Or "Hide" option in the sidebar:

For more advanced filtering options, we can use the filter dialog in the AppMap viewer:
Note analysis of AppMap statistics can also be done using the CLI:
$ ~/.appmap/bin/appmap stats tmp/appmap/java -a 251021155910.appmap.json
Analyzing AppMap: /home/divide/projects/tmp/spring-petclinic/tmp/appmap/java/251021155910.appmap.json
1. function:org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader$PropertiesAutoConfigurationMetadata#get
count: 5862
estimated size: 5.4 MB
2. function:org/springframework/boot/logging/logback/ColorConverter#transform
count: 3624
estimated size: 3.0 MB
3. function:org/springframework/boot/logging/logback/ColorConverter#toAnsiString
count: 3624
estimated size: 2.4 MB
4. function:org/springframework/boot/ansi/AnsiOutput.toString
count: 3624
estimated size: 1.2 MB
5. function:org/springframework/boot/type/classreading/ConcurrentReferenceCachingMetadataReaderFactory#getMetadataReader
count: 2604
estimated size: 2.0 MB
6. function:org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader$PropertiesAutoConfigurationMetadata#getSet
count: 2280
estimated size: 2.1 MB
7. function:org/springframework/boot/env/SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource#getProperty
count: 2011
estimated size: 1.5 MB
8. function:org/springframework/boot/autoconfigure/condition/ConditionMessage#andCondition
count: 1915
estimated size: 1.3 MB
9. function:org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport.get
count: 1899
estimated size: 2.5 MB
10. function:org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition.resolve
count: 1748
estimated size: 1.1 MBprune command can be used to heuristically remove most frequent functions, pruning the map to a target size (note the resulting pruned AppMap will be created in the current directory to avoid overwriting):
$ ~/.appmap/bin/appmap prune -s 5mb tmp/appmap/java/251021155910.appmap.json
The following has been pruned from the original map:
function:org/springframework/boot/logging/logback/ColorConverter#transform
function:org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader$PropertiesAutoConfigurationMetadata#get
function:org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport.get
function:org/springframework/boot/logging/logback/ColorConverter#toAnsiString
function:org/springframework/boot/type/classreading/ConcurrentReferenceCachingMetadataReaderFactory#getMetadataReader
function:org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport.find
function:org/springframework/boot/env/SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource#getProperty
function:org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition#filter
function:org/springframework/boot/ansi/AnsiOutput.toString
function:org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader$PropertiesAutoConfigurationMetadata#getSet
function:org/springframework/boot/context/properties/bind/AbstractBindHandler#onFinish
function:org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition#match
function:org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition.resolve
function:org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport#recordConditionEvaluation
function:org/springframework/boot/logging/logback/EnclosedInSquareBracketsConverter#transform
function:org/springframework/boot/autoconfigure/condition/ConditionEvaluationReport$ConditionAndOutcomes#add
function:org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition$ClassNameFilter$2#matches
function:org/springframework/boot/logging/logback/LogbackLoggingSystem$1#decide
function:org/springframework/boot/context/properties/BindMethodAttribute.get
function:org/springframework/boot/autoconfigure/condition/SpringBootCondition#logOutcome
function:org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader$PropertiesAutoConfigurationMetadata#wasProcessed
function:org/springframework/boot/autoconfigure/condition/SpringBootCondition#matches
function:org/springframework/boot/autoconfigure/condition/ConditionMessage#andCondition
function:org/springframework/boot/autoconfigure/condition/ConditionMessage$Builder#because
function:org/springframework/boot/context/config/ConfigDataEnvironmentContributor#getChildren
function:org/springframework/boot/autoconfigure/condition/ConditionMessage$ItemsBuilder#items
function:org/springframework/boot/convert/ApplicationConversionService#addConverter
function:org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessor#postProcessBeforeInitialization
function:org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration$HealthEndpointGroupsBeanPostProcessor#postProcessAfterInitialization
function:org/springframework/boot/autoconfigure/AutoConfigurationSorter$AutoConfigurationClasses#get
function:org/springframework/boot/context/properties/ConfigurationPropertiesBean.get
function:org/springframework/boot/env/OriginTrackedMapPropertySource#getProperty
function:org/springframework/boot/actuate/autoconfigure/observation/ObservationRegistryPostProcessor#postProcessAfterInitialization
function:org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization
function:org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetailsBeanPostProcessor#postProcessBeforeInitialization
function:org/springframework/boot/io/Base64ProtocolResolver#resolve
function:org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor#postProcessAfterInitialization
function:org/springframework/boot/web/server/WebServerFactoryCustomizerBeanPostProcessor#postProcessAfterInitialization
function:org/springframework/boot/context/config/ConfigDataEnvironmentContributor$ContributorIterator#next
function:org/springframework/boot/web/server/WebServerFactoryCustomizerBeanPostProcessor#postProcessBeforeInitialization
function:org/springframework/boot/web/server/ErrorPageRegistrarBeanPostProcessor#postProcessAfterInitialization
function:org/springframework/boot/web/server/ErrorPageRegistrarBeanPostProcessor#postProcessBeforeInitialization
function:org/springframework/boot/autoconfigure/AutoConfigurationMetadataLoader$PropertiesAutoConfigurationMetadata#getInteger
function:org/springframework/boot/env/RandomValuePropertySource#getProperty
function:org/springframework/boot/autoconfigure/condition/ConditionOutcome.match
function:org/springframework/boot/context/properties/bind/BindConverter#convert
function:org/springframework/boot/context/properties/ConfigurationPropertiesBinder$ConfigurationPropertiesBindHandler#onStart
function:org/springframework/boot/autoconfigure/condition/OnWebApplicationCondition#getOutcomes
function:org/springframework/boot/autoconfigure/condition/OnClassCondition#getOutcomes
function:org/springframework/boot/autoconfigure/condition/OnBeanCondition#getOutcomes
function:org/springframework/boot/type/classreading/ConcurrentReferenceCachingMetadataReaderFactory#createMetadataReader
function:org/springframework/boot/autoconfigure/condition/ConditionMessage$Style$2#applyToItem
function:org/springframework/boot/autoconfigure/AutoConfigurationImportSelector$ConfigurationClassFilter#filter
function:org/springframework/boot/context/properties/bind/Bindable#hasBindRestriction
function:org/springframework/boot/autoconfigure/condition/ConditionMessage$Builder#didNotFind
function:org/springframework/boot/env/OriginTrackedPropertiesLoader$CharacterReader#read
function:org/springframework/boot/autoconfigure/condition/ConditionMessage.forCondition
function:org/springframework/boot/autoconfigure/condition/ConditionMessage$Builder#found
function:org/springframework/boot/autoconfigure/condition/OnBeanCondition#getMatchOutcome
function:org/springframework/boot/autoconfigure/condition/OnClassCondition#getMatchOutcome
function:org/springframework/boot/autoconfigure/condition/OnClassCondition$StandardOutcomesResolver#resolveOutcomes
function:org/springframework/boot/context/properties/bind/Bindable#withSuppliedValue
function:org/springframework/boot/autoconfigure/condition/FilteringSpringBootCondition$ClassNameFilter$1#matches
function:org/springframework/boot/autoconfigure/condition/OnBeanCondition$Spec#extractTypes
function:org/springframework/boot/autoconfigure/AutoConfigurationSorter$AutoConfigurationClasses#getClassesRequestedAfter
function:org/springframework/boot/context/properties/bind/Bindable#withAnnotations
function:org/springframework/boot/context/properties/bind/JavaBeanBinder$BeanProperty#getValue
function:org/springframework/boot/context/properties/bind/JavaBeanBinder$BeanProperty#addGetter
function:org/springframework/boot/autoconfigure/condition/OnBeanCondition#getMatchingBeans
function:org/springframework/boot/context/properties/bind/JavaBeanBinder$BeanProperty#addField
function:org/springframework/boot/autoconfigure/condition/ConditionOutcome.noMatch
function:org/springframework/boot/context/properties/bind/Bindable.of
function:org/springframework/boot/autoconfigure/condition/OnBeanCondition$Spec#validate
function:org/springframework/boot/context/properties/bind/JavaBeanBinder$BeanProperty#addSetter
function:org/springframework/boot/context/config/LocationResourceLoader#isPattern
function:org/springframework/boot/context/properties/bind/BindConverter$TypeConverterConversionService#canConvert
function:org/springframework/boot/context/properties/bind/Binder$Context#clearConfigurationProperty
function:org/springframework/boot/autoconfigure/condition/ConditionMessage.empty
function:org/springframework/boot/actuate/endpoint/EndpointId.of
function:org/springframework/boot/context/properties/bind/Binder#bind
function:org/springframework/boot/util/LambdaSafe$GenericTypeFilter#match
function:org/springframework/boot/context/properties/bind/DataObjectPropertyName.toDashedForm
function:org/springframework/boot/context/properties/EnableConfigurationPropertiesRegistrar#registerBeanDefinitions
function:org/springframework/boot/autoconfigure/AutoConfigurationReplacements#replace
function:org/springframework/boot/logging/DeferredLog$Lines#add
function:org/springframework/boot/context/properties/PropertyMapper#from
function:org/springframework/boot/autoconfigure/condition/ConditionMessage$ItemsBuilder#atAll
function:org/springframework/boot/context/properties/bind/JavaBeanBinder#bind
function:org/springframework/boot/util/LambdaSafe.callback
function:org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.applyScopedProxyMode
function:org/springframework/boot/context/properties/bind/AbstractBindHandler#onSuccess
function:org/springframework/boot/context/properties/EnableConfigurationPropertiesRegistrar.registerMethodValidationExcludeFilter
function:org/springframework/boot/context/properties/EnableConfigurationPropertiesRegistrar.registerInfrastructureBeans
function:org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.register
function:org/springframework/boot/logging/DeferredLog.logTo
function:org/springframework/boot/context/properties/ConfigurationPropertiesBinder.register
function:org/springframework/boot/context/properties/BoundConfigurationProperties.register
function:org/springframework/boot/context/properties/bind/Bindable#getAnnotation
function:org/springframework/boot/context/config/ConfigDataEnvironmentContributor#isActive
function:org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter#accept
function:org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter#map
function:org/springframework/boot/autoconfigure/condition/OnWebApplicationCondition#getMatchOutcome
function:org/springframework/boot/actuate/autoconfigure/endpoint/PropertiesEndpointAccessResolver#accessFor
function:org/springframework/boot/util/LambdaSafe$LambdaSafeCallback#invoke
function:org/springframework/boot/autoconfigure/condition/OnPropertyCondition#getMatchOutcome
function:org/springframework/boot/context/config/LocationResourceLoader#getResource
function:org/springframework/boot/context/properties/PropertyMapper$NullPointerExceptionSafeSupplier#get
function:org/springframework/boot/context/config/ConfigDataEnvironmentContributor#withReplacement
function:org/springframework/boot/context/config/ConfigDataEnvironmentContributor#hasUnprocessedImports
function:org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping#registerMapping
function:org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameter#getAnnotation
function:org/springframework/boot/logging/DeferredLog#trace
function:org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisor#apply
function:org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping#wrapServletWebOperation
function:org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscoverer#createOperation
function:org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar#register
function:org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer$1#createOperation
function:org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscoverer#createOperationKey
function:org/springframework/boot/context/properties/PropertyMapper$Source#to
function:org/springframework/boot/context/properties/bind/AggregateBinder$AggregateSupplier#wasSupplied
function:org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition$StandardExposureOutcomeContributor#getExposureOutcome
function:org/springframework/boot/context/properties/bind/JavaBeanBinder$Bean.get
function:org/springframework/boot/context/properties/bind/IndexedElementsBinder#isAllowRecursiveBinding
function:org/springframework/boot/actuate/autoconfigure/endpoint/expose/IncludeExcludeEndpointFilter$EndpointPatterns#matches
function:org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition#getMatchOutcome
function:org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscoverer#createEndpoint
function:org/springframework/boot/context/properties/bind/Binder.get
function:org/springframework/boot/actuate/autoconfigure/endpoint/web/MappingWebEndpointPathMapper#getRootPath
function:org/springframework/boot/context/properties/bind/JavaBeanBinder$Bean#getSupplier
function:org/springframework/boot/context/config/ConfigDataEnvironmentContributor#hasConfigDataOption
function:org/springframework/boot/actuate/autoconfigure/metrics/export/properties/PropertiesConfigAdapter#get
function:org/springframework/boot/actuate/autoconfigure/endpoint/expose/IncludeExcludeEndpointFilter#match
function:org/springframework/boot/util/LambdaSafe$Callback#invokeAnd
function:org/springframework/boot/autoconfigure/condition/ConditionMessage.of
function:org/springframework/boot/util/LambdaSafe$LambdaSafeCallback#withLogger
function:org/springframework/boot/context/properties/bind/BindResult.of
function:org/springframework/boot/context/properties/bind/Bindable#withExistingValue
function:org/springframework/boot/context/properties/bind/Bindable#withBindRestrictions
function:org/springframework/boot/context/properties/ConfigurationPropertiesBean#asBindTarget
function:org/springframework/boot/autoconfigure/condition/ConditionMessage$Builder#foundExactly
function:org/springframework/boot/context/properties/bind/JavaBeanBinder$BeanProperties#addProperties
function:org/springframework/boot/autoconfigure/condition/ConditionMessage$Style$1#applyToItem
function:org/springframework/boot/context/config/ConfigData$Options#contains
function:org/springframework/boot/context/properties/bind/IndexedElementsBinder#bindIndexed
function:org/springframework/boot/actuate/endpoint/invoke/reflect/OperationMethodParameters#stream
function:org/springframework/boot/context/properties/bind/Bindable#withBindMethod
function:org/springframework/boot/convert/ApplicationConversionService#addFormatterForFieldType
function:org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping#initCorsConfiguration
function:org/springframework/boot/actuate/endpoint/web/annotation/RequestPredicateFactory#getRequestPredicate
function:org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition#getEndpointAnnotation
function:org/springframework/boot/context/properties/BindMethodAttribute.set
function:org/springframework/boot/context/properties/bind/DefaultBindConstructorProvider#getBindConstructor
function:org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter#match
function:org/springframework/boot/web/servlet/ServletContextInitializerBeans$Seen#contains
function:org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer#isEndpointTypeExposed
function:org/springframework/boot/context/properties/ConfigurationPropertiesBinder#bind
function:org/springframework/boot/context/properties/PropertyMapper$Source#whenNonNull
function:org/springframework/boot/context/properties/bind/BoundPropertiesTrackingBindHandler#onSuccessThe list of pruned functions can provide some ideas on what to exclude in appmap.yml.