mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-20 14:27:42 +00:00
Replace root logger calls with module-specific loggers
This commit is contained in:
@@ -11,6 +11,8 @@ from core.constants import HTTP_HEADERS
|
||||
from providers.solarconditions.ionosonde_utils import compute_band_states
|
||||
from providers.solarconditions.solar_conditions_provider import SolarConditionsProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Each station gets polled roughly once every hour (3600 seconds). Note that to avoid a burst of requests to the server
|
||||
# every hour, the requests for data from each station are spaced out throughout the hour, leading to one request being
|
||||
# sent every 1-2 minutes.
|
||||
@@ -64,7 +66,7 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
return stations
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up query of GIRO ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
logger.info(f"Set up query of GIRO ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
self._thread = Thread(target=self._run, name="GIROIonosondeDataProvider")
|
||||
self._thread.start()
|
||||
|
||||
@@ -86,7 +88,7 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
ursi = station["ursi"]
|
||||
name = station["name"]
|
||||
try:
|
||||
logging.debug(f"Polling GIRO ionosonde data for {ursi} ({name})...")
|
||||
logger.debug(f"Polling GIRO ionosonde data for {ursi} ({name})...")
|
||||
now = datetime.now(timezone.utc)
|
||||
from_time = now - timedelta(hours=HISTORY_HOURS)
|
||||
cutoff_ts = from_time.timestamp()
|
||||
@@ -127,11 +129,11 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
self.update_data({"ionosonde_data": ionosonde_data})
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Updated ionosonde data for {ursi} ({name}).")
|
||||
logger.debug(f"Updated ionosonde data for {ursi} ({name}).")
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception fetching GIRO ionosonde data for {ursi} ({name})")
|
||||
logger.exception(f"Exception fetching GIRO ionosonde data for {ursi} ({name})")
|
||||
|
||||
def _fetch_station_data(self, ursi, from_time, to_time):
|
||||
"""Fetch foF2, MUF and LUF readings for a station. Returns (fof2_dict, muf_dict, luf_dict) keyed by UNIX timestamp."""
|
||||
@@ -142,14 +144,14 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
try:
|
||||
http_response = requests.get(url, headers=HTTP_HEADERS, timeout=(5, 15))
|
||||
if not http_response.ok:
|
||||
logging.warning(f"HTTP {http_response.status_code} when calling Giro ionosonde API.")
|
||||
logger.warning(f"HTTP {http_response.status_code} when calling Giro ionosonde API.")
|
||||
return None, None, None
|
||||
return self._parse_all(http_response.text)
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning("Timeout when accessing Giro ionosonde API.")
|
||||
logger.warning("Timeout when accessing Giro ionosonde API.")
|
||||
return None, None, None
|
||||
except ConnectionError:
|
||||
logging.warning("Connection error when accessing Giro ionosonde API.")
|
||||
logger.warning("Connection error when accessing Giro ionosonde API.")
|
||||
return None, None, None
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -9,6 +9,8 @@ from providers.solarconditions.http_solar_conditions_provider import (
|
||||
HTTPSolarConditionsProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
POLL_INTERVAL = 3600 # 1 hour
|
||||
URL = "https://www.hamqsl.com/solarxml.php"
|
||||
|
||||
@@ -24,14 +26,14 @@ class HamQSL(HTTPSolarConditionsProvider):
|
||||
root = ElementTree.fromstring(http_response.text)
|
||||
sd = root.find("solardata")
|
||||
if sd is None:
|
||||
logging.warning("HamQSL solar conditions API returned unexpected XML structure")
|
||||
logger.warning("HamQSL solar conditions API returned unexpected XML structure")
|
||||
return None
|
||||
|
||||
# Some error checking functions in case the data is janky.
|
||||
|
||||
def text(tag, default=None):
|
||||
if sd is None:
|
||||
logging.warning("HamQSL solar conditions API returned unexpected XML structure")
|
||||
logger.warning("HamQSL solar conditions API returned unexpected XML structure")
|
||||
return default
|
||||
el = sd.find(tag)
|
||||
return el.text.strip() if el is not None and el.text else default
|
||||
@@ -79,7 +81,7 @@ class HamQSL(HTTPSolarConditionsProvider):
|
||||
dt = dateutil_parser.parse(updated_str, tzinfos={tz_abbr: timezone})
|
||||
updated = dt.astimezone(pytz.UTC).timestamp()
|
||||
except (ValueError, IndexError):
|
||||
logging.warning(f"HamQSL solar conditions API returned unrecognised timestamp format: {updated_str}")
|
||||
logger.warning(f"HamQSL solar conditions API returned unrecognised timestamp format: {updated_str}")
|
||||
|
||||
# Return the data ready to be put into the solar conditions object.
|
||||
return {
|
||||
|
||||
@@ -9,6 +9,8 @@ from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout
|
||||
from core.constants import HTTP_HEADERS
|
||||
from providers.solarconditions.solar_conditions_provider import SolarConditionsProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HTTPSolarConditionsProvider(SolarConditionsProvider):
|
||||
"""Generic solar conditions provider for providers that request data via HTTP(S). Subclasses implement
|
||||
@@ -22,7 +24,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
|
||||
self._stop_event = Event()
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up query of {self.name} solar conditions API every {self._poll_interval!s} seconds.")
|
||||
logger.info(f"Set up query of {self.name} solar conditions API every {self._poll_interval!s} seconds.")
|
||||
self._thread = Thread(target=self._run, name=f"HTTPSolarConditionsProvider-{self.name}")
|
||||
self._thread.start()
|
||||
|
||||
@@ -37,7 +39,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
|
||||
|
||||
def _poll(self):
|
||||
try:
|
||||
logging.debug(f"Polling {self.name} solar conditions API...")
|
||||
logger.debug(f"Polling {self.name} solar conditions API...")
|
||||
http_response = requests.get(self._url, headers=HTTP_HEADERS, timeout=(5, 30))
|
||||
# Check response code was good
|
||||
if http_response.ok:
|
||||
@@ -46,18 +48,18 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Received data from {self.name} solar conditions API.")
|
||||
logger.debug(f"Received data from {self.name} solar conditions API.")
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.warning(f"HTTP {http_response.status_code} when calling {self.name} solar conditions API.")
|
||||
logger.warning(f"HTTP {http_response.status_code} when calling {self.name} solar conditions API.")
|
||||
|
||||
except ConnectionError:
|
||||
logging.warning(f"Connection error when accessing {self.name} solar conditions API.")
|
||||
logger.warning(f"Connection error when accessing {self.name} solar conditions API.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning(f"Timeout when accessing {self.name} solar conditions API.")
|
||||
logger.warning(f"Timeout when accessing {self.name} solar conditions API.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in HTTP Solar Conditions Provider ({self.name})")
|
||||
logger.exception(f"Exception in HTTP Solar Conditions Provider ({self.name})")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
def _http_response_to_solar_conditions(self, http_response):
|
||||
|
||||
@@ -10,6 +10,8 @@ from core.constants import HTTP_HEADERS
|
||||
from providers.solarconditions.ionosonde_utils import compute_band_states
|
||||
from providers.solarconditions.solar_conditions_provider import SolarConditionsProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
POLL_INTERVAL = 900 # 15 minutes
|
||||
KC2G_URL = "https://prop.kc2g.com/api/stations.json"
|
||||
HISTORY_HOURS = 24
|
||||
@@ -29,7 +31,7 @@ class KC2GProp(SolarConditionsProvider):
|
||||
self._stop_event = Event()
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up query of KC2G ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
logger.info(f"Set up query of KC2G ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
self._thread = Thread(target=self._run, name="KC2GPropProvider")
|
||||
self._thread.start()
|
||||
|
||||
@@ -44,10 +46,10 @@ class KC2GProp(SolarConditionsProvider):
|
||||
|
||||
def _poll(self):
|
||||
try:
|
||||
logging.debug("Polling KC2G ionosonde data...")
|
||||
logger.debug("Polling KC2G ionosonde data...")
|
||||
http_response = requests.get(KC2G_URL, headers=HTTP_HEADERS, timeout=(5, 30))
|
||||
if not http_response.ok:
|
||||
logging.warning(f"HTTP {http_response.status_code} when calling KG2G ionosonde API.")
|
||||
logger.warning(f"HTTP {http_response.status_code} when calling KG2G ionosonde API.")
|
||||
return
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
@@ -114,13 +116,13 @@ class KC2GProp(SolarConditionsProvider):
|
||||
self.update_data({"ionosonde_data": ionosonde_data})
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Updated KC2G ionosonde data for {updated_count} stations.")
|
||||
logger.debug(f"Updated KC2G ionosonde data for {updated_count} stations.")
|
||||
|
||||
except ConnectionError:
|
||||
logging.warning("Connection error when accessing KC2G ionosonde API.")
|
||||
logger.warning("Connection error when accessing KC2G ionosonde API.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning("Timeout when accessing KC2G ionosonde API.")
|
||||
logger.warning("Timeout when accessing KC2G ionosonde API.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception("Exception in KC2G ionosonde data provider")
|
||||
logger.exception("Exception in KC2G ionosonde data provider")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
@@ -6,6 +6,8 @@ from providers.solarconditions.http_solar_conditions_provider import (
|
||||
HTTPSolarConditionsProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
POLL_INTERVAL = 10800 # Every 3 hours
|
||||
URL = "https://services.swpc.noaa.gov/text/3-day-forecast.txt"
|
||||
|
||||
@@ -28,7 +30,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
start_idx = i
|
||||
break
|
||||
if start_idx is None:
|
||||
logging.warning(f"NOAA 3-day forecast: could not find '{section_header}' section")
|
||||
logger.warning(f"NOAA 3-day forecast: could not find '{section_header}' section")
|
||||
return None
|
||||
|
||||
# Find the date header line by scanning the next few lines for month & day patterns
|
||||
@@ -38,11 +40,11 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
date_header_idx = j
|
||||
break
|
||||
if date_header_idx is None:
|
||||
logging.warning(f"NOAA 3-day forecast: could not find date header after '{section_header}'")
|
||||
logger.warning(f"NOAA 3-day forecast: could not find date header after '{section_header}'")
|
||||
return None
|
||||
date_matches = re.findall(r"([A-Za-z]{3})\s+(\d{2})", lines[date_header_idx])
|
||||
if not date_matches:
|
||||
logging.warning(f"NOAA 3-day forecast: no dates in header: {lines[date_header_idx]}")
|
||||
logger.warning(f"NOAA 3-day forecast: no dates in header: {lines[date_header_idx]}")
|
||||
return None
|
||||
|
||||
# Figure out the date based on the line found
|
||||
@@ -52,7 +54,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
dt = datetime.strptime(f"{day_str} {month_str} {year}", "%d %b %Y").replace(tzinfo=timezone.utc)
|
||||
column_timestamps.append(dt.timestamp())
|
||||
except ValueError:
|
||||
logging.warning(f"NOAA 3-day forecast: could not parse date: {month_str} {day_str} {year}")
|
||||
logger.warning(f"NOAA 3-day forecast: could not parse date: {month_str} {day_str} {year}")
|
||||
return None
|
||||
|
||||
# Parse data rows. Each non-empty line should have a text label followed by percentage values
|
||||
@@ -91,26 +93,26 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
start_idx = i
|
||||
break
|
||||
if start_idx is None:
|
||||
logging.warning("NOAA K-index forecast: could not find 'NOAA Kp index breakdown' section")
|
||||
logger.warning("NOAA K-index forecast: could not find 'NOAA Kp index breakdown' section")
|
||||
return None
|
||||
|
||||
# Extract the year from the header line, e.g. "NOAA Kp index breakdown Apr 2-Apr 4, 2026"
|
||||
header_line = lines[start_idx]
|
||||
year_match = re.search(r"\b(\d{4})\b", header_line)
|
||||
if not year_match:
|
||||
logging.warning(f"NOAA K-index forecast: could not extract year from: {header_line}")
|
||||
logger.warning(f"NOAA K-index forecast: could not extract year from: {header_line}")
|
||||
return None
|
||||
year = int(year_match.group(1))
|
||||
|
||||
# Parse the column date headers on the next line, e.g. " Apr 02 Apr 03 Apr 04"
|
||||
if start_idx + 1 >= len(lines):
|
||||
logging.warning("NOAA K-index forecast: missing date header line")
|
||||
logger.warning("NOAA K-index forecast: missing date header line")
|
||||
return None
|
||||
|
||||
date_header_line = lines[start_idx + 2]
|
||||
date_matches = re.findall(r"([A-Za-z]{3})\s+(\d{2})", date_header_line)
|
||||
if not date_matches:
|
||||
logging.warning(f"NOAA K-index forecast: could not parse date headers from: {date_header_line}")
|
||||
logger.warning(f"NOAA K-index forecast: could not parse date headers from: {date_header_line}")
|
||||
return None
|
||||
|
||||
column_dates = []
|
||||
@@ -118,7 +120,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
try:
|
||||
column_dates.append(datetime.strptime(f"{day_str} {month_str} {year}", "%d %b %Y").date())
|
||||
except ValueError:
|
||||
logging.warning(f"NOAA K-index forecast: could not parse date: {month_str} {day_str} {year}")
|
||||
logger.warning(f"NOAA K-index forecast: could not parse date: {month_str} {day_str} {year}")
|
||||
return None
|
||||
|
||||
# Parse each data row, e.g. "00-03UT 2.00 3.00 2.00"
|
||||
@@ -159,7 +161,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
k_index_forecast[key] = kp
|
||||
|
||||
if not k_index_forecast:
|
||||
logging.warning("NOAA K-index forecast: no data rows parsed")
|
||||
logger.warning("NOAA K-index forecast: no data rows parsed")
|
||||
return None
|
||||
|
||||
# Parse Solar Radiation Storm Forecast (single row: "S1 or greater")
|
||||
|
||||
Reference in New Issue
Block a user