Writing a canary script using the Java runtime - Amazon CloudWatch
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

Writing a canary script using the Java runtime

Java project structure for a canary

To create a canary in Java, you need to write your code, compile it, and deploy the compiled artifacts to Synthetics. You can initialize a Java Lambda project in various ways. For instance, you can use a standard Java project setup in your preferred IDE, such as IntelliJ IDEA or Visual Studio Code. Alternatively, you can create the required file structure manually.

A Synthetics Java project contains the following general structure:

/project-root └ src └ main └ java └ canarypackage // name of package | └ ExampleCanary.java // Canary code file | └ other_supporting_classes - resources └ synthetics.json // Synthetics configuration file └ build.gradle OR pom.xml

You can use either Maven or Gradle to build your project and manage dependencies.

In the above structure, the ExampleCanary class is the entry point or handler for the canary.

Java canary class example

This example is for a canary to make a get request to a URL stored in the TESTING_URL Lambda environment variable. The canary does not use any of the methods provided by Synthetics runtime.

package canarypackage; import java.net.HttpURLConnection; import java.net.URL; // Handler value: canary.ExampleCanary::canaryCode public class ExampleCanary { public void canaryCode() throws Exception{ URL url = new URL(System.getenv("TESTING_URL")); HttpURLConnection con=(HttpURLConnection)url.openConnection(); con.setRequestMethod("GET"); con.setConnectTimeout(5000); con.setReadTimeout(5000); int status=con.getResponseCode(); if(status!=200){ throw new Exception("Failed to load " + url + ", status code: " + status); } } }

It is highly recommended to modularize your canaries using the Synthetics provided library function executeStep. The canary makes get calls to two separate URLs procured from URL1 and URL2 environment variables.

Note

To use the executeStep functionality, the handler method for the canary should take in a parameter of type Synthetics as shown below.

package canarypackage; import com.amazonaws.synthetics.Synthetics; import java.net.HttpURLConnection; import java.net.URL; // Handler value: canary.ExampleCanary::canaryCode public class ExampleCanary { public void canaryCode(Synthetics synthetics) throws Exception { createStep("Step1", synthetics, System.getenv("URL1")); createStep("Step2", synthetics, System.getenv("URL2")); return; } private void createStep(String stepName, Synthetics synthetics, String url) throws Exception{ synthetics.executeStep(stepName,()->{ URL obj=new URL(url); HttpURLConnection con=(HttpURLConnection)obj.openConnection(); con.setRequestMethod("GET"); con.setConnectTimeout(5000); con.setReadTimeout(5000); int status=con.getResponseCode(); if(status!=200){ throw new Exception("Failed to load" + url + "status code:" + status); } return null; }).get(); } }

Packaging the project for a canary

Synthetics accepts code for a java canary in the zip format. The zip consists of the class files for the canary code, the jars for any third party dependencies and the Synthetics configuration file.

A Synthetics Java zip contains the following general structure.

example-canary └ lib | └ //third party dependency jars └ java-canary.jar └ synthetics.json

To build this zip from the above project structure, you can use gradle (build.gradle) or maven (pom.xml). Here is an example.

For information on compile time dependencies or interfaces for the Synthetics library, see the README under aws-cloudwatch-synthetics-sdk-java .

plugins { id 'java' } repositories { mavenCentral() } dependencies { // Third party dependencies // example: implementation 'software.amazon.awssdk:s3:2.31.9' // Declares dependency on Synthetics interfaces for compiling only // Refer https://github.com/aws/aws-cloudwatch-synthetics-sdk-java for building from source. compileOnly 'software.amazon.synthetics:aws-cloudwatch-synthetics-sdk-java:1.0.0'} test { useJUnitPlatform() } // Build the zip to be used as Canary code. task buildZip(type: Zip) { archiveFileName.set("example-canary.zip") destinationDirectory.set(file("$buildDir")) from processResources into('lib') { from configurations.runtimeClasspath from(tasks.named("jar")) } from "src/main/java/resources/synthetics.json" doLast { println "Artifact written to: ${archiveFile.get().asFile.absolutePath}" } } java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } tasks.named("build") { dependsOn "buildZip" }

Handler name

The handler name is the entry point for the canary. For Java runtime, the handler is in the following format.

<<full qualified name for canary class>>::<<name of the method to start the execution from>> // for above code: canarypackage.ExampleCanary::canaryCode

CloudWatch Synthetics configurations

You can configure the behavior of the Synthetics Java runtime by providing an optional JSON configuration file named synthetics.json. This file should be packaged in the root directory of the package zip. Though a configuration file is optional, if you don't provide a configuration file, or a configuration key is missing, CloudWatch uses the defaults.

The following are supported configuration values, and their defaults.

{ "step": { "stepSuccessMetric": true, "stepDurationMetric": true, "continueOnStepFailure": false, "stepsReport": true }, "logging": { "logRequest": false, "logResponse": false }, "httpMetrics": { "metric_2xx": true, "metric_4xx": true, "metric_5xx": true, "aggregated2xxMetric": true, "aggregated4xxMetric": true, "aggregated5xxMetric": true }, "canaryMetrics": { "failedCanaryMetric": true, "aggregatedFailedCanaryMetric": true } }

Step configurations

  • continueOnStepFailure – Determines if a script should continue even after a step has failed. The default is false.

  • stepSuccessMetric – Determines if a step's SuccessPercent metric is emitted. The SuccessPercent metric for a step is 100 for the canary run if the step succeeds, and 0 if the step fails. The default is true.

  • stepDurationMetric – Determines if a step's Duration metric is emitted. The Duration metric is emitted as a duration, in milliseconds, of the step's run. The default is true.

Logging configurations

Applies to logs generated by CloudWatch Synthetics. Controls the verbosity of request and response logs.

  • logRequest – Specifies whether to log every request in canary logs. The default is false.

  • logResponse – Specifies whether to log every response in canary logs. The default is false.

HTTP metric configurations

Configurations for metrics related to the count of network requests with different HTTP status codes, emitted by CloudWatch Synthetics for this canary.

  • metric_2xx – Specifies whether to emit the 2xx metric (with the CanaryName dimension) for this canary. The default is true.

  • metric_4xx – Specifies whether to emit the 4xx metric (with the CanaryName dimension) for this canary. The default is true.

  • metric_5xx – Specifies whether to emit the 5xx metric (with the CanaryName dimension) for this canary. The default is true.

  • aggregated2xxMetric – Specifies whether to emit the 2xx metric (without the CanaryName dimension) for this canary. The default is true.

  • aggregated4xxMetric – Specifies whether to emit the 4xx metric (without the CanaryName dimension) for this canary. The default is true.

  • aggregated5xxMetric – Specifies whether to emit the 5xx metric (without the CanaryName dimension) for this canary. The default is true.

Canary metric configurations

Configurations for other metrics emitted by CloudWatch Synthetics.

  • failedCanaryMetric Network Access Analyzer Specifies whether to emit the Failed metric (with the CanaryName dimension) for this canary. The default is true.

  • aggregatedFailedCanaryMetric – Specifies whether to emit the Failed metric (without the CanaryName dimension) for this canary. The default is true.

CloudWatch Synthetics environment variables

You can configure the logging level and format by using environment variables.

Log format

The CloudWatch Synthetics Java runtime creates CloudWatch logs for every canary run. Logs are written in JSON format for convenient querying. Optionally, you can change the log format to TEXT.

  • Environment variable name – CW_SYNTHETICS_LOG_FORMAT

  • Supported values – JSON, TEXT

  • Default –JSON

Log levels

  • Environment variable name – CW_SYNTHETICS_LOG_LEVEL

  • Supported values – TRACE, DEBUG, INFO, WARN, ERROR, FATAL

  • Default – INFO

Apart from the above environment variables, there is a default environment variable added for Java runtime, AWS_LAMBDA-EXEC_WRAPPER environment variable to your function, and set its value to /opt/synthetics-otel-instrument. This environment variable modifies your function's startup behavior for telemetry. If this environment variable already exists, make sure that it's set to the required value.