# Separate transaction for every resolver in AWS Lambda

**URL:** https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688
**Category:** APM
**Tags:** nodejs, aws-lambda
**Created:** [October 28, 2024, 4:54pm UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688 "2024-10-28T16:54:09Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![mkaniaa](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mkaniaa/32/138787_2.png) [@mkaniaa](https://discuss.elastic.co/u/mkaniaa)
#### Post date: [October 28, 2024, 4:54pm UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688/1 "2024-10-28T16:54:09Z")

</div>

Hello! I'm using Elastic APM v8.15.0 with an AWS Lambda function written in Node.js, integrated via `elastic-apm-node v4.7.3`. My Lambda function has three separate resolvers. Here’s my current configuration setup:

```auto

locals {
  lambda_fn_name = "${var.environment}_my_lambda_resolver"
  elastic_apm_send_strategy = "background"
  elastic_apm_verify_server_cert = "false"
  elastic_apm_service_name = "my_lambda_service"
  elastic_apm_enabled = "true"
}

resource "aws_lambda_function" "my_lambda_function" {
  function_name = local.lambda_fn_name
  publish = true
  role = xyz
  image_uri = xyz
  package_type = "xyz"
  timeout = 120

  environment {
    variables = {
      ELASTIC_APM_ENABLED = local.elastic_apm_enabled
      ELASTIC_APM_SERVICE_NAME = local.elastic_apm_service_name
      ELASTIC_APM_SERVER_URL = xyz
      ELASTIC_APM_API_KEY = xyz
      ELASTIC_APM_GLOBAL_LABELS = true
      ELASTIC_APM_SEND_STRATEGY = local.elastic_apm_send_strategy
      ELASTIC_APM_LAMBDA_VERIFY_SERVER_CERT = local.elastic_apm_verify_server_cert
      ELASTIC_APM_ENVIRONMENT = var.environment
      NODE_OPTIONS = "-r elastic-apm-node/start"
    }
  }
}

```

I can see my Lambda function listed in the APM \> Services section, but I’m running into three issues:

1. Separate Transactions per Resolver: Currently, only one transaction is shown per environment. Each resolver function’s calls are grouped under a single transaction, so I can’t differentiate which resolver each trace represents. I attempted to create individual transactions and spans within each resolver, but that resulted in losing the detailed trace samples, leaving only the initial call.

Here’s an example of the resolver setup:

```auto

const myResolverFunction: ResolverHandler<StatusArgs, StatusResult> = async (event, _context, _callback) => {
  const transaction = apm.startTransaction('myResolverFunction', 'resolver');

  try {
    const span = apm.startSpan('my_span');
    const data = await apiClient.runMyResolverOperations();
    span?.end();

    transaction.result = 'success';
    return {
      __typename: 'TaskBatch',
      ...data,
    };
  } catch (error: unknown) {
    transaction.result = 'error';
    if (error instanceof Error) {
      apm.captureError(error);
    } else {
      apm.captureError(String(error));
    }
    throw error;
  } finally {
    transaction.end();
  }
};

```

1. Trace Sample Order: The trace samples appear in a seemingly random order, which complicates analysis. I’d like them sorted by the time they were called.

2. Viewing API Request Bodies: While I can see all API calls made, the request bodies aren’t visible. Is there a way to configure APM to capture and display the full request body of each call?

Any guidance on achieving these three goals in my Elastic APM setup would be greatly appreciated! Thank you!

---

<div class="post-metadata">

### Author: ![trentm](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/trentm/32/77647_2.png) [@trentm](https://discuss.elastic.co/u/trentm)
#### Post date: [October 28, 2024, 8:12pm UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688/2 "2024-10-28T20:12:36Z")

</div>

> [@mkaniaa](#):
>
> My Lambda function has three separate resolvers.

Hi @mkaniaa. By "resolvers" do you mean using AWS AppSync (AWS's thing for building GraphQL services)? [AWS AppSync JavaScript resolver function reference for Lambda - AWS AppSync](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-lambda-js.html)

I haven't used this myself.

I'm struggling a little bit to understand the architecture of your app/service. Are you able to show more code for context, perhaps giving a name for each of the three resolvers so our conversation can be a little bit more concrete.

It is possible you would be able to use the `transaction.name` ([Transaction API | APM Node.js Agent Reference [4.x] | Elastic](https://www.elastic.co/guide/en/apm/agent/nodejs/current/transaction-api.html#transaction-name)) API to tweak the current transaction name to something more meaningful for each different type of call to your Lambda function. I don't know from your description if the resolver name or some GraphQL method name would make sense.

> [@mkaniaa](#):
>
> 1. Trace Sample Order: ...

Do you mean in the service "Transactions" view, e.g.:

 ![Screenshot 2024-10-28 at 1.08.53 PM](https://us1.discourse-cdn.com/elastic/original/3X/6/0/604437a5c7881f01b49e451e23293bd58fa48b8a.png)

I'm not sure if there is a way to control sorting here. In the general case this is a sampling of all transactions for the service for the current time range, limited to 500. I had thought they would show up sorted by transaction start time, but I don't know that that's the case.

> [@mkaniaa](#):
>
> 1. Viewing API Request Bodies:

The [Configuration options | APM Node.js Agent Reference [4.x] | Elastic](https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#capture-body) (`captureBody`) config var exists to capture the HTTP body of _incoming_ requests, but there is no support for capturing the body of outgoing requests, unfortunately.

---

<div class="post-metadata">

### Author: ![mkaniaa](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mkaniaa/32/138787_2.png) [@mkaniaa](https://discuss.elastic.co/u/mkaniaa)
#### Post date: [October 29, 2024, 9:14am UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688/3 "2024-10-29T09:14:45Z")

</div>

Hello @trentm, I really appreciate your reply! According to your questions:

1. Yes, by “resolvers” I mean using AWS AppSync. I have one main AppSyncResolverHandler and a map of 3 resolvers (I simplified the names for this example). The app.ts file is quite simple - it just awaits all the resolvers:

```auto
const resolverMap: ResolverMap = {
  'Query::getTableColumnData': getTableColumnValues,
  'Mutation::updateStatus': updateStatus,
  'Query::getSendToData': getSendToValues,
};

export const handler: AppSyncResolverHandler<any, any> = async (
  event: AppSyncResolverEvent<any, any>,
  context: Context,
  callback: Callback
) => {
  const { fieldName, parentTypeName } = event.info;

  logEvent(event);
  loadAwsConfig();

  const resolver = getResolver({ fieldName, parentTypeName, resolverMap });
  if (resolver) {
    try {
      return await resolver(event, context, callback);
    } catch (e) {
      console.error('Error:', e);
      return errorMapper(e as string | AxiosError<{ detail: string }>);
    }
  } else {
    const errorMsg = `No handler for field '${fieldName}' of type '${parentTypeName}'`;
    return errorMapper(errorMsg);
  }
};

```

1. Yes, this is exactly the "Transactions" view I meant. You can be right - they're are sorted by the call time. I'm wondering if it's possible to show them on some timeline? Currently, I can see only the latency graph, so again - the investigation is hard to perform, when you look for some specific calls that you made for testing.
2. Thank you for the idea of using captureBody - I will try it for sure.

---

<div class="post-metadata">

### Author: ![mkaniaa](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mkaniaa/32/138787_2.png) [@mkaniaa](https://discuss.elastic.co/u/mkaniaa)
#### Post date: [October 29, 2024, 6:02pm UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688/4 "2024-10-29T18:02:59Z")

</div>

@trentm, I tried out the solutions you suggested:

1. Setting `transaction.name` at the start of each resolver did the trick! Now, I can see separate transaction summaries for each resolver - thanks for the tip!

2. Following the documentation, I set `ELASTIC_APM_CAPTURE_BODY` to "all". I do see a new attribute, `http.request.body.original`, for some requests, but the value is consistently `[REDACTED]`. Do you know if this might be due to something in my infrastructure or if there’s another Elastic configuration I might need to adjust? It seems like setting `ELASTIC_APM_CAPTURE_BODY` to "all" might not be enough.

3. I’m still looking for a way to view all transactions for a given resolver on a timeline. Is there a way to add a custom widget to the transaction view to display them on a timeline? Right now, I only see the widget related to latency.

Thanks for your help!

---

<div class="post-metadata">

### Author: ![trentm](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/trentm/32/77647_2.png) [@trentm](https://discuss.elastic.co/u/trentm)
#### Post date: [October 29, 2024, 8:36pm UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688/5 "2024-10-29T20:36:21Z")

</div>

> [@mkaniaa](#):
>
> Following the documentation, I set `ELASTIC_APM_CAPTURE_BODY` to "all". I do see a new attribute, `http.request.body.original`, for some requests, but the value is consistently `[REDACTED]`.

(Again now that `captureBody` is only about capturing the HTTP body for _incoming_ requests to your service.)

Hrm, getting `[REDACTED]` should not happen if it is set to `'all'`. Perhaps something is surprisingly setting that value to something other than `'all'`.

- First thing to check: At startup, the APM agent emits a log line (at "info" level) that includes a dump of its config. E.g.:

```auto
{"log.level":"info","@timestamp":"2024-10-29T20:21:56.138Z","log.logger":"elastic-apm-node","ecs.version":"8.10.0","agentVersion":"4.8.0","env":{"pid":12004,"proctitle":"node","os":"darwin 23.6.0","arch":"arm64","host":"peach.local","timezone":"UTC-0700","runtime":"Node.js v18.20.4"},"config":{"logLevel":{"source":"default","value":"info","commonName":"log_level"},"serverUrl":{"source":"default","value":"http://127.0.0.1:8200/","commonName":"server_url"},"serviceName":{"source":"start","value":"example-trace-http-request","commonName":"service_name"},"serviceVersion":{"source":"default","value":"4.8.0","commonName":"service_version"}},"activationMethod":"require","message":"Elastic APM Node.js Agent v4.8.0"}

```

Can you find that to see if you have a value for `captureBody` in that log line?

- Second thing to check: Are you using central-config? [APM Agent central configuration | Elastic Observability [8.15] | Elastic](https://www.elastic.co/guide/en/observability/current/apm-agent-configuration.html) This could result in the startup value of `captureBody` being changed at runtime. If so, there would be an "info"-level log output from the Node.js APM agent saying something like:

```auto
Central config success: updated captureBody: ${value}

```

> [@mkaniaa](#):
>
> I’m still looking for a way to view all transactions for a given resolver on a timeline. Is there a way to add a custom widget to the transaction view to display them on a timeline?

The built-in APM UI doesn't support this. You'll have to poke around in Discover (in Kibana) or build your own Dashboard.

For example in Discover, searching the `traces-apm*` data stream with the query `service.name: "example-trace-http-request" AND processor.event:"transaction"` (for my example service) looks something like this:

 ![Screenshot 2024-10-29 at 1.28.09 PM](https://us1.discourse-cdn.com/elastic/original/3X/f/0/f0efeb9d97f5b6cb936ab63bf965df4ce7bff1a3.jpeg)

That shows individual transactions, and a chart at the top that shows the number of transactions per time bucket. There are some limited options on that chart provide a breakdown (perhaps by transaction.name).

If that is not sufficient, then you could look at the `Dashboards` in Kibana and play around with a visualization that works for you:

 ![Screenshot 2024-10-29 at 1.35.39 PM](https://us1.discourse-cdn.com/elastic/original/3X/a/a/aae7eab5a4f097b11ae2a1c6d04726af2cdfd2d6.jpeg)

The charting support is very powerful, which is good, but means there is a learning curve.

---

<div class="post-metadata">

### Author: ![mkaniaa](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mkaniaa/32/138787_2.png) [@mkaniaa](https://discuss.elastic.co/u/mkaniaa)
#### Post date: [October 30, 2024, 5:43pm UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688/6 "2024-10-30T17:43:01Z")

</div>

Hello @trentm, here’s the log you requested (with certain addresses altered due to policy):

```auto
{
    "log.level": "info",
    "@timestamp": "2024-10-29T18:23:10.171Z",
    "log.logger": "elastic-apm-node",
    "ecs.version": "8.10.0",
    "agentVersion": "4.7.3",
    "env": {
        "pid": 8,
        "proctitle": "/var/lang/bin/node",
        "os": "linux 5.10.226-235.879.amzn2.x86_64",
        "arch": "x64",
        "host": "111.111.11.111",
        "timezone": "UTC+00",
        "runtime": "Node.js v20.18.0"
    },
    "config": {
        "captureBody": {
            "source": "environment",
            "value": "all",
            "commonName": "capture_body"
        },
        "environment": {
            "source": "environment",
            "value": "dev"
        },
        "globalLabels": {
            "source": "environment",
            "value": [
                [
                    "service_namespace",
                    "service_dev"
                ]
            ],
            "sourceValue": "service_namespace=service_dev"
        },
        "logLevel": {
            "source": "default",
            "value": "info",
            "commonName": "log_level"
        },
        "serverUrl": {
            "source": "environment",
            "value": "https://example-server.com/",
            "commonName": "server_url"
        },
        "apiKey": {
            "source": "environment",
            "value": "[REDACTED]",
            "commonName": "api_key"
        },
        "serviceName": {
            "source": "environment",
            "value": "fsn_appsync_batch_smart_queue",
            "commonName": "service_name"
        },
        "serviceVersion": {
            "source": "default",
            "value": "$LATEST",
            "commonName": "service_version"
        }
    },
    "activationMethod": "env-attach",
    "message": "Elastic APM Node.js Agent v4.7.3"
}

```

> (Again now that `captureBody` is only about capturing the HTTP body for _incoming_ requests to your service.)

I'm trying to understand, how it apply to my case, so maybe I will clarify my setup and goals, here’s what I’d like to achieve and please let me know if it's possible with Elastic APM.

1. **Tracking `event.arguments.input` for Incoming Lambda Events**  
My AppSync Lambda function is triggered by events containing parameters that my resolvers need for various actions. These parameters are nested in `event.arguments.input`, as shown here:

```auto
export const getValues: AppSyncResolverHandler<
  DataArgs,
  ValuesResult
> = async (event, _context, _callback) => {
  apm.setTransactionName('myTransactionName');
  const { columns, teamId, userId } = event.arguments.input;

```

I’d like to see the content of `event.arguments.input` in APM to review the actual values that triggered the resolver. Currently, I can only see `event.outcome` and `event.success_count`.

1. **Capturing Request and Response Bodies for Outgoing API Calls**  
My resolver functions use the incoming event values to make further API requests. Ideally, I’d like to capture both the request body and response content of these outgoing calls. Another example from the code:

```auto
documents = await documentApiClient.getDocuments(documentIds);

```

Right now, I see only:

- **http.request.body.original** always with the value `[REDACTED]`.
- **http.request.env** showing values for REMOTE\_ADDR, SERVER\_NAME, and SERVER\_PORT.
- **http.request.headers** with the correct header values.
- **http.request.method** for the request method used.
- **http.response.headers** with response headers from the API.
- **http.response.status\_code** with the API response status code.

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/4/b/4b87212c75f5e33b9e23accc85de56a769778000.png)

As you can see, the request and response bodies, which are crucial for my investigation, are not accessible or are redacted.

Any insights on whether it’s possible to achieve these goals with Elastic APM would be very helpful. Thank you!

---

<div class="post-metadata">

### Author: ![mkaniaa](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mkaniaa/32/138787_2.png) [@mkaniaa](https://discuss.elastic.co/u/mkaniaa)
#### Post date: [November 8, 2024, 1:50pm UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688/7 "2024-11-08T13:50:43Z")

</div>

Ad 1:  
In the traces for specific APIs, I can see the URL arguments used for the requests, which is sufficient for my needs.

Ad 2:

- I had to set the flag `ELASTIC_APM_CAPTURE_BODY` to `"all"` for each API called by AppSync to view the request bodies for each call.
- I was able to capture response bodies by using labels.

My remaining concern is the `maxLength` for JSON in labels. Here’s how I’m currently creating the label for responses in my Flask API service:

```auto
@app.after_request
def apm_label_response_data(response):
    response_data = response.get_json(silent=True)
    limited_data = json.dumps(response_data)[:10000]
    elasticapm.label(response_body=limited_data)
        
    return response

```

The problem is that my limit doesn’t seem to take effect, as the JSON is still truncated to only 1024 characters. I believe the reason is explained [**here**](https://discuss.elastic.co/t/character-limit-in-fields-longer-then-1024-truncated-with-ellipsis/280415/5). Is there any way to adjust this limit for my label?

---

<div class="post-metadata">

### Author: ![mkaniaa](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mkaniaa/32/138787_2.png) [@mkaniaa](https://discuss.elastic.co/u/mkaniaa)
#### Post date: [November 9, 2024, 4:28pm UTC](https://discuss.elastic.co/t/separate-transaction-for-every-resolver-in-aws-lambda/369688/8 "2024-11-09T16:28:49Z")

</div>

I created a separate topic for the `maxLength` JSON issue: [Max Characters Length for JSON in transaction labels](https://discuss.elastic.co/t/max-characters-length-for-json-in-transaction-labels/370257).

The problem described here is solved and this topic can be closed.
