refactor(log): unified schema — mandatory type/class/method, request_id removed
Every log record now carries type ('http' for the request middleware
line, 'wrapper' for garmin execute() calls and forwarded wrapper.py
stderr, 'app' for everything else), class (Go type with package, or the
package/module for free functions), and method (the emitting Go/Python
function -- wrapper.py stamps it automatically, so its msg prefixes are
gone). msg is optional and omitted when empty; the redundant messages
('HTTP request', 'wrapper call', 'garmin wrapper stderr') are dropped,
the HTTP verb moves to http_method, source=garmin-wrapper is replaced by
type=wrapper, and the request_id middleware plumbing (applog
WithLogger/FromContext) is removed. applog.App(class, method) is the
tagging helper for app records.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -29,16 +29,18 @@ def _log(level, msg, **attrs):
|
||||
stderr line by line and re-emits these through the Go application
|
||||
logger, so the backend's combined output stays a single JSON stream --
|
||||
never print free text to stderr or stdout from this process (stdout is
|
||||
reserved for the request/response protocol)."""
|
||||
entry = {"level": level, "msg": msg}
|
||||
reserved for the request/response protocol). The emitting function is
|
||||
stamped as "method" automatically; the Go side adds the rest of the
|
||||
log schema (type=wrapper, class=wrapper)."""
|
||||
entry = {"level": level, "msg": msg, "method": sys._getframe(1).f_code.co_name}
|
||||
entry.update(attrs)
|
||||
print(json.dumps(entry), file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
def _prompt_mfa():
|
||||
_log("debug", "prompt_mfa(): invoked")
|
||||
_log("debug", "invoked")
|
||||
code = _mfa_input_queue.get(timeout=300)
|
||||
_log("debug", "prompt_mfa(): returning MFA code", code_length=len(code))
|
||||
_log("debug", "returning MFA code", code_length=len(code))
|
||||
return code
|
||||
|
||||
|
||||
@@ -88,12 +90,12 @@ def _startup_login():
|
||||
_client = Garmin(email, password)
|
||||
result_queue = queue.Queue() # private to this call, never shared with authenticate/complete_mfa
|
||||
|
||||
def _do_login():
|
||||
def _do_startup_login():
|
||||
global _auth_state
|
||||
_log("debug", "startup_login(): background thread starting _client.login()", tokenstore=TOKENSTORE)
|
||||
_log("debug", "background thread starting _client.login()", tokenstore=TOKENSTORE)
|
||||
try:
|
||||
_client.login(tokenstore=TOKENSTORE)
|
||||
_log("debug", "startup_login(): _client.login() returned successfully")
|
||||
_log("debug", "_client.login() returned successfully")
|
||||
result_queue.put(("success", None))
|
||||
# A success arriving after the 10s timeout below would land in
|
||||
# an abandoned queue -- flip the state here too, so later calls
|
||||
@@ -105,30 +107,30 @@ def _startup_login():
|
||||
except Exception as exc:
|
||||
_log(
|
||||
"error",
|
||||
"startup_login(): _client.login() failed",
|
||||
"_client.login() failed",
|
||||
error=str(exc),
|
||||
error_type=type(exc).__name__,
|
||||
traceback=traceback.format_exc(),
|
||||
)
|
||||
result_queue.put(("error", str(exc)))
|
||||
|
||||
threading.Thread(target=_do_login, daemon=True).start()
|
||||
threading.Thread(target=_do_startup_login, daemon=True).start()
|
||||
|
||||
try:
|
||||
status, err = result_queue.get(timeout=10)
|
||||
_log("debug", "startup_login(): got result within 10s timeout", status=status)
|
||||
_log("debug", "got result within 10s timeout", status=status)
|
||||
if status == "success":
|
||||
_auth_state = "authenticated"
|
||||
else:
|
||||
_log("error", "startup_login(): login failed", error=err)
|
||||
_log("error", "login failed", error=err)
|
||||
_auth_state = "unauthenticated"
|
||||
except queue.Empty:
|
||||
# No assignment here: the state is already "unauthenticated", and
|
||||
# writing it again could clobber a success the background thread
|
||||
# records right around the timeout boundary (see _do_login).
|
||||
# records right around the timeout boundary (see _do_startup_login).
|
||||
_log(
|
||||
"warn",
|
||||
"startup_login(): hit the 10s timeout but still running in the "
|
||||
"hit the 10s timeout but still running in the "
|
||||
"background and will update auth state if it eventually succeeds",
|
||||
)
|
||||
|
||||
@@ -149,16 +151,16 @@ def _handle_authenticate(_params):
|
||||
"message": "GARMIN_EMAIL and GARMIN_PASSWORD environment variables are required.",
|
||||
}
|
||||
|
||||
def _do_login():
|
||||
_log("debug", "handle_authenticate(): background thread starting _client.login()", tokenstore=TOKENSTORE)
|
||||
def _do_authenticate_login():
|
||||
_log("debug", "background thread starting _client.login()", tokenstore=TOKENSTORE)
|
||||
try:
|
||||
_client.login(tokenstore=TOKENSTORE)
|
||||
_log("debug", "handle_authenticate(): _client.login() returned successfully")
|
||||
_log("debug", "_client.login() returned successfully")
|
||||
_login_result_queue.put(("success", None))
|
||||
except Exception as exc:
|
||||
_log(
|
||||
"error",
|
||||
"handle_authenticate(): _client.login() failed",
|
||||
"_client.login() failed",
|
||||
error=str(exc),
|
||||
error_type=type(exc).__name__,
|
||||
traceback=traceback.format_exc(),
|
||||
@@ -167,21 +169,21 @@ def _handle_authenticate(_params):
|
||||
|
||||
_client = Garmin(email, password)
|
||||
_client.prompt_mfa = _prompt_mfa
|
||||
threading.Thread(target=_do_login, daemon=True).start()
|
||||
threading.Thread(target=_do_authenticate_login, daemon=True).start()
|
||||
|
||||
try:
|
||||
status, err = _login_result_queue.get(timeout=10)
|
||||
_log("debug", "handle_authenticate(): got result within 10s timeout", status=status)
|
||||
_log("debug", "got result within 10s timeout", status=status)
|
||||
if status == "success":
|
||||
_auth_state = "authenticated"
|
||||
return {"status": "success", "message": "Authenticated successfully."}
|
||||
else:
|
||||
_log("error", "handle_authenticate(): login failed", error=err)
|
||||
_log("error", "login failed", error=err)
|
||||
return {"status": "failed", "message": f"Authentication failed: {err}"}
|
||||
except queue.Empty:
|
||||
_log(
|
||||
"warn",
|
||||
"handle_authenticate(): hit the 10s timeout with no result yet, reporting mfa_required",
|
||||
"hit the 10s timeout with no result yet, reporting mfa_required",
|
||||
)
|
||||
_auth_state = "mfa_pending"
|
||||
return {
|
||||
@@ -197,12 +199,12 @@ def _handle_complete_mfa(params):
|
||||
return {"status": "failed", "message": "No MFA in progress. Call authenticate first."}
|
||||
|
||||
code = params["code"]
|
||||
_log("debug", "handle_complete_mfa(): received a code, pushing to mfa queue", code_length=len(code))
|
||||
_log("debug", "received a code, pushing to mfa queue", code_length=len(code))
|
||||
_mfa_input_queue.put(code)
|
||||
|
||||
try:
|
||||
status, err = _login_result_queue.get(timeout=30)
|
||||
_log("debug", "handle_complete_mfa(): got result", status=status, error=err)
|
||||
_log("debug", "got result", status=status, error=err)
|
||||
if status == "success":
|
||||
_auth_state = "authenticated"
|
||||
return {"status": "success", "message": "MFA accepted. Authenticated successfully."}
|
||||
@@ -210,7 +212,7 @@ def _handle_complete_mfa(params):
|
||||
except queue.Empty:
|
||||
_log(
|
||||
"error",
|
||||
"handle_complete_mfa(): hit the 30s timeout with no result yet, reporting unauthenticated",
|
||||
"hit the 30s timeout with no result yet, reporting unauthenticated",
|
||||
)
|
||||
_auth_state = "unauthenticated"
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user