This is a Java agent that allows recording coverage, similar to JaCoCo (which is in fact used underneath). It improves on JaCoCo in the following ways:
- allow configuration via a .properties file
- allow time-interval based dumping of coverage (e.g. "dump coverage every hour") instead of only at JVM shutdown
- directly dumps XML instead of .exec files, i.e. already performs that conversion automatically
- transfers dumped XML files either to a central location for further processing or even uploads them directly to Teamscale
- writes a log file, which makes debugging problems a bit easier
The agent might need additional memory whenever a coverage report is created. If your JVM is configured to only use very little memory, you might have to increase this limit in order for the agent to function properly. How much memory is needed depends on how much code is being analyzed and thus on how you configure the agent.
To upload coverage directly to Teamscale, a Teamscale version of 5.9.0 or higher is required.
Unzip this zip file into any folder. All files contained in the zip file are required for the agent to work.
Configure the agent on your application's JVM:
-javaagent:AGENTJAR=OPTIONS
Where
AGENTJAR
is the path to the Jar file of the Teamscale JaCoCo agent (inside thelib
folder of this zip)OPTIONS
are one or more comma-separated options for the agent in the formatkey1=value1,key2=value2
and so on.
The following options are available:
includes
(recommended): include patterns for classes. Separate multiple patterns with a semicolon. This may speed up the profiled application and reduce the size of the output XML. These patterns are matched against the Java class names. E.g. to match all classes in packagecom.yourcompany
andcom.yourotherpackage
and all their subpackages you can use*com.yourcompany.*;*com.yourotherpackage.*
(the initial star before each package name is a precaution in case your classes are nested inside e.g. asrc
folder, which might be interpreted as a part of the package name. We recommend always using this form). Make sure to include all relevant application code but no external libraries. For further details, please see the JaCoCo documentation in the "Agent" section.excludes
(optional): exclude patterns for classes. Same syntax as theincludes
parameter. For further details, please see the JaCoCo documentation in the "Agent" section.out
(optional): the path to a writable directory where the generated coverage XML files will be stored. (For details see path format section below). Defaults to the subdirectorycoverage
inside the agent's installation directory.config-file
(optional): a file which contains one or more of the previously named options askey=value
entries which are separated by line breaks. The file may also contain comments starting with#
. For details see path format section below. Alternatively you can also set theTEAMSCALE_JAVA_PROFILER_CONFIG_FILE
environment variable to that value.config-id
(optional): a profiler configuration ID as defined in Teamscale. This allows to centrally manage the profiler configuration in Teamscale's UI (under Project Configuration > Profilers since Teamscale 9.4). Alternatively you can also set theTEAMSCALE_JAVA_PROFILER_CONFIG_ID
environment variable to that value.logging-config
(optional): path to a logback configuration XML file (other configuration formats are not supported at the moment). Use this to change the logging behaviour of the agent. Some sample configurations are provided with the agent in thelogging
folder, e.g. to enable debug logging or log directly to the console. (For details see path format section below)proxy-password-file
(optional): path to a file that contains the password for a proxy server authentication. This file may only contain the password and nothing else.proxy-
(optional): For other options regarding teamscale-specific proxy settings see Teamscale-Specific-Proxy Section.mode
(optional): which coverage collection mode to use. Can be eithernormal
ortestwise
(Default isnormal
)debug
(optional):true
,false
or a path to which the logs should be written to.true
if no explicit value given. This option turns on debug mode. The logs will be written to console and the given file path. If no file path is given, the logs files will be stored in the profiled process's temp directory.
You can pass additional options directly to the original JaCoCo agent by prefixing them with jacoco-
, e.g.
jacoco-sessionid=session1
will set the session ID of the profiling session. See the "Agent" section of the JaCoCo
documentation for a list of all available options.
The -javaagent
option MUST be specified BEFORE the -jar
option!
Please check the produced log file for errors and warnings before using the agent in any productive setting.
The log file is written to the profiled process's temp directory in the subfolder teamscale-java-profiler*/logs
by default.
Examples:
- Linux:
/tmp/teamscale-java-profiler-2534-2465434652/logs
- Windows:
C:\Users\%Username%\AppData\Local\Temp\teamscale-java-profiler-2534-2465434652\logs
where%Username%
is the name of the user under which the profiled application is running
Note that defining -Djava.io.tmpdir
will change the temp directory that is being used.
If there is no log file at that location, it means the agent didn't even start and you have not configured it correctly. Check your applications console output for error messages.
It is possible to specify teamscale-specific proxy settings that take prevalence over the system properties of the JVM. If no or not all teamscale-specific proxy settings are provided the agent will fall back to the system properties of the JVM.
It is also possible to specify these options by prefixing the JVM flags for proxies with teamscale.
,
for example -Dteamscale.https.proxyHost
.
The following options are available both for https
and http
:
proxy-https-host
/proxy-http-host
: The host name of the proxy server.proxy-https-port
/proxy-http-port
: The port of the proxy server.proxy-https-user
/proxy-http-user
: The username for the proxy server.proxy-https-password
/proxy-http-password
: The password for the proxy user.
If you want to collect testwise coverage, please have a look below in the Testwise mode section.
All paths supplied to the agent can be absolute or relative to the working directory. Furthermore, paths may contain ant
patterns with *
, **
and ?
.
-
class-dir
: the path under which all class files of the profiled application are stored. Normally, this is inferred by the agent automatically. For some application, profiling performance may improve if you specify it explicitly. May be a directory or a Jar/War/Ear/... file. Separate multiple paths with a semicolon. You may also supply one or more.txt
files with classpath entries separated by newlines (For details see path format section above) -
interval
: the interval in minutes between dumps of the current coverage to an XML file (Default is 480, i.e. 8 hours). If set to 0 coverage is only dumped at JVM shutdown. -
dump-on-exit
: whether a coverage report should be written on JVM shutdown (Default is true). -
duplicates
: defines how JaCoCo handles duplicate class files. This is by default set toWARN
to make the initial setup of the tool as easy as possible. However, this should be set toFAIL
for productive use if possible. In special cases you can also set it toIGNORE
to print no warnings. See the special section onduplicates
below. -
ignore-uncovered-classes
: Whether classes without any recorded coverage should be ignored when generating the XML coverage report. Since Teamscale assumes classes not contained in the report to have no coverage at all, this can reduce report sizes for large systems (Default is false). -
upload-metadata
: paths to files that should also be included in uploaded zips. Separate multiple paths with a semicolon. You can use this to include useful meta data about the deployed application with the coverage, e.g. its version number. -
obfuscate-security-related-outputs
: boolean value determining if security critical information such as access keys are obfuscated when printing them to the console or into the log (default is true). -
teamscale-server-url
: the HTTP(S) URL of the Teamscale instance to which coverage should be uploaded. -
teamscale-project
: the project alias or ID within Teamscale to which the coverage belongs. If not specified, theteamscale.project
property must be specified via thegit.properties
file in at least one of the profiled JARs/WARs/EARs. -
teamscale-user
: the username used to authenticate against Teamscale. The user account must have the "Perform External Uploads" permission on the given project. -
teamscale-access-token
: the access token of the user. -
teamscale-partition
: the partition within Teamscale to upload coverage to. A partition can be an arbitrary string which can be used to encode e.g. the test environment or the tester. These can be individually toggled on or off in Teamscale's UI. -
teamscale-revision
: the source control revision (e.g. SVN revision or Git hash) that has been used to build the system under test. Teamscale uses this to map the coverage to the corresponding source code. For an alternative seeteamscale-revision-manifest-jar
. -
teamscale-repository
The repository id in your Teamscale project which Teamscale should use to look up the revision, if given. Not setting this option will lead to a lookup in all repositories in the Teamscale project. -
teamscale-commit
: the commit (Format:branch:timestamp
) which has been used to build the system under test. Teamscale uses this to map the coverage to the corresponding source code. Thus, this must be the exact code commit from the VCS that was deployed. For an alternative seeteamscale-commit-manifest-jar
andgit-properties-jar
.If Git is your VCS, you can get the commit info via
echo `git rev-parse --abbrev-ref HEAD`:`git --no-pager log -n1 --format="%ct000"`
Note: Getting the branch does most likely not work when called in the build pipeline, because Jenkins, GitLab, Travis etc. checkout a specific commit by its SHA1, which leaves the repository in a detached head mode and thus returns HEAD instead of the branch. In this case the environment variable provided by the build runner should be used instead.
If Subversion is your VCS and your repository follows the SVN convention with
trunk
,branches
, andtags
directories, you can get the commit info viaecho `svn info --show-item url | egrep -o '/(branches|tags)/[^/]+|trunk' | egrep -o '[^/]+$'`:`LANG=C svn info --show-item last-changed-date | date -f - +"%s%3N"`
-
teamscale-revision-manifest-jar
As an alternative toteamscale-revision
the agent accepts the repository revision provided in the given jar/war'sMETA-INF/MANIFEST.MF
file (for details see path format section above). The revision must be supplied as an main attribute calledRevision
(preferred) or as an attribute calledGit_Commit
, which belongs to an entry calledGit
. -
teamscale-commit-manifest-jar
As an alternative toteamscale-commit
the agent accepts values supplied viaBranch
andTimestamp
entries in the given jar/war'sMETA-INF/MANIFEST.MF
file. (For details see path format section above) -
git-properties-jar
As an alternative toteamscale-commit
and/orteamscale-project
the agent accepts values supplied via agit.properties
file generated with the corresponding Maven or Gradle plugin and stored in a jar/war/ear/... If nothing is configured, the agent automatically searches all loaded Jar/War/Ear/... files for agit.properties
file. This file must contain either- one of the properties
git.commit.id
orgit.commit.id.full
with the git SHA1 - the properties
git.branch
andgit.commit.time
(in the formatyyyy-MM-dd'T'HH:mm:ssZ
oryyyy-MM-dd'T'HH:mm:ssXXX
) or - the properties
teamscale.commit.branch
andteamscale.commit.time
(either as an epoch timestamp or in one of the two formats above)
- one of the properties
-
search-git-properties-recursively
Specifies whether to search for git.properties files recursively in folders or archive (jar, war, ear, aar) files. Default: true. -
git-properties-commit-date-format
The Java data patterngit.commit.time
is encoded with ingit.properties
. Defaults toyyyy-MM-dd'T'HH:mm:ssZ
. -
teamscale-message
(optional): the commit message shown within Teamscale for the coverage upload (Default is "Agent coverage upload"). -
config-file
(optional): a file which contains one or more of the previously named options askey=value
entries which are separated by line breaks. The file may also contain comments starting with#
. (For details see path format section above) -
validate-ssl
(optional): defaults to true. Can be used to disable SSL validation (not recommended). -
azure-url
: a HTTPS URL to an azure file storage. Must be in the following format: https://<account>.file.core.windows.net/<share>/(<path>). The <path> is optional; note, that in the case that the given path does not yet exists at the given share, it will be created. -
azure-key
: the access key to the azure file storage. This key is bound to the account, not the share. -
http-server-port
: the port at which the agent should start an HTTP server that listens for commands. The agent's REST API has the following endpoints:[GET] /partition
Returns the name of the currently configured partition.[PUT] /partition
Sets the name of the partition to the string delivered in the request body in plain text. This partition should be used for all followup report dumps (seeteamscale-partition
). For reports that are not directly sent to Teamscale the generated report will contain the partition name as session ID. Before changing the partition, performs a dump of all coverage collected so far under the previous partition.[GET] /message
Returns the name of the currently configured commit message.[PUT] /message
Sets the commit message to the string delivered in the request body in plain text. This message should be used for all followup report dumps (seeteamscale-message
). Before changing the message, performs a dump of all coverage collected so far under the previous message.[POST] /dump
Instructs the agent to dump the collected coverage.[POST] /reset
Instructs the agent to reset the collected coverage. This will discard all coverage collected in the current JVM session.[GET] /revision
Returns the current revision used for uploading to Teamscale. Before changing the revision, performs a dump of all coverage collected so far under the previous revision.[PUT] /revision
Sets the revision to use for uploading to Teamscale. The revision must be in the request body in plain text. Before changing the revision, performs a dump of all coverage collected so far under the previous revision.[GET] /commit
Returns the current commit used for uploading to Teamscale. Before changing the commit, performs a dump of all coverage collected so far under the previous commit.[PUT] /commit
Sets the commit to use for uploading to Teamscale. The commit must be in the request body in plain thext in the format: branch:timestmap Before changing the commit, performs a dump of all coverage collected so far under the previous commit.
Example
curl
command to set the partition:curl --fail -X PUT -d "Manual Tests" -H "Content-Type: text/plain" http://PROFILERURL:PORT/partition
-
sap-nwdi-applications
needed when profiling in a SAP NetWeaver Development Infrastructure. It must be a semicolon separated list of applications. Each application is specified as a fully qualified classname (referred to as marker class) and a Teamscale project alias or ID separated by a colon. The marker class must be guaranteed to be executed when the application is running and is unique amongst the other deployed applications. E.g.com.company.app1.Main:app1alias;com.company.app2.Starter:ts-app2-id
. The coverage is uploaded to master at the timestamp of the last modification date of the given marker class.
artifactory-url
: the HTTP(S) url of the artifactory server to upload the reports to. The URL may include a subpath on the artifactory server, e.g.https://artifactory.acme.com/my-repo/my/subpath
.artifactory-legacy-path
: option to use the upload path as defined pre v24.0.0. Useartifactory-legacy-path=true
to keep the old behavior. Defaults to false. For more details see new upload schema, legacy upload schema and migration.artifactory-partition
(required iflegacy-path
option is not set): the partition name that is parsed later on via Teamscale Artifactory connector options.artifactory-user
(required for artifactory): The name of an artifactory user with write access.artifactory-password
(required for artifactory): The password of the user.artifactory-api-key
(alternative toartifactory-user
andartifactory-password
) The API key for artifactory from a user with write access (c.f. Artifactory Documentation)artifactory-zip-path
(legacy option): The path within the stored ZIP file where the reports are stored. Default is to store at root level. This can be used to encode e.g. a partition name that is parsed later on via Teamscale Artifactory connector options.artifactory-path-suffix
(optional): The path within the storage location between the default path and the uploaded artifact.
/ arbitraryly / many / directories / before / the / upload / root
/ 'uploads'
/ <branch name>
/ <commit timestamp (Java long)>[-<commit hash>]?
/ <upload partition>
/ <artifact type>
/ none / or / some / intermediate / directories /
/ <Zip archive file>
The following options must be set in the Teamscale Artifactory Connector to import reports stored with the new standard upload schema (will get the default with one of the upcoming releases):
- Path Search Pattern:
uploads/**
or**/uploads/**
when directories before the upload root are present - Branch Extraction Pattern:
uploads\/([^/]+)\/\d+(?:-[abcdef0-9]+)?\/[^/]+\/[^/]+\/.*
(default since Teamscale 8.2) - Timestamp Extraction Pattern:
uploads\/[^/]+\/(\d+)(?:-[abcdef0-9]+)?\/[^/]+\/[^/]+\/.*
(default since Teamscale 8.2) - Timestamp Interpretation:
timestamp:millis
(default since Teamscale 8.2) - Prefix Extraction Pattern:
uploads\/[^/]+\/\d+(?:-[abcdef0-9]+)?\/([^/]+\/[^/]+\/.*)
(default since Teamscale 8.2) - Partition Pattern (expert option):
([^/]+)\/[^/]+\/.*
(default since Teamscale 8.2) - Analysis report mapping (expert option):
**/jacoco/**->JACOCO
(part of the default since Teamscale 8.2)
/ arbitraryly / many / directories / before / the / upload / root
/ <branch name>
/ <commit timestamp (Java long)>[-<commit hash>]?
/ <Zip archive file>
- Add a second artifactory connector to the Teamscale project with the new patterns next to the already existing Artifactory connector.
- Update the Teamscale JaCoCo agent.
- Validate that the new reports are handled correctly.
- Remove the old artifactory connector once the data is old enough to be no longer relevant.
If the connection to the Teamscale server should be established via HTTPS, a Java Trust Store can be required, which contains the certificate used by the Teamscale server for inbound HTTPS communication. Please refer to the section on HTTPS configuration in the Teamscale documentation for details on when and how to create a Java Trust Store (please note that from the viewpoint of the agent, Teamscale is the "external" server here. In order to activate usage of the trust store you need to specify the following JVM parameters
-Djavax.net.ssl.trustStore=<Path-to-Truststore-File>
-Djavax.net.ssl.trustStorePassword=<Password>
The testwise coverage mode allows to record coverage per test, which is needed for Test Impact Analysis. This means that
you can distinguish later, which test did produce which coverage. To enable this the mode
option must be set to
testwise
.
Tests are identified by the uniformPath
, which is a file system like path that is used to uniquely identify a test
within Teamscale and should be chosen accordingly. It is furthermore used to make the set of tests hierarchically
navigable within Teamscale.
In the testwise coverage mode the agent only produces an exec file that needs to be converted and augmented with more data from the test system. Please refer to the documentation for the Test Impact Analysis for more details.
The test system (the application executing the test specification) has to inform the agent of when a test started and finished via a REST API. The corresponding server listens at the specified port.
http-server-port
(required): the port at which the agent should start an HTTP server that listens for test events (Recommended port is 8123)class-dir
(required whentia-mode
is set to eitherhttp
orteamscale-upload
): the path under which all class files of the profiled application are stored. May be a directory or a Jar/War/Ear/... file. Separate multiple paths with a semicolon. (For details see path format section above)
The agent's REST API has the following endpoints:
-
[GET] /test
Returns the testPath of the current test. The result will be empty when the test already finished or was not started yet. -
[GET] /revision
Returns the source control revision or commit the system under test was build from. This is required to upload the coverage to Teamscale at the correct point in time. The information can be supplied using any of the optionsteamscale-revision
,teamscale-commit
,teamscale-commit-manifest-jar
, orgit-properties-jar
above. Please note that git.properties auto-discovery is not yet supported for testwise mode.The response is in json format:
{
"type": "COMMIT|REVISION",
"value": "<branch>:<timestamp>|<revision>"
}
-
[POST] /testrun/start
If you configured a connection to Teamscale via theteamscale-
options, this will fetch impacted tests from Teamscale and return them in the response body. The format is the same as returned by Teamscale itself. You may optionally provide a list of all available test cases in the body of the request. These will also be used to generate the testwise coverage report in[POST] /testrun/end
. The format of the request body is:[ { "clusterId": "<ID of the cluster the test belongs to>", "uniformPath": "<Unique name of the test case>", "sourcePath": "<Optional: Path to the source of the test>", "content": "<Optional: Value to detect changes to the test, e.g. hash code, revision, ...>" } ]
Additionally, you may pass the following optional URL query parameters:
include-non-impacted
: If this istrue
, will not perform test-selection, only test-prioritization.baseline
: UNIX timestamp in milliseconds to indicate the time since which changes should be considered. If not given, the time since the last uploaded testwise coverage report is used (i.e. the last time you ran the TIA).
-
[POST] /testrun/end?partial=true
If you configured a connection to Teamscale via theteamscale-
options and enabledtia-mode=teamscale-upload
, this will upload a testwise coverage report to Teamscale.partial
describes whether the recorded tests represent a subset of all tests i.e. only impacted tests were executed. This tells Teamscale to keep -
tests even if they are not present in the latest report. Defaults to
false
. -
[POST] /test/start/{uniformPath}
Signals to the agent that the test with the given uniformPath is about to start. -
[POST] /test/end/{uniformPath}
Signals to the agent that the test with the given uniformPath has just finished. The body of the request may optionally contain the test execution result in json format:
{
"result": "ERROR",
"message": "<stacktrace>|<ignore reason>"
}
result
can be one of:
PASSED
Test execution was successful.IGNORED
The test is currently marked as "do not execute" (e.g. JUnit @Ignore or @Disabled).SKIPPED
Caused by a failing assumption.FAILURE
Caused by a failing assertion.ERROR
Caused by an error during test execution (e.g. exception thrown).
(uniformPath
and duration
is set automatically)
The uniformPath
parameter is a hierarchically structured identifier of the test and must be url encoded.
E.g. com/example/MyTest/testSomething
-> http://localhost:8123/test/start/com%2Fexample%2FMyTest%2FtestSomething
.
You can run the testwise agent in three different modes, configured via the option tia-mode
:
-
exec-file
(default): The agent stores the coverage in a binary*.exec
file within theout
directory. This is most useful when running tests in a CI/CD pipeline where the build tooling can later batch-convert all*.exec
files and upload a testwise coverage report to Teamscale or in situations where the agent must consume as little memory and CPU as possible and thus cannot convert the execution data to a report as required by the other options. It is, however, less convenient as you have to convert the*.exec
files yourself. -
disk
: The agent stores the coverage in JSON*.json
files within theout
directory. This is most useful when running tests in a CI/CD pipeline where the testwise coverage is to be provided in storage systems (such as S3) using custom upload scripts. -
teamscale-upload
: the agent will buffer all testwise coverage and test execution data in-memory and upload the testwise report to Teamscale once you call thePOST /testrun/end
REST endpoint. This option is the most convenient of the different modes as the agent handles all aspects of report generation and the upload to Teamscale for you. This mode may slow down the startup of the system under test and result in a larger memory footprint than theexec-file
mode. -
http
: the agent converts the coverage collected during a test in-process and returns it as a JSON in the response to the[POST] /test/end/...
request. This allows the caller to handle merging coverage of multiple tests into one testwise coverage report, e.g. in situations where more than one agent is running at the same time (e.g. profiling across multiple microservices.) This option may slow down the startup of the system under test and result in a larger memory footprint than theexec-file
mode.The response format looks like this:
{ "uniformPath": "com/example/MyTest/testSomething", "duration": 0.025, "result": "PASSED", "paths": [ { "path": "com/example/project", "files": [ { "fileName": "Calculator.java", "coveredLines": "20-24,26,27,29" }, { "fileName": "SomeOtherClass.java", "coveredLines": "26-28" } ] } ] }
(
duration
andresult
are included if you provided a test execution result in the request body)
Register the agent in WebSphere's startServer.bat
or startServer.sh
.
Please also apply this additional JVM parameter:
-Xshareclasses:none
This option disables a WebSphere internal class cache that causes problems with the profiler.
Please set the agent's includes
parameter so that the WebSphere code is not being profiled.
This ensures that the performance of your application does not degrade.
Also consider to use the config-file
option as WebSphere 17 and probably other versions silently strip any option
parameters exceeding 500 characters without any error message, which can lead to very subtle bugs when running the
profiler.
Register the agent in the JAVA_OPTS
environment variable in the run.conf
file inside the JBoss
installation directory.
Please set the agent's includes
parameter so that the JBoss code is not being profiled.
This ensures that the performance of your application does not degrade.
Please set the agent's includes
parameter so that the Wildfly code is not being profiled.
This ensures that the performance of your application does not degrade.
Register the agent in the JAVA_OPTS
environment variable in the standalone.conf
file inside the Wildfly installation directory.
Register the agent in domain.xml
under
<server-group>
<jvm>
<jvm-options>
<option value="-javaagent:..."/>
</jvm-options>
</jvm>
</server-group>
Register the agent in the CATALINA_OPTS
environment variable inside the bin/setenv.sh
or bin/setenv.bat
script in the Tomcat installation directory. Create this file if it does not yet exist.
Please set the agent's includes
parameter so that the Tomcat code is not being profiled.
This ensures that the performance of your application does not degrade.
To ensure the Tomcat process is properly shutdown you need to add CATALINA_PID="$CATALINA_BASE/bin/catalina.pid"
to the bin/setenv.sh
script and stop Tomcat via catalina stop -force
.
This makes Tomcat store the process ID of the server in the specified file and uses it to kill the process when stopped.
You have two options to register the JVM option:
- Register the agent via the
asadmin
tool. - Register the agent by manually editing
domain.xml
and adding ajvm-options
element.
When using the asadmin
tool, some characters need to be escaped with a backslash, see this StackOverflow answer.
Afterwards, restart Glassfish. Please verify the setup with asadmin
's list-jvm-options
command.
Register the agent by setting a JAVA_OPTIONS
variable such that the Jetty process can see it.
Please note that the Teamscale JaCoCo Agent requires at least Java 8, which is part of NetWeaver Java 7.50. Prior versions of NetWeaver Java are not supported by the Teamscale JaCoCo Agent.
In order to set the JVM agent parameter, you need to use the NetWeaver Administrator to navigate to
Configuration
- Infrastructure
- Java System Properties
and add an additional JVM parameter.
Use the search field to search for agent
, and select the -javaagent:<jarpath>[=<options>]
entry.
In the Name
field, enter the first part, until (including) the .jar
, and provide all the options (without the leading
=
) in the Value
field. We advise to only set the config-file
option here, and provide all other
options via the config file. Choose Add
and then Save
, and restart the Java server from the SAP
Management Console.
Please ask CQSE for special tooling that is available to instrument Java Web Start processes.
If you are using Git, you can use either a Maven or Gradle plugin to store the commit
in any Jar/War/Ear/... file and tell the agent to read it via git-properties-jar
.
Alternatively, it is also convenient to use the MANIFEST entries via teamscale-commit-manifest-jar
to link artifacts to commits,
especially when tests are executed independently of the build. The following assumes that we are using a Git
repository.
To configure this for the maven build add the following to your top level pom.xml
.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId> <!-- Works also for the maven-jar-plugin -->
...
<configuration>
...
<resourceEncoding>UTF-8</resourceEncoding>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<Branch>${branch}</Branch>
<Timestamp>${timestamp}</Timestamp>
</manifestEntries>
</archive>
</configuration>
</plugin>
When executing maven pass in the branch and timestamp to Maven:
mvn ... -Dbranch=$BRANCH_NAME -Dtimestamp=$(git --no-pager log -n1 --format="%ct000")
plugins {
id 'org.ajoberstar.grgit' version '2.3.0'
}
jar {
manifest {
attributes 'Branch': System.getProperty("branch")
attributes 'Timestamp': grgit.log {
maxCommits = 1
}.first().dateTime.toInstant().toEpochMilli()
}
}
./gradlew jar -Dbranch=master
Overwriting the git commit inside git.properties
files with teamscale.commit.branch
and teamscale.commit.time
There are scenarios, in which you need to use a different branch and time for the upload than provided with git.commit.id
or git.branch
and git.commit.time
in a git.properties
file.
This can be achieved by providing the properties teamscale.commt.branch
and teamsacle.commit.time
inside the git.properties
file.
teamscale.commit.time
must be provided either as epoch timestamp in milliseconds or in one of these two formats: yyyy-MM-dd'T'HH:mm:ssZ
or yyyy-MM-dd'T'HH:mm:ssXXX
.
The following explains, how to use the build timestamp instead of the commit timestamp for Maven and Gradle. Thus, we write teamscale.commit.branch=<branch>
and teamscale.commit.time=<build-time>
into the git.properties
file.
-
Set up the git-commit-id maven plugin (see also docs):
<build> <plugins> <plugin> <groupId>io.github.git-commit-id</groupId> <artifactId>git-commit-id-maven-plugin</artifactId> <version>8.0.2</version> <executions> <execution> <id>get-the-git-infos</id> <goals> <goal>revision</goal> </goals> <phase>initialize</phase> </execution> </executions> <configuration> <generateGitPropertiesFile>true</generateGitPropertiesFile> </configuration> </plugin> </plugins> </build>
-
Set up custom entries in the
git.properties
file (see also advanced docs):- Add a file called
git.properties
in the resources folder (usuallyjava/main/ressources
) of your application. The following one contains all default properties + theteamscale.project
,teamscale.commit.branch
andteamscale.commit.time
properties.git.tags=${git.tags} git.branch=${git.branch} git.local.branch.ahead=${git.local.branch.ahead} git.local.branch.behind=${git.local.branch.behind} git.dirty=${git.dirty} git.remote.origin.url=${git.remote.origin.url} git.commit.id=${git.commit.id.full} git.commit.id.abbrev=${git.commit.id.abbrev} git.commit.id.describe=${git.commit.id.describe} git.commit.id.describe-short=${git.commit.id.describe-short} git.commit.user.name=${git.commit.user.name} git.commit.user.email=${git.commit.user.email} git.commit.message.full=${git.commit.message.full} git.commit.message.short=${git.commit.message.short} git.commit.time=${git.commit.time} git.closest.tag.name=${git.closest.tag.name} git.closest.tag.commit.count=${git.closest.tag.commit.count} git.build.user.name=${git.build.user.name} git.build.user.email=${git.build.user.email} git.build.time=${git.build.time} git.build.host=${git.build.host} git.build.version=${git.build.version} git.build.number=${git.build.number} git.build.number.unique=${git.build.number.unique} teamscale.project=my-teamscale-project-id teamscale.commit.branch=${git.branch} teamscale.commit.time=${git.build.time} # alternatively via environment variables # teamscale.commit.branch=${env.BRANCH} # teamscale.commit.time=${env.BUILD_TIME}
- Add the following to your pom file so the commit id plugin picks up your custom
git.properties
file:<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> </resources> </build>
- Add a file called
- Set up the gradle commit id plugin by adding the following to your
build.gradle
file (see also docs):plugins { id "com.gorylenko.gradle-git-properties" version "2.4.2" }
- Set up custom entries in the
git.properties
file (see docs -> custom properties):gitProperties { customProperty "teamscale.project", "my-teamscale-project-id" customProperty "teamscale.commit.branch", { // If you have multiple git.properties, you can also add a custom project property at the start of your build. See https://stackoverflow.com/a/7029021 it.branch.current().getName() // alternatively via environment variable // System.getenv("BRANCH") } customProperty "teamscale.commit.time", { new Date().toInstant().toEpochMilli() // alternatively via environment variable // System.getenv("BUILD_TIME") } }
It is possible to upload the same coverage file to multiple Teamscale projects for different commits. In this case,
the teamscale.project
property has to be provided in each of the profiled Jar/War/Ear/... files
via the contained git.properties
file. For example, the git.properties
file can be generated
using the gradle-git-properties Gradle plugin:
gitProperties {
customProperty 'teamscale.project', 'my-awesome-project'
}
The same can be achieved for Maven using, e.g., the git-commit-id Maven plugin:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
You need to include a properties file with unresolved property values for ${teamscale.project} and ${git.commit.id}
and provide the corresponding values via the pom.xml
file(s) of your artifact(s). Example:
git.commit.id=${git.commit.id}
teamscale.project=${teamscale.project}
Please note that the commit must be provided via the git.properties
in each of the profiled Jar/War/Ear...
files when specifying the teamscale.project
this way. In case teamscale-project
is provided via the agent properties,
its value takes precedence over any teamscale.project
value provided via the git.properties
.
The underlying JaCoCo coverage instrumentation tooling relies on fully qualified class names to uniquely identify classes. However, in practice, applications are often deployed with multiple versions of the same class. This can happen, e.g. if you use the same library in different versions in sub-projects of your code.
At runtime, it is not deterministic, which of these versions will be loaded by the class loader. Thus, when trying to convert the recorded coverage back to covered source code lines, JaCoCo cannot determine which of the two versions was actually profiled.
By default, the agent is configured to only log a warning in these cases and simply pick one of the versions at random. Thus, the reported coverage for such files may not be accurate and may even be totally unusable. It is thus desirable to fix these warnings by ensuring that only one version of each class is deployed with your application. This has to be fixed in your build process.
Please refer to this StackOverflow post and the JaCoCo FAQ for more information.
If you'd like to set up the agent for Docker, please refer to our guide on Java with Docker profiling
This tool is also useful in case you are using the plain JaCoCo agent.
Converting .exec
files produced by raw JaCoCo to XML can be cumbersome. You can run the bin/convert
tool to perform this conversion for you. Run bin/convert --help
to
see all available command line options.
This is especially useful since this conversion does allow for duplicate class files by default, which the raw JaCoCo conversion will not allow.
The caveats listed in the above ignore-duplicates
section still apply!
If the produced coverage failed to be automatically uploaded, it is stored in the provided out
folder. The JaCoCo agent will retry to upload the coverage upon restart with the originally provided configs (e.g. server url, user, etc.). The coverage will be stored until the upload succeeds.
Most likely, you provided invalid parameters to the agent. Please check the agent's log file. If that does not exist, please check stdout of your application. If the agent can't write its log file, it will report the errors on stdout.
You're probably profiling and analyzing more code than necessary (e.g. third-party libraries etc). Make sure to set restrictive include/exclude patterns via the agent's options (see above).
Enable debug logging to see what is being filtered out and fine-tune these patterns.
In that case simply don't specify a class-dir
option.
This is a restriction of JaCoCo. See the above section about ignore-duplicates
.
To fix this error, it is best to resolve the underlying problem (two classes with the same fully qualified name
but different code). If this is not possible or not desirable, you can set ignore-duplicates=true
in the
agent options to turn this error into a warning. Be advised that coverage for all classes that produce this
error/warning in the log may have inaccurate coverage values reported.
Please refer to this StackOverflow post and the JaCoCo FAQ for more information.
Set an appropriate logback logging configuration XML in the agent options:
logging-config=/PATH/TO/logback-config.xml
You can find example logging configurations in the logging folder in the agent installation directory.
Enable debug logging in the logging config. Warning: this may create a lot of log entries!
Error: "The application was shut down before a commit could be found", despite including a git.properties file in your jar/war/...
When using application servers, the git.properties
file in your jar/war/... might not be detected automatically, which results in an "The application was shut down before a commit could be found" error.
To resolve the problem, try specifying git-properties-jar
explicitly.