-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathlogger.py
155 lines (125 loc) · 4.25 KB
/
logger.py
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""
Logger class
This class is used to log messages to the console and/or a file.
Example:
```python
from pandasai.helpers.logger import Logger
logger = Logger()
logger.log("Hello, world!")
# 2021-08-01 12:00:00 [INFO] Hello, world!
logger.logs
#["Hello, world!"]
```
"""
import inspect
import logging
import sys
import time
from typing import List
from pydantic import BaseModel
from pandasai.helpers.telemetry import scarf_analytics
from .path import find_closest
class Log(BaseModel):
"""Log class"""
msg: str
level: int
class Logger:
"""Logger class"""
_logs: List[Log]
_logger: logging.Logger
_verbose: bool
_last_time: float
def __init__(self, save_logs: bool = True, verbose: bool = False):
"""Initialize the logger"""
self._logs = []
self._verbose = verbose
self._last_time = time.time()
if save_logs:
try:
filename = find_closest("pandasai.log")
except ValueError:
filename = "pandasai.log"
handlers = [logging.FileHandler(filename)]
else:
handlers = []
if verbose:
handlers.append(logging.StreamHandler(sys.stdout))
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
handlers=handlers,
)
self._logger = logging.getLogger(__name__)
def log(self, message: str, level: int = logging.INFO):
"""Log a message"""
if level == logging.INFO:
self._logger.info(message)
elif level == logging.WARNING:
self._logger.warning(message)
elif level == logging.ERROR:
self._logger.error(message)
elif level == logging.CRITICAL:
self._logger.critical(message)
self._logs.append(
{
"msg": message,
"level": logging.getLevelName(level),
"time": self._calculate_time_diff(),
"source": self._invoked_from(),
}
)
def _invoked_from(self, level: int = 5) -> str:
"""Return the name of the class that invoked the logger"""
calling_class = None
for frame_info in inspect.stack()[1:]:
frame_locals = frame_info[0].f_locals
calling_instance = frame_locals.get("self")
if calling_instance and calling_instance.__class__ != self.__class__:
calling_class = calling_instance.__class__.__name__
break
level -= 1
if level <= 0:
break
return calling_class
def _calculate_time_diff(self):
"""Calculate the time difference since the last log"""
time_diff = time.time() - self._last_time
self._last_time = time.time()
return time_diff
@property
def logs(self) -> List[str]:
"""Return the logs"""
return self._logs
@property
def verbose(self) -> bool:
"""Return the verbose flag"""
return self._verbose
@verbose.setter
def verbose(self, verbose: bool):
"""Set the verbose flag"""
self._verbose = verbose
self._logger.handlers = []
if verbose:
self._logger.addHandler(logging.StreamHandler(sys.stdout))
else:
# remove the StreamHandler if it exists
for handler in self._logger.handlers:
if isinstance(handler, logging.StreamHandler):
self._logger.removeHandler(handler)
@property
def save_logs(self) -> bool:
"""Return the save_logs flag"""
return len(self._logger.handlers) > 0
@save_logs.setter
def save_logs(self, save_logs: bool):
"""Set the save_logs flag"""
if save_logs and not self.save_logs:
filename = find_closest("pandasai.log")
self._logger.addHandler(logging.FileHandler(filename))
elif not save_logs and self.save_logs:
# remove the FileHandler if it exists
for handler in self._logger.handlers:
if isinstance(handler, logging.FileHandler):
self._logger.removeHandler(handler)
scarf_analytics()