Skip to main content

Logging

Protolink provides a unified logging package to manage console, file-based, and intentionally silent logs consistently.

Overview

Protolink's logging is built around a common BaseLogger abstract class, which ensures that custom and built-in loggers expose the standard logging methods.

[ ConsoleLogger ] [ FileLogger ] [ QuietLogger ] [ BaseLogger ]

By default, an Agent utilizes the ConsoleLogger to output colorful text to stdout, but it's very easy to substitute this with the FileLogger, QuietLogger, or a custom subclass if you use platforms like Datadog or Sentry.

Configuration

You can pass a logger instance directly when initializing your Agent. If you do not pass one, a ConsoleLogger is instantiated automatically, mapped to the selected verbosity.

from protolink.agents import Agent
from protolink.logging import ConsoleLogger, FileLogger, QuietLogger

# Using the built-in FileLogger (e.g., as JSON)
my_logger = FileLogger("agent_activity.log", extension="json", level="DEBUG")

# Pass it directly to your Agent
agent = Agent(
card={
"name": "logger_agent",
"description": "Agent with file logging",
"url": "http://127.0.0.1:8000",
},
transport="http",
logger=my_logger,
)
Default Fallback

If you don't supply a logger, Protolink instantiates a ConsoleLogger for you automatically. The log level is derived from the verbosity argument passed to the Agent (0 suppresses the standard Agent logger methods, 1 -> INFO, 2 -> DEBUG).

Use QuietLogger when you want a logger object but no emitted output at all:

from protolink.agents import Agent
from protolink.logging import QuietLogger

agent = Agent(
card={
"name": "quiet_agent",
"description": "Agent with no log output",
"url": "http://127.0.0.1:8000",
},
transport="http",
logger=QuietLogger(name="quiet_agent"),
)
Quiet vs. low verbosity

verbosity=0 keeps the default console logger but suppresses Protolink's standard Agent log calls. QuietLogger is a reusable no-op BaseLogger that creates no handlers and drops every debug(), info(), warning(), error(), and exception() call wherever it is injected.


Logging API Reference

All Protolink loggers must implement the BaseLogger interface.

Logging moduleLogger Interfaces

The injectable logging surface for colorful console output, file-based logs, structured JSON rows, and intentionally silent production or test runs.

protolink.logging
BaseLogger contractConsoleLoggerFileLoggerQuietLoggerVerbosity-aware agents
Common methodsEvery logger exposes debug, info, warning, error, and exception methods.BaseLogger
ConsoleHuman-readable local output for development, CLIs, and examples.ConsoleLogger
FilesAppend text or structured JSON logs to a configured file path.FileLogger
SilenceDrop output while still satisfying the logger interface.QuietLogger

Logger contract

BaseLogger

abstract classprotolink.logging.BaseLogger
source
class BaseLogger

Define the minimal logging interface accepted by Agent and other ProtoLink components. The class deliberately mirrors the familiar Python logging levels, which makes custom adapters for structured logging or observability systems small and predictable.

Abstract members

namestr

Read-only logical logger name.

debug(message: str, **kwargs: Any) -> None

Emit diagnostic detail.

info(message: str, **kwargs: Any) -> None

Emit normal lifecycle or progress information.

warning(message: str, **kwargs: Any) -> None

Emit a recoverable problem or caution.

error(message: str, **kwargs: Any) -> None

Emit a failed operation.

exception(message: str, **kwargs: Any) -> None

Emit a failure with exception context.

Custom implementations

Subclasses must implement the property and all five methods before they can be instantiated. ProtoLink does not require a subclass to wrap Python's standard logging.Logger.

BaseLogger.name

abstract propertyprotolink.logging.BaseLogger.name
source
name -> str

Return the logical name associated with this logger. Built-in loggers retain the constructor value unchanged.

Returns

namestr

Logger identifier used for display and standard logging record names.

BaseLogger.debug

abstract methodprotolink.logging.BaseLogger.debug
source
debug(
  message: str,
  **kwargs: Any,
) -> None

Record fine-grained diagnostic information that is normally hidden at the default INFO threshold.

Parameters

messagestrrequired

Human-readable log message.

**kwargsAny

Implementation-defined context. The built-in console and file loggers use only extra for this level; other keys are ignored.

Returns

NoneNone

Logging is performed for its side effect.

BaseLogger.info

abstract methodprotolink.logging.BaseLogger.info
source
info(
  message: str,
  **kwargs: Any,
) -> None

Record ordinary lifecycle, status, or progress information.

Parameters

messagestrrequired

Human-readable log message.

**kwargsAny

Implementation-defined context. Built-in emitting loggers forward extra to the standard logging record.

Returns

NoneNone

Logging is performed for its side effect.

BaseLogger.warning

abstract methodprotolink.logging.BaseLogger.warning
source
warning(
  message: str,
  **kwargs: Any,
) -> None

Record a potentially harmful or unexpected condition that did not necessarily stop the current operation.

Parameters

messagestrrequired

Human-readable warning.

**kwargsAny

Implementation-defined context. Built-in emitting loggers recognize extra.

Returns

NoneNone

Logging is performed for its side effect.

BaseLogger.error

abstract methodprotolink.logging.BaseLogger.error
source
error(
  message: str,
  **kwargs: Any,
) -> None

Record a failed operation. Use exception() inside an active exception handler when a traceback should be attached automatically.

Parameters

messagestrrequired

Human-readable failure description.

**kwargsAny

Implementation-defined context. Built-in emitting loggers recognize extra and exc_info; omitted exc_info defaults to False.

Returns

NoneNone

Logging is performed for its side effect.

BaseLogger.exception

abstract methodprotolink.logging.BaseLogger.exception
source
exception(
  message: str,
  **kwargs: Any,
) -> None

Record an error and, by default in the emitting built-ins, attach the active exception traceback.

Parameters

messagestrrequired

Human-readable failure description.

**kwargsAny

Implementation-defined context. Built-in console and file loggers recognize extra and exc_info; omitted exc_info defaults to True.

Returns

NoneNone

Logging is performed for its side effect.

Built-in loggers

ConsoleLogger

classprotolink.logging.ConsoleLogger
source
class ConsoleLogger(
  name: str = "protolink",
  level: int | Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = logging.INFO,
  fmt: str = "%(asctime)s | %(levelname)s | %(name)s | %(message)s",
  datefmt: str = "%Y-%m-%d %H:%M:%S",
)

Write formatted, ANSI-colored records to sys.stdout. Each instance wraps a standard logger named console.<name>, disables propagation to the root logger, and replaces existing handlers on that standard logger to prevent duplicate output after reconfiguration.

Parameters

namestrdefault: "protolink"

Logical name returned by the property and included in the underlying logger's record name.

levelint | log-level stringdefault: logging.INFO

Minimum emitted severity. Recognized strings are DEBUG, INFO, WARNING, ERROR, and CRITICAL. String matching is case-insensitive; an unknown string silently falls back to INFO.

fmtstrdefault: "%(asctime)s | %(levelname)s | %(name)s | %(message)s"

Standard-library logging format. Severity names are centered to eight characters by the console formatter before rendering.

datefmtstrdefault: "%Y-%m-%d %H:%M:%S"

Timestamp format passed to logging.Formatter.

Behavior

debug / info / warningmethod

Forward message and kwargs["extra"]; all other keyword arguments are ignored.

error / exceptionmethod

Also forward kwargs["exc_info"], defaulting to False for error() and True for exception().

Shared standard logger

Constructing another ConsoleLogger with the same name clears and replaces that standard logger's handlers. Existing wrappers with that name therefore observe the new handler configuration.

FileLogger

classprotolink.logging.FileLogger
source
class FileLogger(
  filepath: str | Path,
  name: str = "protolink",
  level: int | Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = logging.INFO,
  fmt: str = "%(asctime)s | %(levelname)s | %(name)s | %(message)s",
  datefmt: str = "%Y-%m-%d %H:%M:%S",
  extension: str | None = None,
)

Append UTF-8 log records to a file, creating missing parent directories. Output is either conventional formatted text or one JSON object per log record.

Parameters

filepathstr | Pathrequired

Destination file. It is opened in append mode and created when absent. Parent directories are created recursively.

namestrdefault: "protolink"

Logical logger name. The underlying standard logger also includes the destination path so different files do not share handlers.

levelint | log-level stringdefault: logging.INFO

Minimum emitted severity. Unknown strings silently resolve to INFO, matching ConsoleLogger.

fmtstrdefault: "%(asctime)s | %(levelname)s | %(name)s | %(message)s"

Standard logging format used only for non-JSON output.

datefmtstrdefault: "%Y-%m-%d %H:%M:%S"

Timestamp format used by both the text and JSON formatter.

extensionstr | Nonedefault: None

Format override. A case-insensitive "json" selects structured output; any other non-empty value selects text output. This option controls formatting only, it does not rename filepath. When omitted, the actual path suffix is used.

JSON records

timestampstr

Formatted record timestamp.

namestr

Underlying standard logger name.

levelstr

Severity name.

messagestr

Rendered record message.

extradict[str, Any]

Included when the record contains fields outside this formatter's standard-key list. On Python 3.12 and newer, the standard taskName field is not yet in that local exclusion list and can therefore appear here even without caller-provided context.

exc_infostr

Formatted traceback included only when exception information is attached.

Raises

OSError

File-system errors from directory creation or opening the destination propagate during construction.

KeyError

Python logging rejects extra keys that overwrite reserved LogRecord attributes.

formatter or write error

Errors raised inside the handler, including a non-JSON-serializable extra value, are passed to Python logging's handleError(). They are normally not re-raised to the logging caller, although development mode may print a diagnostic to stderr.

Structured output

A .json path or extension="json" produces newline-delimited JSON records, which can be ingested independently by log processors.

QuietLogger

classprotolink.logging.QuietLogger
source
class QuietLogger(
  name: str = "protolink",
)

Satisfy the complete logger contract while intentionally discarding every message. It creates no standard logger and no handlers, making it useful in tests, embedded applications, or environments that route observability through a different mechanism.

Parameters

namestrdefault: "protolink"

Logical value returned by the name property.

Behavior

debug / info / warning / error / exceptionmethod

Accept the same message and arbitrary keyword arguments as BaseLogger, ignore all values, and return None.

Lifecycle message helpers

These small helpers are public because Agent uses them to produce friendly startup and shutdown messages. Applications may use them as well, but their exact wording is intentionally nondeterministic.

get_agent_greeting

functionprotolink.logging.get_agent_greeting
source
get_agent_greeting(
  agent_name: str,
) -> str

Choose one startup phrase at random and interpolate the agent name with ANSI bold styling.

Parameters

agent_namestrrequired

Name interpolated directly into the selected phrase.

Returns

messagestr

One of eight greeting strings, including emoji and ANSI escape sequences around the name.

Testing

The helper calls random.choice() on every invocation. Assert meaningful fragments rather than one exact sentence unless randomness is patched.

get_agent_farewell

functionprotolink.logging.get_agent_farewell
source
get_agent_farewell(
  agent_name: str,
) -> str

Choose one shutdown phrase at random and interpolate the ANSI-bold agent name.

Parameters

agent_namestrrequired

Name interpolated directly into the selected phrase.

Returns

messagestr

One of eight farewell strings, including emoji and ANSI escape sequences around the name.