Skip to content

Instantly share code, notes, and snippets.

@dividedmind
Last active October 21, 2025 14:31
Show Gist options
  • Save dividedmind/453140198ecacbf00ead6f00adede9df to your computer and use it in GitHub Desktop.
Save dividedmind/453140198ecacbf00ead6f00adede9df to your computer and use it in GitHub Desktop.
Handling large AppMaps

Handling large AppMaps

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.

obraz

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:

obraz

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.

obraz

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:

obraz

The AppMap is now 80 MB — much better, but still a bit large. Let's open it.

obraz

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"
obraz

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:

obraz

Or "Hide" option in the sidebar: obraz

For more advanced filtering options, we can use the filter dialog in the AppMap viewer:

obraz

Using CLI

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 MB

prune 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#onSuccess

The list of pruned functions can provide some ideas on what to exclude in appmap.yml.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment