Skip to content

Commit fb802ac

Browse files
committed
feat: Enhance logging across the EDGAR API modules with debug-level messages and add NullHandler to prevent logging warnings
1 parent f918346 commit fb802ac

15 files changed

Lines changed: 87 additions & 15 deletions

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Changed
11+
12+
- **edgar/\_\_init\_\_.py**: Added `NullHandler` to the `edgar` logger — follows Python library logging best practice so applications control log output.
13+
- **edgar/session.py**: Downgraded per-request URL, parameter, and rate-limit sleep logs from `info` to `debug`.
14+
- **edgar/async_session.py**: Same `info``debug` log-level fix as `session.py`.
15+
- **edgar/parser.py**: Downgraded pagination URL and entry-count logs from `info` to `debug`.
16+
- **edgar/client.py**: Added `logger` — logs `debug` on init (rate_limit, cache settings).
17+
- **edgar/cache.py**: Added `logger` — logs `debug` on cache hit, miss, expired, set, and invalidate.
18+
- **edgar/tickers.py**: Added `debug` logging for cache hit/miss and successful resolution; `warning` on failed ticker/CIK lookup.
19+
- **edgar/submissions.py**: Added `logger` — logs `debug` on submissions cache hit.
20+
- **edgar/xbrl.py**: Added `logger` — logs `debug` on company_facts cache hit.
21+
- **edgar/datasets.py**: Added `logger` — logs `info` on bulk download start, `debug` on per-file extraction with row counts.
22+
- **edgar/search.py**: Added `logger` — logs `debug` with EFTS search params before request.
23+
- **edgar/company.py**: Added `logger` — logs `debug` on identifier resolution path (CIK vs ticker).
24+
825
## [0.2.0] - 2026-04-19
926

1027
### Added

edgar/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@
1616

1717
from __future__ import annotations
1818

19+
import logging
1920
import os
2021

2122
from edgar.client import EdgarClient
2223
from edgar.async_client import EdgarAsyncClient
2324
from edgar.exceptions import EdgarError, EdgarRequestError, EdgarParseError
2425

26+
# Library best practice: add a NullHandler so users don't see
27+
# "No handlers could be found for logger 'edgar'" warnings.
28+
# Users configure logging in their own applications.
29+
logging.getLogger("edgar").addHandler(logging.NullHandler())
30+
2531
__all__ = [
2632
"EdgarClient",
2733
"EdgarAsyncClient",

edgar/async_session.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ async def make_request( # pylint: disable=too-many-positional-arguments
145145

146146
url = self.build_url(endpoint=endpoint, use_api=use_api, base_url=base_url)
147147

148-
logger.info("URL: %s", url)
149-
logger.info("PARAMETERS %s", params)
148+
logger.debug("URL: %s", url)
149+
logger.debug("Parameters: %s", params)
150150

151151
await self._throttle()
152152

@@ -289,7 +289,7 @@ async def _throttle(self) -> None:
289289
if len(self._request_times) >= self._rate_limit:
290290
sleep_duration = 1.0 - (now - self._request_times[0])
291291
if sleep_duration > 0:
292-
logger.info(
292+
logger.debug(
293293
"Rate limit: %d requests in window, sleeping %.3fs",
294294
len(self._request_times),
295295
sleep_duration,

edgar/cache.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
from __future__ import annotations
44

5+
import logging
56
import time
67

8+
logger = logging.getLogger(__name__)
9+
710

811
# Default TTLs in seconds.
912
TTL_TICKERS = 86400 # 24 hours — ticker data changes ~quarterly
@@ -26,22 +29,27 @@ def get(self, key: str) -> object | None:
2629

2730
entry = self._store.get(key)
2831
if entry is None:
32+
logger.debug("Cache miss: %s", key)
2933
return None
3034
value, expires_at = entry
3135
if time.monotonic() >= expires_at:
3236
del self._store[key]
37+
logger.debug("Cache expired: %s", key)
3338
return None
39+
logger.debug("Cache hit: %s", key)
3440
return value
3541

3642
def set(self, key: str, value: object, ttl: float) -> None:
3743
"""Store *value* under *key* with a TTL of *ttl* seconds."""
3844

3945
self._store[key] = (value, time.monotonic() + ttl)
46+
logger.debug("Cache set: %s (ttl=%.0fs)", key, ttl)
4047

4148
def invalidate(self, key: str) -> None:
4249
"""Remove a single key from the cache."""
4350

4451
self._store.pop(key, None)
52+
logger.debug("Cache invalidated: %s", key)
4553

4654
def clear(self) -> None:
4755
"""Remove all entries from the cache."""

edgar/client.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Main entry-point client for the SEC EDGAR API."""
22

3+
import logging
4+
35
from edgar.cache import TTLCache
46
from edgar.xbrl import Xbrl
57
from edgar.series import Series
@@ -18,6 +20,11 @@
1820
from edgar.ownership_filings import OwnershipFilings
1921
from edgar.variable_insurance_products import VariableInsuranceProducts
2022

23+
from edgar.models import SearchResult
24+
25+
26+
logger = logging.getLogger(__name__)
27+
2128

2229
class EdgarClient:
2330
"""
@@ -59,6 +66,11 @@ def __init__(self, user_agent: str, rate_limit: int = 10, cache: bool = True) ->
5966
)
6067
self._services: dict = {}
6168

69+
logger.debug(
70+
"EdgarClient initialized (rate_limit=%d, cache=%s)",
71+
rate_limit, cache,
72+
)
73+
6274
def __repr__(self) -> str:
6375
"""String representation of the `EdgarClient` object."""
6476

@@ -380,8 +392,6 @@ def search(
380392
'Apple Inc. (AAPL) (CIK 0000320193)'
381393
"""
382394

383-
from edgar.models import SearchResult
384-
385395
raw = self.full_text_search().full_text_search(
386396
q=q,
387397
form_types=form_types,

edgar/company.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import logging
56
from enum import Enum
67
from typing import TYPE_CHECKING, Union
78

@@ -10,6 +11,8 @@
1011
from edgar.submissions import Submissions
1112
from edgar.xbrl import Xbrl
1213

14+
logger = logging.getLogger(__name__)
15+
1316
if TYPE_CHECKING:
1417
from edgar.session import EdgarSession
1518
from edgar.tickers import Tickers
@@ -57,12 +60,14 @@ def __init__(
5760
stripped = str(identifier).lstrip("0")
5861
if stripped.isdigit():
5962
# CIK path — resolve to get metadata.
63+
logger.debug("Resolving identifier as CIK: %s", identifier)
6064
entries = tickers_service.resolve_cik(identifier)
6165
self._cik: str = str(entries[0]["cik_str"]).zfill(10)
6266
self._ticker: str = entries[0]["ticker"]
6367
self._name: str = entries[0]["title"]
6468
else:
6569
# Ticker path — resolve to get CIK.
70+
logger.debug("Resolving identifier as ticker: %s", identifier)
6671
self._cik = tickers_service.resolve_ticker(identifier)
6772
self._ticker = identifier.upper()
6873
# Look up the company name from the resolved CIK.

edgar/datasets.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
import csv
66
import io
7+
import logging
78
import zipfile
89

910
from edgar.session import EdgarSession
1011

12+
logger = logging.getLogger(__name__)
13+
1114
_DERA_BASE = "/files/dera/data/financial-statement-data-sets"
1215

1316

@@ -157,6 +160,7 @@ def get_financial_statements(
157160
raise ValueError(f"quarter must be between 1 and 4, got {quarter}")
158161

159162
endpoint = f"{_DERA_BASE}/{year}q{quarter}.zip"
163+
logger.info("Downloading DERA dataset %dQ%d", year, quarter)
160164
zip_bytes = self.edgar_session.fetch_page(
161165
self.edgar_session.build_url(endpoint=endpoint)
162166
)
@@ -233,5 +237,6 @@ def _extract_tsv_zip(zip_bytes: bytes) -> dict[str, list[dict]]:
233237
text = io.TextIOWrapper(f, encoding="utf-8")
234238
reader = csv.DictReader(text, delimiter="\t")
235239
result[key] = list(reader)
240+
logger.debug("Extracted %s (%d rows)", key, len(result[key]))
236241

237242
return result

edgar/parser.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def parse_entries(
103103
keep_going = False
104104
elif fetch_page:
105105
page_content = fetch_page(next_page)
106-
logger.info("Grabbed Next URL: %s", next_page)
106+
logger.debug("Grabbed next URL: %s", next_page)
107107

108108
if page_content:
109109
try:
@@ -286,7 +286,7 @@ def parse_issuer_table(
286286

287287
master_list.append(master_dict)
288288

289-
logger.info("Pulling URL: %s", next_page_link)
289+
logger.debug("Pulling URL: %s", next_page_link)
290290

291291
if next_page_link and fetch_page:
292292
page_content = fetch_page(next_page_link)
@@ -428,8 +428,8 @@ def parse_variable_products_company_table(
428428
)
429429
product_list_all = product_list_all + product_list
430430

431-
logger.info("Pulling URL: %s", link)
432-
logger.info("Total Entries Scraped: %s", len(product_list_all))
431+
logger.debug("Pulling URL: %s", link)
432+
logger.debug("Total entries scraped: %s", len(product_list_all))
433433

434434
return product_list_all
435435

edgar/search.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
from __future__ import annotations
44

5+
import logging
6+
57
from edgar.session import EdgarSession
68

9+
logger = logging.getLogger(__name__)
10+
711
EFTS_BASE_URL = "https://efts.sec.gov"
812

913

@@ -101,6 +105,8 @@ def full_text_search(
101105
if end_date:
102106
params["enddt"] = end_date
103107

108+
logger.debug("EFTS search params: %s", params)
109+
104110
return self.edgar_session.make_request(
105111
method="get",
106112
endpoint="/LATEST/search-index",

edgar/session.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ def make_request(
189189
# Build the URL.
190190
url = self.build_url(endpoint=endpoint, use_api=use_api, base_url=base_url)
191191

192-
logger.info("URL: %s", url)
193-
logger.info("PARAMETERS %s", params)
192+
logger.debug("URL: %s", url)
193+
logger.debug("Parameters: %s", params)
194194

195195
# Build the request kwargs.
196196
request_kwargs = {
@@ -284,7 +284,7 @@ def _throttle(self) -> None:
284284
if len(self._request_times) >= self._rate_limit:
285285
sleep_duration = 1.0 - (now - self._request_times[0])
286286
if sleep_duration > 0:
287-
logger.info(
287+
logger.debug(
288288
"Rate limit: %d requests in window, sleeping %.3fs",
289289
len(self._request_times),
290290
sleep_duration,

0 commit comments

Comments
 (0)