Skip to content

Commit 217d41f

Browse files
Fix all logfire logging
1 parent cfcb98b commit 217d41f

13 files changed

Lines changed: 139 additions & 132 deletions

File tree

src/solesearch_api/main.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
__version__ = "2.2.0"
1+
__version__ = "3.0.0"
22

3-
import logging
43
import os
54

65
import logfire
@@ -11,15 +10,11 @@
1110
from fastapi_pagination import add_pagination
1211
from starlette.middleware.sessions import SessionMiddleware
1312

14-
from solesearch_api.config import ENVIRONMENT
1513
from solesearch_api.db import initialize_db
1614
from solesearch_api.routes import auth, scrape, sneakers
1715

1816
desc = """
1917
### The Bloomberg Terminal of Sneakers
20-
- Find product information, from every brand, fast.
21-
- Never miss another release date.
22-
- Never buy bricks. Stay ahead of the game with our comprehensive price insights.
2318
2419
[Twitter](https://twitter.com/SoleSearchAPI) | [Github](https://github.com/SoleSearchAPI)
2520
"""
@@ -35,13 +30,12 @@
3530
"email": "support@solesearch.io",
3631
},
3732
description=desc,
38-
responses={404: {"description": "Not found"}}, # Custom 404 page
33+
responses={404: {"description": "404: Not found"}}, # Custom 404 page
3934
)
4035

4136
# Configure Logfire
42-
logfire.configure(environment=ENVIRONMENT)
37+
logfire.configure(service_name="api")
4338
logfire.instrument_fastapi(app)
44-
logger = logging.getLogger(__name__)
4539

4640
# Enable session handling for StocxkX OAuth flow
4741
app.add_middleware(
@@ -61,15 +55,15 @@
6155
async def startup_event():
6256
# Create the database tables
6357
initialize_db()
64-
logger.info("Database initialized")
58+
logfire.info("Database initialized")
6559
# Include all routers
6660
app.include_router(sneakers.router)
6761
app.include_router(auth.router)
6862
app.include_router(scrape.router)
69-
logger.info("Routers imported")
63+
logfire.info("Routers imported")
7064
# Load the pagination module
7165
add_pagination(app)
72-
logger.info("Pagination module loaded")
66+
logfire.info("Pagination module loaded")
7367

7468

7569
@app.get("/docs", include_in_schema=False)

src/solesearch_api/routes/auth.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import logging
21
import os
32
from datetime import UTC, datetime, timedelta
43
from urllib.parse import urlparse
54

5+
import logfire
66
import requests
77
from fastapi import APIRouter, HTTPException, Request
88
from fastapi.responses import RedirectResponse
@@ -80,8 +80,8 @@ async def stockx_oauth_callback(state: str, code: str, request: Request):
8080
seconds=int(tokens["expires_in"]) - 30,
8181
)
8282
await token.save()
83-
logging.info(f"Updated {token_type}")
83+
logfire.info(f"Updated {token_type}")
8484
return tokens
8585
except Exception as e:
86-
logging.exception(e)
86+
logfire.exception(e)
8787
raise HTTPException(status_code=500, detail=str(e))

src/solesearch_api/tasks/__init__.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logfire
22
from celery import Celery
3-
from celery.signals import beat_init, worker_init
3+
from celery.signals import worker_init
44

55
from solesearch_api.config import CELERY_BACKEND, CELERY_BROKER
66

@@ -11,13 +11,6 @@ class CeleryConfig:
1111

1212
@worker_init.connect()
1313
def configure_logfire(*args, **kwargs):
14-
logfire.configure(service_name="worker")
15-
logfire.instrument_celery()
16-
17-
18-
@beat_init.connect()
19-
def init_beat(*args, **kwargs):
20-
logfire.configure(service_name="beat")
2114
logfire.instrument_celery()
2215

2316

src/solesearch_api/tasks/alerting/healthcheck.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import logging
2-
31
import requests
42
from requests_aws4auth import AWS4Auth
53

@@ -12,8 +10,6 @@
1210
)
1311
from solesearch_api.tasks import app
1412

15-
logger = logging.getLogger(__name__)
16-
1713
if ENVIRONMENT == "production":
1814
auth = AWS4Auth(
1915
AWS_ACCESS_KEY_ID,
Lines changed: 75 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,92 @@
1+
import logfire
12
from sqlmodel import Session, select
23

34
from solesearch_api.models.sneaker import Sneaker
45
from solesearch_api.tasks import app
6+
from solesearch_api.db import engine
7+
8+
logfire.configure(service_name="worker")
59

610

711
@app.task(name="create_or_update_sneaker")
812
def create_or_update_sneaker(
9-
session: Session,
1013
sneaker: Sneaker,
1114
):
12-
"""
13-
Create a new sneaker or update an existing one with the same SKU and brand.
14-
15-
This function centralizes the logic for handling sneaker uniqueness based on SKU and brand.
16-
It will update all fields of an existing sneaker with the new data, and handle relationships
17-
like links, images, sizes, and prices.
15+
with Session(engine) as session:
16+
"""
17+
Create a new sneaker or update an existing one with the same SKU and brand.
1818
19-
Args:
20-
session: SQLModel session
21-
sneaker: The sneaker object to create or update
19+
This function centralizes the logic for handling sneaker uniqueness based on SKU and brand.
20+
It will update all fields of an existing sneaker with the new data, and handle relationships
21+
like links, images, sizes, and prices.
2222
23-
Returns:
24-
The created or updated sneaker object
25-
"""
26-
# Check if sneaker already exists
27-
statement = select(Sneaker).where(
28-
Sneaker.sku == sneaker.sku,
29-
Sneaker.brand == sneaker.brand,
30-
)
31-
existing_sneaker = session.exec(statement).first()
23+
Args:
24+
- sneaker: The sneaker object to create or update
3225
33-
if existing_sneaker:
34-
# Update existing sneaker with new information
35-
existing_sneaker.name = sneaker.name or existing_sneaker.name
36-
existing_sneaker.parent_sku = sneaker.parent_sku or existing_sneaker.parent_sku
37-
existing_sneaker.audience = sneaker.audience or existing_sneaker.audience
38-
existing_sneaker.release_date = (
39-
sneaker.release_date or existing_sneaker.release_date
40-
)
41-
existing_sneaker.retail_price = (
42-
sneaker.retail_price or existing_sneaker.retail_price
26+
Returns:
27+
- The created or updated sneaker object
28+
"""
29+
# Check if sneaker already exists with eager loading of relationships
30+
statement = select(Sneaker).where(
31+
Sneaker.sku == sneaker.sku,
32+
Sneaker.brand == sneaker.brand,
4333
)
44-
existing_sneaker.colorway = sneaker.colorway or existing_sneaker.colorway
45-
existing_sneaker.description = (
46-
sneaker.description or existing_sneaker.description
47-
)
48-
existing_sneaker.source = existing_sneaker.source or sneaker.source
49-
existing_sneaker.stockx_id = existing_sneaker.stockx_id or sneaker.stockx_id
50-
existing_sneaker.stadium_goods_id = (
51-
existing_sneaker.stadium_goods_id or sneaker.stadium_goods_id
52-
)
53-
54-
# Add any new links
55-
existing_link_urls = [link.url for link in existing_sneaker.links]
56-
for link in sneaker.links:
57-
if link.url not in existing_link_urls:
58-
existing_sneaker.links.append(link)
34+
existing_sneaker = session.exec(statement).first()
35+
if existing_sneaker is None:
36+
logfire.info(
37+
"Sneaker not found, creating new sneaker",
38+
sneaker=sneaker,
39+
)
40+
# Just add the new sneaker if it doesn't exist already
41+
result = sneaker
42+
else:
43+
logfire.info(
44+
"Sneaker found, updating existing sneaker",
45+
sneaker=sneaker,
46+
existing_sneaker=existing_sneaker,
47+
)
48+
# Update existing sneaker with new information
49+
existing_sneaker.name = sneaker.name or existing_sneaker.name
50+
existing_sneaker.parent_sku = (
51+
sneaker.parent_sku or existing_sneaker.parent_sku
52+
)
53+
existing_sneaker.audience = sneaker.audience or existing_sneaker.audience
54+
existing_sneaker.release_date = (
55+
sneaker.release_date or existing_sneaker.release_date
56+
)
57+
existing_sneaker.retail_price = (
58+
sneaker.retail_price or existing_sneaker.retail_price
59+
)
60+
existing_sneaker.colorway = sneaker.colorway or existing_sneaker.colorway
61+
existing_sneaker.description = (
62+
sneaker.description or existing_sneaker.description
63+
)
64+
existing_sneaker.source = existing_sneaker.source or sneaker.source
65+
existing_sneaker.stockx_id = existing_sneaker.stockx_id or sneaker.stockx_id
66+
existing_sneaker.stadium_goods_id = (
67+
existing_sneaker.stadium_goods_id or sneaker.stadium_goods_id
68+
)
5969

60-
# Add any new images
61-
existing_image_urls = [image.url for image in existing_sneaker.images]
62-
for image in sneaker.images:
63-
if image.url not in existing_image_urls:
64-
existing_sneaker.images.append(image)
70+
# Add any new links
71+
existing_link_urls = {link.url for link in existing_sneaker.links}
72+
for link in sneaker.links:
73+
if link.url not in existing_link_urls:
74+
existing_sneaker.links.append(link)
6575

66-
# Add any new sizes
67-
existing_size_values = [size.value for size in existing_sneaker.sizes]
68-
for size in sneaker.sizes:
69-
if size.value not in existing_size_values:
70-
existing_sneaker.sizes.append(size)
76+
# Add any new images
77+
existing_image_urls = {image.url for image in existing_sneaker.images}
78+
for image in sneaker.images:
79+
if image.url not in existing_image_urls:
80+
session.add(image)
81+
existing_sneaker.images.append(image)
7182

72-
return existing_sneaker
73-
else:
74-
# Just add the new sneaker if it doesn't exist already
75-
session.add(sneaker)
76-
return sneaker
83+
# Add any new sizes
84+
existing_size_values = {size.value for size in existing_sneaker.sizes}
85+
for size in sneaker.sizes:
86+
if size.value not in existing_size_values:
87+
session.add(size)
88+
existing_sneaker.sizes.append(size)
89+
result = existing_sneaker
90+
session.add(result)
91+
session.commit()
92+
return result

src/solesearch_api/tasks/scraping/base.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import json
2-
import logging
32
import os
43
import random
54
import time
65
from datetime import datetime
76
from typing import Callable
87

8+
import logfire
99
import requests
1010
from requests.adapters import HTTPAdapter
1111
from sqlmodel import Session
@@ -16,7 +16,7 @@
1616
from solesearch_api.models.enums import Audience
1717
from solesearch_api.utils.browser import get_default_browser_headers
1818

19-
logger = logging.getLogger(__name__)
19+
logfire.configure(service_name="worker")
2020

2121

2222
class MisconfigurationError(ValueError):
@@ -49,12 +49,23 @@ def __init__(
4949
self.extractor = extractor
5050

5151
def run(self, *args, **kwargs):
52-
with Session(engine) as session:
53-
return self.scrape(session, *args, **kwargs)
52+
logfire.info(
53+
"Running scraping task",
54+
brand=self.brand,
55+
task_name=self.__class__.__name__,
56+
)
57+
result = self.scrape(*args, **kwargs)
58+
logfire.info(
59+
"Scraping task completed",
60+
brand=self.brand,
61+
task_name=self.__class__.__name__,
62+
result=result,
63+
)
64+
return result
5465

5566
def guess_audience(self, gender: str | list[str]) -> Audience:
5667
if gender not in self.audience_mapping:
57-
logger.warning(
68+
logfire.warning(
5869
f"'{gender}' not in {self.__class__.__name__} audience_mapping",
5970
)
6071
return self.audience_mapping.get(gender, Audience.UNKNOWN)
@@ -78,8 +89,13 @@ def get_html(
7889
)
7990
if os.path.exists(file_path):
8091
with open(file_path) as f:
92+
logfire.info(
93+
"HTML data loaded from file",
94+
brand=self.brand,
95+
task_name=self.__class__.__name__,
96+
file_path=file_path,
97+
)
8198
return f.read()
82-
8399
# Create a session with retry strategy
84100
session = requests.Session()
85101
retry_strategy = Retry(
@@ -99,8 +115,13 @@ def get_html(
99115
response = session.get(self.download_url, headers=self.headers, timeout=30)
100116
response.raise_for_status()
101117
html = response.text
102-
103118
self.save_to_file(html, file_path)
119+
logfire.info(
120+
"HTML data saved to file",
121+
brand=self.brand,
122+
task_name=self.__class__.__name__,
123+
file_path=file_path,
124+
)
104125
return html
105126

106127
def get_json(self) -> dict:
@@ -112,8 +133,20 @@ def get_json(self) -> dict:
112133
)
113134
if os.path.exists(file_path):
114135
with open(file_path) as f:
136+
logfire.info(
137+
"JSON data loaded from file",
138+
brand=self.brand,
139+
task_name=self.__class__.__name__,
140+
file_path=file_path,
141+
)
115142
return json.load(f)
116143
html_data = self.get_html()
117144
json_data = self.extractor(html_data)
118145
self.save_to_file(json.dumps(json_data, indent=2), file_path)
146+
logfire.info(
147+
"JSON data saved to file",
148+
brand=self.brand,
149+
task_name=self.__class__.__name__,
150+
file_path=file_path,
151+
)
119152
return json_data

src/solesearch_api/tasks/scraping/retail/adidas/base.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
from datetime import datetime, timezone
2-
import logging
3-
import re
4-
from solesearch_api.models.enums import Audience, Platform
5-
from solesearch_api.models.sneaker import Image, Link, Sneaker
6-
from solesearch_api.tasks.db.base import create_or_update_sneaker
1+
from solesearch_api.models.enums import Audience
72
from solesearch_api.tasks.scraping.base import BaseScrapingTask
83

9-
logger = logging.getLogger(__name__)
10-
114

125
class AdidasScrapingTask(BaseScrapingTask):
136
def __init__(self, *args, **kwargs):

0 commit comments

Comments
 (0)