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.
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,
)
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"),
)
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.
The injectable logging surface for colorful console output, file-based logs, structured JSON rows, and intentionally silent production or test runs.
protolink.loggingBaseLoggerConsoleLoggerFileLoggerQuietLoggerLogger contract
BaseLogger
class BaseLoggerDefine 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
namestrRead-only logical logger name.
debug(message: str, **kwargs: Any) -> NoneEmit diagnostic detail.
info(message: str, **kwargs: Any) -> NoneEmit normal lifecycle or progress information.
warning(message: str, **kwargs: Any) -> NoneEmit a recoverable problem or caution.
error(message: str, **kwargs: Any) -> NoneEmit a failed operation.
exception(message: str, **kwargs: Any) -> NoneEmit a failure with exception context.
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
name -> strReturn the logical name associated with this logger. Built-in loggers retain the constructor value unchanged.
Returns
namestrLogger identifier used for display and standard logging record names.
BaseLogger.debug
debug(
message: str,
**kwargs: Any,
) -> NoneRecord fine-grained diagnostic information that is normally hidden at the
default INFO threshold.
Parameters
messagestrrequiredHuman-readable log message.
**kwargsAnyImplementation-defined context. The built-in console and file loggers use only
extrafor this level; other keys are ignored.
Returns
NoneNoneLogging is performed for its side effect.
BaseLogger.info
info(
message: str,
**kwargs: Any,
) -> NoneRecord ordinary lifecycle, status, or progress information.
Parameters
messagestrrequiredHuman-readable log message.
**kwargsAnyImplementation-defined context. Built-in emitting loggers forward
extrato the standard logging record.
Returns
NoneNoneLogging is performed for its side effect.
BaseLogger.warning
warning(
message: str,
**kwargs: Any,
) -> NoneRecord a potentially harmful or unexpected condition that did not necessarily stop the current operation.
Parameters
messagestrrequiredHuman-readable warning.
**kwargsAnyImplementation-defined context. Built-in emitting loggers recognize
extra.
Returns
NoneNoneLogging is performed for its side effect.
BaseLogger.error
error(
message: str,
**kwargs: Any,
) -> NoneRecord a failed operation. Use exception() inside an active exception handler
when a traceback should be attached automatically.
Parameters
messagestrrequiredHuman-readable failure description.
**kwargsAnyImplementation-defined context. Built-in emitting loggers recognize
extraandexc_info; omittedexc_infodefaults toFalse.
Returns
NoneNoneLogging is performed for its side effect.
BaseLogger.exception
exception(
message: str,
**kwargs: Any,
) -> NoneRecord an error and, by default in the emitting built-ins, attach the active exception traceback.
Parameters
messagestrrequiredHuman-readable failure description.
**kwargsAnyImplementation-defined context. Built-in console and file loggers recognize
extraandexc_info; omittedexc_infodefaults toTrue.
Returns
NoneNoneLogging is performed for its side effect.
Built-in loggers
ConsoleLogger
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.INFOMinimum emitted severity. Recognized strings are
DEBUG,INFO,WARNING,ERROR, andCRITICAL. String matching is case-insensitive; an unknown string silently falls back toINFO.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 / warningmethodForward
messageandkwargs["extra"]; all other keyword arguments are ignored.error / exceptionmethodAlso forward
kwargs["exc_info"], defaulting toFalseforerror()andTrueforexception().
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
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 | PathrequiredDestination 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.INFOMinimum emitted severity. Unknown strings silently resolve to
INFO, matchingConsoleLogger.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: NoneFormat override. A case-insensitive
"json"selects structured output; any other non-empty value selects text output. This option controls formatting only, it does not renamefilepath. When omitted, the actual path suffix is used.
JSON records
timestampstrFormatted record timestamp.
namestrUnderlying standard logger name.
levelstrSeverity name.
messagestrRendered 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
taskNamefield is not yet in that local exclusion list and can therefore appear here even without caller-provided context.exc_infostrFormatted traceback included only when exception information is attached.
Raises
OSErrorFile-system errors from directory creation or opening the destination propagate during construction.
KeyErrorPython logging rejects
extrakeys that overwrite reservedLogRecordattributes.formatter or write errorErrors raised inside the handler, including a non-JSON-serializable
extravalue, are passed to Python logging'shandleError(). They are normally not re-raised to the logging caller, although development mode may print a diagnostic to stderr.
A .json path or extension="json" produces
newline-delimited JSON records, which can be ingested independently by log
processors.
QuietLogger
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
nameproperty.
Behavior
debug / info / warning / error / exceptionmethodAccept the same
messageand arbitrary keyword arguments asBaseLogger, ignore all values, and returnNone.
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
get_agent_greeting(
agent_name: str,
) -> strChoose one startup phrase at random and interpolate the agent name with ANSI bold styling.
Parameters
agent_namestrrequiredName interpolated directly into the selected phrase.
Returns
messagestrOne of eight greeting strings, including emoji and ANSI escape sequences around the name.
The helper calls random.choice() on every invocation. Assert
meaningful fragments rather than one exact sentence unless randomness is
patched.
get_agent_farewell
get_agent_farewell(
agent_name: str,
) -> strChoose one shutdown phrase at random and interpolate the ANSI-bold agent name.
Parameters
agent_namestrrequiredName interpolated directly into the selected phrase.
Returns
messagestrOne of eight farewell strings, including emoji and ANSI escape sequences around the name.