# Troubleshoot function execution and logs

A function created with [Functions](/en/documentation/platform/functions/) can fail at instantiation, at invocation, or during execution. Without log output, you cannot tell which. Each section below names one symptom, gives its cause, and states the fix. Start with the logs.

---

## No log output from a function

You call `console.log` in the function, and no message reaches you.

A function writes log messages with `console.log`, the same way JavaScript does in a browser. Azion collects that output and delivers it in three places: [Azion CLI](/en/documentation/devtools/cli/), [Data Stream](/en/documentation/platform/data-stream/), and [Real-Time Events](/en/documentation/platform/real-time-events/). Until you open one of them, a function that fails and a function that never runs look the same.

First, confirm that the code logs something. This handler logs a message and returns a response:

```javascript
export default {
  async fetch(request, env, ctx) {
    console.log('Hello World');
    return new Response('Checking console output.', { status: 200 });
  },
};
```

To read the output from the terminal with Azion CLI:

```bash
azion logs cells --tail
```

The terminal prints the console messages of the last 5 minutes and keeps printing new ones. Add `--function-id` to restrict the output to one function. The subcommand is named `cells` because a function runs inside a Cell, the isolation environment Azion builds on V8 isolates.

To send the same output to an endpoint you own:

1. **Open Data Stream**

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

2. **Select + Stream**

3. **Enter a name for the stream**

4. **In the Data Settings section, set Source to Functions**

5. **Set Template to Functions Event Collector**

6. **In the Destination section, keep Connector as Standard HTTP/HTTPS POST and enter the URL that receives the data**

7. **Select Save**

The stream sends the log messages of your functions to that URL.

The **Functions Event Collector** template fills the **Data Set** box with this preset:

```json
{
	"time": "$time",
	"client": "$client",
	"configuration": "$global_id",
	"edgeFunctionID": "$edge_function_id",
	"requestID": "$request_id",
	"messageSource": "$message_source",
	"logLevel": "$log_level",
	"logMessage": "$log_message"
}
```

| Variable            | Definition                                                    |
| ------------------- | ------------------------------------------------------------- |
| `$time`             | Date and time of the request.                                 |
| `$client`           | Unique Azion customer identifier.                             |
| `$global_id`        | Settings identification.                                      |
| `$edge_function_id` | Function identifier.                                          |
| `$request_id`       | Request identifier.                                           |
| `$message_source`   | The source of the message.                                    |
| `$log_level`        | Level of the log created: ERROR, WARN, INFO, DEBUG, or TRACE. |
| `$log_message`      | Message used on the log when the function is requested.       |

To read the same output in Azion Console:

1. **Open Real-Time Events**

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

2. **Select the Functions Console tab**

3. **Use the filters to narrow the query**

4. **(Optional) Select the Data Stream tab to see the records sent to your endpoints**

5. **Select an item to see its details**

The **Functions Console** tab lists one entry per log message, with its level, its line, and the identifier of the request that produced it.

---

## The function never runs

The function exists in **Functions**, and no request executes it. Nothing from its `console.log` calls reaches the function logs.

Creating a function does not execute it. A function runs when an application instantiates it and a [Rules Engine](/en/documentation/platform/applications/rules-engine/) rule selects that instance through the **Run Function** behavior. Without both, the code never executes.

Check the chain in order:

- **Functions module**: in the **Main Settings** tab of the application, turn on **Functions**, then select **Save**.
- **Function instance**: in the **Functions Instances** tab, select **+ Function Instance**, select your function, and set its **Args**. The instance binds the function to the application and carries the JSON arguments passed to its execution context. Refer to [Function instances](/en/documentation/platform/applications/functions-instances/).
- **Run Function rule**: in the **Rules Engine** tab, add a rule, set its criteria, and select the **Run Function** behavior with your instance.
- **Rule criteria**: a rule executes its behaviors only when the request meets its criteria. Compare the criteria against the URI you request.
- **Behavior order**: **Deliver**, **Deny (403 Forbidden)**, and **Finish Request Phase** end the processing of a request. A **Run Function** arranged after one of them never executes. Move it above the behavior that finishes the request.

The next request that meets the criteria executes the function, and its messages reach the function logs.

---

## Run Function is missing from the behaviors list

You add a rule in the **Rules Engine** tab of an application, and **Run Function** is not among the behaviors.

The **Run Function** behavior depends on two modules: [Application Accelerator](/en/documentation/platform/applications/#application-accelerator) and **Functions**. While Application Accelerator is turned off, Rules Engine offers only part of its variables and behaviors.

To turn on both modules:

1. **Open your application**

   Access [Azion Console](https://console.azion.com/) > **Applications** > your application.

2. **In the Main Settings tab, under the Modules section, turn on Application Accelerator**

3. **Turn on Functions**

4. **Select Save**

**Run Function** appears in the behaviors list of a rule, in the request phase and in the response phase.

---

## The function fails at instantiation

The function instance carries a large **Args** object, and the function fails at instantiation.

**Args** is the JSON object a function instance passes to the execution context of the function. The field accepts a maximum of 100 KB. Arguments above that size make the function fail at instantiation.

- **Trim the Args object**: reduce the JSON until it is under 100 KB.
- **Request a higher limit**: the 100 KB is a default. To raise it for your plan, contact [technical support](/en/documentation/support/).

With the arguments under 100 KB, the instance saves and the function receives the object in its execution context.

---

## The function stops before it returns a response

The function starts, and the invocation ends before the code returns its response.

One invocation runs inside three ceilings, and a fourth bounds the execution context that carries it:

- **CPU execution time, 2 s**: the maximum CPU time per invocation. It measures active computation, not wall-clock time, so time spent waiting on `fetch()` does not count against it. A function that exceeds it is terminated.
- **Execution time, 5 min**: the maximum wall-clock time for one invocation, including I/O wait, `fetch()` calls, and async operations.
- **Sub-requests, 50**: the maximum number of outbound `fetch()` calls in one invocation.
- **Memory per isolate, 512 MB**: the maximum memory for a single execution context (V8 isolate), including heap, stack, and all runtime allocations.

Reduce the work the invocation performs:

- **Find where the pressure sits**: a function that spends its run waiting on an origin stays far from the CPU ceiling, however long the request takes. A function that parses or transforms a large body on every request spends that budget instead.
- **Move work off the response path**: work whose result the response does not need is handed to `ctx.waitUntil()`, which extends the lifetime of the function past the response. That takes the work off the response path without taking it out of the invocation. The 5-minute wall clock still bounds it.
- **Consolidate the outbound calls**: a design whose call count grows with its input, one request per item in a list, crosses the sub-request ceiling as soon as the list passes 50 items. Replace one call per member with one call that returns the set. That trade has its own boundary: the body a function can process is capped by plan, at 100 MB on Hobby, 200 MB on Pro, and 500 MB on Enterprise.
- **Request a higher limit**: every ceiling is a default. To raise one for your plan, contact [technical support](/en/documentation/support/).

For the complete set, refer to [Limits](/en/documentation/platform/functions/limits/).

A function whose work fits inside the CPU and wall-clock ceilings runs to the end and returns its response.

---

## `ERROR` entries in the function logs

The function logs carry entries whose level is `ERROR`, such as `TypeError: Object not found`.

Every log entry carries a level and a line source. `CONSOLE` marks a line your own code wrote with `console.log`. `RUNTIME` marks a line Azion Runtime produced, such as an exception raised while the function executed.

- **Read the whole request**: the request identifier aggregates every message from a single request. Filter on it to see what the function logged before the error.
- **Query the events**: the `functionConsoleEvents` query returns `level`, `lineSource`, and `line` for a time range. Refer to [Query function logs with GraphQL API](/en/documentation/guides/application-development/functions-and-runtime/debugging-functions-graphql/).
- **Filter in Azion Console**: the **Functions Console** tab of [Real-Time Events](/en/documentation/platform/real-time-events/) shows the same fields with filters.

You now have the exception message and the identifier of the request that produced it.

---

## The preview renders nothing

You open [Preview deployment](/en/documentation/platform/functions/preview-deployment/) for a function, and no response appears next to the code editor. The preview shows a warning that names the missing function.

Preview deployment renders the outcome of an auxiliary function in your source code, called `PreviewProvider`. That function builds a simulated request and hands the result to the preview. Without `PreviewProvider`, the preview has nothing to render.

- **Add the `PreviewProvider` function**: it creates the request the preview sends to your handler. Refer to [Preview deployment](/en/documentation/platform/functions/preview-deployment/).
- **Debug the running function with `inspect`**: the `inspect` tool inside Preview deployment shows the behavior of the function in real time.

The preview renders the response next to the code editor, and the **open** button shows it in a separate tab.

---

## Related resources

- [Preview deployment](/en/documentation/platform/functions/preview-deployment.md): Reproduce the response in Azion Console before the function serves traffic.
- [Function instances](/en/documentation/platform/applications/functions-instances.md): How an instance binds a function to an application and carries its Args.
- [Rules Engine](/en/documentation/platform/applications/rules-engine.md): The processing phases, the criteria, and the Run Function behavior.
- [Debug functions on Data Stream](/en/documentation/guides/platform/observability/debugging-functions-data-stream.md): The full stream setup, including the workload association.
- [Real-Time Events](/en/documentation/platform/real-time-events.md): The fields of the Functions Console data source.
- [Azion CLI logs](/en/documentation/devtools/cli/logs.md): The flags of the `azion logs cells` command.
