Skip to content

Bug: addDimensions() should create a set #3777

Open
@dreamorosi

Description

@dreamorosi

Expected Behavior

When calling the metrics.addDimensions() method of the Metrics utility, the dimensions should create a new dimension set rather than concatenating the dimensions into the existing set.

Basically this:

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'orders',
});

export const handler = async () => {
  metrics.addDimension('environment', 'prod');

  metrics.addDimensions({
    dimension1: "1",
    dimension2: "2"
  });

  // continue emitting & flushing metrics as today
  metrics.addMetric('foo', MetricUnit.Count, 1);
  metrics.publishStoredMetrics();
};

should generate this:

{
   "_aws":{
      "CloudWatchMetrics":[
         {
            "Namespace":"A",
            "Dimensions":[
               ["dimension1"],
               ["dimension1", "dimension2"],
            ],
            "Metrics":[
               {
                  "Name":"foo",
                  "Unit":"Bytes"
               }
            ]
         }
      ]
   },
   "dimension1":"1",
   "dimension2":"2",
   "foo":[1.0]
}

Current Behavior

Currently this:

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'orders',
});

export const handler = async () => {
  metrics.addDimension('environment', 'prod');

  metrics.addDimensions({
    dimension1: "1",
    dimension2: "2"
  });

  // continue emitting & flushing metrics as today
  metrics.addMetric('foo', MetricUnit.Count, 1);
  metrics.publishStoredMetrics();
};

instead generates this:

{
   "_aws":{
      "CloudWatchMetrics":[
         {
            "Namespace":"A",
            "Dimensions":[
               ["dimension1", "dimension2"],
            ],
            "Metrics":[
               {
                  "Name":"foo",
                  "Unit":"Bytes"
               }
            ]
         }
      ]
   },
   "dimension1":"1",
   "dimension2":"2",
   "foo":[1.0]
}

Code snippet

See above.

Steps to Reproduce

See above.

Possible Solution

The EMF specification already supports this type of use case, and specifically, it represents a dimension set as an array/list of dimensions within the Dimensions array/list of a metric, for example:

{
   "_aws":{
      "CloudWatchMetrics":[
         {
            "Namespace":"A",
            "Dimensions":[
               ["dimension1"],
               ["dimension1", "dimension2"],
               ["dimension1", "dimension2", "dimension3"]
            ],
            "Metrics":[
               {
                  "Name":"foo",
                  "Unit":"Bytes"
               }
            ]
         }
      ]
   },
   "dimension1":"1",
   "dimension2":"2",
   "dimension3":"3",
   "foo":[1.0]
}

In terms of developer experience, this is how the updated addDimensions() would work alongside existing functions:

Adding a new dimension set:

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'orders',
});

export const handler = async () => {
  // existing functionality for single dimension
  metrics.addDimension('environment', 'prod');

  // fixed feature
  metrics.addDimensions({
    dimension1: "1",
    dimension2: "2"
  });

  // continue emitting & flushing metrics as today
  metrics.addMetric('successfulBooking', MetricUnit.Count, 1);
  metrics.publishStoredMetrics();
};

Handling overwritten dimensions

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'orders',
});

export const handler = async () => {
  metrics.addDimension('dimension1', 'A');

  metrics.addDimensions({
    dimension1: "B", // last value to be set is used
    dimension2: "2"
  });
};
Click to see EMF output
{
   "_aws":{
      "CloudWatchMetrics":[
         {
            "Namespace":"A",
            "Dimensions":[
               ["dimension1"],
               ["dimension1", "dimension2"]
            ],
            "Metrics":[
               {
                  "Name":"foo",
                  "Unit":"Bytes"
               }
            ]
         }
      ]
   },
   "dimension1":"B",
   "dimension2":"2",
   "foo": [1.0]
}

Interaction with default dimensions:

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'orders',
  defaultDimensions: {
    environment: 'prod'
  }
});

export const handler = async () => {
  metrics.addDimensions({ // this set will include `environment` from default dimensions
    dimension1: "1",
    dimension2: "2"
  });
};
Click to see EMF output
{
   "_aws":{
      "CloudWatchMetrics":[
         {
            "Namespace":"A",
            "Dimensions":[
               ["environment", "dimension1", "dimension2"]
            ],
            "Metrics":[
               {
                  "Name":"foo",
                  "Unit":"Bytes"
               }
            ]
         }
      ]
   },
   "environment":"prod"
   "dimension1":"1",
   "dimension2":"2",
   "foo": [1.0]
}

Note: when default dimensions are added at runtime, for example via the metrics.setDefaultDimensions() method, only dimension sets added after the default dimensions were set will include these dimensions. Changes are not retroactive.

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'orders',
  defaultDimensions: {
    environment: 'prod'
  }
});

export const handler = async () => {
  metrics.addDimensions({"dimension1": "1", "dimension2": "2"}) # this includes environment

  // for some reason I want to add more default dimensions
  metrics.setDefaultDimensions({ tenantId: "1"}) // this does not set tenant_id retroactively into the previous set
  metrics.addDimensionSet({"foo": "1", "bar": "2"}) # this includes environment and tenantiId
}

In terms of implementation, below is a non-exhaustive list of actions that should be done:

  • update implementation of addDimensions() here to create a set instead of multiple separate dimensions
  • update documentation
  • update unit tests under packages/metrics/tests/unit/dimensions.ts

Powertools for AWS Lambda (TypeScript) version

latest

AWS Lambda function runtime

22.x

Packaging format used

npm

Execution logs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingconfirmedThe scope is clear, ready for implementationmetricsThis item relates to the Metrics Utility

    Type

    No type

    Projects

    Status

    Working on it

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions