Skip to content

Commit d9db6ed

Browse files
committed
Improved Logging Functionality
1 parent 0823817 commit d9db6ed

File tree

6 files changed

+196
-79
lines changed

6 files changed

+196
-79
lines changed

CHANGELOG.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,22 @@
22

33
All notable changes to LocalLab will be documented in this file.
44

5-
## [0.4.53] - 2024-04-21
5+
## [0.5.2] - 2024-04-30
6+
7+
### Fixed
8+
9+
- Fixed duplicate logging issue where the same log message appeared multiple times
10+
- Improved color detection for terminal output - now only uses colors when supported
11+
- Prevented multiple handlers from being added to the same logger
12+
- Disabled uvicorn's default logging configuration to prevent duplication
13+
- Enhanced logger initialization to ensure consistent formatting
14+
- Added proper cleanup of existing handlers before adding new ones
15+
- Improved compatibility with different terminal environments
16+
17+
## [0.5.1] - 2024-04-21
618

719
### Added
20+
821
- Enhanced error handling and reliability in both clients
922
- Added timeout handling to sync client streaming methods
1023
- Improved event loop cleanup and resource management
@@ -14,26 +27,29 @@ All notable changes to LocalLab will be documented in this file.
1427
- Added proper cleanup of resources on client closure
1528

1629
### Fixed
30+
1731
- Fixed potential memory leaks in event loop handling
1832
- Fixed thread cleanup in synchronous client
1933
- Improved error propagation between async and sync clients
2034
- Added proper timeout handling in streaming operations
2135
- Enhanced connection state management
2236

23-
## [0.4.52] - 2024-04-21
37+
## [0.5.0] - 2024-04-21
2438

2539
### Fixed
40+
2641
- Fixed package structure to avoid duplicate exports
2742
- Updated version numbers to be consistent across all files
2843
- Fixed imports in sync_client.py to use correct package name
2944
- Improved package import reliability
3045
- Ensured both LocalLabClient and SyncLocalLabClient are properly exported
3146

32-
## [0.4.51] - 2024-04-21
47+
## [0.5.01] - 2024-04-21
3348

3449
### Fixed
50+
3551
- Fixed SyncLocalLabClient not being exported from locallab_client package
36-
- Added proper exports for both LocalLabClient and SyncLocalLabClient in package __init__.py
52+
- Added proper exports for both LocalLabClient and SyncLocalLabClient in package **init**.py
3753
- Ensured both sync and async clients are available through the main package import
3854

3955
## [0.4.50] - 2024-04-21

locallab/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
LocalLab - A lightweight AI inference server for running LLMs locally
33
"""
44

5-
__version__ = "0.5.1" # Updated to match setup.py
5+
__version__ = "0.5.2" # Updated to match setup.py
66

77
# Only import what's necessary initially, lazy-load the rest
88
from .logger import get_logger

locallab/config.py

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
def get_env_var(key: str, *, default: Any = None, var_type: Type = str) -> Any:
1414
"""Get environment variable with type conversion and validation."""
1515
value = os.environ.get(key)
16-
16+
1717
if value is None:
1818
try:
1919
from .cli.config import get_config_value
@@ -22,13 +22,13 @@ def get_env_var(key: str, *, default: Any = None, var_type: Type = str) -> Any:
2222
config_key = "ngrok_auth_token"
2323
elif key == HF_TOKEN_ENV:
2424
config_key = "huggingface_token"
25-
25+
2626
config_value = get_config_value(config_key)
2727
if config_value is not None:
2828
value = config_value
2929
except:
3030
pass
31-
31+
3232
if value is None:
3333
return default
3434

@@ -61,20 +61,20 @@ def get_ngrok_token() -> Optional[str]:
6161
def save_config(config: Dict[str, Any]):
6262
"""Save configuration to file"""
6363
from .cli.config import ensure_config_dir, CONFIG_FILE
64-
64+
6565
ensure_config_dir()
66-
66+
6767
# Ensure tokens are stored as proper strings
6868
if "ngrok_auth_token" in config:
6969
token = str(config["ngrok_auth_token"]).strip()
7070
config["ngrok_auth_token"] = token
7171
set_env_var(NGROK_TOKEN_ENV, token)
72-
72+
7373
if "huggingface_token" in config:
7474
token = str(config["huggingface_token"]).strip()
7575
config["huggingface_token"] = token
7676
set_env_var(HF_TOKEN_ENV, token)
77-
77+
7878
try:
7979
with open(CONFIG_FILE, "w") as f:
8080
json.dump(config, f, indent=2)
@@ -159,18 +159,18 @@ def can_run_model(model_id: str) -> bool:
159159

160160
# Get available memory with a buffer
161161
available_ram = (psutil.virtual_memory().available / (1024 ** 3)) * 0.8 # 80% of available RAM in GB
162-
162+
163163
# Adjust requirements based on optimizations
164164
if get_env_var("LOCALLAB_ENABLE_QUANTIZATION", default=False, var_type=bool):
165165
# Quantization reduces memory usage
166166
requirements["min_ram"] *= 0.5
167167
if "min_vram" in requirements:
168168
requirements["min_vram"] *= 0.5
169-
169+
170170
if get_env_var("LOCALLAB_ENABLE_CPU_OFFLOADING", default=False, var_type=bool):
171171
# CPU offloading allows running with less RAM
172172
requirements["min_ram"] *= 0.7
173-
173+
174174
# Check RAM
175175
if available_ram < requirements["min_ram"]:
176176
logger.warning(f"Insufficient RAM. Available: {available_ram:.1f}GB, Required: {requirements['min_ram']}GB")
@@ -324,18 +324,26 @@ def estimate_model_requirements(model_id: str) -> Dict[str, float]:
324324
ENABLE_FILE_LOGGING = get_env_var(
325325
"LOCALLAB_ENABLE_FILE_LOGGING", default=False, var_type=bool)
326326

327-
# Configure logging
328-
logging.basicConfig(
329-
level=getattr(logging, LOG_LEVEL.upper()),
330-
format=LOG_FORMAT,
331-
handlers=[
332-
logging.StreamHandler() if ENABLE_CONSOLE_LOGGING else logging.NullHandler(),
333-
logging.FileHandler(
334-
LOG_FILE) if ENABLE_FILE_LOGGING and LOG_FILE else logging.NullHandler()
335-
]
336-
)
327+
# Import our logger instead of using basicConfig
328+
# This prevents duplicate logging configuration
329+
from .logger import get_logger
330+
331+
# Set up file logging if enabled
332+
if ENABLE_FILE_LOGGING and LOG_FILE:
333+
# Add file handler to root logger
334+
file_handler = logging.FileHandler(LOG_FILE)
335+
file_formatter = logging.Formatter(LOG_FORMAT)
336+
file_handler.setFormatter(file_formatter)
337337

338-
logger = logging.getLogger("locallab")
338+
# Add to root logger
339+
root_logger = logging.getLogger()
340+
root_logger.addHandler(file_handler)
341+
342+
# Set log level
343+
root_logger.setLevel(getattr(logging, LOG_LEVEL.upper()))
344+
345+
# Get the logger for this module
346+
logger = get_logger("locallab")
339347

340348

341349
def get_system_resources() -> Dict[str, Any]:
@@ -563,7 +571,7 @@ def get_env_var(key: str, *, default: Any = None, var_type: Type = str) -> Any:
563571
"""
564572
# First check environment variables
565573
value = os.environ.get(key)
566-
574+
567575
# If not found in environment, try the config file
568576
if value is None:
569577
try:
@@ -574,14 +582,14 @@ def get_env_var(key: str, *, default: Any = None, var_type: Type = str) -> Any:
574582
config_key = key[9:].lower()
575583
else:
576584
config_key = key.lower()
577-
585+
578586
config_value = get_config_value(config_key)
579587
if config_value is not None:
580588
value = config_value
581589
except (ImportError, ModuleNotFoundError):
582590
# If the config module isn't available yet, just use the environment variable
583591
pass
584-
592+
585593
# If still not found, use default
586594
if value is None:
587595
return default
@@ -602,7 +610,7 @@ def get_hf_token(interactive: bool = False) -> Optional[str]:
602610
"""Get HuggingFace token from environment or config"""
603611
# First check environment
604612
token = os.environ.get("HUGGINGFACE_TOKEN", "").strip()
605-
613+
606614
# Then check config
607615
if not token:
608616
try:
@@ -613,43 +621,43 @@ def get_hf_token(interactive: bool = False) -> Optional[str]:
613621
os.environ["HUGGINGFACE_TOKEN"] = token
614622
except:
615623
pass
616-
624+
617625
# If interactive and still no token, prompt user
618626
if not token and interactive:
619627
try:
620628
click.echo("\n🔑 HuggingFace token is required for accessing this model.")
621629
click.echo("Get your token from: https://huggingface.co/settings/tokens")
622-
630+
623631
token = click.prompt(
624632
"Enter your HuggingFace token",
625633
type=str,
626634
default="",
627635
show_default=False
628636
).strip()
629-
637+
630638
if token:
631639
if len(token) < 20:
632640
click.echo("\n❌ Invalid token format. Please check your token.")
633641
return None
634-
642+
635643
click.echo(f"\n✅ Token saved: {token}")
636644
os.environ["HUGGINGFACE_TOKEN"] = token
637-
645+
638646
# Save to config
639647
from .cli.config import set_config_value
640648
set_config_value("huggingface_token", token)
641649
else:
642650
click.echo("\nSkipping token...")
643651
except:
644652
pass
645-
653+
646654
return token
647655

648656
def get_ngrok_token() -> Optional[str]:
649657
"""Get ngrok token from environment or config"""
650658
# First check environment
651659
token = os.environ.get("NGROK_AUTHTOKEN", "").strip()
652-
660+
653661
# Then check config
654662
if not token:
655663
try:
@@ -660,26 +668,26 @@ def get_ngrok_token() -> Optional[str]:
660668
os.environ["NGROK_AUTHTOKEN"] = token
661669
except:
662670
pass
663-
671+
664672
return token
665673

666674
def save_config(config: Dict[str, Any]):
667675
"""Save configuration to file"""
668676
from .cli.config import ensure_config_dir, CONFIG_FILE
669-
677+
670678
ensure_config_dir()
671-
679+
672680
# Ensure tokens are stored as proper strings
673681
if "ngrok_auth_token" in config:
674682
token = str(config["ngrok_auth_token"]).strip()
675683
config["ngrok_auth_token"] = token
676684
set_env_var(NGROK_TOKEN_ENV, token)
677-
685+
678686
if "huggingface_token" in config:
679687
token = str(config["huggingface_token"]).strip()
680688
config["huggingface_token"] = token
681689
set_env_var(HF_TOKEN_ENV, token)
682-
690+
683691
try:
684692
with open(CONFIG_FILE, "w") as f:
685693
json.dump(config, f, indent=2)

0 commit comments

Comments
 (0)