Logger Manager

What is LoggerManager?

LoggerManager is a singleton that centralizes logging configuration across all GLLM components.

  1. It initializes the root logger exactly once.

  2. It configures a default handler and formatter based on the LOG_FORMAT environment variable.

  3. It provides a simple API to get loggers, change formats/levels, and attach custom handlers.

  4. It ensures that child loggers from different parts of the SDK share consistent behavior.

Internally, LoggerManager lives in gllm_core.utils.logger_manager.LoggerManager and is used by components such as Component to obtain properly configured loggers.

Prerequisites

This example specifically requires completion of all setup steps listed on the Prerequisites page.

Installation

# you can use a Conda environment
pip install --extra-index-url https://oauth2accesstoken:$(gcloud auth print-access-token)@glsdk.gdplabs.id/gen-ai-internal/simple/ gllm-core

Quickstart

A minimal usage pattern looks like this:

End-to-end behavior:

  1. LoggerManager() creates (or returns) the singleton instance and initializes the root logger on first use.

  2. get_logger("my_app") returns a child logger under the shared root.

  3. Child loggers reuse the same handlers and formatter as the root logger.

  4. Messages you log are formatted according to the active log mode (text, simple, or JSON).

Logging Modes

LoggerManager supports three logging modes, controlled by the LOG_FORMAT environment variable.

  1. Text mode

    1. Activated when LOG_FORMAT=text (or when LOG_FORMAT is unset and the default is used).

    2. Uses TextRichHandler, a RichHandler subclass with per-level colors and [LoggerName] prefixes.

    3. Typical for local development where rich, human-readable logs are preferred.

  2. Simple mode

    1. Activated when LOG_FORMAT=simple.

    2. Uses SimpleRichHandler, a thin wrapper around logging.StreamHandler that prints lines such as:

    3. Keeps Rich coloring but avoids column-based layout.

  3. JSON mode

    1. Activated when LOG_FORMAT=json.

    2. Uses a standard StreamHandler with AppJSONFormatter.

    3. Outputs structured JSON objects, for example:

    4. Best suited for log aggregation systems and machine parsing.

If LOG_FORMAT is not set or contains an unsupported value, LoggerManager falls back to text mode.

Getting Loggers

Use get_logger to obtain either the root logger or a named child logger.

Behavior summary:

  1. Calling get_logger() with no name returns the root logger.

  2. Calling get_logger(name) returns a child logger with that name.

  3. Child loggers:

    1. Have propagate = False to avoid double-logging.

    2. Inherit handlers from the root if they do not have handlers yet.

  4. All loggers share the same formatter and respect the same log level set on the root.

Configuring Levels and Formats

LoggerManager exposes methods to adjust logging behavior at runtime.

  1. Set log level for the entire hierarchy

    1. Updates the level of the root logger.

    2. Affects all child loggers unless they override the level individually.

  2. Set a custom log format string

    1. Rebuilds the formatter using the new format string.

    2. Applies the new formatter to all registered handlers.

    3. Preserves the current date format (or default) unless changed separately.

  3. Set a custom date format

    1. Rebuilds the formatter with the new date format.

    2. Keeps the existing message format string.

    3. Updates all handlers to use the new formatter.

Adding Custom Handlers

You can add extra handlers (for example, file or HTTP handlers) while retaining the central formatting logic.

The handler integration process is:

  1. add_handler sets the current formatter on the new handler.

  2. The handler is attached to the root logger.

  3. The handler is stored in the manager’s internal handler list.

  4. Future get_logger calls ensure child loggers inherit this handler when needed.

This allows you to:

  1. Configure one or more console handlers via log mode.

  2. Attach additional targets (files, sockets, streams) using the same formatting and mode.

  3. Keep all configuration coordinated through a single LoggerManager instance.

JSON Error Payloads

When running in JSON mode, error-related fields are grouped under an error key by AppJSONFormatter.

  1. The formatter keeps only a small set of top-level fields:

    1. timestamp

    2. name

    3. level

    4. message

  2. The following extra fields, when present, are remapped:

    1. exc_infoerror.message

    2. stack_infoerror.stacktrace

    3. error_codeerror.code

  3. The resulting JSON object only includes an error block if at least one of those fields is non-empty.

Example usage:

In JSON mode this yields a structure similar to:

Rich-Based Handlers

Two handlers leverage Rich for colored, readable logs.

  1. TextRichHandler

    1. Subclasses rich.logging.RichHandler.

    2. Applies a color from TEXT_COLOR_MAP based on the log level.

    3. Wraps messages with [color][LoggerName] ...[/] and escapes internal close tags.

    4. Best when you want column-based, rich console output.

  2. SimpleRichHandler

    1. Subclasses logging.StreamHandler.

    2. Uses a rich.console.Console bound to the handler’s stream.

    3. Prints formatted log lines with color, but without the full Rich logging layout.

    4. Useful for simple colored logs that still look like classic log lines.

Both handlers benefit from:

  1. Centralized formatter configuration through LoggerManager.

  2. A shared TEXT_COLOR_MAP so levels have consistent colors.

  3. Escape logic for Rich close tags to avoid breaking markup when messages themselves contain [/.

Best Practices

  1. Use LoggerManager everywhere

    1. Prefer LoggerManager().get_logger(__name__) over logging.getLogger(__name__) directly.

    2. This keeps configuration centralized and consistent.

  2. Pick a log mode per environment

    1. LOG_FORMAT=text for local development.

    2. LOG_FORMAT=simple for minimal, colored logs.

    3. LOG_FORMAT=json for production environments with log shipping.

  3. Avoid reconfiguring handlers manually

    1. Let LoggerManager manage the root logger and its handlers.

    2. Use add_handler, set_log_format, and set_date_format instead of calling logging.basicConfig in multiple places.

  4. Attach structured error information

    1. Use extra={"error_code": "..."} when logging errors.

    2. Enable exc_info=True to capture stack traces.

    3. This pays off especially in JSON mode for observability.

  5. Keep log levels appropriate

    1. Use DEBUG for development and detailed troubleshooting.

    2. Use INFO for standard runtime information.

    3. Use WARNING, ERROR, CRITICAL for exceptional situations.

Last updated