# 8.1. Logging

Morloc Manual > Managing Runs | https://morloc-project.github.io/docs/runs/logging.html | prev: https://morloc-project.github.io/docs/runs/index.md | next: https://morloc-project.github.io/docs/runs/benchmarking.md

Morloc programs can emit per-call log lines around any labeled term in source. Logging is opt-in — a term emits start, pass, and fail messages only after the user wires it up in the program’s YAML config.

The log lines go to stderr, so the program’s stdout (the computed result and any user-printed data) is unaffected. Templates are user-controlled and may include ANSI color codes; colors are automatically stripped when stderr is not a terminal so log files and pipes never contain control bytes.

## 8.1.1. Enabling logging

Two things turn logging on for a term:

1.  The term must be labeled in source code. A labeled term is written `label@term`. For example, `a@map` is the term `map` with label `a`. Labels are per-call-site, so `(a@foo x, b@foo y)` labels two distinct invocations of the same `foo`.
2.  The label must appear in the program’s `.yaml` config under `labeled-groups` with `log: true`. The config lives next to the `.loc` source: for `main.loc`, the config is `main.yaml`.

A minimal config:

```yaml
labeled-groups:
  big: { log: true }
```

A label group can be applied to many terms (`big@read`, `big@parse`, `big@save`); all of them log under the same group.

## 8.1.2. Template placeholders

The compiler emits up to three lines per labeled call — **start** (entry), **pass** (success), and **fail** (exception). Each line’s text is a user template with `{placeholder}` substitutions. The default template is:

```yaml
log-template:
  start: "[{date}] {module}:{line}:{name}:{lang} start"
  pass:  "[{date}] {module}:{line}:{name}:{lang} pass (time={runtime})"
  fail:  "[{date}] {module}:{line}:{name}:{lang} fail (time={runtime})"
```

Templates resolve in this order, per subfield: per-label override > program-wide `log-template` (top of the config) > built-in default. A `null` subfield silences that event. A common case is to silence the verbose start and pass messages and keep only the failure trace:

```yaml
log-template:
  start: null
  pass:  null
  fail:  "{date} {module}:{line}:{name} FAILED (time={runtime})"
```

Setting all three subfields to `null` while keeping `log: true` is rejected at compile time as contradictory (use `log: false` instead).

Available placeholders:

| Placeholder | Value |
| --- | --- |
| `{name}` | The labeled term’s identifier in source (e.g. `map` for `big@map`). |
| `{group}` | The label group name (e.g. `big` for `big@map`). |
| `{lang}` | The pool language: `py`, `cpp`, `r`, etc. |
| `{module}` | The source file path of the labeled reference. |
| `{line}` | Line number of the labeled reference. |
| `{column}` | Column number of the labeled reference. |
| `{index}` | The manifold ID assigned by the compiler. Useful for cross-referencing with `morloc dump` output. |
| `{date}` | UTC ISO 8601 timestamp at the moment of emission, second resolution (e.g. `2026-06-08T16:59:59Z`). |
| `{runtime}` | Elapsed time in seconds since the call’s start, with microsecond precision. Pass `0.0` at the start event. |
| `{id}` | Call id pairing a start with its pass/fail. Format `{pid}:{counter}`; unique within a pool process. |

Unknown placeholder names are a compile-time error citing the file and line of the offending YAML entry.

## 8.1.3. Color codes

A `{c:NAME}` placeholder expands to the corresponding ANSI SGR escape sequence at compile time. Apply a color, render the text, then reset with `{c:reset}`:

```yaml
start: "{c:blue}{name}{c:reset} start"
```

Attributes and clears:

| Placeholder | Effect |
| --- | --- |
| `{c:reset}` | Reset every attribute to terminal default. |
| `{c:bold}` | Bold / increased intensity. |
| `{c:dim}` | Faint. |
| `{c:italic}` | Italic. |
| `{c:underline}` | Underline. |
| `{c:blink}` | Slow blink. |
| `{c:rapid-blink}` | Rapid blink. Spotty terminal support; prefer `{c:blink}`. |
| `{c:reverse}` | Swap foreground and background. |
| `{c:hidden}` | Conceal text (still occupies space). |
| `{c:strike}` | Strikethrough. |
| `{c:no-bold}` | Cancel bold (ANSI conflates with dim; see `{c:no-dim}`). |
| `{c:no-dim}` | Cancel dim (same SGR code as `{c:no-bold}`). |
| `{c:no-italic}` | Cancel italic. |
| `{c:no-underline}` | Cancel underline. |
| `{c:no-blink}` | Cancel blink. |
| `{c:no-reverse}` | Cancel reverse. |
| `{c:no-hidden}` | Cancel conceal. |
| `{c:no-strike}` | Cancel strikethrough. |

Foreground colors:

| Standard | Bright |
| --- | --- |
| `{c:black}` | `{c:bright-black}` (alias: `{c:gray}`, `{c:grey}`) |
| `{c:red}` | `{c:bright-red}` |
| `{c:green}` | `{c:bright-green}` |
| `{c:yellow}` | `{c:bright-yellow}` |
| `{c:blue}` | `{c:bright-blue}` |
| `{c:magenta}` | `{c:bright-magenta}` |
| `{c:cyan}` | `{c:bright-cyan}` |
| `{c:white}` | `{c:bright-white}` |
| `{c:default}` | — |

Background colors:

| Standard | Bright |
| --- | --- |
| `{c:bg-black}` | `{c:bg-bright-black}` |
| `{c:bg-red}` | `{c:bg-bright-red}` |
| `{c:bg-green}` | `{c:bg-bright-green}` |
| `{c:bg-yellow}` | `{c:bg-bright-yellow}` |
| `{c:bg-blue}` | `{c:bg-bright-blue}` |
| `{c:bg-magenta}` | `{c:bg-bright-magenta}` |
| `{c:bg-cyan}` | `{c:bg-bright-cyan}` |
| `{c:bg-white}` | `{c:bg-bright-white}` |
| `{c:bg-default}` | — |

## 8.1.4. Terminal detection and NO\_COLOR

Color codes from `{c:…​}` placeholders (or raw ANSI escapes a user writes directly into a template) are emitted unchanged when stderr is a terminal **and** the environment variable `NO_COLOR` is unset. In every other case — stderr redirected to a pipe or file, or `NO_COLOR` set to any value — the runtime strips all CSI sequences before writing, so the output is plain text.

This means the same program produces colored output when run interactively:

```bash
./main '[[1,2],[3,4,5]]'      # colors emitted to terminal
```

and plain text when piped or redirected:

```bash
./main '[[1,2],[3,4,5]]' 2> run.log    # run.log is plain text
./main '[[1,2],[3,4,5]]' 2>&1 | grep pass    # grep sees plain text
```

To suppress color even in an interactive terminal (e.g. for screen captures, color-blind users, or terminals with non-standard palettes), set `NO_COLOR`:

```bash
NO_COLOR=1 ./main '[[1,2],[3,4,5]]'
```

`NO_COLOR` follows the convention at [no-color.org](https://no-color.org/): any non-empty value disables color; the variable being unset means color is allowed.

## 8.1.5. Worked example

The source labels two terms — `a@map` and `b@sum` — in a small two-stage pipeline that sums each inner list:

```morloc
module main (foo)

import root-py

sum :: [Real] -> Real
sum = fold (+) 0.0

foo :: [[Real]] -> [Real]
foo = a@map b@sum
```

The config enables both labels and uses a maximalist template that exercises every color category and every placeholder:

```yaml
log-template:
  start: "{date} {c:gray}{module}:{line}:{column}:{lang}{c:reset} {name}:{id}: {c:blue}start{c:reset}"
  pass:  "{date} {c:gray}{module}:{line}:{column}:{lang}{c:reset} {name}:{id}: {c:green}pass{c:reset} {c:grey}(time: {runtime}){c:reset}"
  fail:  "{date} {c:gray}{module}:{line}:{column}:{lang}{c:reset} {name}:{id}: {c:red}fail{c:reset} {c:grey}(time: {runtime}){c:reset}"

labeled-groups:
  a: { log: true }
  b: { log: true }
```

Build and run on a two-row input:

```bash
morloc make -o main main.loc
./main '[[1,2],[3,4,5]]'
```

Output to stderr, rendered with the colors a terminal would show (one start/pass pair per call; `a@map` brackets two inner `b@sum` calls; the nested call ids `0`, `1`, `2` pair start lines with their corresponding pass lines):

```stderr
2026-06-08T16:59:59Z main.loc:9:7:py map:33804:0: start
2026-06-08T16:59:59Z main.loc:9:13:py sum:33804:1: start
2026-06-08T16:59:59Z main.loc:9:13:py sum:33804:1: pass (time: 0.000023)
2026-06-08T16:59:59Z main.loc:9:13:py sum:33804:2: start
2026-06-08T16:59:59Z main.loc:9:13:py sum:33804:2: pass (time: 0.000009)
2026-06-08T16:59:59Z main.loc:9:7:py map:33804:0: pass (time: 0.000520)
```

Output to stdout (the actual program result):

```
[3,12]
```

Run the same command with `NO_COLOR=1` for plain output even when stderr is a terminal:

```bash
NO_COLOR=1 ./main '[[1,2],[3,4,5]]'
```
