Java APM Agent configuration files: Explained!

Edit for Nov 20th, 2013: the information on this page primarily is for the On Premise solution of SCOM 2012 R2. Though users of Application Insights may find some of the information useful as well.

 

The Java APM Agent has three configuration files.  In this blog post I will give a brief explanation of their content and purpose. 

  • Starter.properties: bootstrapping
  • SEAgent.config.xml: controls event sending intervals
  • pmonitor.config.xml: instrumentation information (i.e. which methods and applications to instrument for monitoring)

Note: this information is for the preview release of the Java APM Agent (version 1.9.100.5).  This information should be relevant for the forthcoming release code as well; however, any of this could change by RTM.  :)

Starter.properties

This file is a simple set of name/value pairs that contains information for bootstrapping the Java APM agent.  In general, you will not modify this file. Perhaps the only interesting configuration here is the
ability to control the log level and location of the log file (note: make sure you pick a directory that you have write access too!).

The shipped configuration file looks like: 

# Jars to load by inner classloader.
# Use semicolons to separate the JAR entries
#
ClassPath=apm_monitor.jar;asm-all-3.3.1.jar;apm_producers.jar

# Path to pmonitor.config
#
MonitorConfigFileName=pmonitor.config.xml

# Path to SEAgent.config
#
AgentConfigFileName=SEAgent.config.xml

# Log file name
#
# Example for full-path location:
#                 LogFileName=c:\\tracelog\\apm-java-agent.log
#
# Example for relative path (to agent jar files):
#                 LogFileName=apm-java-agent.log
#
LogFileName=apm-java-agent.log
 
# Log level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)
#
LogLevel=INFO

Note: the Classpath property is semi-colon separated. While in the java world, that is not the correct classpath separator on all platforms, in the interest having a single configuration file.

SEAgent.config.xml

The SEAgent.config.xml file controls the where to send events, the frequency of sending events, length of troubleshooting windows, and interval for gathering counters.  In general, this file should generally be left alone unless you want to modify the intervals the collection intervals (i.e. if you have changed the intervals in the MPs, you should update the intervals in this file as well - in that case, update the eventSending frequency to the number which corresponds to the MP value).

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- Event sending class -->
    <!-- -->
    <!-- MBeanEventLog: -->
    <!-- communicates to JMX MBean Store. -->
    <!-- Performance Counters can be reached by the JMX Query: -->
    <!-- -->
    <!-- com.microsoft.ManagementServices.APMAgent:type=COUNTER -->
    <!-- -->
    <!-- Performance and Exception Events can be reached by the -->
    <!-- JMX Query: -->
    <!-- -->
    <!-- com.microsoft.ManagementServices.APMAgent:type=EVENT -->
    <!-- -->
    <eventSender>com.microsoft.ManagementServices.APMAgent.EventSender.MBeanEventLog</eventSender>

    <!-- Application Performance Counter configuration -->
    <appPerfCounters>
      <!-- The frequency (in seconds) to generate application -->
      <!-- performance counters. The default window is 5 minutes -->
      <!-- (i.e. 15*60 = 900 seconds). -->
      <!-- -->
      <!-- Once generated, these event will be placed in the -->
      <!-- event sending queue (see the eventSending section -->
      <!-- below). -->
      <frequency>900</frequency>
    </appPerfCounters>

    <!-- Troubleshooting configuration -->
    <troubleshooting>
      <!-- The length of the window (in seconds) to collect JVM -->
      <!-- statistics prior to the event. The default window is -->
      <!-- 15 minutes (i.e. 15*60 = 900 seconds). -->
      <windowLength>900</windowLength>

      <!-- The frequency (in seconds) to collect JVM statistics. -->
      <!-- The default frequency is 30 seconds. -->
      <frequency>30</frequency>
    </troubleshooting>

    <!-- Event Sending configuration -->
    <eventSending>
      <!-- The frequency (in seconds) to check outgoing event -->
      <!-- queue for new events. The default frequency is 15 -->
      <!-- minutes (i.e. 900 seconds). -->
      <frequency>900</frequency>
    </eventSending>  

</configuration>

You will notice that the configuration file contains a field for specifying the eventSender class to use.  At present, there is one supported class in the SCOM scenario: com.microsoft.ManagementServices.APMAgent.EventSender.MBeanEventLog.

 Fun fact: the name SEAgent stands for Structured Event Agent.

pmonitor.config.xml

At a high level, the pmonitor.config.xml contains instrumentation information detailing which methods to instrument as well thresholds. It is by far the largest configuration file, and more than likely the one that you (as a customer) are most likely interested in modifying.  This file controls which methods to instrument, the thresholds for generating events (i.e. entrypoints), thresholds for gathering addition called method information (i.e. resources), ability to exclude events from certain applications, etc… With a few simple edits, you can start gathering additional information relevant to your applications.

This is a much larger file, so I won't be pasting the whole file for the sake of brevity.  Rather, I'll piecemeal go over some of the more interesting sections.

Before doing any of that, allow me to explain some terminology.  The Java APM solution does byte-code instrumentation.  When you start the Java process, you should modify the start-up script to include some additional  command-line parameters (see https://blogs.technet.com/b/random_happy_dev_thoughts/archive/2013/08/20/command-line-options-to-enabling-application-performance-monitoring-for-tomcat.aspx).  Using the -javaagent command-line parameter we are able to start the monitoring agent before the main() method is called.  The code then "does stuff" so that when classes specified in the pmonitor.config.xml are loaded, some additional byte-code is inserted to gather information that can then be displayed for you.  The default configuration comes with some very basic presets, you will likely need to modify them to best suit your environment's needs.

For example, suppose your code looked like:

entryPoint() {
   resource01ServiceCall()
   resource02OtherServiceCall()
   resource03LongCall()
   resource06JdbcCall()
}

resource01ServiceCall() {}

resource02OtherServiceCall() {}
resource03LongCall()
{
   resource04LongCall()
}

resource04LongCall()
{
   resource05LongCall()
}

resource05LongCall() {}

resource06JdbcCall() {}
 

If all of the performance thresholds were violated this would generate an event like (and this is not actual XML, but rather the best way I could this to render this data in text (Crammond don't do MSPAINT).

<data>
  <eventForEntryPoint>
    <eventForResource01ServiceCall />
    <eventForResource02OtherServiceCall />
    <eventForResource03LongCall>
      <eventForResource04LongCall>
        <eventForResource05LongCall />
      </eventForResource04LongCall>
    </eventForResource03LongCall>
    <eventForResource06JdbcCall />
  </eventForEntryPoint>
</data>

 With that example out of the way, the next step is specifying methods and thresholds.

At the top of the file is the ability to set global thresholds.  These thresholds are general fallback defaults that will be applied if no other specific threshold applies.  The major value corresponds to an entry point threshold (in milliseconds), where an entry point is the initial point where the incoming call starts.  The minor value corresponds to a resource threshold (in milliseconds), where a resource is a method called somewhere after during the call to an entry point. 

<ss:options>
   <!-- Major refers to the default threshold for an entrypoint. -->
   <!-- Minor refers to the default threshold for a resource. -->
   <ss:defaultSettings major="5000" minor="100" />
</ss:options>

 

Next is a section for declaring entry points and resources.  At a broad level, each entry is simiple the full name of the package and method  separated by dots in the following syntax:

 com.contoso.package.myMethodName

 Method signatures may also by supplied in the Java syntax too - if you have done JNI calls before this will look 'legible' (see https://www.rgagnon.com/javadetails/java-0286.html for a shortlist).  For the method myMethod(boolean b, double radix, String message) in the package com.contoso.package, the instrumented method name should be:

 com.contoso.package.myMethodName(ZDL(java/lang/String);)

 Another interesting feature here is that per method thresholds can be specified by adding either a major or minor for entry points and resources respectively:

 <ss:entrypoints>
    <ss:entrypoint name="com.contoso.package.myEntryPointMethodName" major="200"/>
 </ss:entrypoints>
 <ss:resources>
    <ss:resource name="com.contoso.package.myResourceMethodName" minor="100" />
 </ss:resources>

 Not to be overwhelming, but there is even more thresholds setting that you can enable!  It is also possible to enable/disable instrumentation on a per "application" basis (where application is defined by the producer choose [that is for another post], mostly this refers to  the context path).  For instance, if you had an application deployed called BeanSpy (with the servlet context path /BeanSpy).

For instance, the application could be disabled.

<ss:applications>
   <ss:application name="/BeanSpy" enable="false" />
</ss:applications>

Or the application could be set to have different default thresholds:

<ss:applications>
   <ss:application name="/ContosoOrderService"
enable="true" major="3500" minor="900" />
</ss:applications>

Or even specify unique thresholds for entry points and resources:

<ss:applications>
   <ss:application name="/" enable="true">
      <ss:entrypoints>
         <ss:entrypoint name="javax.servlet.http.HttpServlet.service" major="6000"/>
      </ss:entrypoints>
      <ss:resources>
         <ss:resource name="com.contoso.package.resource01ServiceCall"
                      minor="100"/>
         <ss:resource name="com.contoso.package.resource02OtherServiceCall"
                      minor="50"/>
      </ss:resources>
   </ss:application>
</ss:applications>

For the application above, you can see that I choose the root context (i.e. /).  It so happens that the root context is a special case for the servlet provider as this is the value inserted when a context could not be determined.  Unfortunately, on the database side it isn't a good idea to use an empty string as an identifier.  :)