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

Use a log file instead of STDOUT for logging #181

Merged
merged 4 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ If your network imposes you to use a custom certicifate (error "SSL" at build),
### The website sends an error
Try to find error log
* In terminal, run `docker logs basegun-backend`
* If you cannot access terminal or don't see anything, go to `localhost:8000/logs` or `preprod.basegun.fr/logs` to see latest logs.
* If you cannot access terminal or don't see anything, go to `localhost:5000/logs` or `preprod.basegun.fr/logs` to see latest logs.

=> Error "missing model": Download model from the url specified in the [backend Dockerfile](https://github.com/datalab-mi/Basegun/blob/develop/backend/Dockerfile).

Expand Down
19 changes: 3 additions & 16 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
FROM python:3.9-slim-buster as base

WORKDIR /app

# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser

# certificates config
ARG CACERT_LOCATION
COPY ./cert/. /etc/ssl/certs/
Expand Down Expand Up @@ -45,13 +32,13 @@ RUN mkdir -p src/weights \
# launch website
FROM base as dev
RUN pip --default-timeout=300 install --no-cache-dir -r requirements/dev.txt
CMD ["uvicorn", "src.main:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
CMD ["uvicorn", "src.main:app", "--reload", "--host", "0.0.0.0", "--port", "5000"]

FROM base as test
RUN pip install -r requirements/dev.txt && pip install requests && rm -r /root/.cache
COPY tests/ tests/
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "5000"]

FROM base as prod
RUN pip install --no-cache-dir -r requirements/prod.txt
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "5000"]
4 changes: 2 additions & 2 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ If you are in a network blocked with proxy, remember to add arg `--build_arg htt

### Without Docker
```bash
uvicorn src.main:app --reload --host 0.0.0.0 --port 8000
uvicorn src.main:app --reload --host 0.0.0.0 --port 5000
```

### With Docker
```bash
docker run --rm -d -p 8000:8000 --name basegun_back -e VERSION=1.2 basegun-back:dev
docker run --rm -d -p 5000:5000 --name basegun_back -e VERSION=1.2 basegun-back:dev
```
Remember afterwards to stop container `docker stop basegun_back`

Expand Down
55 changes: 47 additions & 8 deletions backend/src/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

import os
import sys
import logging
from logging.handlers import TimedRotatingFileHandler
from datetime import datetime
Expand Down Expand Up @@ -46,25 +45,41 @@ def init_variable(var_name: str, path: str) -> str:
return VAR


def setup_logs() -> logging.Logger:
"""Setup environment for logs
def setup_logs(log_dir: str) -> logging.Logger:
"""Setups environment for logs

Args:
log_dir (str): folder for log storage
logging.Logger: logger object
"""
print(">>> Reload logs config")
# clear previous logs
for f in os.listdir(log_dir):
os.remove(os.path.join(log_dir, f))
# configure new logs
formatter = GelfFormatter()
logger = logging.getLogger("Basegun")
handler = logging.StreamHandler(sys.stdout)
# new log file at midnight
log_file = os.path.join(log_dir, "log.json")
handler = TimedRotatingFileHandler(
log_file,
when="midnight",
interval=1,
backupCount=7)
logger.setLevel(logging.INFO)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger


def get_device(user_agent) -> str:
"""Explicitly give the device of a user-agent object
"""Explicitly gives the device of a user-agent object

Args:
user_agent: info given by the user browser

Returns:
str: mobile, pc, tablet or other
"""
if user_agent.is_mobile:
return "mobile"
Expand All @@ -76,7 +91,18 @@ def get_device(user_agent) -> str:
return "other"


def get_base_logs(user_agent, user_id) -> dict:
def get_base_logs(user_agent, user_id: str) -> dict:
"""Generates the common information for custom logs in basegun.
Each function can add some info specific to the current process,
then we insert these custom logs as extra

Args:
user_agent: user agent object
user_id (str): UUID identifying a unique user

Returns:
dict: the base custom information
"""
extras_logging = {
"bg_date": datetime.now().isoformat(),
"bg_user_id": user_id,
Expand All @@ -91,7 +117,7 @@ def get_base_logs(user_agent, user_id) -> dict:


def upload_image_ovh(content: bytes, img_name: str):
""" Uploads an image to owh swift container basegun-public
"""Uploads an image to basegun ovh swift container
path uploaded-images/WORKSPACE/img_name
where WORKSPACE is dev, preprod or prod

Expand Down Expand Up @@ -158,7 +184,8 @@ def upload_image_ovh(content: bytes, img_name: str):
)

# Logs
logger = setup_logs()
PATH_LOGS = init_variable("PATH_LOGS", "../logs")
logger = setup_logs(PATH_LOGS)

# Load model
MODEL_PATH = os.path.join(
Expand Down Expand Up @@ -216,6 +243,18 @@ def version():
return APP_VERSION


@app.get("/logs")
def logs():
if "WORKSPACE" in os.environ and os.environ["WORKSPACE"] != "prod":
with open(os.path.join(PATH_LOGS, "log.json"), "r") as f:
lines = f.readlines()
res = [json.loads(l) for l in lines]
res.reverse()
return res
else:
return PlainTextResponse("Forbidden")


@app.post("/upload")
async def imageupload(
request: Request,
Expand Down
28 changes: 27 additions & 1 deletion backend/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class TestModel(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(TestModel, self).__init__(*args, **kwargs)
self.url = "http://localhost:8000"
self.url = "http://localhost:5000"

def test_home(self):
"""Checks that the route / is alive"""
Expand Down Expand Up @@ -59,6 +59,21 @@ def test_upload_and_logs(self):
self.assertEqual(image_one.size, image_two.size)
diff = ImageChops.difference(image_one, image_two)
self.assertFalse(diff.getbbox())
# checks that the result is written in logs
r = requests.get(self.url + "/logs")
self.assertEqual(r.status_code, 200)
# checks the latest log "Upload to OVH"
self.assertEqual(r.json()[0]["_bg_image_url"], r.json()[1]["_bg_image_url"])
self.assertEqual(r.json()[0]["short_message"], "Upload to OVH successful")
# checks the previous log "Identification request"
log = r.json()[1]
self.check_log_base(log)
self.assertEqual(log["short_message"], "Identification request")
self.assertTrue("-" in log["_bg_user_id"])
self.assertEqual(log["_bg_geolocation"], geoloc)
self.assertEqual(log["_bg_label"], "revolver")
self.assertAlmostEqual(log["_bg_confidence"], 98.43, places=1)
self.assertTrue(log["_bg_upload_time"]>=0)

def test_feedback_and_logs(self):
"""Checks that the feedback works properly"""
Expand All @@ -68,7 +83,18 @@ def test_feedback_and_logs(self):
image_url = "https://storage.gra.cloud.ovh.net/v1/test"
r = requests.post(self.url + "/identification-feedback",
json={"image_url": image_url, "feedback": True, "confidence": confidence, "label": label, "confidence_level": confidence_level})

self.assertEqual(r.status_code, 200)
r = requests.get(self.url + "/logs")
self.assertEqual(r.status_code, 200)
log = r.json()[0]
self.check_log_base(log)
self.assertEqual(log["short_message"], "Identification feedback")
self.assertEqual(log["_bg_image_url"], image_url)
self.assertTrue(log["_bg_feedback_bool"])
self.assertEqual(log["_bg_confidence"], confidence)
self.assertEqual(log["_bg_label"], label)
self.assertEqual(log["_bg_confidence_level"], confidence_level)

def test_geoloc_api(self):
"""Checks that the geolocation api works properly"""
Expand Down
3 changes: 1 addition & 2 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ services:
context: ./backend
target: ${BUILD_TARGET:-dev}
container_name: basegun-backend
user: appuser
environment:
- PATH_LOGS=/tmp/logs
- OS_USERNAME
Expand All @@ -26,7 +25,7 @@ services:
- REQUESTS_CA_BUNDLE=$CACERT_LOCATION
image: basegun-backend:${TAG:-2.0}-dev
ports:
- 8000:8000
- 5000:5000
volumes:
- $PWD/backend/src:/app/src
- $PWD/backend/tests:/app/tests
Expand Down
2 changes: 1 addition & 1 deletion docker-compose-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ services:
- WORKSPACE=${WORKSPACE:-prod}
image: basegun-backend:${TAG}-prod
ports:
- 8000:8000
- 5000:5000

frontend:
build:
Expand Down
2 changes: 1 addition & 1 deletion frontend/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ http {
}

location /api/ {
proxy_pass http://basegun-backend:8000/;
proxy_pass http://basegun-backend:5000/;
client_max_body_size 5M;
proxy_read_timeout 1800;
proxy_connect_timeout 1800;
Expand Down
2 changes: 1 addition & 1 deletion frontend/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default defineConfig({
host: true,
proxy: {
'^/api': {
target: `http://${apiHost}:8000`,
target: `http://${apiHost}:5000`,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
Expand Down
1 change: 1 addition & 0 deletions infra/kube/helm/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ metadata:
data:
WORKSPACE: {{ .Values.backend.config.workspace }}
PATH_IMGS: {{ .Values.backend.config.path_imgs }}
PATH_LOGS: /tmp/log/basegun/
---
apiVersion: v1
kind: ConfigMap
Expand Down
4 changes: 2 additions & 2 deletions infra/kube/helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ backend:
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 8000
containerPort: 8000
port: 5000
containerPort: 5000
autoscaling:
enabled: false
minReplicas: 1
Expand Down