libLogit

LOGIT Lifecycle And Compatibility Rules

This page explains how a LOGIT is expected to behave over time, not just what fields it contains.

If the contract matrix answers “what is a LOGIT?”, this page answers:

The Portable Lifecycle

Every binding should preserve the same conceptual flow:

  1. create a blank LOGIT
  2. configure output and level during startup
  3. emit events directly or through a builder/stream helper
  4. flush when deterministic visibility is required
  5. close when the application or subsystem is shutting down

The punctuation varies. The lifecycle should not.

Construction Rules

A blank LOGIT should be safe to create before the app has finished deciding where logs belong.

That means:

Emit Rules

libLogit currently supports three public emit shapes:

Shape Meaning
logit.log(level, message) Direct/immediate event emission.
logit(level) << "a" << "b" Streaming emission.
logit.at(level).append("a").append("b").commit() Builder-style emission.

All three should map to the same event model:

Builder Rules

Builder or stream helpers should follow these rules:

Current Alpha evidence is strongest in Python, where tests already verify:

Flush And Close

The Beta contract direction is:

flush()

Use flush() when the caller needs buffered data made visible before normal shutdown.

Bindings with buffered or persisted sinks should treat flush() as:

close()

Use close() when the owning application or subsystem is done with the logger.

Bindings with persisted sinks should treat close() as:

Destructor And Finalizer Behavior

Destructor or finalizer emission is allowed as best effort. It is not the portable deterministic contract.

Public docs should always prefer explicit commit, explicit flush, and explicit close when the caller needs reliable visibility.

That is especially important because languages handle object destruction very differently.

Unsupported Field Rules

Optional or advanced fields are where SDK drift starts if we are not careful.

A binding must never silently suggest support it does not actually have.

For any optional field, a binding should do one of these:

  1. implement it and test it
  2. reject it clearly during setup/config load
  3. document it as a no-op with explicit limits

The thing a binding should not do is accept a field quietly and then leave the user believing it worked.

Compatibility Aliases Versus Canonical Names

The current repo still accepts several compatibility names for migration:

The Beta direction is:

What Is Portable Today

You can teach these as the stable public story today:

What Still Needs Beta Hardening

These areas still need stronger cross-binding proof: