# Logging

## Purpose

The logging library provides a simple UART-based logging interface for embedded firmware.

Its main job is to let the application print formatted log messages such as:

```
[INFO] MOTOR: Initialization complete
[WARNING] SENSOR: Value out of range: 8123
[ERROR] CAN: Failed to transmit frame
```

The module is intentionally small:

- <span style="white-space: pre-wrap;">it supports </span>**three log levels**
- it formats messages in a consistent style
- it sends all output over a single UART
- it provides convenience macros for compile-time log filtering

<p class="callout warning"><span style="white-space: pre-wrap;">This is a </span>**printf-style logging system**, not a structured logger, ring buffer, or asynchronous trace system.</p>

## What problem this solves

Without this module, code would need to:

- manually format strings
- manually select a UART
- manually prepend log level tags
- duplicate formatting logic across the project

This library centralizes that behavior so all logs:

- use the same format
- use the same transport
- can be filtered by log level at compile time
- remain easy to call from application code

## Output format

<span style="white-space: pre-wrap;">Every log produced by </span>`<span class="editor-theme-code">LOG()</span>`<span style="white-space: pre-wrap;"> is formatted as:</span>

```
[LEVEL] TAG: message\r\n
```

### Example

```c
LOG(LOG_INFO, "IMU", "Sensor ready");
```

produces:

```
[INFO] IMU: Sensor ready\r\n
```

Another example:

```c
LOG(LOG_ERROR, "ETH", "TX failed with code %d", err);
```

produces something like:

```
[ERROR] ETH: TX failed with code -3\r\n
```

### Components

A log line contains:

- <span style="white-space: pre-wrap;">opening bracket </span>`<span class="editor-theme-code">[</span>`
- <span style="white-space: pre-wrap;">level string such as </span>`<span class="editor-theme-code">INFO</span>`
- <span style="white-space: pre-wrap;">closing bracket and space </span>`<span class="editor-theme-code">]</span>`
- tag string
- <span style="white-space: pre-wrap;">separator </span>`<span class="editor-theme-code">:</span>`
- user message string
- <span style="white-space: pre-wrap;">CRLF line ending </span>`<span class="editor-theme-code">\r\n</span>`

The line ending is Windows/terminal friendly and also common for UART console output.

## Public API overview

The public interface consists of:

- `<span class="editor-theme-code">LogLevel</span>`
- `<span class="editor-theme-code">log_level_to_string()</span>`
- `<span class="editor-theme-code">LOG_init()</span>`
- `<span class="editor-theme-code">LOG()</span>`
- convenience macros:
    - `<span class="editor-theme-code">LOGE</span>`
    - `<span class="editor-theme-code">LOGW</span>`
    - `<span class="editor-theme-code">LOGI</span>`

---

## Log levels

### Enum definition

```c
typedef enum {
  LOG_INFO,
  LOG_WARNING,
  LOG_ERROR,
  _LOG_LAST_LEVEL_DONT_EDIT
} LogLevel;
```

### Meaning

The library supports three levels:

- `<span class="editor-theme-code">LOG_INFO</span>`
- `<span class="editor-theme-code">LOG_WARNING</span>`
- `<span class="editor-theme-code">LOG_ERROR</span>`

These are stored as enum values starting at 0.

The final enum value:

```
_LOG_LAST_LEVEL_DONT_EDIT
```

is not an actual log level. It is a sentinel used to:

- count how many log levels exist
- validate the string table size

### Why that sentinel exists

The library keeps a parallel array of strings:

```c
static const char *LOG_LEVEL_STRINGS[] = {
    "INFO",
    "WARNING",
    "ERROR",
};
```

The enum and string table must stay aligned.

<span style="white-space: pre-wrap;">The sentinel lets the code verify that automatically with </span>`<span class="editor-theme-code">static_assert</span>`.

### `<span class="editor-theme-code">log_level_to_string()</span>`

```c
static inline const char *log_level_to_string(LogLevel logLevel);
```

#### Purpose

<span style="white-space: pre-wrap;">Converts a </span>`<span class="editor-theme-code">LogLevel</span>`<span style="white-space: pre-wrap;"> enum into its corresponding string.</span>

#### Valid conversions

- `<span class="editor-theme-code">LOG_INFO</span>`<span style="white-space: pre-wrap;"> -&gt; </span>`<span class="editor-theme-code">"INFO"</span>`
- `<span class="editor-theme-code">LOG_WARNING</span>`<span style="white-space: pre-wrap;"> -&gt; </span>`<span class="editor-theme-code">"WARNING"</span>`
- `<span class="editor-theme-code">LOG_ERROR</span>`<span style="white-space: pre-wrap;"> -&gt; </span>`<span class="editor-theme-code">"ERROR"</span>`

If the value is outside the valid range, it returns:

```
"NoLevel"
```

#### Notes

<span style="white-space: pre-wrap;">This function is declared </span>`<span class="editor-theme-code">static inline</span>`<span style="white-space: pre-wrap;"> in the header, so each translation unit including the header gets its own inline copy.</span>

It also contains a compile-time check:

```c
static_assert((sizeof(LOG_LEVEL_STRINGS) / sizeof(LOG_LEVEL_STRINGS[0])) ==
                    _LOG_LAST_LEVEL_DONT_EDIT,
                "Mismatch in number of log level strings!");
```

This prevents someone from adding or removing enum levels without updating the string table.

That is one of the few parts of this module behaving like it has trust issues, which is correct.

## Backend independence of the API

<p class="callout info"><span style="white-space: pre-wrap;">Although the current implementation sends logs over </span>**UART**<span style="white-space: pre-wrap;">, the </span>**public API itself is not inherently UART-specific**.</p>

From the perspective of code using the logger, the interface is simply:

- <span style="white-space: pre-wrap;">initialize the logging system with </span>`<span class="editor-theme-code">LOG_init(...)</span>`
- <span style="white-space: pre-wrap;">emit logs with </span>`<span class="editor-theme-code">LOG(...)</span>`
- <span style="white-space: pre-wrap;">or use the convenience macros </span>`<span class="editor-theme-code">LOGI</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">LOGW</span>`<span style="white-space: pre-wrap;">, and </span>`<span class="editor-theme-code">LOGE</span>`

Nothing in normal application code needs to know how the log is actually transported.

### What this means in practice

The current board-specific implementation uses:

- <span style="white-space: pre-wrap;">a UART handle passed into </span>`<span class="editor-theme-code">LOG_init()</span>`
- `<span class="editor-theme-code">HAL_UART_Transmit()</span>`<span style="white-space: pre-wrap;"> for output</span>
- `<span class="editor-theme-code">_write()</span>`<span style="white-space: pre-wrap;"> retargeting for stdout</span>

But that is only one possible backend.

A different board or firmware target could keep the same header/API and provide a different implementation, for example:

- USB CDC logging
- SWO / ITM logging
- RTT logging
- CAN or Ethernet debug output
- buffered logging to memory
- semihosting during development

### Why this matters

<p class="callout success"><span style="white-space: pre-wrap;">This separation means the API should be understood as a </span>**logical logging interface**, not as a UART contract.</p>

In this codebase, each board can provide its own implementation behind the same header, as long as it preserves the expected external behavior of the API.

That makes the module portable across boards without forcing higher-level application code to care about the physical logging transport, which is one of the few times abstraction is actually doing something useful instead of just breeding paperwork.

### Maintenance guidance

If a future board needs a different logging transport, the preferred approach is:

- <span style="white-space: pre-wrap;">keep </span>`<span class="editor-theme-code">logging.h</span>`<span style="white-space: pre-wrap;"> stable if possible</span>
- replace or adapt the implementation file for that board
- preserve the meaning of:
    - `<span class="editor-theme-code">LOG_init()</span>`
    - `<span class="editor-theme-code">LOG()</span>`
    - `<span class="editor-theme-code">LOGI/LOGW/LOGE</span>`

This allows application code to remain unchanged while the backend changes per target.

## Initialization

### `<span class="editor-theme-code">LOG_init()</span>`

```c
void LOG_init(void *arg);
```

### Purpose

Initializes the logging system by providing the UART handle that will be used for all later output.

### Expected argument

`<span class="editor-theme-code">arg</span>`<span style="white-space: pre-wrap;"> must point to a valid </span>`<span class="editor-theme-code">UART_HandleTypeDef</span>`.

### In practice:

```c
LOG_init(&huart2);
```

or whichever UART handle should be used for logging.

### What it does internally

`<span class="editor-theme-code">LOG_init() </span>`performs these steps:

1. <span style="white-space: pre-wrap;">Casts </span>`<span class="editor-theme-code">args</span>`<span style="white-space: pre-wrap;"> to </span>`<span class="editor-theme-code">UART_HandleTypeDef</span>`
2. copies the pointed-to UART handle into a private static variable
3. <span style="white-space: pre-wrap;">sets an internal </span>`<span class="editor-theme-code">initialized</span>`<span style="white-space: pre-wrap;"> flag</span>
4. <span style="white-space: pre-wrap;">writes a boot banner directly using </span>`<span class="editor-theme-code">_write()</span>`
5. emits an info log saying logging was initialized

## Important requirements

`<span class="editor-theme-code">LOG_init()</span>`<span style="white-space: pre-wrap;"> must be called before any normal logging is expected to work.</span>

<span style="white-space: pre-wrap;">If </span>`<span class="editor-theme-code">LOG()</span>`<span style="white-space: pre-wrap;"> is called before initialization, it silently returns without output.</span>

## Main logging function

### `<span class="editor-theme-code">LOG()</span>`

```c
void LOG(LogLevel level, const char *TAG, const char *log_message, ...);
```

### Purpose

Formats and transmits a log line over UART.

### Parameters

#### `<span class="editor-theme-code">level</span>`

The severity level of the message.

Expected values:

- `<span class="editor-theme-code">LOG_INFO</span>`
- `<span class="editor-theme-code">LOG_WARNING</span>`
- `<span class="editor-theme-code">LOG_ERROR</span>`

If the value is invalid, the implementation falls back to:

```c
"UNKNOWN"
```

for formatting.

#### `<span class="editor-theme-code">TAG</span>`

A short text label identifying the source of the log.

Typical examples:

- `<span class="editor-theme-code">"IMU"</span>`
- `<span class="editor-theme-code">"CAN"</span>`
- `<span class="editor-theme-code">"DISPATCHER"</span>`
- `<span class="editor-theme-code">"ETH"</span>`

This appears in the formatted output after the log level.

#### `<span class="editor-theme-code">log_message</span>`

A printf-style format string.

Examples:

- `<span class="editor-theme-code">"Init done"</span>`
- `<span class="editor-theme-code">"Received packet %u"</span>`
- `<span class="editor-theme-code">"Voltage too high: %d mV"</span>`

Optional variadic arguments used by the format string.

### Example usage

```
LOG(LOG_INFO, "MOTOR", "Started with speed %u", speed);
LOG(LOG_WARNING, "TEMP", "High temperature: %d", temp);
LOG(LOG_ERROR, "FLASH", "Write failed");
```

### Behavior when not initialized

If logging has not been initialized yet, the function returns immediately:

```c
if (initialized == 0) {
    return;
}
```

No output is produced.

This is deliberate.

## Internal formatting process

The implementation builds the final message in two phases.

### Phase 1: Build a full format string

It first constructs a format string like:

```
[INFO] MOTOR: Started with speed %u\r\n
```

<span style="white-space: pre-wrap;">This is stored in dynamically allocated memory called </span>`<span class="editor-theme-code">format_message</span>`.

### Phase 2: Format variadic arguments into final output

<span style="white-space: pre-wrap;">It then uses </span>`<span class="editor-theme-code">vsnprintf()</span>`<span style="white-space: pre-wrap;"> twice:</span>

1. once to calculate the final required length
2. once to write the fully formatted message into another dynamically allocated buffer

That final message is transmitted using:

```c
HAL_UART_Transmit(&huart_handler, (uint8_t *)total_message, total_len, HAL_MAX_DELAY);
```

After transmission, both heap allocations are freed.

## <span style="white-space: pre-wrap;">Retargeted </span>`<span class="editor-theme-code">_write()</span>`

```c
int _write(int file, char *ptr, int len);
```

### Purpose

This function retargets standard output to the configured UART.

### Behavior

<span style="white-space: pre-wrap;">If the file descriptor is </span>`<span class="editor-theme-code">1</span>`:

```c
if (file == 1)
```

<span style="white-space: pre-wrap;">the function transmits the provided buffer over UART using </span>`<span class="editor-theme-code">HAL_UART_Transmit()</span>`.

<span style="white-space: pre-wrap;">It then returns </span>`<span class="editor-theme-code">len</span>`.

### Why this exists

<span style="white-space: pre-wrap;">On many embedded toolchains, overriding </span>`<span class="editor-theme-code">_write()</span>`<span style="white-space: pre-wrap;"> allows C library output functions such as </span>`<span class="editor-theme-code">printf()</span>`<span style="white-space: pre-wrap;"> to write to UART.</span>

<p class="callout warning">That means this module is not only a custom logging module. It also partially redirects stdout.</p>

### Important note

<span style="white-space: pre-wrap;">This implementation only handles file descriptor </span>`<span class="editor-theme-code">1</span>`, which is typically stdout.

It does not distinguish stderr or other descriptors.

### <span style="white-space: pre-wrap;">Interaction with </span>`<span class="editor-theme-code">LOG()</span>`

`<span class="editor-theme-code">LOG()</span>`<span style="white-space: pre-wrap;"> does not actually use </span>`<span class="editor-theme-code">printf()</span>`<span style="white-space: pre-wrap;"> or </span>`<span class="editor-theme-code">_write()</span>`<span style="white-space: pre-wrap;"> for its main output path. It calls </span>`<span class="editor-theme-code">HAL_UART_Transmit()</span>`<span style="white-space: pre-wrap;"> directly after formatting its message.</span>

`<span class="editor-theme-code">LOG_init()</span>`<span style="white-space: pre-wrap;"> does use </span>`<span class="editor-theme-code">_write()</span>`<span style="white-space: pre-wrap;"> once to print the boot banner.</span>

<span style="white-space: pre-wrap;">So </span>`<span class="editor-theme-code">_write()</span>`<span style="white-space: pre-wrap;"> exists mainly for stdout retargeting and the boot line, not as the core mechanism used by </span>`<span class="editor-theme-code">LOG()</span>`<span style="white-space: pre-wrap;"> itself.</span>

## Convenience macros

The header defines these macros:

- `<span class="editor-theme-code">LOGE</span>`
- `<span class="editor-theme-code">LOGW</span>`
- `<span class="editor-theme-code">LOGI</span>`

<span style="white-space: pre-wrap;">These call </span>`<span class="editor-theme-code">LOG()</span>`<span style="white-space: pre-wrap;"> with a fixed level, but only if that level is enabled by </span>`<span class="editor-theme-code">CONFIG_LOG_LEVEL</span>`.

## Default log level configuration

<span style="white-space: pre-wrap;">If </span>`<span class="editor-theme-code">CONFIG_LOG_LEVEL</span>`<span style="white-space: pre-wrap;"> is not defined by the build system, the header sets:</span>

```c
#define CONFIG_LOG_LEVEL LOG_INFO
```

This means all log levels are enabled by default.

### Macro behavior

#### `<span class="editor-theme-code">LOGE</span>`

```c
#define LOGE(TAG, format, ...) LOG(LOG_ERROR, TAG, format, ##__VA_ARGS__)
```

Enabled when:

```c
(CONFIG_LOG_LEVEL <= LOG_ERROR)
```

<span style="white-space: pre-wrap;">Because </span>`<span class="editor-theme-code">LOG_ERROR</span>`<span style="white-space: pre-wrap;"> is the highest enum value in this setup, this macro is enabled for all current supported configurations.</span>

#### `<span class="editor-theme-code">LOGW</span>`

```c
#define LOGW(TAG, format, ...) LOG(LOG_WARNING, TAG, format, ##__VA_ARGS__)
```

Enabled when:

```
(CONFIG_LOG_LEVEL <= LOG_WARNING)
```

#### `<span class="editor-theme-code">LOGI</span>`

```c
#define LOGI(TAG, format, ...) LOG(LOG_INFO, TAG, format, ##__VA_ARGS__)
```

Enabled when:

```
(CONFIG_LOG_LEVEL <= LOG_INFO)
```

---

## Filtering semantics

Since the enum values are ordered:

- `<span class="editor-theme-code">LOG_INFO = 0</span>`
- `<span class="editor-theme-code">LOG_WARNING = 1</span>`
- `<span class="editor-theme-code">LOG_ERROR = 2</span>`

<span style="white-space: pre-wrap;">a lower configured value means </span>**more logs enabled**.

### Examples

#### `<span class="editor-theme-code">CONFIG_LOG_LEVEL = LOG_INFO</span>`

Enabled:

- info
- warning
- error

#### `<span class="editor-theme-code">CONFIG_LOG_LEVEL = LOG_WARNING</span>`

Enabled:

- warning
- error

Disabled:

- info

#### `<span class="editor-theme-code">CONFIG_LOG_LEVEL = LOG_ERROR</span>`

Enabled:

- error only

Disabled:

- warning
- info

### Disabled macro behavior

When disabled, the macro expands to:

```
(void)0
```

So the call is compiled out.

This is compile-time filtering, not runtime filtering.

That matters because disabled log calls impose essentially no runtime cost.

## Typical usage pattern

### Initialization

At system startup, once the UART peripheral is ready:

```c
LOG_init(&huart2);
```

This should happen before any code that expects logging output.

---

### Logging from application code

Use one of the convenience macros in normal code:

```c
LOGI("NET", "Ethernet initialized");
LOGW("ADC", "Reading outside expected range: %u", sample);
LOGE("FLASH", "Erase failed at sector %u", sector);
```

This is the intended public usage style.

<span style="white-space: pre-wrap;">Using </span>`<span class="editor-theme-code">LOG()</span>`<span style="white-space: pre-wrap;"> directly is also valid when needed.</span>

### Tag conventions

The module does not enforce tag format, so the team should adopt a convention.

A good pattern is to use short subsystem names, such as:

- `<span class="editor-theme-code">"ETH"</span>`
- `<span class="editor-theme-code">"CAN"</span>`
- `<span class="editor-theme-code">"SCHED"</span>`
- `<span class="editor-theme-code">"MOTOR"</span>`
- `<span class="editor-theme-code">"UI"</span>`
- `<span class="editor-theme-code">"SENSOR"</span>`

Keep tags short enough for readable UART logs.

Since this logger is plain text over UART, bloated tags just make the output harder to scan.