Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 13 additions & 22 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
### AL ###
#Template for AL projects for Dynamics 365 Business Central
#launch.json folder
.vscode/
#Cache folder
.alcache/
#Symbols folder
.alpackages/
#Snapshots folder
.snapshots/
#Testing Output folder
.output/
#Extension App-file
*.app
#Rapid Application Development File
rad.json
#Translation Base-file
*.g.xlf
#License-file
*.flf
#Test results file
TestResults.xml
__pycache__/
*.py[cod]
*$py.class
.pytest_cache/
.coverage
htmlcov/
.env
.venv/
venv/
dist/
build/
*.egg-info/
.DS_Store
99 changes: 99 additions & 0 deletions src/alp/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import logging
import time
from typing import Any, Dict, Optional

class ALPLogger:
"""
Specialized logger for Adaptive Learning Process (ALP) iterations.
Provides comprehensive logging capabilities with performance tracking.
"""
def __init__(self, name: str = "ALP", log_level: int = logging.INFO):
"""
Initialize the ALP logger with configurable name and log level.

:param name: Name of the logger instance
:param log_level: Logging level (default: logging.INFO)
"""
self.logger = logging.getLogger(name)
self.logger.setLevel(log_level)

# Create console handler if not already configured
if not self.logger.handlers:
console_handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
console_handler.setFormatter(formatter)
self.logger.addHandler(console_handler)

def log_iteration(
self,
iteration: int,
metrics: Optional[Dict[str, Any]] = None,
level: int = logging.INFO
) -> None:
"""
Log details of a single iteration with performance metrics.

:param iteration: Current iteration number
:param metrics: Dictionary of performance metrics
:param level: Logging level
"""
metrics = metrics or {}
log_message = f"Iteration {iteration}"

# Add metrics to log message
if metrics:
metric_str = " | ".join(f"{k}: {v}" for k, v in metrics.items())
log_message += f" - Metrics: {metric_str}"

# Log the message at specified level
self.logger.log(level, log_message)

def log_error(
self,
iteration: int,
error: Exception,
context: Optional[Dict[str, Any]] = None
) -> None:
"""
Log an error that occurred during an iteration.

:param iteration: Iteration number when error occurred
:param error: Exception that was raised
:param context: Additional context about the error
"""
context = context or {}
context_str = " | ".join(f"{k}: {v}" for k, v in context.items())
error_message = (
f"Error in iteration {iteration}: {str(error)}\n"
f"Context: {context_str}"
)
self.logger.error(error_message, exc_info=True)

def track_performance(
self,
start_time: float,
end_time: float
) -> Dict[str, float]:
"""
Calculate and log performance metrics for an iteration.

:param start_time: Start time of the iteration
:param end_time: End time of the iteration
:return: Performance metrics dictionary
"""
elapsed_time = end_time - start_time
performance_metrics = {
"elapsed_time": elapsed_time,
"timestamp": end_time
}

# Log performance metrics
self.log_iteration(
iteration=0, # Use 0 for overall performance tracking
metrics=performance_metrics,
level=logging.DEBUG
)

return performance_metrics
53 changes: 53 additions & 0 deletions tests/test_alp_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import logging
import time
import pytest
from src.alp.logging import ALPLogger

def test_alp_logger_initialization():
"""Test logger initialization with default and custom parameters."""
logger = ALPLogger()
assert logger.logger.name == "ALP"
assert logger.logger.level == logging.INFO

custom_logger = ALPLogger(name="CustomALP", log_level=logging.DEBUG)
assert custom_logger.logger.name == "CustomALP"
assert custom_logger.logger.level == logging.DEBUG

def test_log_iteration(caplog):
"""Test logging of iteration with metrics."""
logger = ALPLogger()
caplog.set_level(logging.INFO)

metrics = {"loss": 0.5, "accuracy": 0.95}
logger.log_iteration(iteration=10, metrics=metrics)

assert "Iteration 10" in caplog.text
assert "loss: 0.5" in caplog.text
assert "accuracy: 0.95" in caplog.text

def test_log_error(caplog):
"""Test error logging with context."""
logger = ALPLogger()
caplog.set_level(logging.ERROR)

try:
raise ValueError("Test error")
except ValueError as e:
logger.log_error(iteration=5, error=e, context={"model": "test_model"})

assert "Error in iteration 5" in caplog.text
assert "Test error" in caplog.text
assert "model: test_model" in caplog.text

def test_track_performance():
"""Test performance tracking method."""
logger = ALPLogger()

start_time = time.time() - 1.0 # Simulate 1 second elapsed
end_time = time.time()

performance = logger.track_performance(start_time, end_time)

assert "elapsed_time" in performance
assert "timestamp" in performance
assert performance["elapsed_time"] >= 1.0