Skip to content

Bug: addDimensions() should create a set #3777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dreamorosi opened this issue Mar 25, 2025 · 0 comments
Open

Bug: addDimensions() should create a set #3777

dreamorosi opened this issue Mar 25, 2025 · 0 comments
Labels
bug Something isn't working confirmed The scope is clear, ready for implementation metrics This item relates to the Metrics Utility on-hold This item is on-hold and will be revisited in the future

Comments

@dreamorosi
Copy link
Contributor

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

@dreamorosi dreamorosi added bug Something isn't working triage This item has not been triaged by a maintainer, please wait labels Mar 25, 2025
@dreamorosi dreamorosi added on-hold This item is on-hold and will be revisited in the future confirmed The scope is clear, ready for implementation metrics This item relates to the Metrics Utility and removed triage This item has not been triaged by a maintainer, please wait labels Mar 25, 2025
@dreamorosi dreamorosi moved this from Triage to On hold in Powertools for AWS Lambda (TypeScript) Mar 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working confirmed The scope is clear, ready for implementation metrics This item relates to the Metrics Utility on-hold This item is on-hold and will be revisited in the future
Projects
Development

No branches or pull requests

1 participant