Skip to content

Commit 9777c0c

Browse files
committed
feat: complete backtest repos and fix the logging
1 parent bd550fa commit 9777c0c

File tree

3 files changed

+77
-142
lines changed

3 files changed

+77
-142
lines changed

requirements-dev.txt

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# This file is autogenerated by pip-compile with Python 3.12
33
# by the following command:
44
#
5-
# pip-compile requirements-dev.in
5+
# pip-compile --output-file=requirements-dev.txt --strip-extras requirements-dev.in requirements-dev.in
66
#
77
annotated-types==0.7.0
88
# via pydantic
@@ -14,25 +14,25 @@ astroid==3.3.10
1414
# via pylint
1515
babel==2.17.0
1616
# via mkdocs-material
17-
backrefs==5.8
17+
backrefs==5.9
1818
# via mkdocs-material
1919
bandit==1.8.3
2020
# via -r requirements-dev.in
2121
black==25.1.0
2222
# via -r requirements-dev.in
23-
boto3==1.38.36
23+
boto3==1.38.46
2424
# via -r D:\git\stock-data-stream\requirements.in
25-
botocore==1.38.36
25+
botocore==1.38.46
2626
# via
2727
# boto3
2828
# s3transfer
29-
bracex==2.5.post1
29+
bracex==2.6
3030
# via wcmatch
3131
build==1.2.2.post1
3232
# via -r requirements-dev.in
3333
bump-my-version==1.1.2
3434
# via -r requirements-dev.in
35-
certifi==2025.4.26
35+
certifi==2025.6.15
3636
# via
3737
# httpcore
3838
# httpx
@@ -65,7 +65,7 @@ commitizen==4.8.3
6565
# via -r requirements-dev.in
6666
contourpy==1.3.2
6767
# via matplotlib
68-
coverage[toml]==7.9.0
68+
coverage==7.9.1
6969
# via pytest-cov
7070
cycler==0.12.1
7171
# via matplotlib
@@ -85,7 +85,7 @@ flake8==7.2.0
8585
# flake8-docstrings
8686
flake8-docstrings==1.7.0
8787
# via -r requirements-dev.in
88-
fonttools==4.58.3
88+
fonttools==4.58.4
8989
# via matplotlib
9090
ghp-import==2.1.0
9191
# via mkdocs
@@ -116,7 +116,7 @@ jaraco-classes==3.4.0
116116
# via keyring
117117
jaraco-context==6.0.1
118118
# via keyring
119-
jaraco-functools==4.1.0
119+
jaraco-functools==4.2.1
120120
# via keyring
121121
jinja2==3.1.6
122122
# via
@@ -136,7 +136,7 @@ loguru==0.7.3
136136
# via -r D:\git\stock-data-stream\requirements.in
137137
mando==0.7.1
138138
# via radon
139-
markdown==3.8
139+
markdown==3.8.2
140140
# via
141141
# mkdocs
142142
# mkdocs-material
@@ -231,26 +231,26 @@ py-cpuinfo==9.0.0
231231
# via pytest-benchmark
232232
pycodestyle==2.13.0
233233
# via flake8
234-
pydantic==2.11.6
234+
pydantic==2.11.7
235235
# via
236236
# bump-my-version
237237
# pydantic-settings
238238
pydantic-core==2.33.2
239239
# via pydantic
240-
pydantic-settings==2.9.1
240+
pydantic-settings==2.10.1
241241
# via bump-my-version
242242
pydocstyle==6.3.0
243243
# via flake8-docstrings
244244
pyflakes==3.3.2
245245
# via flake8
246-
pygments==2.19.1
246+
pygments==2.19.2
247247
# via
248248
# mkdocs-material
249249
# readme-renderer
250250
# rich
251251
pylint==3.3.6
252252
# via -r requirements-dev.in
253-
pymdown-extensions==10.15
253+
pymdown-extensions==10.16
254254
# via mkdocs-material
255255
pyparsing==3.2.3
256256
# via matplotlib
@@ -280,7 +280,7 @@ python-dateutil==2.9.0.post0
280280
# ghp-import
281281
# matplotlib
282282
# pandas
283-
python-dotenv==1.1.0
283+
python-dotenv==1.1.1
284284
# via
285285
# -r D:\git\stock-data-stream\requirements.in
286286
# pydantic-settings
@@ -333,7 +333,7 @@ ruff==0.11.6
333333
# via -r requirements-dev.in
334334
s3transfer==0.13.0
335335
# via boto3
336-
scipy==1.15.3
336+
scipy==1.16.0
337337
# via -r D:\git\stock-data-stream\requirements.in
338338
six==1.17.0
339339
# via
@@ -345,6 +345,8 @@ snowballstemmer==3.0.1
345345
# via pydocstyle
346346
stevedore==5.4.1
347347
# via bandit
348+
tenacity==8.5.0
349+
# via -r D:\git\stock-data-stream\requirements.in
348350
termcolor==2.3.0
349351
# via
350352
# -r requirements-dev.in
@@ -388,7 +390,7 @@ virtualenv==20.31.2
388390
# via pre-commit
389391
watchdog==6.0.0
390392
# via mkdocs
391-
wcmatch==10.0
393+
wcmatch==10.1
392394
# via bump-my-version
393395
wcwidth==0.2.13
394396
# via prompt-toolkit

requirements.txt

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
# This file is autogenerated by pip-compile with Python 3.12
33
# by the following command:
44
#
5-
# pip-compile 'D:\git\stock-data-stream\requirements.in'
5+
# pip-compile --output-file=requirements.txt --strip-extras requirements.in requirements.in
66
#
7-
boto3==1.38.36
8-
# via -r D:\git\stock-data-stream\requirements.in
9-
botocore==1.38.36
7+
boto3==1.38.46
8+
# via -r requirements.in
9+
botocore==1.38.46
1010
# via
1111
# boto3
1212
# s3transfer
@@ -20,7 +20,7 @@ contourpy==1.3.2
2020
# via matplotlib
2121
cycler==0.12.1
2222
# via matplotlib
23-
fonttools==4.58.3
23+
fonttools==4.58.4
2424
# via matplotlib
2525
idna==3.10
2626
# via requests
@@ -31,52 +31,52 @@ jmespath==1.0.1
3131
kiwisolver==1.4.8
3232
# via matplotlib
3333
loguru==0.7.3
34-
# via -r D:\git\stock-data-stream\requirements.in
34+
# via -r requirements.in
3535
matplotlib==3.10.3
36-
# via -r D:\git\stock-data-stream\requirements.in
36+
# via -r requirements.in
3737
numpy==1.26.4
3838
# via
39-
# -r D:\git\stock-data-stream\requirements.in
39+
# -r requirements.in
4040
# contourpy
4141
# matplotlib
4242
# pandas
4343
# scipy
4444
packaging==25.0
4545
# via matplotlib
4646
pandas==2.2.3
47-
# via -r D:\git\stock-data-stream\requirements.in
47+
# via -r requirements.in
4848
pika==1.3.2
49-
# via -r D:\git\stock-data-stream\requirements.in
49+
# via -r requirements.in
5050
pillow==11.2.1
5151
# via matplotlib
5252
prometheus-client==0.22.1
53-
# via -r D:\git\stock-data-stream\requirements.in
53+
# via -r requirements.in
5454
pyparsing==3.2.3
5555
# via matplotlib
5656
python-dateutil==2.9.0.post0
5757
# via
5858
# botocore
5959
# matplotlib
6060
# pandas
61-
python-dotenv==1.1.0
62-
# via -r D:\git\stock-data-stream\requirements.in
61+
python-dotenv==1.1.1
62+
# via -r requirements.in
6363
pytz==2025.2
6464
# via pandas
6565
requests==2.32.4
66-
# via -r D:\git\stock-data-stream\requirements.in
66+
# via -r requirements.in
6767
s3transfer==0.13.0
6868
# via boto3
69-
scipy==1.15.3
70-
# via -r D:\git\stock-data-stream\requirements.in
69+
scipy==1.16.0
70+
# via -r requirements.in
7171
six==1.17.0
7272
# via python-dateutil
7373
tenacity==8.5.0
74-
# via -r D:\git\stock-data-stream\requirements.in
74+
# via -r requirements.in
7575
tzdata==2025.2
7676
# via pandas
7777
urllib3==2.5.0
7878
# via
79-
# -r D:\git\stock-data-stream\requirements.in
79+
# -r requirements.in
8080
# botocore
8181
# requests
8282
win32-setctime==1.2.0

src/app/utils/setup_logger.py

Lines changed: 40 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,53 @@
1-
"""Configure and return a logger for the application.
2-
3-
Supports optional structured (JSON-style) logging for production
4-
environments and redaction of sensitive fields.
5-
"""
6-
7-
import json
81
import logging
9-
import re
10-
11-
from app import config_shared
12-
13-
14-
class StructuredFormatter(logging.Formatter):
15-
"""Structured log formatter that outputs logs as JSON strings."""
16-
17-
def format(self, record: logging.LogRecord) -> str:
18-
"""Format a log record as a JSON string.
19-
20-
Args:
21-
record (logging.LogRecord): The log record.
22-
23-
Returns:
24-
str: A JSON-formatted string of log fields.
25-
26-
"""
27-
log_record = {
28-
"timestamp": self.formatTime(record, self.datefmt),
29-
"level": record.levelname,
30-
"logger": record.name,
31-
"message": record.getMessage(),
32-
"module": record.module,
33-
"filename": record.filename,
34-
"funcName": record.funcName,
35-
"lineno": record.lineno,
36-
}
37-
return json.dumps(log_record)
38-
39-
40-
class RedactFilter(logging.Filter):
41-
"""Logging filter that redacts sensitive information from log messages."""
42-
43-
SENSITIVE_KEYS = {"api_key", "apikey", "token", "secret", "password", "auth"}
44-
45-
def filter(self, record: logging.LogRecord) -> bool:
46-
"""Filter the log record, redacting sensitive values in the message.
47-
48-
Args:
49-
record (logging.LogRecord): The log record.
50-
51-
Returns:
52-
bool: Always True to allow the record after modification.
53-
54-
"""
55-
message = record.getMessage()
56-
57-
for key in self.SENSITIVE_KEYS:
58-
# Match key=value and "key": "value" formats
59-
patterns = [
60-
re.compile(rf"({key}\s*=\s*)(\S+)", re.IGNORECASE),
61-
re.compile(rf'("{key}"\s*:\s*")([^"]+)(")', re.IGNORECASE),
62-
]
63-
for pattern in patterns:
64-
message = pattern.sub(
65-
lambda m: f"{m.group(1)}***{m.group(3) if m.lastindex and m.lastindex >= 3 else ''}",
66-
message,
67-
)
68-
69-
record.msg = message
70-
return True
2+
import sys
3+
from logging import Logger
4+
from typing import Optional
715

726

737
def setup_logger(
74-
name: str | None = None,
8+
name: Optional[str] = None,
759
level: int = logging.INFO,
7610
structured: bool = False,
77-
redact_sensitive: bool | None = None,
78-
) -> logging.Logger:
79-
"""Configure and return a logger for the application.
11+
) -> Logger:
12+
"""
13+
Configure and return a logger with optional redaction and structured logging.
8014
8115
Args:
82-
name (Optional[str]): Name of the logger.
83-
level (int): Logging level (e.g., logging.INFO).
84-
structured (bool): Whether to use structured (JSON) logging.
85-
redact_sensitive (Optional[bool]): Override redaction setting (defaults to config).
16+
name (Optional[str]): Logger name.
17+
level (int): Log level (e.g., logging.INFO).
18+
structured (bool): Enable structured (JSON) logging. (Not yet implemented)
8619
8720
Returns:
88-
logging.Logger: A configured logger instance.
89-
21+
Logger: Configured logger instance.
9022
"""
91-
logger_name = name or "poller"
92-
logger = logging.getLogger(logger_name)
93-
94-
if not logger.hasHandlers():
95-
logger.setLevel(level)
96-
handler = logging.StreamHandler()
97-
formatter: logging.Formatter = (
98-
StructuredFormatter()
99-
if structured
100-
else logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
101-
)
102-
handler.setFormatter(formatter)
103-
104-
# Determine redaction setting
105-
enable_redaction = (
106-
redact_sensitive
107-
if redact_sensitive is not None
108-
else config_shared.get_redact_sensitive_logs()
109-
)
110-
if enable_redaction:
111-
handler.addFilter(RedactFilter())
112-
logger.debug("🔐 Log redaction enabled")
113-
114-
logger.addHandler(handler)
23+
logger = logging.getLogger(name)
24+
if logger.handlers:
25+
return logger
26+
27+
# Delay import to avoid circular dependency
28+
try:
29+
from app import config_shared
30+
redact = config_shared.get_redact_sensitive_logs()
31+
except Exception:
32+
redact = False
33+
34+
formatter = logging.Formatter(
35+
fmt="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
36+
datefmt="%Y-%m-%d %H:%M:%S"
37+
)
38+
39+
handler = logging.StreamHandler(sys.stdout)
40+
handler.setFormatter(formatter)
41+
logger.addHandler(handler)
42+
logger.setLevel(level)
43+
44+
if redact:
45+
logger.info("🔒 Redaction of sensitive data is ENABLED")
46+
else:
47+
logger.info("🔓 Redaction of sensitive data is DISABLED")
48+
49+
# NOTE: 'structured' is accepted but not yet implemented
50+
if structured:
51+
logger.warning("⚠️ Structured logging requested but not yet supported.")
11552

11653
return logger
117-
118-
119-
# Default logger instance for modules
120-
logger = setup_logger(level=logging.DEBUG)

0 commit comments

Comments
 (0)