Monitoring and Logging DynamoDB Interactions in Cucumber Tests

As software engineers, AWS professionals, and automated testers, ensuring the reliability and performance of our applications is paramount. One crucial aspect of this is monitoring and logging interactions with AWS services, such as DynamoDB, during automated testing. This article will delve into how to effectively monitor and log DynamoDB interactions in Cucumber tests, providing in-depth explanations and relevant code examples.

Why Monitor and Log DynamoDB Interactions?

Monitoring and logging DynamoDB interactions during testing helps us:

  1. Identify Performance Bottlenecks: Understand how your application interacts with DynamoDB and identify slow queries or operations.
  2. Debugging and Troubleshooting: Quickly pinpoint issues by analyzing logs.
  3. Audit and Compliance: Maintain records of data access and modifications for compliance purposes.

Setting Up Your Environment

Before we dive into the specifics, ensure you have the following set up:

  1. AWS SDK: Ensure you have the AWS SDK for JavaScript or Java, depending on your project’s language.
  2. Cucumber: Cucumber should be configured for your project.
  3. Logging Library: Choose a logging library such as winston for Node.js or log4j for Java.

Monitoring and Logging in Node.js with Cucumber

Let’s start with a Node.js project. We will use the winston logging library and the AWS SDK for JavaScript.

Step 1: Install Dependencies

npm install aws-sdk winston cucumber

Step 2: Configure AWS SDK and Winston Logger

Create a logger.js file to configure the Winston logger.

// logger.js
const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'dynamodb-interactions.log' })
  ]
});

module.exports = logger;

Configure AWS SDK in a separate file, say aws-config.js.

// aws-config.js
const AWS = require('aws-sdk');

AWS.config.update({
  region: 'us-east-1',
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});

const dynamoDB = new AWS.DynamoDB.DocumentClient();

module.exports = dynamoDB;

Step 3: Log DynamoDB Interactions

In your Cucumber step definitions, log DynamoDB interactions.

// steps.js
const { Given, When, Then } = require('@cucumber/cucumber');
const dynamoDB = require('./aws-config');
const logger = require('./logger');

Given('a DynamoDB table {string}', async function (tableName) {
  this.tableName = tableName;
});

When('I put an item with key {string} and value {string}', async function (key, value) {
  const params = {
    TableName: this.tableName,
    Item: {
      key: key,
      value: value
    }
  };

  logger.info('Putting item into DynamoDB', { params });

  try {
    await dynamoDB.put(params).promise();
    logger.info('Successfully put item into DynamoDB');
  } catch (error) {
    logger.error('Error putting item into DynamoDB', { error });
  }
});

Then('the item with key {string} should have value {string}', async function (key, expectedValue) {
  const params = {
    TableName: this.tableName,
    Key: {
      key: key
    }
  };

  logger.info('Getting item from DynamoDB', { params });

  try {
    const data = await dynamoDB.get(params).promise();
    const actualValue = data.Item ? data.Item.value : null;
    logger.info('Successfully retrieved item from DynamoDB', { data });
    
    if (actualValue !== expectedValue) {
      throw new Error(`Expected ${expectedValue} but got ${actualValue}`);
    }
  } catch (error) {
    logger.error('Error getting item from DynamoDB', { error });
    throw error;
  }
});

Monitoring and Logging in Java with Cucumber

For Java projects, we will use log4j for logging and the AWS SDK for Java.

Step 1: Add Dependencies

Add the following dependencies to your pom.xml file.

<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-java-sdk-dynamodb</artifactId>
  <version>1.11.1000</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.13.3</version>
</dependency>
<dependency>
  <groupId>io.cucumber</groupId>
  <artifactId>cucumber-java</artifactId>
  <version>6.10.4</version>
</dependency>

Step 2: Configure AWS SDK and Log4j

Create a Log4j2.xml configuration file.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
    </Console>
    <File name="File" fileName="dynamodb-interactions.log">
      <PatternLayout>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n</pattern>
      </PatternLayout>
    </File>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="Console"/>
      <AppenderRef ref="File"/>
    </Root>
  </Loggers>
</Configuration>

Configure the AWS SDK in your Java class.

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class DynamoDBConfig {

    private static final Logger logger = LogManager.getLogger(DynamoDBConfig.class);

    public static DynamoDB getDynamoDBClient() {
        BasicAWSCredentials awsCreds = new BasicAWSCredentials("access_key_id", "secret_key_id");

        AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("dynamodb.us-east-1.amazonaws.com", "us-east-1"))
                .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
                .build();

        return new DynamoDB(client);
    }
}

Step 3: Log DynamoDB Interactions

In your step definitions, log DynamoDB interactions using Log4j.

import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.Item;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class DynamoDBSteps {

    private static final Logger logger = LogManager.getLogger(DynamoDBSteps.class);
    private String tableName;
    private final DynamoDB dynamoDB = DynamoDBConfig.getDynamoDBClient();

    @Given("a DynamoDB table {string}")
    public void aDynamoDBTable(String tableName) {
        this.tableName = tableName;
    }

    @When("I put an item with key {string} and value {string}")
    public void iPutAnItemWithKeyAndValue(String key, String value) {
        Table table = dynamoDB.getTable(tableName);
        Item item = new Item().withPrimaryKey("key", key).withString("value", value);
        logger.info("Putting item into DynamoDB: {}", item.toJSONPretty());

        try {
            table.putItem(item);
            logger.info("Successfully put item into DynamoDB");
        } catch (Exception e) {
            logger.error("Error putting item into DynamoDB", e);
        }
    }

    @Then("the item with key {string} should have value {string}")
    public void theItemWithKeyShouldHaveValue(String key, String expectedValue) {
        Table table = dynamoDB.getTable(tableName);

        try {
            Item item = table.getItem("key", key);
            String actualValue = item == null ? null : item.getString("value");
            logger.info("Successfully retrieved item from DynamoDB: {}", item.toJSONPretty());

            if (!expectedValue.equals(actualValue)) {
                throw new AssertionError(String.format("Expected %s but got %s", expectedValue, actualValue));
            }
        } catch (Exception e) {
            logger.error("Error getting item from DynamoDB", e);
            throw e;
        }
    }
}

Conclusion

Monitoring and logging DynamoDB interactions in Cucumber tests is essential for identifying performance bottlenecks, debugging issues, and ensuring audit compliance. By setting up a robust logging mechanism using tools like Winston or Log4j, you can gain valuable insights into your application’s interactions with DynamoDB. The provided code examples demonstrate how to configure and implement this logging in both Node.js and Java environments, helping you ensure the reliability and performance of your applications.

If you found this article helpful and are interested in integrating Cucumber Automation Framework with AWS DynamoDB, I suggest you check out some of the other articles I’ve written for this series:

If you’re looking for a deeper dive into some of the concepts and specifics discussed in my article, feel free to reach out to me directly or as always you can checkout the official AWS DynamoDB Developer Documentation for more information.

Related Posts