File size: 2,581 Bytes
14ac8e9
 
 
0c217a7
 
14ac8e9
0c217a7
 
 
14ac8e9
0c217a7
 
 
 
 
 
14ac8e9
 
0c217a7
14ac8e9
0c217a7
 
14ac8e9
 
 
 
 
0c217a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14ac8e9
 
0c217a7
 
14ac8e9
 
 
 
0c217a7
14ac8e9
0c217a7
14ac8e9
 
0c217a7
 
 
14ac8e9
0c217a7
14ac8e9
0c217a7
 
 
14ac8e9
0c217a7
14ac8e9
0c217a7
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import logging
import functools
import time
from pathlib import Path
import copy

# ---------------------------
# Logging Configuration Setup
# ---------------------------

# Setup log directory and file path
LOG_DIR = Path(__file__).resolve().parent / "logs"
LOG_DIR.mkdir(exist_ok=True)
LOG_FILE_PATH = LOG_DIR / "app.log"

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    handlers=[
        logging.StreamHandler(),                   # Console
        logging.FileHandler(LOG_FILE_PATH, mode='a')  # File
    ]
)

logger = logging.getLogger(__name__)


# -----------------------------------------
# Utility: Redact sensitive values in dicts
# -----------------------------------------

SENSITIVE_KEYS = [
    "OPENAI_API_KEY", "GOOGLE_API_KEY", "GROQ_API_KEY", "TAVILY_API_KEY"
]

def redact_keys(data: dict, keys_to_redact=None):
    """
    Redact sensitive keys in a dictionary before logging.
    """
    if keys_to_redact is None:
        keys_to_redact = SENSITIVE_KEYS

    redacted = copy.deepcopy(data)
    for key in keys_to_redact:
        if key in redacted:
            redacted[key] = "***REDACTED***"
    return redacted


# -----------------------------------------
# Decorator: Log function entry and exit
# -----------------------------------------

def log_entry_exit(func):
    """
    A decorator that logs function entry, exit, execution time,
    and captures exceptions if any.
    """
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        func_name = func.__name__
        logger.info(f"\n{'='*20} ENTER: {func_name} {'='*20}")
        start_time = time.perf_counter()

        try:
            result = func(*args, **kwargs)
            execution_time = time.perf_counter() - start_time
            logger.info(f"{func_name} completed in {execution_time:.4f} seconds")
            logger.info(f"{'='*20} EXIT: {func_name} {'='*21}\n")
            return result

        except Exception as e:
            execution_time = time.perf_counter() - start_time
            logger.error(f"Exception in {func_name} after {execution_time:.4f} seconds: {e}", exc_info=True)
            logger.info(f"{'='*20} FAILED: {func_name} {'='*20}\n")
            raise

    return wrapper


# -----------------------------------------
# Example Usage of Redacted Logging
# -----------------------------------------

def log_session_state(session_state: dict):
    redacted_state = redact_keys(session_state)
    logger.info("Session state updated: %s", redacted_state)