Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

logs: Set up file and console logs for main app and API #15

Merged
merged 10 commits into from
Sep 17, 2024
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<p align="center">
<a href="https://github.com/essteer/masquer/actions/workflows/test.yaml"><img src="https://github.com/essteer/masquer/actions/workflows/test.yaml/badge.svg"></a>
<a href="https://pypi.org/project/masquer/"><img src="https://img.shields.io/badge/PyPI-v1.2.2-3775A9.svg?style=flat&logo=PyPI&logoColor=white"></a>
<a href="https://pypi.org/project/masquer/"><img src="https://img.shields.io/badge/Python-3.9_|_3.10_|_3.11_|_3.12-3776AB.svg?style=flat&logo=Python&logoColor=white"></a>
<a href="https://pypi.org/project/masquer/"><img src="https://img.shields.io/badge/Python-3.9_~_3.12-3776AB.svg?style=flat&logo=Python&logoColor=white"></a>
<a href="https://snyk.io/test/github/essteer/masquer"><img src="https://snyk.io/test/github/essteer/masquer/badge.svg?name=Snyk&style=flat&logo=Snyk"></a>
</p>

Expand Down Expand Up @@ -353,4 +353,4 @@ From the `Choose a tag` dropdown enter the new version number in the format `v0.

Add any other necessary comments then click `Publish release`.

The `docker.yaml` GitHub workflow will then build a Docker image of the new version and push the build to [Docker Hub](https://hub.docker.com/r/essteer/masquer).
The `docker.yaml` GitHub workflow will then build a Docker image of the new version and push the build to [Docker Hub](https://hub.docker.com/r/essteer/masquer).
37 changes: 24 additions & 13 deletions src/api/main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from fastapi import FastAPI
from masquer.__about__ import __version__
from src.logging_config import setup_logging, get_logger
essteer marked this conversation as resolved.
Show resolved Hide resolved
from .routes import router


setup_logging(__name__)
logger = get_logger(__name__)

DESCRIPTION = """
Use `masquer` to obtain any combination of a random user-agent, referer or header data template, then use this with a library like [`requests`](https://github.com/psf/requests) to control the session data you send to other services.

Expand All @@ -13,24 +17,31 @@
A basic header template with common attributes — like [`"Upgrade-Insecure-Requests": "1"`](https://stackoverflow.com/questions/31950470/what-is-the-upgrade-insecure-requests-http-header/32003517#32003517) — is also provided and defaults to the most common referer and user-agent data from the above lists.
"""

SUMMARY = "A tool to generate random user-agent and referer data for HTTP requests."


def get_app() -> FastAPI:
"""
Create a FastAPI app with the specified attributes
"""
app = FastAPI(
title="Masquer API",
summary="A tool to generate random user-agent and referer data for HTTP requests.",
description=DESCRIPTION,
version=__version__,
license_info={
"name": "MIT License",
"url": "https://github.com/essteer/masquer/blob/main/LICENSE",
},
)
app.include_router(router)

return app
try:
app = FastAPI(
title="Masquer API",
summary=SUMMARY,
description=DESCRIPTION,
version=__version__,
license_info={
"name": "MIT License",
"url": "https://github.com/essteer/masquer/blob/main/LICENSE",
},
)
app.include_router(router)
logger.info("FastAPI app init OK")

return app

except Exception as e:
logger.error(f"FastAPI app init error: {e}")


app = get_app()
Expand Down
6 changes: 6 additions & 0 deletions src/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from masquer import masq
from src.logging_config import setup_logging, get_logger


setup_logging(__name__)
logger = get_logger(__name__)
router = APIRouter()


Expand All @@ -13,5 +16,8 @@ def get_masq(
rf: Union[bool, None] = False,
hd: Union[bool, None] = False,
):
logger.info(f"Request: [{ua=} {rf=} {hd=}]")
response = masq(ua, rf, hd)
logger.info(f"Response: [{response}]")

return JSONResponse(content=response)
43 changes: 43 additions & 0 deletions src/logging_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import logging
from logging.handlers import RotatingFileHandler
import os


def setup_logging(name):
"""
Configures logging for use in the main package
and the FastAPI application
"""
root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
logs_dir = os.path.join(root_dir, "logs")
if not os.path.exists(logs_dir):
os.makedirs(logs_dir)

log_filepath = os.path.join(logs_dir, "app.log")

file_handler = RotatingFileHandler(
log_filepath, maxBytes=10 * 1024 * 1024, backupCount=5, encoding="utf-8"
)
file_handler.setLevel(logging.INFO)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

formatter = logging.Formatter(
"{asctime} - {levelname} - {filename}:{lineno} - {message}",
style="{",
datefmt="%Y-%m-%d %H:%M:%S",
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
logger.addHandler(file_handler)
logger.addHandler(console_handler)

logger.info("Logging system init OK")


def get_logger(name):
return logging.getLogger(name)
19 changes: 17 additions & 2 deletions src/masquer/app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from src.logging_config import setup_logging, get_logger
from .utils.response import get_response
from .utils.validate import validate_args


setup_logging(__name__)
logger = get_logger(__name__)


def masq(ua: bool = True, rf: bool = False, hd: bool = False) -> dict:
"""
Compiles and returns header data via weighted random selection
Expand All @@ -19,9 +24,19 @@ def masq(ua: bool = True, rf: bool = False, hd: bool = False) -> dict:
useragent | referer | header data as requested
"""
valid_args = validate_args(ua, rf, hd)

if not valid_args:
return "Error: ua|rf|hd must be blank or boolean"
logger.warning(f"Invalid args: [{ua=} {rf=} {hd=}]")
return {"error": "ua|rf|hd must be blank or boolean"}

logger.debug(f"Valid args: [{ua=} {rf=} {hd=}]")

try:
response = get_response(ua, rf, hd)
logger.debug(f"Response: [{response}]")

response = get_response(ua, rf, hd)
except Exception as e:
logger.error(f"Error getting response: {e}")
return {"error": "Failed to retrieve response"}

return response
2 changes: 1 addition & 1 deletion tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_masq_with_boolean_args(self):

def test_masq_with_invalid_args(self):
"""Calling masq() with invalid args returns an error message"""
error_msg = "Error: ua|rf|hd must be blank or boolean"
error_msg = {"error": "ua|rf|hd must be blank or boolean"}
self.assertEqual(masq(0), error_msg)
self.assertEqual(masq(ua=0), error_msg)
self.assertEqual(masq(rf=0), error_msg)
Expand Down
18 changes: 11 additions & 7 deletions update.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
import requests
import sys
from bs4 import BeautifulSoup
from src.logging_config import setup_logging, get_logger


setup_logging(__name__)
logger = get_logger(__name__)

ROOT_DIR = os.path.abspath(os.path.dirname(__file__))
ASSETS_DIR = os.path.join(ROOT_DIR, "assets")
UTILS_DIR = os.path.join(ROOT_DIR, "src", "masquer", "utils")
Expand All @@ -22,11 +26,11 @@ def update_useragents() -> bool:
with open(os.path.join(ASSETS_DIR, "useragents.json"), "w") as f:
json.dump(json_string, f)

print(f"{sys.argv[0]}: fetched useragent data")
logger.info("Fetched useragent data")
return True

except Exception as e:
print(f"{sys.argv[0]}: error fetching useragent data {e}")
logger.error(f"Error fetching useragent data: {e}")
return False


Expand Down Expand Up @@ -67,11 +71,11 @@ def update_referers() -> bool:
with open(os.path.join(ASSETS_DIR, "referers.json"), "w") as f:
json.dump(output, f)

print(f"{sys.argv[0]}: fetched referer data")
logger.info("Fetched referer data")
return True

except Exception as e:
print(f"{sys.argv[0]}: error fetching referer data {e}")
logger.error(f"Error fetching referer data: {e}")
return False


Expand Down Expand Up @@ -126,15 +130,15 @@ def update_assets() -> bool:
f.write("USERAGENT_WEIGHTS = " + str(useragent_weights))
f.write("\n")

print(f"{sys.argv[0]}: saved useragent and referer JSON data to assets.py")
logger.info("Saved useragent and referer JSON data to assets.py")
return True

except FileNotFoundError:
print(f"{sys.argv[0]}: asset update error JSON assets not found")
logger.error("Asset update error: JSON assets not found")
return False

except Exception as e:
print(f"{sys.argv[0]}: asset update error {type(e)}: {e}")
logger.error(f"Asset update error: {type(e)}: {e}")
return False


Expand Down
24 changes: 17 additions & 7 deletions update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,29 @@ set -eu

this_script=$(basename "$0")
python_script="update.py"
log_filepath="logs/app.log"
venv_path=".venv/bin/activate"

function timestamp() {
date +"%Y-%m-%d %H:%M:%S"
}

if [[ ! -f "${log_filepath}" ]]; then
mkdir -p logs
touch logs/app.log
echo "$(timestamp) - INFO - ${this_script} - Log file created at ${log_filepath}" | tee -a "${log_filepath}"
fi

if [[ ! -f "${venv_path}" ]]; then
echo "${this_script}: error — virtual environment not found at ${venv_path}"
echo "$(timestamp) - ERROR - ${this_script} - Virtual environment not found at ${venv_path}" | tee -a "${log_filepath}"
echo " create a new virtual environment or update 'venv_path' in ${this_script}"
exit 1
fi

source "${venv_path}"

if [[ ! -f "${python_script}" ]]; then
echo "${this_script}: error — ${python_script} not found"
echo "$(timestamp) - ERROR - ${this_script} - ${python_script} not found" | tee -a "${log_filepath}"
deactivate
exit 1
fi
Expand All @@ -31,17 +42,16 @@ exit_code=$?

case ${exit_code} in
0)
echo "${this_script}: asset update successful"
echo "$(timestamp) - INFO - ${this_script} - Asset update OK" | tee -a "${log_filepath}"
;;
2)
echo "${this_script}: asset update failedcheck file paths in ${python_script}"
echo "$(timestamp) - ERROR - ${this_script} - Asset update failed: check file paths in ${python_script}" | tee -a "${log_filepath}"
;;
3)
echo "${this_script}: asset update failed — ensure BeautifulSoup4 is"
echo " installed and check source URLs in ${python_script} are live"
echo "$(timestamp) - ERROR - ${this_script} - Asset update failed: ensure BeautifulSoup4 is installed and check source URLs in ${python_script} are live" | tee -a "${log_filepath}"
;;
*)
echo "${this_script}: asset update failed with unexpected exit code ${exit_code}"
echo "$(timestamp) - ERROR - ${this_script} - Asset update failed with unexpected exit code ${exit_code}" | tee -a "${log_filepath}"
;;
esac

Expand Down