mirror of
https://github.com/Telecominfraproject/wlan-lanforge-scripts.git
synced 2025-11-01 11:18:03 +00:00
lanforge_api.py: several debugging enhancements
- Logg class message parameter are renamed to msg to be consistent with python logging package - fixes possible duplicate log statements because there were no return statements in the logg method - adds class method for registering method names to turn on debug logging from - adds class method for registering tags or keywords to turn on logging from - adds Logg.by_tag() and Log.by_method() - adds more specific logging to json_post() - some import formatting requested by Matthew Signed-off-by: Jed Reynolds <jed@bitratchet.com>
This commit is contained in:
@@ -70,29 +70,28 @@
|
|||||||
class which appends subclasses to it.
|
class which appends subclasses to it.
|
||||||
|
|
||||||
----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----"""
|
----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if sys.version_info[0] != 3:
|
if sys.version_info[0] != 3:
|
||||||
print("This script requires Python 3")
|
print("This script requires Python 3")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
import urllib
|
from datetime import datetime
|
||||||
from urllib import request
|
from enum import Enum
|
||||||
from urllib import error
|
from enum import IntFlag
|
||||||
from urllib import parse
|
|
||||||
import http.client
|
import http.client
|
||||||
from http.client import HTTPResponse
|
from http.client import HTTPResponse
|
||||||
|
import inspect
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
|
from pprint import pprint, pformat
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from datetime import datetime
|
import urllib
|
||||||
from enum import IntFlag
|
from urllib import request, error, parse
|
||||||
from pprint import pprint
|
|
||||||
from pprint import pformat
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
SESSION_HEADER = 'X-LFJson-Session'
|
SESSION_HEADER = 'X-LFJson-Session'
|
||||||
LOGGER = Logger('json_api')
|
LOGGER = Logger('json_api')
|
||||||
@@ -218,7 +217,33 @@ def print_diagnostics(url_: str = None,
|
|||||||
|
|
||||||
|
|
||||||
class Logg:
|
class Logg:
|
||||||
|
"""
|
||||||
|
This method presently defines various log "levels" but does not yet express
|
||||||
|
ability to log "areas" or "keywords".
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- LOG BUFFER a list that only holds last 100 lines logged to it. This is useful
|
||||||
|
for emitting when an exception happens in a loop and you are not interested
|
||||||
|
in the first 10e6 log entries
|
||||||
|
|
||||||
|
- KEYWORD LOGGING: pair a --debug_kw=keyword,keyword set on the command line to only
|
||||||
|
recieve log output from log statements matching those keywords
|
||||||
|
|
||||||
|
- CLASS/METHOD/FUNCTION logging: --debug_fn=class.method,module.func set on the command
|
||||||
|
line that activates logging in the method or function listed. See inspection techniques
|
||||||
|
listed near this SO question https://stackoverflow.com/a/5104943/11014343
|
||||||
|
|
||||||
|
- BITWISE LOG LEVELS: --log_level=DEBUG|FILEIO|JSON|HTTP a maskable combination of enum_bitmask
|
||||||
|
names that combine to a value that can trigger logging.
|
||||||
|
|
||||||
|
Please also consider how log messages can be formatted:
|
||||||
|
https://stackoverflow.com/a/20112491/11014343:
|
||||||
|
logging.basicConfig(format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
|
||||||
|
"""
|
||||||
DEFAULT_LEVEL = logging.WARNING
|
DEFAULT_LEVEL = logging.WARNING
|
||||||
|
DefaultLogger = LOGGER
|
||||||
|
method_name_list: list[str] = []
|
||||||
|
tag_list: list[str] = []
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
log_level: int = DEFAULT_LEVEL,
|
log_level: int = DEFAULT_LEVEL,
|
||||||
@@ -243,53 +268,149 @@ class Logg:
|
|||||||
self.logger = Logger(name, level=log_level)
|
self.logger = Logger(name, level=log_level)
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
self.logg(level=logging.WARNING, message="Logger begun: " + self.name)
|
self.logg(level=logging.WARNING, msg="Logger begun: " + self.name)
|
||||||
|
|
||||||
def logg(self,
|
@classmethod
|
||||||
|
def logg(cls,
|
||||||
level: int = logging.WARNING,
|
level: int = logging.WARNING,
|
||||||
message: str = None):
|
msg: str = None):
|
||||||
"""
|
"""
|
||||||
|
Use this *class method* to send logs to the DefaultLogger instance created when this class was created
|
||||||
:param level: python logging priority
|
:param level:
|
||||||
:param message: text to send to logging channel
|
:param msg:
|
||||||
:return: None
|
:return:
|
||||||
"""
|
"""
|
||||||
if _not(message):
|
if _not(msg):
|
||||||
return
|
return
|
||||||
if level == logging.CRITICAL:
|
if level == logging.CRITICAL:
|
||||||
self.logger.critical(message)
|
cls.DefaultLogger.critical(msg)
|
||||||
|
return
|
||||||
|
if level == logging.ERROR:
|
||||||
|
cls.DefaultLogger.error(msg)
|
||||||
|
return
|
||||||
|
if level == logging.WARNING:
|
||||||
|
cls.DefaultLogger.warning(msg)
|
||||||
|
return
|
||||||
|
if level == logging.INFO:
|
||||||
|
cls.DefaultLogger.info(msg)
|
||||||
|
return
|
||||||
|
if level == logging.DEBUG:
|
||||||
|
cls.DefaultLogger.debug(msg)
|
||||||
|
return
|
||||||
|
|
||||||
|
def by_level(self,
|
||||||
|
level: int = logging.WARNING,
|
||||||
|
msg: str = None):
|
||||||
|
"""
|
||||||
|
Use this *instance* version of the method for logging when you have a specific logger
|
||||||
|
customized for a purpose. Otherwise please use Logg.logg().
|
||||||
|
:param level: python logging priority
|
||||||
|
:param msg: text to send to logging channel
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
if _not(msg):
|
||||||
|
return
|
||||||
|
|
||||||
|
if level == logging.CRITICAL:
|
||||||
|
self.logger.critical(msg)
|
||||||
|
return
|
||||||
|
|
||||||
if level == logging.ERROR:
|
if level == logging.ERROR:
|
||||||
self.logger.error(message)
|
self.logger.error(msg)
|
||||||
|
return
|
||||||
|
|
||||||
if level == logging.WARNING:
|
if level == logging.WARNING:
|
||||||
self.logger.warning(message)
|
self.logger.warning(msg)
|
||||||
|
return
|
||||||
|
|
||||||
if level == logging.INFO:
|
if level == logging.INFO:
|
||||||
self.logger.info(message)
|
self.logger.info(msg)
|
||||||
|
return
|
||||||
|
|
||||||
if level == logging.DEBUG:
|
if level == logging.DEBUG:
|
||||||
self.logger.debug(message)
|
self.logger.debug(msg)
|
||||||
|
return
|
||||||
|
print("UNKNOWN: " + msg)
|
||||||
|
|
||||||
def error(self, message: str = None):
|
def error(self, message: str = None):
|
||||||
if not message:
|
if not message:
|
||||||
return
|
return
|
||||||
self.logg(level=logging.ERROR, message=message)
|
self.logg(level=logging.ERROR, msg=message)
|
||||||
|
|
||||||
def warning(self, message: str = None):
|
def warning(self, message: str = None):
|
||||||
if not message:
|
if not message:
|
||||||
return
|
return
|
||||||
self.logg(level=logging.WARNING, message=message)
|
self.logg(level=logging.WARNING, msg=message)
|
||||||
|
|
||||||
def info(self, message: str = None):
|
def info(self, message: str = None):
|
||||||
if not message:
|
if not message:
|
||||||
return
|
return
|
||||||
self.logg(level=logging.INFO, message=message)
|
self.logg(level=logging.INFO, msg=message)
|
||||||
|
|
||||||
def debug(self, message: str = None):
|
def debug(self, message: str = None):
|
||||||
if not message:
|
if not message:
|
||||||
return
|
return
|
||||||
self.logg(level=logging.DEBUG, message=message)
|
self.logg(level=logging.DEBUG, msg=message)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register_method_name(cls, methodname: str = None) -> None:
|
||||||
|
"""
|
||||||
|
Use this method to register names of functions you want to allow logging from
|
||||||
|
:param methodname:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not methodname:
|
||||||
|
return
|
||||||
|
cls.method_name_list.append(methodname)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register_tag(cls, tag: str = None) -> None:
|
||||||
|
"""
|
||||||
|
Use this method to register keywords you want to allow logging from
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not tag:
|
||||||
|
return
|
||||||
|
if tag in cls.tag_list:
|
||||||
|
return
|
||||||
|
cls.tag_list.append(tag)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_method(cls, msg: str = None) -> None:
|
||||||
|
"""
|
||||||
|
should only log if we're in the method_list
|
||||||
|
reminder: https://stackoverflow.com/a/13514318/11014343
|
||||||
|
import inspect
|
||||||
|
import types
|
||||||
|
from typing import cast
|
||||||
|
this_fn_name = cat(types.FrameType, inspect.currentframe()).f_code.co_name
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
caller = inspect.currentframe().f_back.f_code.co_name
|
||||||
|
|
||||||
|
if caller in cls.method_name_list:
|
||||||
|
cls.logg(level=cls.DEFAULT_LEVEL, msg=f"[{caller}] {msg}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
pprint(e)
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_tag(cls, tag: str = None, msg: str = None) -> None:
|
||||||
|
"""
|
||||||
|
should only log if we're in the method_list
|
||||||
|
reminder: https://stackoverflow.com/a/13514318/11014343
|
||||||
|
import inspect
|
||||||
|
import types
|
||||||
|
from typing import cast
|
||||||
|
this_fn_name = cat(types.FrameType, inspect.currentframe()).f_code.co_name
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if tag not in cls.tag_list:
|
||||||
|
return
|
||||||
|
|
||||||
|
cls.logg(level=cls.DEFAULT_LEVEL, msg=f"[{tag}] {msg}")
|
||||||
|
|
||||||
|
|
||||||
class BaseLFJsonRequest:
|
class BaseLFJsonRequest:
|
||||||
@@ -374,7 +495,7 @@ class BaseLFJsonRequest:
|
|||||||
if corrected_url.find(' ') >= 1:
|
if corrected_url.find(' ') >= 1:
|
||||||
corrected_url = corrected_url.replace(' ', '%20')
|
corrected_url = corrected_url.replace(' ', '%20')
|
||||||
if debug:
|
if debug:
|
||||||
self.logger.debug("%s: url [%s] now [%s]" % (str(__class__), url, corrected_url))
|
self.logger.by_method("%s: url [%s] now [%s]" % (str(__class__), url, corrected_url))
|
||||||
return corrected_url
|
return corrected_url
|
||||||
|
|
||||||
def add_error(self, message: str = None):
|
def add_error(self, message: str = None):
|
||||||
@@ -445,19 +566,19 @@ class BaseLFJsonRequest:
|
|||||||
opener = request.build_opener(request.ProxyHandler(self.session_instance.proxy_map))
|
opener = request.build_opener(request.ProxyHandler(self.session_instance.proxy_map))
|
||||||
request.install_opener(opener)
|
request.install_opener(opener)
|
||||||
|
|
||||||
if debug:
|
# if debug:
|
||||||
self.logger.debug("formPost: url: " + url)
|
self.logger.by_method("form_post: url: " + url)
|
||||||
if (post_data is not None) and (post_data is not self.No_Data):
|
if (post_data is not None) and (post_data is not self.No_Data):
|
||||||
urlenc_data = urllib.parse.urlencode(post_data).encode("utf-8")
|
urlenc_data = urllib.parse.urlencode(post_data).encode("utf-8")
|
||||||
|
self.logger.by_method("formPost: data looks like:" + str(urlenc_data))
|
||||||
if debug:
|
if debug:
|
||||||
self.logger.debug("formPost: data looks like:" + str(urlenc_data))
|
|
||||||
print("formPost: url: " + url)
|
print("formPost: url: " + url)
|
||||||
myrequest = request.Request(url=url,
|
myrequest = request.Request(url=url,
|
||||||
data=urlenc_data,
|
data=urlenc_data,
|
||||||
headers=self.default_headers)
|
headers=self.default_headers)
|
||||||
else:
|
else:
|
||||||
myrequest = request.Request(url=url, headers=self.default_headers)
|
myrequest = request.Request(url=url, headers=self.default_headers)
|
||||||
self.logger.info("json_post: No data sent to [%s]" % url)
|
self.logger.by_method("json_post: No data sent to [%s]" % url)
|
||||||
|
|
||||||
myrequest.headers['Content-type'] = 'application/x-www-form-urlencoded'
|
myrequest.headers['Content-type'] = 'application/x-www-form-urlencoded'
|
||||||
|
|
||||||
@@ -528,7 +649,7 @@ class BaseLFJsonRequest:
|
|||||||
exit(1)
|
exit(1)
|
||||||
responses: list[HTTPResponse] = []
|
responses: list[HTTPResponse] = []
|
||||||
url = self.get_corrected_url(url)
|
url = self.get_corrected_url(url)
|
||||||
|
self.logger.by_method("url: "+url)
|
||||||
if (post_data is not None) and (post_data is not self.No_Data):
|
if (post_data is not None) and (post_data is not self.No_Data):
|
||||||
myrequest = request.Request(url=url,
|
myrequest = request.Request(url=url,
|
||||||
method=method_,
|
method=method_,
|
||||||
@@ -539,7 +660,7 @@ class BaseLFJsonRequest:
|
|||||||
headers=self.default_headers,
|
headers=self.default_headers,
|
||||||
method=method_,
|
method=method_,
|
||||||
data=post_data)
|
data=post_data)
|
||||||
self.logger.info("json_post: empty post sent to [%s]" % url)
|
self.logger.by_method("empty post sent to [%s]" % url)
|
||||||
|
|
||||||
if not connection_timeout_sec:
|
if not connection_timeout_sec:
|
||||||
if self.session_instance.get_timeout_sec():
|
if self.session_instance.get_timeout_sec():
|
||||||
@@ -556,10 +677,10 @@ class BaseLFJsonRequest:
|
|||||||
elif _is(session_id_):
|
elif _is(session_id_):
|
||||||
myrequest.headers[SESSION_HEADER] = str(session_id_)
|
myrequest.headers[SESSION_HEADER] = str(session_id_)
|
||||||
else:
|
else:
|
||||||
self.logger.warning("Request sent without X-LFJson-ID header: "+url)
|
self.logger.warning("Request sent without X-LFJson-ID header: " + url)
|
||||||
if debug:
|
if debug:
|
||||||
self.logger.warning("json_post headers to "+url)
|
self.logger.by_method("headers sent to: " + url)
|
||||||
self.logger.warning(pformat(myrequest.headers))
|
self.logger.by_method(pformat(myrequest.headers))
|
||||||
|
|
||||||
# https://stackoverflow.com/a/59635684/11014343
|
# https://stackoverflow.com/a/59635684/11014343
|
||||||
|
|
||||||
@@ -578,21 +699,21 @@ class BaseLFJsonRequest:
|
|||||||
resp_data = response.read().decode('utf-8')
|
resp_data = response.read().decode('utf-8')
|
||||||
jzon_data = None
|
jzon_data = None
|
||||||
if debug and die_on_error:
|
if debug and die_on_error:
|
||||||
self.logger.debug(__name__ +
|
self.logger.warning(__name__ +
|
||||||
" ----- json_post: %d debug: --------------------------------------------" %
|
" ----- json_post: %d debug: --------------------------------------------" %
|
||||||
attempt)
|
attempt)
|
||||||
self.logger.debug("URL: %s :%d " % (url, response.status))
|
self.logger.warning("URL: %s :%d " % (url, response.status))
|
||||||
self.logger.debug(__name__+" ----- headers -------------------------------------------------")
|
self.logger.warning(__name__ + " ----- headers -------------------------------------------------")
|
||||||
if response.status != 200:
|
if response.status != 200:
|
||||||
self.logger.error(pformat(response.getheaders()))
|
self.logger.error(pformat(response.getheaders()))
|
||||||
self.logger.error(__name__+" ----- response -------------------------------------------------")
|
self.logger.error(__name__ + " ----- response -------------------------------------------------")
|
||||||
self.logger.error(pformat(resp_data))
|
self.logger.error(pformat(resp_data))
|
||||||
self.logger.error(" ----- -------------------------------------------------")
|
self.logger.error(" ----- -------------------------------------------------")
|
||||||
responses.append(response)
|
responses.append(response)
|
||||||
header_items = response.getheaders()
|
header_items = response.getheaders()
|
||||||
if debug:
|
if debug:
|
||||||
self.logger.warning("BaseJsonRequest::json_post: response headers:")
|
self.logger.by_method("BaseJsonRequest::json_post: response headers:")
|
||||||
self.logger.warning(pformat(header_items))
|
self.logger.by_method(pformat(header_items))
|
||||||
if SESSION_HEADER in header_items:
|
if SESSION_HEADER in header_items:
|
||||||
if self.session_id != response.getheader(SESSION_HEADER):
|
if self.session_id != response.getheader(SESSION_HEADER):
|
||||||
self.logger.warning("established session header [%s] different from response session header[%s]"
|
self.logger.warning("established session header [%s] different from response session header[%s]"
|
||||||
@@ -609,11 +730,14 @@ class BaseLFJsonRequest:
|
|||||||
raise ValueError("reponse_json_list needs to be type list")
|
raise ValueError("reponse_json_list needs to be type list")
|
||||||
jzon_data = json.loads(resp_data)
|
jzon_data = json.loads(resp_data)
|
||||||
if debug:
|
if debug:
|
||||||
self.logger.debug(__name__+":----- json_post debug: ------------------------------------------")
|
self.logger.debug(
|
||||||
|
__name__ + ":----- json_post debug: ------------------------------------------")
|
||||||
self.logger.debug("URL: %s :%d " % (url, response.status))
|
self.logger.debug("URL: %s :%d " % (url, response.status))
|
||||||
self.logger.debug(__name__+" ----- headers -------------------------------------------------")
|
self.logger.debug(
|
||||||
|
__name__ + " ----- headers -------------------------------------------------")
|
||||||
self.logger.debug(pformat(response.getheaders()))
|
self.logger.debug(pformat(response.getheaders()))
|
||||||
self.logger.debug(__name__+" ----- response -------------------------------------------------")
|
self.logger.debug(
|
||||||
|
__name__ + " ----- response -------------------------------------------------")
|
||||||
self.logger.debug(pformat(jzon_data))
|
self.logger.debug(pformat(jzon_data))
|
||||||
self.logger.debug("-------------------------------------------------")
|
self.logger.debug("-------------------------------------------------")
|
||||||
response_json_list.append(jzon_data)
|
response_json_list.append(jzon_data)
|
||||||
|
|||||||
Reference in New Issue
Block a user