Skip to content

Commit 663b3e7

Browse files
committed
Fix logger implementation errors and standardize on new loguru system and expectations
1 parent 7aed12e commit 663b3e7

File tree

3 files changed

+232
-0
lines changed

3 files changed

+232
-0
lines changed

src/guidellm/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""
2+
Guidellm is a package that provides an easy and intuitive interface for
3+
evaluating and benchmarking large language models (LLMs).
4+
"""
5+
6+
from .logger import LoggerConfig, configure_logger, logger
7+
8+
__all__ = ["logger", "configure_logger", "LoggerConfig"]

src/guidellm/logger.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""
2+
Logger configuration for GuideLLM.
3+
4+
This module provides a flexible logging configuration using the loguru library.
5+
It supports console and file logging with options to configure via environment
6+
variables or direct function calls.
7+
8+
Environment Variables:
9+
- GUIDELLM_LOG_DISABLED: Disable logging (default: false).
10+
- GUIDELLM_CLEAR_LOGGERS: Clear existing loggers from loguru (default: true).
11+
- GUIDELLM_LOG_LEVEL: Log level for console logging
12+
(default: none, options: DEBUG, INFO, WARNING, ERROR, CRITICAL).
13+
- GUIDELLM_LOG_FILE: Path to the log file for file logging
14+
(default: guidellm.log if log file level set else none)
15+
- GUIDELLM_LOG_FILE_LEVEL: Log level for file logging
16+
(default: INFO if log file set else none).
17+
18+
Usage:
19+
from guidellm import logger, configure_logger, LoggerConfig
20+
21+
# Configure metrics with default settings
22+
configure_logger(
23+
config=LoggerConfig(
24+
disabled=False,
25+
clear_loggers=True,
26+
console_log_level="DEBUG",
27+
log_file=None,
28+
log_file_level=None,
29+
)
30+
)
31+
32+
logger.debug("This is a debug message")
33+
logger.info("This is an info message")
34+
"""
35+
36+
import os
37+
import sys
38+
from dataclasses import dataclass
39+
from typing import Optional
40+
41+
from loguru import logger
42+
43+
__all__ = ["LoggerConfig", "configure_logger", "logger"]
44+
45+
46+
@dataclass
47+
class LoggerConfig:
48+
disabled: bool = False
49+
clear_loggers: bool = True
50+
console_log_level: Optional[str] = "INFO"
51+
log_file: Optional[str] = None
52+
log_file_level: Optional[str] = None
53+
54+
55+
def configure_logger(config: Optional[LoggerConfig] = None):
56+
"""
57+
Configure the metrics for LLM Compressor.
58+
This function sets up the console and file logging
59+
as per the specified or default parameters.
60+
61+
Note: Environment variables take precedence over the function parameters.
62+
63+
:param config: The configuration for the logger to use.
64+
:type config: LoggerConfig
65+
"""
66+
67+
_ENV_CONFIG = LoggerConfig(
68+
disabled=os.getenv("GUIDELLM_LOG_DISABLED") == "true",
69+
clear_loggers=os.getenv("GUIDELLM_CLEAR_LOGGERS") == "true",
70+
console_log_level=os.getenv("GUIDELLM_LOG_LEVEL"),
71+
log_file=os.getenv("GUIDELLM_LOG_FILE"),
72+
log_file_level=os.getenv("GUIDELLM_LOG_FILE_LEVEL"),
73+
)
74+
75+
if not config:
76+
config = LoggerConfig()
77+
# override from environment variables, if set
78+
logger_config = LoggerConfig(
79+
disabled=_ENV_CONFIG.disabled or config.disabled,
80+
console_log_level=_ENV_CONFIG.console_log_level or config.console_log_level,
81+
log_file=_ENV_CONFIG.log_file or config.log_file,
82+
log_file_level=_ENV_CONFIG.log_file_level or config.log_file_level,
83+
)
84+
85+
if logger_config.disabled:
86+
logger.disable("guidellm")
87+
return
88+
89+
logger.enable("guidellm")
90+
91+
if logger_config.clear_loggers:
92+
logger.remove()
93+
94+
if logger_config.console_log_level:
95+
# log as a human readable string with the time, function, level, and message
96+
logger.add(
97+
sys.stdout,
98+
level=logger_config.console_log_level.upper(),
99+
format="{time} | {function} | {level} - {message}",
100+
)
101+
102+
if logger_config.log_file or logger_config.log_file_level:
103+
log_file = logger_config.log_file or "guidellm.log"
104+
log_file_level = logger_config.log_file_level or "INFO"
105+
# log as json to the file for easier parsing
106+
logger.add(log_file, level=log_file_level.upper(), serialize=True)
107+
108+
109+
# invoke logger setup on import with default values enabling console logging with INFO
110+
# and disabling file logging
111+
configure_logger(
112+
config=LoggerConfig(
113+
disabled=False,
114+
clear_loggers=True,
115+
console_log_level="INFO",
116+
log_file=None,
117+
log_file_level=None,
118+
)
119+
)

tests/unit/test_logger.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import pytest
2+
3+
from guidellm import LoggerConfig, configure_logger, logger
4+
5+
6+
@pytest.fixture(autouse=True)
7+
def reset_logger():
8+
# Ensure logger is reset before each test
9+
logger.remove()
10+
yield
11+
logger.remove()
12+
13+
14+
def test_default_logger_settings(capsys):
15+
configure_logger()
16+
17+
# Default settings should log to console with INFO level and no file logging
18+
logger.info("Info message")
19+
logger.debug("Debug message")
20+
21+
captured = capsys.readouterr()
22+
assert captured.out.count("Info message") == 1
23+
assert "Debug message" not in captured.out
24+
25+
26+
def test_configure_logger_console_settings(capsys):
27+
# Test configuring the logger to change console log level
28+
config = LoggerConfig(console_log_level="DEBUG")
29+
configure_logger(config=config)
30+
logger.info("Info message")
31+
logger.debug("Debug message")
32+
33+
captured = capsys.readouterr()
34+
assert captured.out.count("Info message") == 1
35+
assert captured.out.count("Debug message") == 1
36+
37+
38+
def test_configure_logger_file_settings(tmp_path):
39+
# Test configuring the logger to log to a file
40+
log_file = tmp_path / "test.log"
41+
config = LoggerConfig(log_file=str(log_file), log_file_level="DEBUG")
42+
configure_logger(config=config)
43+
logger.info("Info message")
44+
logger.debug("Debug message")
45+
46+
with open(log_file, "r") as f:
47+
log_contents = f.read()
48+
assert log_contents.count('"message": "Info message"') == 1
49+
assert log_contents.count('"message": "Debug message"') == 1
50+
51+
52+
def test_configure_logger_console_and_file(capsys, tmp_path):
53+
# Test configuring the logger to change both console and file settings
54+
log_file = tmp_path / "test.log"
55+
config = LoggerConfig(
56+
console_log_level="ERROR", log_file=str(log_file), log_file_level="INFO"
57+
)
58+
configure_logger(config=config)
59+
logger.info("Info message")
60+
logger.error("Error message")
61+
62+
captured = capsys.readouterr()
63+
assert "Info message" not in captured.out
64+
assert captured.out.count("Error message") == 1
65+
66+
with open(log_file, "r") as f:
67+
log_contents = f.read()
68+
assert log_contents.count('"message": "Info message"') == 1
69+
assert log_contents.count('"message": "Error message"') == 1
70+
71+
72+
def test_environment_variable_override(monkeypatch, capsys, tmp_path):
73+
# Test environment variables override settings
74+
monkeypatch.setenv("GUIDELLM_LOG_LEVEL", "ERROR")
75+
monkeypatch.setenv("GUIDELLM_LOG_FILE", str(tmp_path / "env_test.log"))
76+
monkeypatch.setenv("GUIDELLM_LOG_FILE_LEVEL", "DEBUG")
77+
78+
configure_logger(config=LoggerConfig())
79+
logger.info("Info message")
80+
logger.error("Error message")
81+
logger.debug("Debug message")
82+
83+
captured = capsys.readouterr()
84+
assert "Info message" not in captured.out
85+
assert captured.out.count("Error message") == 1
86+
assert "Debug message" not in captured.out
87+
88+
with open(tmp_path / "env_test.log", "r") as f:
89+
log_contents = f.read()
90+
assert log_contents.count('"message": "Error message"') == 1
91+
assert log_contents.count('"message": "Info message"') == 1
92+
assert log_contents.count('"message": "Debug message"') == 1
93+
94+
95+
def test_environment_variable_disable_logging(monkeypatch, capsys):
96+
# Test environment variable to disable logging
97+
monkeypatch.setenv("GUIDELLM_LOG_DISABLED", "true")
98+
99+
configure_logger(config=LoggerConfig())
100+
logger.info("Info message")
101+
logger.error("Error message")
102+
103+
captured = capsys.readouterr()
104+
assert captured.out == ""
105+
assert captured.err == ""

0 commit comments

Comments
 (0)