Skip to main content

Universal training monitor for ML runs

Project description

RunLogger

The official Python client for Runlog — real-time ML training monitor.

Works with any training framework: PyTorch, JAX, TensorFlow, or plain Python.


Table of Contents

  1. Installation
  2. Quickstart
  3. Initialization
  4. Logging Metrics
  5. Logging Evaluations
  6. Pausing & Resuming
  7. Finishing a Run
  8. Logging Artifacts
  9. Offline Mode
  10. Manual Sync CLI
  11. Auto Run Names
  12. System Stats
  13. Context Manager
  14. Run Configuration
  15. Tags & Notes
  16. API Token
  17. Plans & Limits
  18. Error Handling
  19. Full Example
  20. License
  21. FAQ

Installation

pip install runlog-sdk

Or install from source:

pip install git+https://github.com/runlog-in/runlog-sdk.git

Quickstart

import time
import math
import random
from runlogger import RunLogger

logger = RunLogger(
    base_url="https://runlog.in",
    project_name="my-project",
    api_token="rl-...",
)

for step in range(1000):
    # simulated loss — replace with your real training step
    loss = 3.5 * math.exp(-0.002 * step) + random.uniform(-0.05, 0.05)

    logger.log(step=step, loss=round(loss, 4))
    time.sleep(1)   # remove in real training

logger.finish()

Initialization

logger = RunLogger(
    base_url="https://runlog.in",
    api_token="rl-...",
    project_name="my-project",
    # run_name auto-generated if omitted
    config={...},                    # optional
    start_step=0,                    # optional
    metrics=[],                      # optional
    tags=[],                         # optional
    notes="",                        # optional
    log_system_stats=True,           # optional
    offline_mode=True,               # optional
    capture_terminal=True,           # optional
    verbose=False,                   # optional
)

Parameters

Parameter Type Default Description
base_url str required Your Runlog server URL.
api_token str required Your personal API token from the dashboard.
project_name str required The project this run belongs to.
run_name str auto-generated A name for this run. Auto-generated if not provided.
config dict {} Hyperparameters and settings for this run.
start_step int 0 Step number this run starts from. Useful when resuming.
metrics list [] Metric names you plan to log. Optional — metrics are tracked automatically.
tags list [] Tags for grouping and filtering runs on the dashboard.
notes str "" Free-text notes about this run.
log_system_stats bool True Automatically include GPU and CPU/RAM stats with every log call.
offline_mode bool True Preserve data locally when connection is unavailable. See Offline Mode.
capture_terminal bool True Capture stdout/stderr and stream terminal output to the dashboard alongside metrics.
verbose bool False Print internal debug info — packet counts, intervals, orphan detail. Useful for troubleshooting.

Logging Metrics

logger.log(step=step, loss=0.42, lr=0.001, grad_norm=1.2)

Parameters

Parameter Type Description
step int required — the current training step
**kwargs float / int / bool Any metrics you want to track

Returns

True if the log was accepted, False if logging has stopped.

Notes

  • Pass any number of metrics in a single call.
  • Metric names are flexible — use whatever makes sense for your experiment.
  • If log_system_stats=True, GPU and CPU stats are added automatically.
  • If a daily log limit applies to your plan, RunLogger warns you when it is reached.

Example

import time
import math
import random

for step in range(1000):
    loss      = 3.5 * math.exp(-0.002 * step) + random.uniform(-0.05, 0.05)
    lr        = 3e-4 * min(1.0, step / 100)
    grad_norm = random.uniform(0.5, 1.5)

    logger.log(
        step=step,
        train_loss=round(loss, 4),
        lr=round(lr, 7),
        grad_norm=round(grad_norm, 4),
        tokens_per_sec=random.randint(58000, 66000),
    )
    time.sleep(1)   # remove in real training

Logging Evaluations

logger.log_eval(step=step, val_loss=0.38, perplexity=46.2)

Use log_eval() for metrics computed on a validation or test set. These are tracked separately from training metrics on the dashboard.

Parameters

Same as log()step is required, any additional metrics as keyword arguments.

Returns

True if accepted, False otherwise.

Example

import time
import math
import random

for step in range(1000):
    loss = 3.5 * math.exp(-0.002 * step) + random.uniform(-0.05, 0.05)
    logger.log(step=step, loss=round(loss, 4))

    if step % 100 == 0 and step > 0:
        val_loss = loss + random.uniform(0.01, 0.05)   # simulated eval
        logger.log_eval(
            step=step,
            val_loss=round(val_loss, 4),
            perplexity=round(math.exp(val_loss), 2),
        )

    time.sleep(1)   # remove in real training

Pausing & Resuming

You can pause a running training job from the Runlog dashboard. To support this, check should_pause() in your training loop:

import time
import math
import random

for step in range(1000):
    loss = 3.5 * math.exp(-0.002 * step) + random.uniform(-0.05, 0.05)
    logger.log(step=step, loss=round(loss, 4))
    time.sleep(1)   # remove in real training

    if logger.should_pause():
        print("Training paused from dashboard")
        logger.finish(status="paused")
        break

Returns

True if a pause has been requested from the dashboard, False otherwise.

Notes

  • Your training loop is responsible for stopping — RunLogger does not interrupt your code.
  • When you click Resume on the dashboard, the pause flag is cleared automatically.

Finishing a Run

Always call finish() at the end of your script:

logger.finish()

Parameters

Parameter Type Default Description
status str "completed" Final status shown on the dashboard

Status values

Status When to use
completed Training finished successfully
crashed An error occurred
paused Training was intentionally paused

Notes

  • Always call finish() or use the Context Manager. Runs left without a final status stay marked as running on the dashboard indefinitely.

Logging Artifacts

logger.log_artifact(
    path="checkpoints/step-1000.pt",
    name="checkpoint-step-1000",
    type="model",
    metadata={"step": 1000, "val_loss": 0.38},
)

Parameters

Parameter Type Description
path str Local file path to upload
name str Display name on the dashboard
type str Category — e.g. "model", "dataset", "config"
metadata dict Optional key-value info attached to the artifact

Returns

True if uploaded successfully, False if an error occurred.

Notes

  • Artifact upload blocks until complete.
  • Large files may take time depending on your connection speed.

Offline Mode

logger = RunLogger(
    ...,
    offline_mode=True,   # default
)

When offline_mode=True, your data is preserved locally in a SQLite DB if the connection is unavailable. Once connectivity is restored, everything syncs to the dashboard automatically — in order, with no gaps.

When offline_mode=False, any data logged during a connection gap is lost.

When to use each

Scenario Recommended
Long training runs offline_mode=True
Unreliable or intermittent network offline_mode=True
Short scripts, stable connection Either
No local disk writes allowed offline_mode=False

Plan requirement

Offline mode requires your Runlog plan to support it. If your plan does not include it, offline mode is disabled automatically at startup with a warning. If your plan is upgraded, it activates without restarting.

Previous run recovery

If a previous run was interrupted while offline, its data is automatically recovered and synced the next time you start a run from the same directory. No manual steps required.


Manual Sync CLI

If a run was interrupted and you want to sync its data manually without starting a new run, use the runlogger-sync command:

# scan the default dumps/ directory
runlogger-sync

# scan a specific directory
runlogger-sync --dir /path/to/runs

# sync one specific file
runlogger-sync --file dumps/.runlog_abc123.db

# show full debug output
runlogger-sync --verbose
runlogger-sync -v

Options

Flag Description
--dir Directory to scan (default: dumps/)
--file Sync a single specific DB file
--base-url Server URL — fallback if not in DB (or set RUNLOGGER_URL)
--token API token — fallback if not in DB (or set RUNLOGGER_TOKEN)
--verbose, -v Show debug detail: IDs, per-batch info, log uploads

Environment variables

export RUNLOGGER_URL=https://runlog.in
export RUNLOGGER_TOKEN=rl-...
runlogger-sync

Notes

  • The token and server URL are stored inside each DB file, so you usually don't need to pass them.
  • Unrecoverable DB files (no token, no payload) are discarded automatically.
  • Safe to run multiple times — already-synced packets are skipped.

Auto Run Names

If you do not provide a run_name, one is generated automatically:

cosmic-nebula-42
silver-ridge-317
eager-summit-5

Format: adjective-noun-number — readable, memorable, and unique across any practical project scale.


System Stats

If pynvml is installed, these GPU stats are included automatically with every log() call:

Key Description
gpu_util GPU utilization %
gpu_mem_used GPU memory used (MB)
gpu_mem_total GPU total memory (MB)

If psutil is installed, these CPU/RAM stats are included:

Key Description
cpu_util CPU utilization %
ram_used RAM used (MB)
ram_total Total RAM (MB)

To disable:

logger = RunLogger(..., log_system_stats=False)

Context Manager

import time
import math
import random

with RunLogger(
    base_url="https://runlog.in",
    api_token="rl-...",
    project_name="my-project",
) as logger:
    for step in range(1000):
        loss = 3.5 * math.exp(-0.002 * step) + random.uniform(-0.05, 0.05)
        logger.log(step=step, loss=round(loss, 4))
        time.sleep(1)   # remove in real training
  • Normal exit → finish(status="completed")
  • Exception raised → finish(status="crashed")

The exception is not suppressed — it propagates normally after the run is closed.


Run Configuration

logger = RunLogger(
    ...,
    config={
        "model":        "68M",
        "architecture": "llama",
        "dataset":      "fineweb-10BT",
        "batch_size":   8,
        "seq_len":      1024,
        "lr":           3e-4,
        "warmup_steps": 100,
        "max_steps":    5000,
        "optimizer":    "adamw",
        "precision":    "bf16",
    },
)

Config is displayed on the run detail page. It is set once at initialization — pass all relevant hyperparameters upfront.


Tags & Notes

Tags

logger = RunLogger(
    ...,
    tags=["baseline", "bf16", "fineweb"],
)

Tags appear on the dashboard and can be used to filter and group runs across a project.

Notes

logger = RunLogger(
    ...,
    notes="Testing SwiGLU vs GELU — same LR schedule, different FFN.",
)

Free-text notes visible on the run detail page.


API Token

Get your API token from the Runlog dashboard under Settings → API Tokens.

Tokens start with rl-. Keep your token private — do not commit it to source control. Use environment variables:

import os
from runlogger import RunLogger

logger = RunLogger(
    base_url="https://runlog.in",
    api_token=os.environ["RUNLOG_API_TOKEN"],
    project_name="my-project",
)

If an invalid token is provided, a RuntimeError is raised immediately at startup.


Plans & Limits

Limits are applied automatically based on your plan — no configuration needed.

Limit What happens
Daily log limit RunLogger warns you and stops logging for the rest of the day. Resets at midnight.
Max metrics tracked Metric keys beyond your plan's limit are ignored.
Log rate Data is recorded at the rate your plan allows. The most recent value always gets through.
Offline mode Only available on supported plans.

If your plan changes, limits update automatically — no restart needed.


Error Handling

Errors raised at startup

RuntimeError: Invalid API token: ...
RuntimeError: [Runlog] account is banned.

Everything else degrades gracefully

  • Connection issues → data is preserved if offline mode is on, retried automatically
  • Upload failures → logged to console, training continues unaffected

Recommended pattern

try:
    with RunLogger(...) as logger:
        for step in range(max_steps):
            loss = train()
            logger.log(step=step, loss=loss)
except RuntimeError as e:
    print(f"RunLogger error: {e}")
    # continue training without logging, or exit

Full Example

import math
import os
import random
import time
from runlogger import RunLogger
import time

# ── simulated training helpers ────────────────────────────────
# replace these with your real training code
_step_loss = [3.5]

def train_one_step():
    _step_loss[0] = _step_loss[0] * 0.9995 + random.uniform(-0.02, 0.02)
    time.sleep(0.05)   # remove in real training
    return max(0.1, _step_loss[0])

def evaluate():
    return _step_loss[0] + random.uniform(0.01, 0.05)

def get_lr(step, warmup=100, base_lr=3e-4):
    if step < warmup:
        return base_lr * step / warmup
    return base_lr * 0.5 * (1 + math.cos(math.pi * step / 5000))

def get_grad_norm():
    return random.uniform(0.5, 1.5)

def save_checkpoint(step):
    pass   # your checkpoint logic here
# ─────────────────────────────────────────────────────────────

logger = RunLogger(
    base_url="https://runlog.in",
    api_token=os.environ["RUNLOG_API_TOKEN"],
    project_name="koyna-v2",
    run_name="sft-run-1",
    offline_mode=True,
    config={
        "model":        "68M",
        "architecture": "llama",
        "dataset":      "mes_v2",
        "batch_size":   8,
        "seq_len":      1024,
        "lr":           3e-4,
        "warmup_steps": 100,
        "max_steps":    5000,
        "optimizer":    "adamw",
        "precision":    "bf16",
    },
    tags=["sft", "marathi", "68M"],
    notes="SFT on mes_v2 combined dataset, 883K samples.",
)

TOTAL_STEPS = 5000
EVAL_EVERY  = 500
best_val    = float("inf")

try:
    for step in range(1, TOTAL_STEPS + 1):
        loss      = train_one_step()
        lr        = get_lr(step)
        grad_norm = get_grad_norm()

        logger.log(
            step=step,
            loss=round(loss, 4),
            lr=round(lr, 7),
            grad_norm=round(grad_norm, 4),
        )

        if step % EVAL_EVERY == 0:
            val_loss = evaluate()
            is_best  = val_loss < best_val
            if is_best:
                best_val = val_loss
                save_checkpoint(step)
                logger.log_artifact(
                    path=f"checkpoints/step-{step}.pt",
                    name=f"checkpoint-step-{step}",
                    type="model",
                    metadata={"step": step, "val_loss": val_loss},
                )
            logger.log_eval(
                step=step,
                val_loss=round(val_loss, 4),
                perplexity=round(math.exp(val_loss), 2),
                is_best=is_best,
            )

        if logger.should_pause():
            print("Paused from dashboard")
            logger.finish(status="paused")
            break
        time.sleep(1)
    else:
        logger.finish(status="completed")

except KeyboardInterrupt:
    logger.finish(status="crashed")

except Exception as e:
    logger.finish(status="crashed")
    raise

License

RunLogger is distributed under the Business Source License 1.1.

  • Free to use for any purpose when connecting to runlog.in
  • Modification permitted for personal use only
  • Redistribution or use with other servers requires written permission

For commercial licensing: runlog.uk@gmail.com

Full terms: runlog.in/auth/terms


FAQ

Do I need to call finish() if I use the context manager? No — it is called automatically.

What if I forget finish()? The run stays marked as running on the dashboard. Always call finish() or use the context manager.

Can I use RunLogger with multi-GPU / DDP training? Yes. Log only from rank 0 to avoid duplicate data:

if rank == 0:
    logger.log(step=step, loss=loss)

Can I log string values as metrics? No — metric values must be int, float, or bool. Pass strings in config, tags, or notes instead.

Can I have multiple loggers in one script? Yes. Each RunLogger instance is independent and creates its own run.

Does RunLogger affect training performance? No. All logging is non-blocking — your training loop is not slowed down.

What if my machine is killed mid-training? If offline_mode=True, all data logged before the crash is preserved and recovered automatically the next time you start a run from the same directory.

Can I use RunLogger with a self-hosted Runlog instance? Yes. Set base_url to your own server address.

How do I debug connection or sync issues? Pass verbose=True to RunLogger(...) or use runlogger-sync --verbose to see full internal detail.

Where are offline DB files stored? In a dumps/ directory relative to where your training script runs. Files are named .runlog_<run_id>.db and cleaned up automatically after sync.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

runlog_sdk-0.0.1.tar.gz (24.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

runlog_sdk-0.0.1-py3-none-any.whl (25.5 kB view details)

Uploaded Python 3

File details

Details for the file runlog_sdk-0.0.1.tar.gz.

File metadata

  • Download URL: runlog_sdk-0.0.1.tar.gz
  • Upload date:
  • Size: 24.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for runlog_sdk-0.0.1.tar.gz
Algorithm Hash digest
SHA256 93ebd970ee94a017720182f4a0d813d0fe4c9eec4f4a06fb05bc712ea3ab5b09
MD5 65c7c71880412f75792a8f50763ab57a
BLAKE2b-256 245d07c46f81274e43651ebac8cdad19ee2b909bb39a4cae49dde3391b41e820

See more details on using hashes here.

File details

Details for the file runlog_sdk-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: runlog_sdk-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 25.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for runlog_sdk-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 08f7cbefb7bee99ec3a855b9ad35c83125607bc7788c08d1fba483d18e046b77
MD5 1730fff28eddcf84af88df07354a90fd
BLAKE2b-256 57d5d7cfe801254f9245c169e6d47136249a9fdedfb082f41a046ebe8d543016

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page