# log.trace

The `log.trace` function writes a message to the Atmos log at the trace level. Use it for the finest-grained detail, such as each iteration of a loop or every value a script considers.

## Usage

```python
log.trace(message, **fields)
```

## Arguments

- **`message`**

  Required. Exactly one positional argument: the message, as a string. Passing more than one positional
  argument, or a value that is not a string, fails with an argument error.
- **`**fields`**

  (Optional) Any number of keyword arguments, written to the log as structured key/value pairs after the
  message. Field names must be identifiers: letters, digits, and underscores, not starting with a digit.
  A value that is `None`, a boolean, an integer, a float, or a string keeps its native type. Any other value,
  such as a list or dictionary, is logged using its `str()` form.

## Returns

The return value is `None`. The call is a side effect and never becomes the step value.

## Behavior

- The message goes to the Atmos logger, so it honors `--logs-level` (or `ATMOS_LOGS_LEVEL`) and `logs.file`. Below
  the configured level, the call prints nothing. See [logs configuration](/cli/configuration/logs) for the levels
  and the log destination.
- Log output never goes to stdout, so it cannot change what `print` or [`output`](/functions/automation/output)
  produce.
- Every message carries a `step` field: the step name for a workflow or custom command step, or the script path for a
  standalone script.
- Inside [`steps.parallel`](/functions/automation/steps.parallel) tasks, a `task` field names the running task, for
  example `task=dev`, or `task=grp/inner` for nested groups. A group with a single task adds no `task` field.
- If you pass a field named `step` or `task`, your value replaces the automatic one.
- Secrets that Atmos knows about are masked in the message and in field values.

## Examples

### Trace each item in a loop

```python
for stack in ["dev", "prod"]:
    log.trace("evaluating stack", stack = stack)
```

```text
 TRCE  evaluating stack step=scan stack=dev
 TRCE  evaluating stack step=scan stack=prod
```

Run the workflow with `ATMOS_LOGS_LEVEL=Trace` (or `--logs-level=Trace`) to see the lines. At the default level, the calls print nothing.

### Fields set by Atmos

```python
def apply(stack):
    log.trace("applying", stack = stack)

steps.parallel(tasks = [steps.task(s, apply, args = [s]) for s in ["dev", "prod"]])
```

Each line carries the `step` field and a `task` field naming the task that wrote it, for example
`step=apply task=dev stack=dev`.

## Errors

- A call with no message, with more than one positional argument, or with a message that is not a string fails with an
  argument error that names `log.trace`.
- A field name that is not an identifier, such as `bad-name`, fails with an argument error that quotes the name.

## Related

- Other levels: [`log.debug`](/functions/automation/log.debug), [`log.info`](/functions/automation/log.info), [`log.warn`](/functions/automation/log.warn), [`log.error`](/functions/automation/log.error).
- [`ui.info`](/functions/automation/ui.info), [`ui.success`](/functions/automation/ui.success), and [`ui.warning`](/functions/automation/ui.warning) show status to the person running the program.
- [`print`](/functions/automation/print) writes data to stdout.
- [Atmos Automation Language](/automation/language) and the [script step](/steps/type/script#logging)
