smartpylogger 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smartpylogger-0.1.0/LICENSE +7 -0
- smartpylogger-0.1.0/PKG-INFO +102 -0
- smartpylogger-0.1.0/pyproject.toml +60 -0
- smartpylogger-0.1.0/setup.cfg +4 -0
- smartpylogger-0.1.0/setup.py +20 -0
- smartpylogger-0.1.0/smartpylogger/README.md +76 -0
- smartpylogger-0.1.0/smartpylogger/__init__.py +11 -0
- smartpylogger-0.1.0/smartpylogger/logger.py +283 -0
- smartpylogger-0.1.0/smartpylogger/utils.py +28 -0
- smartpylogger-0.1.0/smartpylogger.egg-info/PKG-INFO +102 -0
- smartpylogger-0.1.0/smartpylogger.egg-info/SOURCES.txt +15 -0
- smartpylogger-0.1.0/smartpylogger.egg-info/dependency_links.txt +1 -0
- smartpylogger-0.1.0/smartpylogger.egg-info/entry_points.txt +2 -0
- smartpylogger-0.1.0/smartpylogger.egg-info/requires.txt +11 -0
- smartpylogger-0.1.0/smartpylogger.egg-info/top_level.txt +1 -0
- smartpylogger-0.1.0/tests/test_catch.py +33 -0
- smartpylogger-0.1.0/tests/test_send.py +12 -0
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2025 bombaclaat inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,102 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: smartpylogger
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Lightweight structured logger for Py projects with AI analysis!
|
5
|
+
Author-email: Your Name <your@email.com>
|
6
|
+
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/smartpylogger
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Operating System :: OS Independent
|
11
|
+
Requires-Python: >=3.7
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
License-File: LICENSE
|
14
|
+
Requires-Dist: colorama
|
15
|
+
Requires-Dist: httpx
|
16
|
+
Requires-Dist: fastapi
|
17
|
+
Requires-Dist: starlette
|
18
|
+
Provides-Extra: dev
|
19
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
20
|
+
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
21
|
+
Requires-Dist: black>=21.0; extra == "dev"
|
22
|
+
Requires-Dist: flake8>=3.8; extra == "dev"
|
23
|
+
Requires-Dist: mypy>=0.800; extra == "dev"
|
24
|
+
Dynamic: license-file
|
25
|
+
Dynamic: requires-python
|
26
|
+
|
27
|
+
# SmartPyLogger
|
28
|
+
|
29
|
+
```text
|
30
|
+
....................................................................................
|
31
|
+
....................................................................................
|
32
|
+
..........................................-++=..:...................................
|
33
|
+
.............-.........................=**#######+:.........-.......................
|
34
|
+
.............................-.......:=#***##**##*=.....................-...........
|
35
|
+
......-.............................:+******#**##*#=................................
|
36
|
+
.............-...................:=*********####%##*:...............................
|
37
|
+
..............................:+************#*####=..................-..............
|
38
|
+
...-*+=:.................-=****##*****####*######%+:................................
|
39
|
+
...-*#*#**##*********##**#*******#######**#%%#***#%+................................
|
40
|
+
...:=*****#*##*######****#*****#####*#*++--*%##***##=.........-.....................
|
41
|
+
....-******###*##**###**###*#*###****+-...:-*%%#**##%-..................-...........
|
42
|
+
....:=******###*###****#**#**#####**+:......-*%##***%#-.............................
|
43
|
+
.....:+*#***##***###******##**###**+:.......:-*%##**#%*-............................
|
44
|
+
......-*#****######***#**#*##*##**+:.........:-*%#*+*#%#:...........................
|
45
|
+
......:-*******#**********#***##**-...........-:=*##***#%*:.........................
|
46
|
+
........:-+#***************##**##**-............:=*##****#%*-.......................
|
47
|
+
............:=*#************+*###*#+.............-=*###**#%@%#-.....................
|
48
|
+
.............:-+#*************+*###*-......-......:-+*#%####%%*:....................
|
49
|
+
.......-.......:-=*#***************+.................-=*#%%%*###%=..................
|
50
|
+
..................:--##*************-.................:-+*%#****#%*.................
|
51
|
+
............-.......:--+#************#..................-=*#%#**#%%%-...............
|
52
|
+
........................::*********--+:...................:-=*###***#%*.............
|
53
|
+
.../PPPP/PPPPPPPPP..........++#*****--#....................:-=+###*#%%%.............
|
54
|
+
..| PPPPPPPPPPPPPPP..............-'++-#......................:=#%#***%%+............
|
55
|
+
..| PPPP_______/PPPP..-.........................-.............:=#%#***%%+...........
|
56
|
+
..| PPPP......| PPPP..........................................:=#%#***%%+...........
|
57
|
+
..| PPPPPPPPPPPPPPP./YYYY..../YYYY........-...................:=#%#***%%+...........
|
58
|
+
..| PPPP/PPPPPPPP..| YYYY...| YYYY............................:=*%##**#%#-..........
|
59
|
+
..| PPPP_______/...| YYYY...| YYYY.............................=*%%##*#%%=..........
|
60
|
+
..| PPPP............\ YYYYYYYYYYYY.....ooo....................=+%%%#**%%+...........
|
61
|
+
..| PPPP.............\ YYYYY_/YYYY.../OOOOO....-.............:=+###**#%#:...........
|
62
|
+
../____/..............\___/.| YYYY...| OOO...................=+#%#**#%#:............
|
63
|
+
...................../YYYY..| YYYY....\__/.................:=+***++++**-............
|
64
|
+
.............-......| YYYYY/ YYYYY...-..............................................
|
65
|
+
.....................\ YYYYYYYYYY...................................................
|
66
|
+
.......-..............\________/....................................................
|
67
|
+
....................................................................................
|
68
|
+
....................................................................................
|
69
|
+
```
|
70
|
+
|
71
|
+
Thank you so much for downloading and using SmartPyLogger!
|
72
|
+
Developed by Niklavs Visockis, Ludvig Bergström and Jonas Lorenz -
|
73
|
+
in June of 2025 at the Couchbase x AWS x Cillers Hackathon.
|
74
|
+
Special thanks goes out to the Couchbase team and AWS for sponsoring this project.
|
75
|
+
|
76
|
+
### FastAPI middleware for comprehensive request/response logging.
|
77
|
+
|
78
|
+
## Installation
|
79
|
+
|
80
|
+
```bash
|
81
|
+
pip install -e .
|
82
|
+
```
|
83
|
+
|
84
|
+
## Usage
|
85
|
+
|
86
|
+
```python
|
87
|
+
from fastapi import FastAPI
|
88
|
+
from smartpylogger import LoggingMiddleware
|
89
|
+
|
90
|
+
app = FastAPI()
|
91
|
+
|
92
|
+
# Add your middleware
|
93
|
+
app.add_middleware(
|
94
|
+
LoggingMiddleware,
|
95
|
+
api_key="YOUR_API_KEY",
|
96
|
+
allowed_origins=["IP", ...]
|
97
|
+
)
|
98
|
+
```
|
99
|
+
|
100
|
+
## What it logs:
|
101
|
+
|
102
|
+
- **Request details**: Nothing yet
|
@@ -0,0 +1,60 @@
|
|
1
|
+
[build-system]
|
2
|
+
requires = ["setuptools>=61.0"]
|
3
|
+
build-backend = "setuptools.build_meta"
|
4
|
+
|
5
|
+
[project]
|
6
|
+
name = "smartpylogger"
|
7
|
+
version = "0.1.0"
|
8
|
+
description = "Lightweight structured logger for Py projects with AI analysis!"
|
9
|
+
readme = "smartpylogger/README.md"
|
10
|
+
requires-python = ">=3.7"
|
11
|
+
license = {text = "MIT"}
|
12
|
+
authors = [
|
13
|
+
{name = "Your Name", email = "your@email.com"}
|
14
|
+
]
|
15
|
+
classifiers = [
|
16
|
+
"Programming Language :: Python :: 3",
|
17
|
+
"License :: OSI Approved :: MIT License",
|
18
|
+
"Operating System :: OS Independent"
|
19
|
+
]
|
20
|
+
|
21
|
+
dependencies = [
|
22
|
+
"colorama",
|
23
|
+
"httpx",
|
24
|
+
"fastapi",
|
25
|
+
"starlette"
|
26
|
+
]
|
27
|
+
|
28
|
+
[tool.setuptools]
|
29
|
+
packages = ["smartpylogger"]
|
30
|
+
|
31
|
+
[project.urls]
|
32
|
+
Homepage = "https://github.com/yourusername/smartpylogger"
|
33
|
+
|
34
|
+
[project.optional-dependencies]
|
35
|
+
dev = [
|
36
|
+
"pytest>=6.0",
|
37
|
+
"pytest-cov>=2.0",
|
38
|
+
"black>=21.0",
|
39
|
+
"flake8>=3.8",
|
40
|
+
"mypy>=0.800",
|
41
|
+
]
|
42
|
+
|
43
|
+
[project.scripts]
|
44
|
+
couchbase-middleware = "couchbase_middleware.cli:main"
|
45
|
+
|
46
|
+
[tool.black]
|
47
|
+
line-length = 88
|
48
|
+
target-version = ['py38']
|
49
|
+
|
50
|
+
[tool.mypy]
|
51
|
+
python_version = "3.8"
|
52
|
+
warn_return_any = true
|
53
|
+
warn_unused_configs = true
|
54
|
+
disallow_untyped_defs = true
|
55
|
+
|
56
|
+
[tool.pytest.ini_options]
|
57
|
+
testpaths = ["tests"]
|
58
|
+
python_files = ["test_*.py"]
|
59
|
+
python_classes = ["Test*"]
|
60
|
+
python_functions = ["test_*"]
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from setuptools import setup, find_packages
|
2
|
+
|
3
|
+
with open("smartpylogger/README.md", "r", encoding="utf-8") as f:
|
4
|
+
long_description = f.read()
|
5
|
+
setup(
|
6
|
+
name="smartpylogger",
|
7
|
+
version="0.1.0",
|
8
|
+
description="Lightweight structured logger for Py projects with AI analysis!",
|
9
|
+
packages=find_packages(),
|
10
|
+
# install_requires=[], # or just remove this line entirely
|
11
|
+
long_description=long_description,
|
12
|
+
long_description_content_type="text/markdown",
|
13
|
+
classifiers=[
|
14
|
+
"Programming Language :: Python :: 3",
|
15
|
+
"License :: OSI Approved :: MIT License", # or whichever license
|
16
|
+
"Operating System :: OS Independent",
|
17
|
+
],
|
18
|
+
python_requires='>=3.7',
|
19
|
+
)
|
20
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# SmartPyLogger
|
2
|
+
|
3
|
+
```text
|
4
|
+
....................................................................................
|
5
|
+
....................................................................................
|
6
|
+
..........................................-++=..:...................................
|
7
|
+
.............-.........................=**#######+:.........-.......................
|
8
|
+
.............................-.......:=#***##**##*=.....................-...........
|
9
|
+
......-.............................:+******#**##*#=................................
|
10
|
+
.............-...................:=*********####%##*:...............................
|
11
|
+
..............................:+************#*####=..................-..............
|
12
|
+
...-*+=:.................-=****##*****####*######%+:................................
|
13
|
+
...-*#*#**##*********##**#*******#######**#%%#***#%+................................
|
14
|
+
...:=*****#*##*######****#*****#####*#*++--*%##***##=.........-.....................
|
15
|
+
....-******###*##**###**###*#*###****+-...:-*%%#**##%-..................-...........
|
16
|
+
....:=******###*###****#**#**#####**+:......-*%##***%#-.............................
|
17
|
+
.....:+*#***##***###******##**###**+:.......:-*%##**#%*-............................
|
18
|
+
......-*#****######***#**#*##*##**+:.........:-*%#*+*#%#:...........................
|
19
|
+
......:-*******#**********#***##**-...........-:=*##***#%*:.........................
|
20
|
+
........:-+#***************##**##**-............:=*##****#%*-.......................
|
21
|
+
............:=*#************+*###*#+.............-=*###**#%@%#-.....................
|
22
|
+
.............:-+#*************+*###*-......-......:-+*#%####%%*:....................
|
23
|
+
.......-.......:-=*#***************+.................-=*#%%%*###%=..................
|
24
|
+
..................:--##*************-.................:-+*%#****#%*.................
|
25
|
+
............-.......:--+#************#..................-=*#%#**#%%%-...............
|
26
|
+
........................::*********--+:...................:-=*###***#%*.............
|
27
|
+
.../PPPP/PPPPPPPPP..........++#*****--#....................:-=+###*#%%%.............
|
28
|
+
..| PPPPPPPPPPPPPPP..............-'++-#......................:=#%#***%%+............
|
29
|
+
..| PPPP_______/PPPP..-.........................-.............:=#%#***%%+...........
|
30
|
+
..| PPPP......| PPPP..........................................:=#%#***%%+...........
|
31
|
+
..| PPPPPPPPPPPPPPP./YYYY..../YYYY........-...................:=#%#***%%+...........
|
32
|
+
..| PPPP/PPPPPPPP..| YYYY...| YYYY............................:=*%##**#%#-..........
|
33
|
+
..| PPPP_______/...| YYYY...| YYYY.............................=*%%##*#%%=..........
|
34
|
+
..| PPPP............\ YYYYYYYYYYYY.....ooo....................=+%%%#**%%+...........
|
35
|
+
..| PPPP.............\ YYYYY_/YYYY.../OOOOO....-.............:=+###**#%#:...........
|
36
|
+
../____/..............\___/.| YYYY...| OOO...................=+#%#**#%#:............
|
37
|
+
...................../YYYY..| YYYY....\__/.................:=+***++++**-............
|
38
|
+
.............-......| YYYYY/ YYYYY...-..............................................
|
39
|
+
.....................\ YYYYYYYYYY...................................................
|
40
|
+
.......-..............\________/....................................................
|
41
|
+
....................................................................................
|
42
|
+
....................................................................................
|
43
|
+
```
|
44
|
+
|
45
|
+
Thank you so much for downloading and using SmartPyLogger!
|
46
|
+
Developed by Niklavs Visockis, Ludvig Bergström and Jonas Lorenz -
|
47
|
+
in June of 2025 at the Couchbase x AWS x Cillers Hackathon.
|
48
|
+
Special thanks goes out to the Couchbase team and AWS for sponsoring this project.
|
49
|
+
|
50
|
+
### FastAPI middleware for comprehensive request/response logging.
|
51
|
+
|
52
|
+
## Installation
|
53
|
+
|
54
|
+
```bash
|
55
|
+
pip install -e .
|
56
|
+
```
|
57
|
+
|
58
|
+
## Usage
|
59
|
+
|
60
|
+
```python
|
61
|
+
from fastapi import FastAPI
|
62
|
+
from smartpylogger import LoggingMiddleware
|
63
|
+
|
64
|
+
app = FastAPI()
|
65
|
+
|
66
|
+
# Add your middleware
|
67
|
+
app.add_middleware(
|
68
|
+
LoggingMiddleware,
|
69
|
+
api_key="YOUR_API_KEY",
|
70
|
+
allowed_origins=["IP", ...]
|
71
|
+
)
|
72
|
+
```
|
73
|
+
|
74
|
+
## What it logs:
|
75
|
+
|
76
|
+
- **Request details**: Nothing yet
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# backend/smartpylogger/__init__.py
|
2
|
+
|
3
|
+
# Version (optional, but good practice)
|
4
|
+
__version__ = "0.1.0"
|
5
|
+
|
6
|
+
# Import and expose your main classes/functions
|
7
|
+
from .logger import LoggingMiddleware
|
8
|
+
from .utils import * # if you want to expose all utils
|
9
|
+
|
10
|
+
# Define what is available for import *
|
11
|
+
__all__ = ["LoggingMiddleware"]
|
@@ -0,0 +1,283 @@
|
|
1
|
+
"""
|
2
|
+
FastAPI middleware for request/response logging
|
3
|
+
|
4
|
+
This logger ONLY sends POST request data to api_server.py
|
5
|
+
api_server.py handles all database operations and dashboard communication
|
6
|
+
"""
|
7
|
+
|
8
|
+
import subprocess
|
9
|
+
import os
|
10
|
+
import time
|
11
|
+
import platform
|
12
|
+
from datetime import datetime
|
13
|
+
from random import randint
|
14
|
+
|
15
|
+
# Colors for console output
|
16
|
+
from colorama import Fore, Style
|
17
|
+
|
18
|
+
import json
|
19
|
+
# import requests
|
20
|
+
import httpx # replace requests with httpx for async support & speed
|
21
|
+
from typing import Dict, Any, Optional
|
22
|
+
|
23
|
+
from fastapi import Request, Response, HTTPException
|
24
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
25
|
+
from starlette.middleware.base import RequestResponseEndpoint
|
26
|
+
|
27
|
+
|
28
|
+
API_URL="http://localhost:8000" ### CHANGE TO EXTERNAL IP LATER
|
29
|
+
|
30
|
+
class ClientError(Exception):
|
31
|
+
pass
|
32
|
+
|
33
|
+
class LoggingMiddleware(BaseHTTPMiddleware):
|
34
|
+
"""FastAPI middleware - intercepts requests and sends to api_server.py"""
|
35
|
+
|
36
|
+
def __init__(self, app, api_key: str = "", allowed_origins: Optional[list[str]] = None, api_limit_daily: int = 1000, censored_words: Optional[list[str]] = None, banned_words_path: Optional[str] = None):
|
37
|
+
"""Initialize middleware with API credentials"""
|
38
|
+
|
39
|
+
print(f"""{Fore.GREEN}
|
40
|
+
..............................................................................
|
41
|
+
..........................................-++=..:.............................
|
42
|
+
.............-.........................=**#######+:.........-.................
|
43
|
+
.............................-.......:=#***##**##*=.....................-.....
|
44
|
+
......-.............................:+******#**##*#=..........................
|
45
|
+
.............-...................:=*********####%##*:.........................
|
46
|
+
..............................:+************#*####=..................-........
|
47
|
+
...-*+=:.................-=****##*****####*######%+:..........................
|
48
|
+
...-*#*#**##*********##**#*******#######**#%%#***#%+..........................
|
49
|
+
...:=*****#*##*######****#*****#####*#*++--*%##***##=.........-...............
|
50
|
+
....-******###*##**###**###*#*###****+-...:-*%%#**##%-..................-.....
|
51
|
+
....:=******###*###****#**#**#####**+:......-*%##***%#-.......................
|
52
|
+
.....:+*#***##***###******##**###**+:.......:-*%##**#%*-......................
|
53
|
+
......-*#****######***#**#*##*##**+:.........:-*%#*+*#%#:.....................
|
54
|
+
......:-*******#**********#***##**-...........-:=*##***#%*:...................
|
55
|
+
........:-+#***************##**##**-............:=*##****#%*-.................
|
56
|
+
............:=*#************+*###*#+.............-=*###**#%@%#-...............
|
57
|
+
.............:-+#*************+*###*-......-......:-+*#%####%%*:..............
|
58
|
+
.......-.......:-=*#***************+.................-=*#%%%*###%=............
|
59
|
+
..................:--##*************-.................:-+*%#****#%*...........
|
60
|
+
............-.......:--+#************#..................-=*#%#**#%%%-.........
|
61
|
+
........................::*********--+:...................:-=*###***#%*.......
|
62
|
+
.../PPPP/PPPPPPPPP..........++#*****--#....................:-=+###*#%%%.......
|
63
|
+
..| PPPPPPPPPPPPPPP..............-'++-#......................:=#%#***%%+......
|
64
|
+
..| PPPP_______/PPPP..-.........................-.............:=#%#***%%+.....
|
65
|
+
..| PPPP......| PPPP..........................................:=#%#***%%+.....
|
66
|
+
..| PPPPPPPPPPPPPPP./YYYY..../YYYY........-...................:=#%#***%%+.....
|
67
|
+
..| PPPP/PPPPPPPP..| YYYY...| YYYY............................:=*%##**#%#-....
|
68
|
+
..| PPPP_______/...| YYYY...| YYYY.............................=*%%##*#%%=....
|
69
|
+
..| PPPP............( YYYYYYYYYYYY.....ooo....................=+%%%#**%%+.....
|
70
|
+
..| PPPP.............( YYYYY_/YYYY.../OOOOO....-.............:=+###**#%#:.....
|
71
|
+
../____/..............(__/..| YYYY...| OOO....................=+#%#**#%#:.....
|
72
|
+
...................../YYYY..| YYYY....(__/...................:=+***++++**-....
|
73
|
+
.............-......| YYYYY/ YYYYY...-........................................
|
74
|
+
.....................( YYYYYYYYYY.............................................
|
75
|
+
.......-..............(________/..............................................
|
76
|
+
..............................................................................
|
77
|
+
..............................................................................
|
78
|
+
{Style.RESET_ALL}
|
79
|
+
Thank you so much for downloading and using SmartPyLogger!
|
80
|
+
Developed by Niklavs Visockis, Ludvig Bergström and Jonas Lorenz -
|
81
|
+
in June of 2025 at the Couchbase x AWS x Cillers Hackathon.
|
82
|
+
Special thanks goes out to the Couchbase team and AWS for sponsoring this project.
|
83
|
+
|
84
|
+
""")
|
85
|
+
|
86
|
+
|
87
|
+
# Timing start:
|
88
|
+
start = time.perf_counter()
|
89
|
+
|
90
|
+
print(f"{Fore.MAGENTA}[STATUS]{Style.RESET_ALL}: Initializing LoggingMiddleware...")
|
91
|
+
|
92
|
+
# Basic configuration
|
93
|
+
self.api_key = api_key
|
94
|
+
self.api_url = API_URL
|
95
|
+
self.allowed_origins = allowed_origins or []
|
96
|
+
self.api_limit_daily = api_limit_daily # Limit for API requests, default to 1000
|
97
|
+
self.censored_words = censored_words or []
|
98
|
+
self.banned_words_path = banned_words_path
|
99
|
+
|
100
|
+
# Validator paths
|
101
|
+
self.content_validator_path = ""
|
102
|
+
self.ip_validator_path = ""
|
103
|
+
|
104
|
+
super().__init__(app) # Inhereting from BaseHTTPMiddleware
|
105
|
+
|
106
|
+
self.app_name = getattr(app, "title", None)
|
107
|
+
|
108
|
+
### ---- VALIDATE USER ---- ###
|
109
|
+
|
110
|
+
### Check if API key is provided and return session ID if it is, otherwise raise error
|
111
|
+
try:
|
112
|
+
response = httpx.post(
|
113
|
+
self.api_url + "/api/auth/validate",
|
114
|
+
json={"api_key": self.api_key, "appSessionName": self.app_name},
|
115
|
+
timeout=10.0 # 10 second timeout
|
116
|
+
)
|
117
|
+
|
118
|
+
self.auth = response.json()["app_session_id"] ### Get session ID to see if API key is valid
|
119
|
+
|
120
|
+
# print(self.auth)
|
121
|
+
|
122
|
+
if self.auth != "0":
|
123
|
+
print(f"{Fore.MAGENTA}[STATUS]{Style.RESET_ALL}: API key loaded successfully. Session initialization...")
|
124
|
+
print(f"{Fore.MAGENTA}[STATUS]{Style.RESET_ALL}: Session init success! Session ID: "
|
125
|
+
f"{Fore.BLUE}{str(self.auth)}")
|
126
|
+
|
127
|
+
elif self.auth == "0":
|
128
|
+
print(f"{Fore.RED}[ERROR]: Invalid API key")
|
129
|
+
raise HTTPException(
|
130
|
+
status_code=401,
|
131
|
+
detail="Invalid API key"
|
132
|
+
)
|
133
|
+
|
134
|
+
else:
|
135
|
+
print(f"{Fore.RED}[ERROR]: Unknown error during API key validation or session initialization")
|
136
|
+
raise HTTPException(
|
137
|
+
status_code=500,
|
138
|
+
detail="Unknown error during API key validation"
|
139
|
+
)
|
140
|
+
|
141
|
+
except httpx.ConnectError:
|
142
|
+
print(f"{Fore.RED}[ERROR]: Cannot connect to validation server at {self.api_url}")
|
143
|
+
raise HTTPException(
|
144
|
+
status_code=503,
|
145
|
+
detail="Validation server unavailable"
|
146
|
+
)
|
147
|
+
except httpx.TimeoutException:
|
148
|
+
print(f"{Fore.RED}[ERROR]: Timeout connecting to validation server")
|
149
|
+
raise HTTPException(
|
150
|
+
status_code=503,
|
151
|
+
detail="Validation server timeout"
|
152
|
+
)
|
153
|
+
except Exception as e:
|
154
|
+
print(f"{Fore.RED}[ERROR]: Unexpected error during validation: {e}")
|
155
|
+
raise HTTPException(
|
156
|
+
status_code=500,
|
157
|
+
detail="Validation error"
|
158
|
+
)
|
159
|
+
|
160
|
+
### ---- CHECK USER MACHINE AND PICK EXEC. PATH ---- ###
|
161
|
+
|
162
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
163
|
+
system = platform.system().lower()
|
164
|
+
try:
|
165
|
+
if system == "windows": # obviously
|
166
|
+
self.ip_validator_path = os.path.join(current_dir, "validators", "ip_validator_windows.exe")
|
167
|
+
self.content_validator_path = os.path.join(current_dir, "validators", "contains_windows.exe")
|
168
|
+
|
169
|
+
elif system == "darwin": # macOS
|
170
|
+
self.ip_validator_path = os.path.join(current_dir, "validators", "ip_validator_mac")
|
171
|
+
self.content_validator_path = os.path.join(current_dir, "validators", "contains_mac")
|
172
|
+
|
173
|
+
else: # linux
|
174
|
+
self.ip_validator_path = os.path.join(current_dir, "validators", "ip_validator_linux")
|
175
|
+
self.content_validator_path = os.path.join(current_dir, "validators", "contains_linux")
|
176
|
+
|
177
|
+
except Exception as e:
|
178
|
+
print(f"{Fore.RED}[ERROR]: Error setting validator paths: {e}")
|
179
|
+
raise HTTPException(
|
180
|
+
status_code=500,
|
181
|
+
detail="Error setting validator paths"
|
182
|
+
)
|
183
|
+
|
184
|
+
print(f"{Fore.MAGENTA}[STATUS]{Style.RESET_ALL}: You're all set up! Using {system} validators.")
|
185
|
+
|
186
|
+
# Timing end:
|
187
|
+
end = time.perf_counter()
|
188
|
+
|
189
|
+
print(f"{Fore.MAGENTA}[STATUS]{Style.RESET_ALL}: LoggingMiddleware initialized in {1000 * (end - start):.3f} ms.")
|
190
|
+
|
191
|
+
|
192
|
+
### ---- MAIN ASYNC DISPATCH METHOD ---- ###
|
193
|
+
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: # type: ignore
|
194
|
+
"""Intercept request/response, sensor for bad words quickly and send off to api_server.py"""
|
195
|
+
# Read the request body & load to JSON to send off to API
|
196
|
+
body = await request.body()
|
197
|
+
sender_ip = request.client.host # type: ignore
|
198
|
+
request_method = request.method
|
199
|
+
timestamp = datetime.now().strftime("%Y/%m/%d/%H:%M:%S")
|
200
|
+
|
201
|
+
try:
|
202
|
+
body_dict = json.loads(body)
|
203
|
+
except Exception:
|
204
|
+
body_dict = {}
|
205
|
+
|
206
|
+
# Wrap it for the /api/schemas endpoint
|
207
|
+
payload = {"api_key":self.api_key,
|
208
|
+
"session_id":self.auth,
|
209
|
+
"app_name": self.app_name,
|
210
|
+
"request_method": request_method,
|
211
|
+
"request_data": body_dict,
|
212
|
+
"allowed_origins": self.allowed_origins,
|
213
|
+
"sender_ip": sender_ip,
|
214
|
+
"timestamp": timestamp,
|
215
|
+
"flag": 0}
|
216
|
+
|
217
|
+
# 1. IP VALIDATION (fatal, but log first)
|
218
|
+
ip_validator_path = self.ip_validator_path
|
219
|
+
payload_json = json.dumps(payload)
|
220
|
+
result = subprocess.run(
|
221
|
+
[ip_validator_path, payload_json],
|
222
|
+
capture_output=True,
|
223
|
+
text=True,
|
224
|
+
timeout=5
|
225
|
+
)
|
226
|
+
|
227
|
+
# print("Go IP validator output:", repr(result.stdout))
|
228
|
+
if result.stdout:
|
229
|
+
try:
|
230
|
+
validated_payload = json.loads(result.stdout)
|
231
|
+
print("Go validator returned valid JSON:", validated_payload)
|
232
|
+
except json.JSONDecodeError:
|
233
|
+
print("Go validator did not return valid JSON:", result.stdout)
|
234
|
+
validated_payload = payload # fallback to original here
|
235
|
+
else:
|
236
|
+
print("Go validator returned no output!")
|
237
|
+
validated_payload = payload # fallback to original also
|
238
|
+
|
239
|
+
|
240
|
+
# 4. If IP was blocked, now raise the HTTP error COPY OF CORS BROTHA
|
241
|
+
if result.returncode != 0:
|
242
|
+
|
243
|
+
try:
|
244
|
+
httpx.post(
|
245
|
+
self.api_url + "/api/schemas",
|
246
|
+
json=validated_payload,
|
247
|
+
timeout=5.0
|
248
|
+
)
|
249
|
+
except Exception as e:
|
250
|
+
print(f"{Fore.YELLOW}[WARNING]: Could not send blocked request to API: {e}")
|
251
|
+
|
252
|
+
raise HTTPException(
|
253
|
+
status_code=403,
|
254
|
+
detail=f"Request blocked: Unauthorized IP address. {result.stdout.strip()}"
|
255
|
+
)
|
256
|
+
|
257
|
+
|
258
|
+
# 2. CONTENT VALIDATION (non-fatal, but log)
|
259
|
+
content_validator_path = self.content_validator_path
|
260
|
+
banned_words_path = self.banned_words_path or "bad_words.txt" # fallback if not set
|
261
|
+
result = subprocess.run(
|
262
|
+
[content_validator_path, payload_json, banned_words_path],
|
263
|
+
capture_output=True,
|
264
|
+
text=True,
|
265
|
+
timeout=5
|
266
|
+
)
|
267
|
+
# print("Go validator output:", repr(result.stdout))
|
268
|
+
|
269
|
+
try:
|
270
|
+
httpx.post(
|
271
|
+
self.api_url + "/api/schemas",
|
272
|
+
json=validated_payload,
|
273
|
+
timeout=5.0
|
274
|
+
)
|
275
|
+
except Exception as e:
|
276
|
+
print(f"{Fore.YELLOW}[WARNING]: Could not send request to API: {e}")
|
277
|
+
|
278
|
+
# Decriment the API limit
|
279
|
+
self.api_limit_daily -= 1
|
280
|
+
|
281
|
+
# 5. Otherwise, continue as normal
|
282
|
+
response = await call_next(request)
|
283
|
+
return response
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""
|
2
|
+
Utility functions for the smartpylogger package
|
3
|
+
|
4
|
+
Unsure of what this will actually contain
|
5
|
+
"""
|
6
|
+
|
7
|
+
import json
|
8
|
+
import hashlib
|
9
|
+
import time
|
10
|
+
from typing import Dict, Any, Optional
|
11
|
+
|
12
|
+
|
13
|
+
def format_schema(data: Dict[str, Any]) -> Dict[str, Any]:
|
14
|
+
"""Formatting and structure schema data for API submission"""
|
15
|
+
return {
|
16
|
+
"schema_version": "1.0",
|
17
|
+
"timestamp": time.time(),
|
18
|
+
"data": data,
|
19
|
+
}
|
20
|
+
|
21
|
+
def format_timestamp(timestamp: float) -> str:
|
22
|
+
"""Format timestamp for logging"""
|
23
|
+
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
|
24
|
+
|
25
|
+
def create_request_id() -> str:
|
26
|
+
"""Generate unique request ID"""
|
27
|
+
return f"req_{int(time.time() * 1000)}_{hash(time.time()) % 10000}"
|
28
|
+
|
@@ -0,0 +1,102 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: smartpylogger
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Lightweight structured logger for Py projects with AI analysis!
|
5
|
+
Author-email: Your Name <your@email.com>
|
6
|
+
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/smartpylogger
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Operating System :: OS Independent
|
11
|
+
Requires-Python: >=3.7
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
License-File: LICENSE
|
14
|
+
Requires-Dist: colorama
|
15
|
+
Requires-Dist: httpx
|
16
|
+
Requires-Dist: fastapi
|
17
|
+
Requires-Dist: starlette
|
18
|
+
Provides-Extra: dev
|
19
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
20
|
+
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
21
|
+
Requires-Dist: black>=21.0; extra == "dev"
|
22
|
+
Requires-Dist: flake8>=3.8; extra == "dev"
|
23
|
+
Requires-Dist: mypy>=0.800; extra == "dev"
|
24
|
+
Dynamic: license-file
|
25
|
+
Dynamic: requires-python
|
26
|
+
|
27
|
+
# SmartPyLogger
|
28
|
+
|
29
|
+
```text
|
30
|
+
....................................................................................
|
31
|
+
....................................................................................
|
32
|
+
..........................................-++=..:...................................
|
33
|
+
.............-.........................=**#######+:.........-.......................
|
34
|
+
.............................-.......:=#***##**##*=.....................-...........
|
35
|
+
......-.............................:+******#**##*#=................................
|
36
|
+
.............-...................:=*********####%##*:...............................
|
37
|
+
..............................:+************#*####=..................-..............
|
38
|
+
...-*+=:.................-=****##*****####*######%+:................................
|
39
|
+
...-*#*#**##*********##**#*******#######**#%%#***#%+................................
|
40
|
+
...:=*****#*##*######****#*****#####*#*++--*%##***##=.........-.....................
|
41
|
+
....-******###*##**###**###*#*###****+-...:-*%%#**##%-..................-...........
|
42
|
+
....:=******###*###****#**#**#####**+:......-*%##***%#-.............................
|
43
|
+
.....:+*#***##***###******##**###**+:.......:-*%##**#%*-............................
|
44
|
+
......-*#****######***#**#*##*##**+:.........:-*%#*+*#%#:...........................
|
45
|
+
......:-*******#**********#***##**-...........-:=*##***#%*:.........................
|
46
|
+
........:-+#***************##**##**-............:=*##****#%*-.......................
|
47
|
+
............:=*#************+*###*#+.............-=*###**#%@%#-.....................
|
48
|
+
.............:-+#*************+*###*-......-......:-+*#%####%%*:....................
|
49
|
+
.......-.......:-=*#***************+.................-=*#%%%*###%=..................
|
50
|
+
..................:--##*************-.................:-+*%#****#%*.................
|
51
|
+
............-.......:--+#************#..................-=*#%#**#%%%-...............
|
52
|
+
........................::*********--+:...................:-=*###***#%*.............
|
53
|
+
.../PPPP/PPPPPPPPP..........++#*****--#....................:-=+###*#%%%.............
|
54
|
+
..| PPPPPPPPPPPPPPP..............-'++-#......................:=#%#***%%+............
|
55
|
+
..| PPPP_______/PPPP..-.........................-.............:=#%#***%%+...........
|
56
|
+
..| PPPP......| PPPP..........................................:=#%#***%%+...........
|
57
|
+
..| PPPPPPPPPPPPPPP./YYYY..../YYYY........-...................:=#%#***%%+...........
|
58
|
+
..| PPPP/PPPPPPPP..| YYYY...| YYYY............................:=*%##**#%#-..........
|
59
|
+
..| PPPP_______/...| YYYY...| YYYY.............................=*%%##*#%%=..........
|
60
|
+
..| PPPP............\ YYYYYYYYYYYY.....ooo....................=+%%%#**%%+...........
|
61
|
+
..| PPPP.............\ YYYYY_/YYYY.../OOOOO....-.............:=+###**#%#:...........
|
62
|
+
../____/..............\___/.| YYYY...| OOO...................=+#%#**#%#:............
|
63
|
+
...................../YYYY..| YYYY....\__/.................:=+***++++**-............
|
64
|
+
.............-......| YYYYY/ YYYYY...-..............................................
|
65
|
+
.....................\ YYYYYYYYYY...................................................
|
66
|
+
.......-..............\________/....................................................
|
67
|
+
....................................................................................
|
68
|
+
....................................................................................
|
69
|
+
```
|
70
|
+
|
71
|
+
Thank you so much for downloading and using SmartPyLogger!
|
72
|
+
Developed by Niklavs Visockis, Ludvig Bergström and Jonas Lorenz -
|
73
|
+
in June of 2025 at the Couchbase x AWS x Cillers Hackathon.
|
74
|
+
Special thanks goes out to the Couchbase team and AWS for sponsoring this project.
|
75
|
+
|
76
|
+
### FastAPI middleware for comprehensive request/response logging.
|
77
|
+
|
78
|
+
## Installation
|
79
|
+
|
80
|
+
```bash
|
81
|
+
pip install -e .
|
82
|
+
```
|
83
|
+
|
84
|
+
## Usage
|
85
|
+
|
86
|
+
```python
|
87
|
+
from fastapi import FastAPI
|
88
|
+
from smartpylogger import LoggingMiddleware
|
89
|
+
|
90
|
+
app = FastAPI()
|
91
|
+
|
92
|
+
# Add your middleware
|
93
|
+
app.add_middleware(
|
94
|
+
LoggingMiddleware,
|
95
|
+
api_key="YOUR_API_KEY",
|
96
|
+
allowed_origins=["IP", ...]
|
97
|
+
)
|
98
|
+
```
|
99
|
+
|
100
|
+
## What it logs:
|
101
|
+
|
102
|
+
- **Request details**: Nothing yet
|
@@ -0,0 +1,15 @@
|
|
1
|
+
LICENSE
|
2
|
+
pyproject.toml
|
3
|
+
setup.py
|
4
|
+
smartpylogger/README.md
|
5
|
+
smartpylogger/__init__.py
|
6
|
+
smartpylogger/logger.py
|
7
|
+
smartpylogger/utils.py
|
8
|
+
smartpylogger.egg-info/PKG-INFO
|
9
|
+
smartpylogger.egg-info/SOURCES.txt
|
10
|
+
smartpylogger.egg-info/dependency_links.txt
|
11
|
+
smartpylogger.egg-info/entry_points.txt
|
12
|
+
smartpylogger.egg-info/requires.txt
|
13
|
+
smartpylogger.egg-info/top_level.txt
|
14
|
+
tests/test_catch.py
|
15
|
+
tests/test_send.py
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
smartpylogger
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from smartpylogger import LoggingMiddleware
|
2
|
+
from fastapi import FastAPI, Request
|
3
|
+
import requests
|
4
|
+
import os
|
5
|
+
import dotenv
|
6
|
+
|
7
|
+
dotenv.load_dotenv()
|
8
|
+
|
9
|
+
app = FastAPI()
|
10
|
+
print(os.getenv("API_KEY"))
|
11
|
+
|
12
|
+
# Get the absolute path to the banned words file
|
13
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
14
|
+
banned_words_path = os.path.join(current_dir, "my_banned_words.txt")
|
15
|
+
|
16
|
+
app.add_middleware(
|
17
|
+
LoggingMiddleware,
|
18
|
+
api_key=os.getenv("API_KEY"), # This is where the middleware would forward, but the app itself runs on 8500
|
19
|
+
allowed_origins=["127.0.0.1"],
|
20
|
+
api_limit_daily=1000,
|
21
|
+
banned_words_path=banned_words_path
|
22
|
+
)
|
23
|
+
|
24
|
+
API_URL = "http://localhost:8500"
|
25
|
+
|
26
|
+
@app.post("/mock")
|
27
|
+
async def mock_post(request: Request):
|
28
|
+
data = await request.json()
|
29
|
+
return {"received": data}
|
30
|
+
|
31
|
+
if __name__ == "__main__":
|
32
|
+
import uvicorn
|
33
|
+
uvicorn.run(app, host="0.0.0.0", port=8500)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import requests
|
2
|
+
|
3
|
+
API_URL = "http://localhost:8500/mock"
|
4
|
+
|
5
|
+
def test_post():
|
6
|
+
payload = {"foo": "JS", "number": 123}
|
7
|
+
response = requests.post(API_URL, json=payload)
|
8
|
+
print("Status code:", response.status_code)
|
9
|
+
print("Response:", response.text)
|
10
|
+
|
11
|
+
if __name__ == "__main__":
|
12
|
+
test_post()
|