# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
import functools
from typing import Callable
from loguru import logger
import sys
from lipforcing.utils.distributed import get_rank, is_rank0
# Default logging level — can be changed later
LOG_LEVEL = "INFO"
logger.remove(0)
logger.add(
sys.stdout,
format="[{time:MMM D, YYYY - HH:mm:ss} | "
"{level} | "
"{name}:{function}:{line} ] {message}",
)
def formatter(record):
"""
Custom formatter function to conditionally add the MPI rank.
"""
# Base format string for all levels
msg = (
"[{time:MMM D, YYYY - HH:mm:ss} | "
"{level} | "
"{name}:{function}:{line} ] {message}\n"
)
# For DEBUG messages, add the rank
if LOG_LEVEL == "DEBUG":
msg = f"[RANK: {get_rank()}] {msg}"
# For all other levels, use the base format
return msg
def set_log_level(level: str):
"""Change log level dynamically."""
global LOG_LEVEL
LOG_LEVEL = level.upper()
logger.remove() # Remove all existing handlers
logger.add(
sys.stdout,
format=formatter,
level=LOG_LEVEL,
)
def rank0_if_not_debug(func: Callable) -> Callable:
"""
A decorator factory that applies the rank0_only decorator if LOG_LEVEL is not DEBUG.
"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
# At runtime, check the global flag to decide which version to run.
if LOG_LEVEL == "DEBUG" or is_rank0():
return func(*args, **kwargs)
return wrapper
@rank0_if_not_debug
def trace(msg: str):
logger.opt(depth=2).trace(msg)
@rank0_if_not_debug
def info(msg: str):
logger.opt(depth=2).info(msg)
@rank0_if_not_debug
def debug(msg: str):
logger.opt(depth=2).debug(msg)
@rank0_if_not_debug
def success(msg: str):
logger.opt(depth=2).success(msg)
@rank0_if_not_debug
def critical(msg: str):
logger.opt(depth=2).critical(msg)
@rank0_if_not_debug
def warning(msg: str):
logger.opt(depth=2).warning(msg)
@rank0_if_not_debug
def error(msg: str):
logger.opt(depth=2).error(msg)