Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
add process id and caller info to debug trace (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbasan authored Mar 3, 2023
1 parent 252ae0c commit d94d823
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
40 changes: 39 additions & 1 deletion vmngclient/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,52 @@
import logging
import logging.config
from functools import lru_cache
from importlib import metadata
from importlib.machinery import PathFinder
from os import environ
from pathlib import Path
from typing import Final
from traceback import FrameSummary, StackSummary
from typing import Final, List

import urllib3


def get_first_external_stack_frame(stack: StackSummary) -> FrameSummary:
"""
Get the first python frame
on the stack before entering vmngclient module
"""
for index, frame in enumerate(stack):
if is_file_in_package(frame.filename):
break
return stack[index - 1]


@lru_cache()
def is_file_in_package(fname: str) -> bool:
"""
Checks if filepath given by string
is part of vmngclient source code
"""
return Path(fname) in pkg_src_list


def list_package_sources() -> List[Path]:
"""
Creates a list containing paths to all python source files
for current package
"""
pkg_srcs: List[Path] = []
if pkg_spec := PathFinder.find_spec(__package__):
if pkg_origin := pkg_spec.origin:
pkg_srcs = list(Path(pkg_origin).parent.glob("**/*.py"))
return pkg_srcs


LOGGING_CONF_DIR: Final[str] = str(Path(__file__).parents[0] / "logging.conf")
__version__ = metadata.version(__package__)
pkg_src_list = list_package_sources()


if environ.get("VMNGCLIENT_DEVEL") is not None:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
Expand Down
20 changes: 20 additions & 0 deletions vmngclient/response.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import multiprocessing
from functools import wraps
from pprint import pformat
from traceback import extract_stack
from typing import Any, Callable, Optional, Sequence, Type, TypeVar, Union, cast

from attr import define # type: ignore
from requests import PreparedRequest, Request, Response
from requests.exceptions import JSONDecodeError

from vmngclient import get_first_external_stack_frame
from vmngclient.dataclasses import DataclassBase
from vmngclient.typed_list import DataSequence
from vmngclient.utils.creation_tools import create_dataclass
Expand All @@ -20,6 +23,22 @@ class ErrorInfo(DataclassBase):
code: str


def with_proc_info_header(method: Callable[..., str]) -> Callable[..., str]:
"""
Adds process ID and external caller information before first line of returned string
"""

@wraps(method)
def wrapper(*args, **kwargs) -> str:
wrapped = method(*args, **kwargs)
fname, line_no, function, _ = get_first_external_stack_frame(extract_stack())
external_caller_info = "%s:%d %s(...)" % (fname, line_no, function)
header = f"{multiprocessing.current_process()} {external_caller_info}\n"
return header + wrapped

return wrapper


def response_debug(response: Optional[Response], request: Union[Request, PreparedRequest, None]) -> str:
"""Returns human readable string containing Request-Response contents (helpful for debugging).
Expand Down Expand Up @@ -67,6 +86,7 @@ def response_debug(response: Optional[Response], request: Union[Request, Prepare
return pformat(debug_dict, width=80, sort_dicts=False)


@with_proc_info_header
def response_history_debug(response: Optional[Response], request: Union[Request, PreparedRequest, None]) -> str:
"""Returns human readable string containing Request-Response history contents for given response.
Expand Down

0 comments on commit d94d823

Please sign in to comment.