1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-06-17 15:51:14 +03:00

Merge pull request #719 from EDCD/enhancement/trace-logging

Implement a TRACE level of logging
This commit is contained in:
Athanasius 2020-09-28 11:19:24 +01:00 committed by GitHub
commit ae82f27925
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 287 additions and 81 deletions

4
.mypy.ini Normal file
View File

@ -0,0 +1,4 @@
[mypy]
follow_imports = skip
ignore_missing_imports = True
scripts_are_modules = True

67
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,67 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0
hooks:
- id: check-merge-conflict
- id: debug-statements
- id: end-of-file-fixer
- id: mixed-line-ending
#- repo: https://github.com/pre-commit/mirrors-autopep8
# rev: ''
# hooks:
# - id: autopep8
# flake8 --show-source <file>
- repo: https://gitlab.com/pycqa/flake8
rev: ''
hooks:
- id: flake8
- repo: https://github.com/pre-commit/pygrep-hooks
rev: ''
hooks:
- id: python-no-eval
- id: python-no-log-warn
# This is a pain where a comment begins with the word 'type' otherwise
# - id: python-use-type-annotations
# mypy - static type checking
# mypy --follow-imports skip <file>
- repo: https://github.com/pre-commit/mirrors-mypy
rev: ''
hooks:
- id: mypy
args: [ "--follow-imports", "skip", "--ignore-missing-imports", "--scripts-are-modules" ]
### # pydocstyle.exe <file>
### - repo: https://github.com/FalconSocial/pre-commit-mirrors-pep257
### rev: ''
### hooks:
### - id: pep257 # docstring conventions
### # Alternate https://github.com/PyCQA/pydocstyle
# - repo: https://github.com/digitalpulp/pre-commit-php
# rev: ''
# hooks:
# -id: php-unit
# safety.exe check -r requirements.txt
- repo: https://github.com/Lucas-C/pre-commit-hooks-safety
rev: ''
hooks:
- id: python-safety-dependencies-check
entry: safety
args: [check, --bare, -r]
language: system
default_language_version:
python: python3.7
default_stages: [ commit, push ]
#files: '([^\.].+/)*.py'
exclude: cp37-setup-new.py

70
EDMC.py
View File

@ -1,19 +1,24 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# """Command-line interface. Requires prior setup through the GUI."""
# Command-line interface. Requires prior setup through the GUI.
#
import argparse import argparse
import json import json
import locale import locale
import logging
import os import os
import re import re
import sys import sys
from os.path import getmtime, join from os.path import getmtime, join
from time import sleep, time from time import sleep, time
from typing import Any, Optional from typing import TYPE_CHECKING, Any, Optional
# See EDMCLogging.py docs.
# isort: off
from EDMCLogging import edmclogger, logger, logging
if TYPE_CHECKING:
from logging import trace, TRACE # type: ignore # noqa: F401
# isort: on
edmclogger.set_channels_loglevel(logging.INFO)
# workaround for https://github.com/EDCD/EDMarketConnector/issues/568 # workaround for https://github.com/EDCD/EDMarketConnector/issues/568
os.environ["EDMC_NO_UI"] = "1" os.environ["EDMC_NO_UI"] = "1"
@ -21,7 +26,6 @@ os.environ["EDMC_NO_UI"] = "1"
import collate import collate
import commodity import commodity
import companion import companion
import EDMCLogging
import edshipyard import edshipyard
import l10n import l10n
import loadout import loadout
@ -40,11 +44,9 @@ sys.path.append(config.internal_plugin_dir)
import eddn # noqa: E402 import eddn # noqa: E402
# isort: on # isort: on
logger = EDMCLogging.Logger(appcmdname).get_logger()
logger.setLevel(logging.INFO)
def log_locale(prefix: str) -> None: def log_locale(prefix: str) -> None:
"""Log the current state of locale settings."""
logger.debug(f'''Locale: {prefix} logger.debug(f'''Locale: {prefix}
Locale LC_COLLATE: {locale.getlocale(locale.LC_COLLATE)} Locale LC_COLLATE: {locale.getlocale(locale.LC_COLLATE)}
Locale LC_CTYPE: {locale.getlocale(locale.LC_CTYPE)} Locale LC_CTYPE: {locale.getlocale(locale.LC_CTYPE)}
@ -62,12 +64,30 @@ EXIT_SUCCESS, EXIT_SERVER, EXIT_CREDENTIALS, EXIT_VERIFICATION, EXIT_LAGGING, EX
JOURNAL_RE = re.compile(r'^Journal(Beta)?\.[0-9]{12}\.[0-9]{2}\.log$') JOURNAL_RE = re.compile(r'^Journal(Beta)?\.[0-9]{12}\.[0-9]{2}\.log$')
# quick and dirty version comparison assuming "strict" numeric only version numbers
def versioncmp(versionstring): def versioncmp(versionstring):
"""Quick and dirty version comparison assuming "strict" numeric only version numbers."""
return list(map(int, versionstring.split('.'))) return list(map(int, versionstring.split('.')))
def deep_get(target: dict, *args: str, default=None) -> Any: def deep_get(target: dict, *args: str, default=None) -> Any:
"""
Walk into a dict and return the specified deep value.
Example usage:
>>> thing = {'a': {'b': {'c': 'foo'} } }
>>> deep_get(thing, ('a', 'b', 'c'), None)
'foo'
>>> deep_get(thing, ('a', 'b'), None)
{'c': 'foo'}
>>> deep_get(thing, ('a', 'd'), None)
None
:param target: The dict to walk into for the desired value.
:param args: The list of keys to walk down through.
:param default: What to return if the target has no value.
:return: The value at the target deep key.
"""
if not hasattr(target, 'get'): if not hasattr(target, 'get'):
raise ValueError(f"Cannot call get on {target} ({type(target)})") raise ValueError(f"Cannot call get on {target} ({type(target)})")
@ -83,6 +103,7 @@ def deep_get(target: dict, *args: str, default=None) -> Any:
def main(): def main():
"""Run the main code of the program."""
try: try:
# arg parsing # arg parsing
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@ -93,7 +114,16 @@ def main():
) )
parser.add_argument('-v', '--version', help='print program version and exit', action='store_const', const=True) parser.add_argument('-v', '--version', help='print program version and exit', action='store_const', const=True)
parser.add_argument('--loglevel', metavar='loglevel', help='Set the logging loglevel to one of: CRITICAL, ERROR, WARNING, INFO, DEBUG') # noqa: E501 group_loglevel = parser.add_mutually_exclusive_group()
group_loglevel.add_argument('--loglevel',
metavar='loglevel',
help='Set the logging loglevel to one of: '
'CRITICAL, ERROR, WARNING, INFO, DEBUG, TRACE',
)
group_loglevel.add_argument('--trace',
help='Set the Debug logging loglevel to TRACE',
action='store_true',
)
parser.add_argument('-a', metavar='FILE', help='write ship loadout to FILE in Companion API json format') parser.add_argument('-a', metavar='FILE', help='write ship loadout to FILE in Companion API json format')
parser.add_argument('-e', metavar='FILE', help='write ship loadout to FILE in E:D Shipyard plain text format') parser.add_argument('-e', metavar='FILE', help='write ship loadout to FILE in E:D Shipyard plain text format')
parser.add_argument('-l', metavar='FILE', help='write ship locations to FILE in CSV format') parser.add_argument('-l', metavar='FILE', help='write ship locations to FILE in CSV format')
@ -117,11 +147,14 @@ def main():
return return
if args.loglevel: if args.trace:
if args.loglevel not in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'): edmclogger.set_channels_loglevel(logging.TRACE)
print('loglevel must be one of: CRITICAL, ERROR, WARNING, INFO, DEBUG', file=sys.stderr)
elif args.loglevel:
if args.loglevel not in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'TRACE'):
print('loglevel must be one of: CRITICAL, ERROR, WARNING, INFO, DEBUG, TRACE', file=sys.stderr)
sys.exit(EXIT_ARGS) sys.exit(EXIT_ARGS)
logger.setLevel(args.loglevel) edmclogger.set_channels_loglevel(args.loglevel)
logger.debug(f'Startup v{appversion} : Running on Python v{sys.version}') logger.debug(f'Startup v{appversion} : Running on Python v{sys.version}')
logger.debug(f'''Platform: {sys.platform} logger.debug(f'''Platform: {sys.platform}
@ -250,10 +283,9 @@ sys.path: {sys.path}'''
stats.export_status(data, args.t) stats.export_status(data, args.t)
if data['commander'].get('docked'): if data['commander'].get('docked'):
print('{},{}'.format( print(f'{deep_get(data, "lastSystem", "name", default="Unknown")},'
deep_get(data, 'lastSystem', 'name', default='Unknown'), f'{deep_get(data, "lastStarport", "name", default="Unknown")}'
deep_get(data, 'lastStarport', 'name', default='Unknown') )
))
else: else:
print(deep_get(data, 'lastSystem', 'name', default='Unknown')) print(deep_get(data, 'lastSystem', 'name', default='Unknown'))

View File

@ -5,6 +5,34 @@ This module provides for a common logging-powered log facility.
Mostly it implements a logging.Filter() in order to get two extra Mostly it implements a logging.Filter() in order to get two extra
members on the logging.LogRecord instance for use in logging.Formatter() members on the logging.LogRecord instance for use in logging.Formatter()
strings. strings.
If type checking, e.g. mypy, objects to `logging.trace(...)` then include this
stanza:
# See EDMCLogging.py docs.
# isort: off
if TYPE_CHECKING:
from logging import trace, TRACE # type: ignore # noqa: F401
# isort: on
This is needed because we add the TRACE level and the trace() function
ourselves at runtime.
To utilise logging in core code, or internal plugins, include this:
from EDMCLogging import get_main_logger
logger = get_main_logger()
To utilise logging in a 'found' (third-party) plugin, include this:
import os
import logging
plugin_name = os.path.basename(os.path.dirname(__file__))
# plugin_name here *must* be the name of the folder the plugin resides in
# See, plug.py:load_plugins()
logger = logging.getLogger(f'{appname}.{plugin_name}')
""" """
import inspect import inspect
@ -44,6 +72,17 @@ from config import appcmdname, appname, config
_default_loglevel = logging.DEBUG _default_loglevel = logging.DEBUG
# Define a TRACE level
LEVEL_TRACE = 5
logging.addLevelName(LEVEL_TRACE, "TRACE")
logging.TRACE = LEVEL_TRACE # type: ignore
logging.Logger.trace = lambda self, message, *args, **kwargs: self._log( # type: ignore
logging.TRACE, # type: ignore
message,
args,
**kwargs
)
class Logger: class Logger:
""" """
@ -66,9 +105,9 @@ class Logger:
""" """
self.logger = logging.getLogger(logger_name) self.logger = logging.getLogger(logger_name)
# Configure the logging.Logger # Configure the logging.Logger
# This needs to always be DEBUG in order to let DEBUG level messages # This needs to always be TRACE in order to let TRACE level messages
# through to check the *handler* levels. # through to check the *handler* levels.
self.logger.setLevel(logging.DEBUG) self.logger.setLevel(logging.TRACE) # type: ignore
# Set up filter for adding class name # Set up filter for adding class name
self.logger_filter = EDMCContextFilter() self.logger_filter = EDMCContextFilter()
@ -102,8 +141,8 @@ class Logger:
encoding='utf-8', encoding='utf-8',
delay=False delay=False
) )
# Yes, we always want these rotated files to be at DEBUG level # Yes, we always want these rotated files to be at TRACE level
self.logger_channel_rotating.setLevel(logging.DEBUG) self.logger_channel_rotating.setLevel(logging.TRACE) # type: ignore
self.logger_channel_rotating.setFormatter(self.logger_formatter) self.logger_channel_rotating.setFormatter(self.logger_formatter)
self.logger.addHandler(self.logger_channel_rotating) self.logger.addHandler(self.logger_channel_rotating)
@ -118,10 +157,33 @@ class Logger:
def get_streamhandler(self) -> logging.Handler: def get_streamhandler(self) -> logging.Handler:
""" """
Obtain the self.logger_channel StreamHandler instance. Obtain the self.logger_channel StreamHandler instance.
:return: logging.StreamHandler :return: logging.StreamHandler
""" """
return self.logger_channel return self.logger_channel
def set_channels_loglevel(self, level: int) -> None:
"""
Set the specified log level on the channels.
:param level: A valid `logging` level.
:return: None
"""
self.logger_channel.setLevel(level)
self.logger_channel_rotating.setLevel(level)
def set_console_loglevel(self, level: int) -> None:
"""
Set the specified log level on the console channel.
:param level: A valid `logging` level.
:return: None
"""
if self.logger_channel.level != logging.TRACE: # type: ignore
self.logger_channel.setLevel(level)
else:
logger.trace("Not changing log level because it's TRACE") # type: ignore
def get_plugin_logger(plugin_name: str, loglevel: int = _default_loglevel) -> logging.Logger: def get_plugin_logger(plugin_name: str, loglevel: int = _default_loglevel) -> logging.Logger:
""" """
@ -131,16 +193,21 @@ def get_plugin_logger(plugin_name: str, loglevel: int = _default_loglevel) -> lo
coming from, but we don't need to set up *everything* for them. coming from, but we don't need to set up *everything* for them.
The name will be '{config.appname}.{plugin.name}', e.g. The name will be '{config.appname}.{plugin.name}', e.g.
'EDMarketConnector.miggytest'. This means that any logging sent through 'EDMarketConnector.plugintest', or using appcmdname for EDMC CLI tool.
there *also* goes to the channels defined in the 'EDMarketConnector' Note that `plugin_name` must be the same as the name of the folder the
logger, so we can let that take care of the formatting. plugin resides in.
This means that any logging sent through there *also* goes to the channels
defined in the 'EDMarketConnector' (or 'EDMC') logger, so we can let that
take care of the formatting.
If we add our own channel then the output gets duplicated (assuming same If we add our own channel then the output gets duplicated (assuming same
logLevel set). logLevel set).
However we do need to attach our filter to this still. That's not at However we do need to attach our filter to this still. That's not at
the channel level. the channel level.
:param name: Name of this Logger.
:param plugin_name: Name of this Logger. **Must** be the name of the
folder the plugin resides in.
:param loglevel: Optional logLevel for this Logger. :param loglevel: Optional logLevel for this Logger.
:return: logging.Logger instance, all set up. :return: logging.Logger instance, all set up.
""" """
@ -335,7 +402,6 @@ class EDMCContextFilter(logging.Filter):
def get_main_logger() -> logging.Logger: def get_main_logger() -> logging.Logger:
"""Return the correct logger for how the program is being run.""" """Return the correct logger for how the program is being run."""
if not os.getenv("EDMC_NO_UI"): if not os.getenv("EDMC_NO_UI"):
# GUI app being run # GUI app being run
return logging.getLogger(appname) return logging.getLogger(appname)
@ -348,5 +414,11 @@ def get_main_logger() -> logging.Logger:
loglevel = config.get('loglevel') loglevel = config.get('loglevel')
if not loglevel: if not loglevel:
loglevel = logging.INFO loglevel = logging.INFO
edmclogger = Logger(appname, loglevel=loglevel)
if not os.getenv('EDMC_NO_UI'):
base_logger_name = appname
else:
base_logger_name = appcmdname
edmclogger = Logger(base_logger_name, loglevel=loglevel)
logger = edmclogger.get_logger() logger = edmclogger.get_logger()

View File

@ -0,0 +1 @@
EDMarketConnector.exe --trace

View File

@ -1,20 +1,29 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from builtins import str import argparse
from builtins import object import html
import sys
from sys import platform
import json import json
import locale import locale
import re
import sys
import webbrowser
from builtins import object, str
from os import chdir, environ from os import chdir, environ
from os.path import dirname, isdir, join from os.path import dirname, isdir, join
import re from sys import platform
import html from time import localtime, strftime, time
from time import time, localtime, strftime from typing import TYPE_CHECKING
import webbrowser
from config import appname, applongname, appversion, appversion_nobuild, copyright, config from EDMCLogging import edmclogger, logger, logging
# See EDMCLogging.py docs.
# isort: off
if TYPE_CHECKING:
from logging import trace, TRACE # type: ignore # noqa: F401
# isort: on
from config import applongname, appname, appversion, appversion_nobuild, config, copyright
# TODO: Test: Make *sure* this redirect is working, else py2exe is going to cause an exit popup # TODO: Test: Make *sure* this redirect is working, else py2exe is going to cause an exit popup
if __name__ == "__main__": if __name__ == "__main__":
@ -36,10 +45,11 @@ if getattr(sys, 'frozen', False):
environ['TK_LIBRARY'] = join(dirname(sys.path[0]), 'lib', 'tk') environ['TK_LIBRARY'] = join(dirname(sys.path[0]), 'lib', 'tk')
import tkinter as tk import tkinter as tk
from tkinter import ttk
import tkinter.filedialog import tkinter.filedialog
import tkinter.font import tkinter.font
import tkinter.messagebox import tkinter.messagebox
from tkinter import ttk
from ttkHyperlinkLabel import HyperlinkLabel from ttkHyperlinkLabel import HyperlinkLabel
if __debug__: if __debug__:
@ -49,18 +59,18 @@ if __debug__:
signal.signal(signal.SIGTERM, lambda sig, frame: pdb.Pdb().set_trace(frame)) signal.signal(signal.SIGTERM, lambda sig, frame: pdb.Pdb().set_trace(frame))
import companion
import commodity import commodity
from commodity import COMMODITY_CSV import companion
import td
import stats
import prefs
import plug import plug
import prefs
import stats
import td
from commodity import COMMODITY_CSV
from dashboard import dashboard
from hotkey import hotkeymgr from hotkey import hotkeymgr
from l10n import Translations from l10n import Translations
from monitor import monitor from monitor import monitor
from protocol import protocolhandler from protocol import protocolhandler
from dashboard import dashboard
from theme import theme from theme import theme
SERVER_RETRY = 5 # retry pause for Companion servers [s] SERVER_RETRY = 5 # retry pause for Companion servers [s]
@ -613,17 +623,17 @@ class AppWindow(object):
def crewroletext(role): def crewroletext(role):
# Return translated crew role. Needs to be dynamic to allow for changing language. # Return translated crew role. Needs to be dynamic to allow for changing language.
return { return {
None : '', None: '',
'Idle' : '', 'Idle': '',
'FighterCon': _('Fighter'), # Multicrew role 'FighterCon': _('Fighter'), # Multicrew role
'FireCon' : _('Gunner'), # Multicrew role 'FireCon': _('Gunner'), # Multicrew role
'FlightCon' : _('Helm'), # Multicrew role 'FlightCon': _('Helm'), # Multicrew role
}.get(role, role) }.get(role, role)
while True: while True:
entry = monitor.get_entry() entry = monitor.get_entry()
if not entry: if not entry:
logger.debug('No entry from monitor.get_entry()') logger.trace('No entry from monitor.get_entry()')
return return
# Update main window # Update main window
@ -640,8 +650,8 @@ class AppWindow(object):
self.ship_label['text'] = _('Ship') + ':' # Main window self.ship_label['text'] = _('Ship') + ':' # Main window
self.ship.configure( self.ship.configure(
text=monitor.state['ShipName'] text=monitor.state['ShipName']
or companion.ship_map.get(monitor.state['ShipType'], monitor.state['ShipType']) or companion.ship_map.get(monitor.state['ShipType'], monitor.state['ShipType'])
or '', or '',
url=self.shipyard_url) url=self.shipyard_url)
else: else:
self.cmdr['text'] = '' self.cmdr['text'] = ''
@ -675,7 +685,7 @@ class AppWindow(object):
self.login() self.login()
if not entry['event'] or not monitor.mode: if not entry['event'] or not monitor.mode:
logger.debug('Startup or in CQC, returning') logger.trace('Startup or in CQC, returning')
return # Startup or in CQC return # Startup or in CQC
if entry['event'] in ['StartUp', 'LoadGame'] and monitor.started: if entry['event'] in ['StartUp', 'LoadGame'] and monitor.started:
@ -934,7 +944,8 @@ class AppWindow(object):
def onexit(self, event=None): def onexit(self, event=None):
# http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
if platform != 'darwin' or self.w.winfo_rooty() > 0: if platform != 'darwin' or self.w.winfo_rooty() > 0:
config.set('geometry', '+{1}+{2}'.format(*self.w.geometry().split('+'))) x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267'
config.set('geometry', f'+{x}+{y}')
self.w.withdraw() # Following items can take a few seconds, so hide the main window while they happen self.w.withdraw() # Following items can take a few seconds, so hide the main window while they happen
protocolhandler.close() protocolhandler.close()
hotkeymgr.unregister() hotkeymgr.unregister()
@ -986,13 +997,13 @@ def enforce_single_instance() -> None:
# Ensure only one copy of the app is running under this user account. OSX does this automatically. Linux TODO. # Ensure only one copy of the app is running under this user account. OSX does this automatically. Linux TODO.
if platform == 'win32': if platform == 'win32':
import ctypes import ctypes
from ctypes.wintypes import HWND, LPWSTR, LPCWSTR, INT, BOOL, LPARAM from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPCWSTR, LPWSTR
EnumWindows = ctypes.windll.user32.EnumWindows # noqa: N806 EnumWindows = ctypes.windll.user32.EnumWindows # noqa: N806
GetClassName = ctypes.windll.user32.GetClassNameW # noqa: N806 GetClassName = ctypes.windll.user32.GetClassNameW # noqa: N806
GetClassName.argtypes = [HWND, LPWSTR, ctypes.c_int] # noqa: N806 GetClassName.argtypes = [HWND, LPWSTR, ctypes.c_int]
GetWindowText = ctypes.windll.user32.GetWindowTextW # noqa: N806 GetWindowText = ctypes.windll.user32.GetWindowTextW # noqa: N806
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int] # noqa: N806 GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW # noqa: N806 GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW # noqa: N806
GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd # noqa: N806 GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd # noqa: N806
@ -1059,7 +1070,27 @@ Locale LC_TIME: {locale.getlocale(locale.LC_TIME)}'''
if __name__ == "__main__": if __name__ == "__main__":
enforce_single_instance() enforce_single_instance()
from EDMCLogging import logger # Command-line arguments
parser = argparse.ArgumentParser(
prog=appname,
description="Utilises Elite Dangerous Journal files and the Frontier "
"Companion API (CAPI) service to gather data about a "
"player's state and actions to upload to third-party sites "
"such as EDSM, Inara.cz and EDDB."
)
parser.add_argument('--trace',
help='Set the Debug logging loglevel to TRACE',
action='store_true',
)
args = parser.parse_args()
if args.trace:
logger.setLevel(logging.TRACE)
edmclogger.set_channels_loglevel(logging.TRACE)
else:
edmclogger.set_channels_loglevel(logging.DEBUG)
logger.info(f'Startup v{appversion} : Running on Python v{sys.version}') logger.info(f'Startup v{appversion} : Running on Python v{sys.version}')
logger.debug(f'''Platform: {sys.platform} logger.debug(f'''Platform: {sys.platform}
@ -1114,7 +1145,7 @@ sys.path: {sys.path}'''
ui_scale = 100 ui_scale = 100
config.set('ui_scale', ui_scale) config.set('ui_scale', ui_scale)
theme.default_ui_scale = root.tk.call('tk', 'scaling') theme.default_ui_scale = root.tk.call('tk', 'scaling')
logger.debug(f'Default tk scaling = {theme.default_ui_scale}') logger.trace(f'Default tk scaling = {theme.default_ui_scale}')
theme.startup_ui_scale = ui_scale theme.startup_ui_scale = ui_scale
root.tk.call('tk', 'scaling', theme.default_ui_scale * float(ui_scale) / 100.0) root.tk.call('tk', 'scaling', theme.default_ui_scale * float(ui_scale) / 100.0)
app = AppWindow(root) app = AppWindow(root)

View File

@ -274,7 +274,7 @@ class Auth(object):
s = random.SystemRandom().getrandbits(8 * 32) s = random.SystemRandom().getrandbits(8 * 32)
self.state = self.base64_url_encode(s.to_bytes(32, byteorder='big')) self.state = self.base64_url_encode(s.to_bytes(32, byteorder='big'))
# Won't work under IE: https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/ # Won't work under IE: https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/
logger.debug(f'Trying auth from scratch for Commander "{self.cmdr}"') logger.info(f'Trying auth from scratch for Commander "{self.cmdr}"')
challenge = self.base64_url_encode(hashlib.sha256(self.verifier).digest()) challenge = self.base64_url_encode(hashlib.sha256(self.verifier).digest())
webbrowser.open( webbrowser.open(
f'{SERVER_AUTH}{URL_AUTH}?response_type=code&audience=frontier&scope=capi&client_id={CLIENT_ID}&code_challenge={challenge}&code_challenge_method=S256&state={self.state}&redirect_uri={protocolhandler.redirect}' # noqa: E501 # I cant make this any shorter f'{SERVER_AUTH}{URL_AUTH}?response_type=code&audience=frontier&scope=capi&client_id={CLIENT_ID}&code_challenge={challenge}&code_challenge_method=S256&state={self.state}&redirect_uri={protocolhandler.redirect}' # noqa: E501 # I cant make this any shorter
@ -399,7 +399,7 @@ class Session(object):
raise CredentialsError('Missing credentials') # Shouldn't happen raise CredentialsError('Missing credentials') # Shouldn't happen
elif self.state == Session.STATE_OK: elif self.state == Session.STATE_OK:
logger.debug('already logged in') logger.debug('already logged in (state == STATE_OK)')
return True # already logged in return True # already logged in
else: else:
@ -409,7 +409,6 @@ class Session(object):
return True # already logged in return True # already logged in
else: else:
# changed account or retrying login during auth
logger.debug('changed account or retrying login during auth') logger.debug('changed account or retrying login during auth')
self.close() self.close()
self.credentials = credentials self.credentials = credentials
@ -461,7 +460,7 @@ class Session(object):
def query(self, endpoint: str) -> CAPIData: def query(self, endpoint: str) -> CAPIData:
"""Perform a query against the specified CAPI endpoint.""" """Perform a query against the specified CAPI endpoint."""
logger.debug(f'Performing query for endpoint "{endpoint}"') logger.trace(f'Performing query for endpoint "{endpoint}"')
if self.state == Session.STATE_INIT: if self.state == Session.STATE_INIT:
if self.login(): if self.login():
return self.query(endpoint) return self.query(endpoint)
@ -471,7 +470,7 @@ class Session(object):
raise CredentialsError('cannot make a query when unauthorized') raise CredentialsError('cannot make a query when unauthorized')
try: try:
logger.debug('Trying...') logger.trace('Trying...')
r = self.session.get(self.server + endpoint, timeout=timeout) r = self.session.get(self.server + endpoint, timeout=timeout)
except Exception as e: except Exception as e:
@ -479,7 +478,7 @@ class Session(object):
raise ServerError(f'unable to get endpoint {endpoint}') from e raise ServerError(f'unable to get endpoint {endpoint}') from e
if r.url.startswith(SERVER_AUTH): if r.url.startswith(SERVER_AUTH):
logger.debug('Redirected back to Auth Server') logger.info('Redirected back to Auth Server')
# Redirected back to Auth server - force full re-authentication # Redirected back to Auth server - force full re-authentication
self.dump(r) self.dump(r)
self.invalidate() self.invalidate()
@ -521,7 +520,7 @@ class Session(object):
self.retrying = False self.retrying = False
if 'timestamp' not in data: if 'timestamp' not in data:
logger.debug('timestamp not in data, adding from response headers') logger.trace('timestamp not in data, adding from response headers')
data['timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', parsedate(r.headers['Date'])) # type: ignore data['timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', parsedate(r.headers['Date'])) # type: ignore
return data return data

View File

@ -243,7 +243,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
for line in loghandle: for line in loghandle:
try: try:
if b'"event":"Location"' in line: if b'"event":"Location"' in line:
logger.debug('"Location" event in the past at startup') logger.trace('"Location" event in the past at startup')
self.parse_entry(line) # Some events are of interest even in the past self.parse_entry(line) # Some events are of interest even in the past
@ -327,7 +327,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
loghandle.seek(log_pos, SEEK_SET) # reset EOF flag # TODO: log_pos reported as possibly unbound loghandle.seek(log_pos, SEEK_SET) # reset EOF flag # TODO: log_pos reported as possibly unbound
for line in loghandle: for line in loghandle:
if b'"event":"Location"' in line: if b'"event":"Location"' in line:
logger.debug('Found "Location" event, appending to event_queue') logger.trace('Found "Location" event, appending to event_queue')
self.event_queue.append(line) self.event_queue.append(line)
@ -340,7 +340,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# Check whether we're still supposed to be running # Check whether we're still supposed to be running
if threading.current_thread() != self.thread: if threading.current_thread() != self.thread:
logger.inof("We're not meant to be running, exiting...") logger.info("We're not meant to be running, exiting...")
return # Terminate return # Terminate
if self.game_was_running: if self.game_was_running:
@ -538,7 +538,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
self.planet = entry.get('Body') if entry.get('BodyType') == 'Planet' else None self.planet = entry.get('Body') if entry.get('BodyType') == 'Planet' else None
if event_type == 'Location': if event_type == 'Location':
logger.debug('"Location" event') logger.trace('"Location" event')
elif event_type == 'FSDJump': elif event_type == 'FSDJump':
self.planet = None self.planet = None
@ -827,7 +827,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
entry = self.parse_entry(self.event_queue.pop(0)) entry = self.parse_entry(self.event_queue.pop(0))
if entry['event'] == 'Location': if entry['event'] == 'Location':
logger.debug('"Location" event') logger.trace('"Location" event')
if not self.live and entry['event'] not in (None, 'Fileheader'): if not self.live and entry['event'] not in (None, 'Fileheader'):
# Game not running locally, but Journal has been updated # Game not running locally, but Journal has been updated
@ -856,7 +856,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
]) ])
if entry['event'] == 'Location': if entry['event'] == 'Location':
logger.debug('Appending "Location" event to event_queue') logger.trace('Appending "Location" event to event_queue')
self.event_queue.append(json.dumps(entry, separators=(', ', ':'))) self.event_queue.append(json.dumps(entry, separators=(', ', ':')))

View File

@ -304,7 +304,7 @@ def notify_journal_entry(cmdr, is_beta, system, station, entry, state):
:returns: Error message from the first plugin that returns one (if any) :returns: Error message from the first plugin that returns one (if any)
""" """
if entry['event'] in ('Location'): if entry['event'] in ('Location'):
logger.debug('Notifying plugins of "Location" event') logger.trace('Notifying plugins of "Location" event')
error = None error = None
for plugin in PLUGINS: for plugin in PLUGINS:

View File

@ -333,7 +333,7 @@ def journal_entry(
) -> None: ) -> None:
"""Journal Entry hook.""" """Journal Entry hook."""
if entry['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked'): if entry['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked'):
logger.debug(f'''{entry["event"]} logger.trace(f'''{entry["event"]}
Commander: {cmdr} Commander: {cmdr}
System: {system} System: {system}
Station: {station} Station: {station}
@ -431,7 +431,7 @@ entry: {entry!r}'''
this.queue.put((cmdr, materials)) this.queue.put((cmdr, materials))
if entry['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked'): if entry['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked'):
logger.debug(f'''{entry["event"]} logger.trace(f'''{entry["event"]}
Queueing: {entry!r}''' Queueing: {entry!r}'''
) )
this.queue.put((cmdr, entry)) this.queue.put((cmdr, entry))
@ -530,12 +530,12 @@ def worker() -> None:
if should_send(pending): if should_send(pending):
if any(p for p in pending if p['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked')): if any(p for p in pending if p['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked')):
logger.debug("pending has at least one of " logger.trace("pending has at least one of "
"('CarrierJump', 'FSDJump', 'Location', 'Docked')" "('CarrierJump', 'FSDJump', 'Location', 'Docked')"
" and it passed should_send()") " and it passed should_send()")
for p in pending: for p in pending:
if p['event'] in ('Location'): if p['event'] in ('Location'):
logger.debug('"Location" event in pending passed should_send(), ' logger.trace('"Location" event in pending passed should_send(), '
f'timestamp: {p["timestamp"]}') f'timestamp: {p["timestamp"]}')
creds = credentials(cmdr) # TODO: possibly unbound creds = credentials(cmdr) # TODO: possibly unbound
@ -554,13 +554,13 @@ def worker() -> None:
if any(p for p in pending if p['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked')): if any(p for p in pending if p['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked')):
data_elided = data.copy() data_elided = data.copy()
data_elided['apiKey'] = '<elided>' data_elided['apiKey'] = '<elided>'
logger.debug("pending has at least one of " logger.trace("pending has at least one of "
"('CarrierJump', 'FSDJump', 'Location', 'Docked')" "('CarrierJump', 'FSDJump', 'Location', 'Docked')"
" Attempting API cal...") " Attempting API cal...")
for p in pending: for p in pending:
if p['event'] in ('Location'): if p['event'] in ('Location'):
logger.debug('Attempting API call for "Location" event with timestamp: ' logger.trace('Attempting API call for "Location" event with timestamp: '
f'{p["timestamp"]}') f'{p["timestamp"]}')
r = this.session.post('https://www.edsm.net/api-journal-v1', data=data, timeout=_TIMEOUT) r = this.session.post('https://www.edsm.net/api-journal-v1', data=data, timeout=_TIMEOUT)

View File

@ -14,7 +14,7 @@ from typing import TYPE_CHECKING, Any, Callable, Optional, Type, Union
import myNotebook as nb # noqa: N813 import myNotebook as nb # noqa: N813
import plug import plug
from config import applongname, appname, appversion, config from config import applongname, appversion, config
from EDMCLogging import edmclogger, get_main_logger from EDMCLogging import edmclogger, get_main_logger
from hotkey import hotkeymgr from hotkey import hotkeymgr
from l10n import Translations from l10n import Translations
@ -1060,7 +1060,7 @@ class PreferencesDialog(tk.Toplevel):
config.set('system_provider', self.system_provider.get()) config.set('system_provider', self.system_provider.get())
config.set('station_provider', self.station_provider.get()) config.set('station_provider', self.station_provider.get())
config.set('loglevel', self.select_loglevel.get()) config.set('loglevel', self.select_loglevel.get())
edmclogger.get_streamhandler().setLevel(self.select_loglevel.get()) edmclogger.set_console_loglevel(self.select_loglevel.get())
lang_codes = {v: k for k, v in self.languages.items()} # Codes by name lang_codes = {v: k for k, v in self.languages.items()} # Codes by name
config.set('language', lang_codes.get(self.lang.get()) or '') # or '' used here due to Default being None above config.set('language', lang_codes.get(self.lang.get()) or '') # or '' used here due to Default being None above