---
name: azion-debug-functions-with-data-stream
description: >-
  Send the log messages of your functions, with their level and request ID, to an endpoint with Data Stream, in Azion Console or with the Azion API.
---

# Debug functions with Data Stream

You can send the log messages of your functions to an endpoint with a [Data Stream](/en/documentation/platform/data-stream/) stream, from Azion Console or with the Azion API. To trace which Rules Engine rules ran on a request instead, refer to [Debug rules created with Rules Engine](/en/documentation/guides/application-development/getting-started/debug-rules/).

A function writes log messages with `console.log`. The *Functions* data source carries each message with its level, its source, and the ID of the request, and the *Functions Event Collector* template places them in one log line per message. For every variable of the data source, refer to [Data sources and variables](/en/documentation/platform/data-stream/data-sources-and-variables/#functions).

---

Select your interface once. The prerequisites and every task below show only that path.

## Prerequisites

- An Azion account with the **Edit Data Stream** permission. For the permissions, refer to [Stream settings](/en/documentation/platform/data-stream/stream-settings/#permissions).
- A function that writes log messages with `console.log`, running in an application that a [workload](/en/documentation/platform/workloads/) serves. To create one, refer to [Functions](/en/documentation/platform/functions/).
- An endpoint that receives the log lines, such as an HTTP endpoint. For the endpoints a stream can send to, refer to [Endpoints](/en/documentation/platform/data-stream/endpoints/).

**Console**

- Access to Azion Console. To sign in, refer to [How to access Azion Console](/en/documentation/guides/platform/account-and-billing/how-to-access-azion-console/).

**API**

- A personal token. To create one, refer to [How to manage a personal token](/en/documentation/guides/platform/account-and-billing/personal-tokens/).
- The ID of the workload that serves the application of the function.
- `curl`.

---

## Create the stream

The stream reads the *Functions* data source of one workload through a workload filter. Unlike sampling, a workload filter leaves your other streams active.

**Console**

To create the stream in Azion Console:

1. **Open Data Stream**

   Access [Azion Console](https://console.azion.com/) > **Data Stream**.

2. **Select + Stream**

3. **Name the stream**

   In the **General** section, enter a **Name**. For example: `functions-debug`.

4. **Select the Functions data source**

   In the **Input** section, select *Functions* in **Data Source**.

5. **Turn off Sampling**

   In the **Transform** section, turn off **Sampling** while **Option** is still *All Current and Future Workloads*, its starting value. A stream cannot carry sampling and a workload filter together.

6. **Choose the workload**

   In the **Transform** section, set **Option** to *Filter Workloads*. In **Available Workload**, select the workload of the function and move it to **Chosen Workload** with the arrow. For the pick-list controls, refer to [Associate workloads with a stream](/en/documentation/guides/platform/observability/data-stream-associate-workloads/).

7. **Select the template**

   In the **Render Template** section, select *Functions Event Collector* in **Template**. The **Data Set** field shows the keys of each log line.

8. **Select the endpoint**

   In the **Output** section, select your endpoint in **Connector**, and enter the fields it shows. Each endpoint has its own fields, listed on [Endpoints](/en/documentation/platform/data-stream/endpoints/).

9. **Keep the stream active**

   In the **Status** section, keep **Active** turned on.

10. **Select Save**

The Console shows `Your data stream has been created`. The stream appears in the **Data Stream** list with `functions_console` in the **Source** column and the *Active* status.

**API**

To create the stream with the API, send a `POST` request to `https://api.azion.com/v4/workspace/stream/streams`. This example sends the log lines to an HTTP endpoint. Replace `<workload-id>` with the ID of your workload, and the `url` and the header with the values of your endpoint:

```bash
curl -X POST 'https://api.azion.com/v4/workspace/stream/streams' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Token [TOKEN VALUE]' \
  -d '{
    "name": "functions-debug",
    "active": true,
    "inputs": [
      { "type": "raw_logs", "attributes": { "data_source": "functions_console" } }
    ],
    "transform": [
      { "type": "filter_workloads", "attributes": { "workloads": [<workload-id>] } },
      { "type": "render_template", "attributes": { "template": 86 } }
    ],
    "outputs": [
      {
        "type": "standard",
        "attributes": {
          "url": "https://logs.example.com/ingest",
          "headers": { "Authorization": "Bearer [API KEY]" }
        }
      }
    ]
  }'
```

The `functions_console` data source is *Functions* in the Console, and template `86` is *Functions Event Collector*. A `201` answer carries the stored stream under `data`. Keep its `id`: it identifies the stream in every later request, such as `/v4/workspace/stream/streams/<stream-id>`. For every key of the body, refer to [Stream settings](/en/documentation/platform/data-stream/stream-settings/#stream-object).

Saving the stream checks the format of each field and does not contact the endpoint. A wrong URL or credential surfaces only when the stream sends. An activation takes effect after one to two minutes.

---

## Confirm the delivery

[Real-Time Events](/en/documentation/platform/real-time-events/data-sources/#data-stream) records every send of a stream, delivered or not, with the status code the endpoint returned. Send a few requests that run the function, then wait about a minute: a stream sends a batch every 60 seconds, or sooner when it reaches 2,000 log lines.

**Console**

To find the sends in Azion Console:

1. **Open Real-Time Events**

   Access [Azion Console](https://console.azion.com/) > **Real-Time Events**.

2. **Select the Data Stream data source**

3. **Read the latest sends**

   Each row is one send. Find the rows whose **Endpoint Type** matches your endpoint, such as `HTTP_POST`, and read their **Status Code**.

A **Status Code** of `200` means the endpoint accepted the batch. **Streamed Lines** gives the number of log lines in the batch.

**API**

To read the sends with the API, query the `dataStreamedEvents` dataset of the Real-Time Events GraphQL API. Replace the dates with a range that covers the activation of the stream:

```bash
curl -X POST 'https://api.azion.com/v4/events/graphql' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Token [TOKEN VALUE]' \
  -d '{"query":"query { dataStreamedEvents(limit: 20, filter: {tsRange: {begin: \"2026-10-02T19:25:00\", end: \"2026-10-02T19:45:00\"}}, orderBy: [ts_DESC]) { ts jobName endpointType statusCode streamedLines url } }"}'
```

The API answers `200` with one record for each send, the latest first:

```json
{
  "data": {
    "dataStreamedEvents": [
      {
        "ts": "2026-10-02T19:29:00Z",
        …
        "endpointType": "HTTP_POST",
        "statusCode": 405,
        "streamedLines": 2,
        "url": "https://example.com/logs"
      },
      …
    ]
  }
}
```

`statusCode` carries the status the endpoint answered, and `streamedLines` the log lines of the batch. A `statusCode` of `200` means the endpoint accepted the batch, and an empty `dataStreamedEvents` list means the stream has not sent in the range. For every field, refer to [Real-Time Events GraphQL fields](/en/documentation/devtools/graphql/gql-real-time-events-fields/#datastreamedevents-data-stream).

A status other than `200` is the answer of the endpoint, and `503` means Data Stream found the endpoint unavailable. For the causes, refer to [Troubleshoot Data Stream](/en/documentation/platform/data-stream/troubleshooting/).

At the endpoint, each log line carries the keys of the *Functions Event Collector* template. Read these keys to debug the function:

- `logLevel` holds the level of the message: `ERROR`, `WARN`, `INFO`, `DEBUG`, or `TRACE`. It carries the `$log_level` variable.
- `logMessage` holds the message the function passed to the log function. It carries the `$log_message` variable.
- `messageSource` reads `CONSOLE` for a message written with the console API, such as `console.log`, and `RUNTIME` for an error message.
- `requestID` identifies the request, so the messages of one request share it.

To keep the variable names as keys, or to send fewer of them, select a custom template instead. For an example data set, refer to [Templates and payload](/en/documentation/platform/data-stream/templates-and-payload/#custom-templates).

---

## Next steps

- [Data sources and variables](/en/documentation/platform/data-stream/data-sources-and-variables.md#functions): Read what each variable of the Functions data source holds, with an example value.
- [Functions](/en/documentation/platform/functions.md): Write and run the functions whose messages the stream collects.
- [Create a custom template](/en/documentation/guides/application-development/frameworks/data-stream-custom-template.md): Choose which Functions variables each log line carries, and under which keys.
- [Troubleshoot Data Stream](/en/documentation/platform/data-stream/troubleshooting.md): Find what to change when a send returns a status other than 200.
