---
myst:
html_meta:
description: "Send log messages with structured arguments to a dataset's log channel."
---
# Stream logs
{.lead}
A log stream sends timestamped messages with string arguments, such as a severity, to a log channel in a dataset.
Use it for logs your program produces or parses itself.
For journald exports and other JSON Lines files, [upload them](/guides/datasets/journal-json.md) instead.
```{include} /guides/_snippets/connect-the-client.md
```
## Parse log lines
{py:class}`~nominal.core.LogPoint` holds a timestamp, a message and string arguments.
This parser reads lines such as `2026-09-10T14:38:19Z [WARN] Motor temperature above target`:
```python
from collections.abc import Iterator
from nominal.core import LogPoint
def parse_logs(path: str) -> Iterator[LogPoint]:
with open(path, encoding="utf-8") as f:
for line in f:
if line.strip():
timestamp, level, message = line.split(maxsplit=2)
yield LogPoint.create(timestamp, message.rstrip(), args={"level": level.strip("[]")})
```
## Send the logs
{py:meth}`~nominal.core.Dataset.get_log_stream` batches entries in the background. Leaving the `with` block sends the rest.
```python
dataset = client.create_dataset("Engine test logs")
with dataset.get_log_stream() as stream:
for point in parse_logs("engine.log"):
stream.enqueue("engine_logs", point.timestamp, point.message, tags=point.args)
```
For a log stream, `tags` are the entry's arguments.
One stream can write to several log channels.
The stream logs upload failures rather than raising them, so check the client's logs.
To send synchronously and raise on errors, use {py:meth}`~nominal.core.Dataset.write_logs`:
```python
dataset.write_logs(parse_logs("engine.log"), channel_name="engine_logs")
```
Both can send some batches before one fails, so a retry may duplicate entries.
## View the logs
Add the dataset to an [asset](/guides/assets.md#add-data-with-data-scopes) or a [run](/guides/runs.md).
In a workbook on that asset or run, add a **Logs** panel and select the `engine_logs` channel.