diff --git a/config.py b/config.py index aa9221023..00ec14f21 100644 --- a/config.py +++ b/config.py @@ -1,17 +1,18 @@ import os import sys -# TODO: move all logging back to pyfa.py main loop -# We moved it here just to avoid rebuilding windows skeleton for now (any change to pyfa.py needs it) -import logging -import logging.handlers +from logbook import Logger + +pyfalog = Logger(__name__) # Load variable overrides specific to distribution type try: import configforced except ImportError: + pyfalog.warning("Failed to import: configforced") configforced = None + # Turns on debug mode debug = False # Defines if our saveddata will be in pyfa root or not @@ -29,23 +30,6 @@ savePath = None saveDB = None gameDB = None - -class StreamToLogger(object): - """ - Fake file-like stream object that redirects writes to a logger instance. - From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ - """ - - def __init__(self, logger, log_level=logging.INFO): - self.logger = logger - self.log_level = log_level - self.linebuf = '' - - def write(self, buf): - for line in buf.rstrip().splitlines(): - self.logger.log(self.log_level, line.rstrip()) - - def isFrozen(): if hasattr(sys, 'frozen'): return True @@ -66,10 +50,7 @@ def defPaths(customSavePath): global gameDB global saveInRoot - if debug: - logLevel = logging.DEBUG - else: - logLevel = logging.WARN + pyfalog.debug("Configuring Pyfa") # The main pyfa directory which contains run.py # Python 2.X uses ANSI by default, so we need to convert the character encoding @@ -97,25 +78,6 @@ def defPaths(customSavePath): os.environ["REQUESTS_CA_BUNDLE"] = getPyfaPath(certName).encode('utf8') os.environ["SSL_CERT_FILE"] = getPyfaPath(certName).encode('utf8') - loggingFormat = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' - logging.basicConfig(format=loggingFormat, level=logLevel) - handler = logging.handlers.RotatingFileHandler(getSavePath("log.txt"), maxBytes=1000000, backupCount=3) - formatter = logging.Formatter(loggingFormat) - handler.setFormatter(formatter) - logging.getLogger('').addHandler(handler) - - logging.info("Starting pyfa") - - if hasattr(sys, 'frozen'): - stdout_logger = logging.getLogger('STDOUT') - sl = StreamToLogger(stdout_logger, logging.INFO) - sys.stdout = sl - - # This interferes with cx_Freeze's own handling of exceptions. Find a way to fix this. - # stderr_logger = logging.getLogger('STDERR') - # sl = StreamToLogger(stderr_logger, logging.ERROR) - # sys.stderr = sl - # The database where we store all the fits etc saveDB = getSavePath("saveddata.db") diff --git a/eos/db/__init__.py b/eos/db/__init__.py index aa320892a..dda1b34ff 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -24,6 +24,9 @@ from sqlalchemy.orm import sessionmaker import migration from eos import config +from logbook import Logger + +pyfalog = Logger(__name__) class ReadOnlyException(Exception): @@ -47,6 +50,7 @@ try: "SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'" ).fetchone()[0] except: + pyfalog.warning("Missing gamedata version.") config.gamedata_version = None saveddata_connectionstring = config.saveddata_connectionstring diff --git a/eos/db/migration.py b/eos/db/migration.py index 6dc59565c..c8fdcb4ac 100644 --- a/eos/db/migration.py +++ b/eos/db/migration.py @@ -1,11 +1,11 @@ -import logging +from logbook import Logger import shutil import time import config import migrations -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) def getVersion(db): @@ -37,7 +37,7 @@ def update(saveddata_engine): for version in xrange(dbVersion, appVersion): func = migrations.updates[version + 1] if func: - logger.info("Applying database update: %d", version + 1) + pyfalog.info("Applying database update: %d", version + 1) func(saveddata_engine) # when all is said and done, set version to current diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py index 994583ce5..0d133d5e6 100644 --- a/eos/db/saveddata/databaseRepair.py +++ b/eos/db/saveddata/databaseRepair.py @@ -18,9 +18,9 @@ # =============================================================================== from sqlalchemy.exc import DatabaseError -import logging +from logbook import Logger -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class DatabaseCleanup(object): @@ -33,7 +33,7 @@ class DatabaseCleanup(object): results = saveddata_engine.execute(query) return results except DatabaseError: - logger.error("Failed to connect to database or error executing query:\n%s", query) + pyfalog.error("Failed to connect to database or error executing query:\n{0}", query) return None @staticmethod @@ -41,7 +41,7 @@ class DatabaseCleanup(object): # Find orphaned character skills. # This solves an issue where the character doesn't exist, but skills for that character do. # See issue #917 - logger.debug("Running database cleanup for character skills.") + pyfalog.debug("Running database cleanup for character skills.") query = "SELECT COUNT(*) AS num FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)" results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -53,14 +53,14 @@ class DatabaseCleanup(object): if row and row['num']: query = "DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)" delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) @staticmethod def OrphanedFitDamagePatterns(saveddata_engine): # Find orphaned damage patterns. # This solves an issue where the damage pattern doesn't exist, but fits reference the pattern. # See issue #777 - logger.debug("Running database cleanup for orphaned damage patterns attached to fits.") + pyfalog.debug("Running database cleanup for orphaned damage patterns attached to fits.") query = "SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL" results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -80,20 +80,20 @@ class DatabaseCleanup(object): rows = uniform_results.fetchall() if len(rows) == 0: - logger.error("Missing uniform damage pattern.") + pyfalog.error("Missing uniform damage pattern.") elif len(rows) > 1: - logger.error("More than one uniform damage pattern found.") + pyfalog.error("More than one uniform damage pattern found.") else: uniform_damage_pattern_id = rows[0]['ID'] update_query = "UPDATE 'fits' SET 'damagePatternID' = {} " \ "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL".format(uniform_damage_pattern_id) update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query) - logger.error("Database corruption found. Cleaning up %d records.", update_results.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount) @staticmethod def OrphanedFitCharacterIDs(saveddata_engine): # Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern. - logger.debug("Running database cleanup for orphaned characters attached to fits.") + pyfalog.debug("Running database cleanup for orphaned characters attached to fits.") query = "SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL" results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -113,22 +113,22 @@ class DatabaseCleanup(object): rows = all5_results.fetchall() if len(rows) == 0: - logger.error("Missing 'All 5' character.") + pyfalog.error("Missing 'All 5' character.") elif len(rows) > 1: - logger.error("More than one 'All 5' character found.") + pyfalog.error("More than one 'All 5' character found.") else: all5_id = rows[0]['ID'] update_query = "UPDATE 'fits' SET 'characterID' = " + str(all5_id) + \ " WHERE characterID not in (select ID from characters) OR characterID IS NULL" update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query) - logger.error("Database corruption found. Cleaning up %d records.", update_results.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount) @staticmethod def NullDamagePatternNames(saveddata_engine): # Find damage patterns that are missing the name. # This solves an issue where the damage pattern ends up with a name that is null. # See issue #949 - logger.debug("Running database cleanup for missing damage pattern names.") + pyfalog.debug("Running database cleanup for missing damage pattern names.") query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name IS NULL OR name = ''" results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -140,14 +140,14 @@ class DatabaseCleanup(object): if row and row['num']: query = "DELETE FROM damagePatterns WHERE name IS NULL OR name = ''" delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) @staticmethod def NullTargetResistNames(saveddata_engine): # Find target resists that are missing the name. # This solves an issue where the target resist ends up with a name that is null. # See issue #949 - logger.debug("Running database cleanup for missing target resist names.") + pyfalog.debug("Running database cleanup for missing target resist names.") query = "SELECT COUNT(*) AS num FROM targetResists WHERE name IS NULL OR name = ''" results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -159,14 +159,14 @@ class DatabaseCleanup(object): if row and row['num']: query = "DELETE FROM targetResists WHERE name IS NULL OR name = ''" delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) @staticmethod def OrphanedFitIDItemID(saveddata_engine): # Orphaned items that are missing the fit ID or item ID. # See issue #954 for table in ['drones', 'cargo', 'fighters']: - logger.debug("Running database cleanup for orphaned %s items.", table) + pyfalog.debug("Running database cleanup for orphaned {0} items.", table) query = "SELECT COUNT(*) AS num FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format( table) results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -180,10 +180,10 @@ class DatabaseCleanup(object): query = "DELETE FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format( table) delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) for table in ['modules']: - logger.debug("Running database cleanup for orphaned %s items.", table) + pyfalog.debug("Running database cleanup for orphaned {0} items.", table) query = "SELECT COUNT(*) AS num FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format( table) results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -196,7 +196,7 @@ class DatabaseCleanup(object): if row and row['num']: query = "DELETE FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table) delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) @staticmethod def NullDamageTargetPatternValues(saveddata_engine): @@ -204,7 +204,7 @@ class DatabaseCleanup(object): # See issue #954 for profileType in ['damagePatterns', 'targetResists']: for damageType in ['em', 'thermal', 'kinetic', 'explosive']: - logger.debug("Running database cleanup for null %s values. (%s)", profileType, damageType) + pyfalog.debug("Running database cleanup for null {0} values. ({1})", profileType, damageType) query = "SELECT COUNT(*) AS num FROM {0} WHERE {1}Amount IS NULL OR {1}Amount = ''".format(profileType, damageType) results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -218,13 +218,13 @@ class DatabaseCleanup(object): query = "UPDATE '{0}' SET '{1}Amount' = '0' WHERE {1}Amount IS NULL OR Amount = ''".format(profileType, damageType) delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) @staticmethod def DuplicateSelectedAmmoName(saveddata_engine): # Orphaned items that are missing the fit ID or item ID. # See issue #954 - logger.debug("Running database cleanup for duplicated selected ammo profiles.") + pyfalog.debug("Running database cleanup for duplicated selected ammo profiles.") query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name = 'Selected Ammo'" results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) @@ -236,4 +236,4 @@ class DatabaseCleanup(object): if row and row['num'] > 1: query = "DELETE FROM damagePatterns WHERE name = 'Selected Ammo'" delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index 0814d901f..e14162523 100644 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -17,9 +17,9 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class HandledList(list): @@ -198,7 +198,7 @@ class HandledImplantBoosterList(HandledList): # if needed, remove booster that was occupying slot oldObj = next((m for m in self if m.slot == thing.slot), None) if oldObj: - logging.info("Slot %d occupied with %s, replacing with %s", thing.slot, oldObj.item.name, thing.item.name) + pyfalog.info("Slot %d occupied with %s, replacing with %s", thing.slot, oldObj.item.name, thing.item.name) oldObj.itemID = 0 # hack to remove from DB. See GH issue #324 self.remove(oldObj) @@ -222,7 +222,7 @@ class HandledProjectedModList(HandledList): oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None) if oldEffect: - logging.info("System effect occupied with %s, replacing with %s", oldEffect.item.name, proj.item.name) + pyfalog.info("System effect occupied with %s, replacing with %s", oldEffect.item.name, proj.item.name) self.remove(oldEffect) HandledList.append(self, proj) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index e25fe48d4..8d017715d 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -2,9 +2,9 @@ # # Used by: # Module: Reactive Armor Hardener -import logging +from logbook import Logger -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) runTime = "late" type = "active" @@ -23,7 +23,7 @@ def handler(fit, module, context): damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'), ) - # logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + # pyfalog.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) resistanceShiftAmount = module.getModifiedItemAttr( 'resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction @@ -39,7 +39,7 @@ def handler(fit, module, context): cycleList = [] loopStart = -20 for num in range(50): - # logger.debug("Starting cycle %d.", num) + # pyfalog.debug("Starting cycle %d.", num) # The strange order is to emulate the ingame sorting when different types have taken the same amount of damage. # This doesn't take into account stacking penalties. In a few cases fitting a Damage Control causes an inaccurate result. damagePattern_tuples = [ @@ -77,7 +77,7 @@ def handler(fit, module, context): RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1 RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2 RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3 - # logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) + # pyfalog.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) # See if the current RAH profile has been encountered before, indicating a loop. for i, val in enumerate(cycleList): @@ -87,7 +87,7 @@ def handler(fit, module, context): abs(RAHResistance[2] - val[2]) <= tolerance and \ abs(RAHResistance[3] - val[3]) <= tolerance: loopStart = i - # logger.debug("Loop found: %d-%d", loopStart, num) + # pyfalog.debug("Loop found: %d-%d", loopStart, num) break if loopStart >= 0: break @@ -95,7 +95,7 @@ def handler(fit, module, context): cycleList.append(list(RAHResistance)) if loopStart < 0: - logger.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: %f/%f/%f/%f", + pyfalog.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: {0}/{1}/{2}/{3}", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) # Average the profiles in the RAH loop, or the last 20 if it didn't find a loop. @@ -110,7 +110,7 @@ def handler(fit, module, context): average[i] = round(average[i] / numCycles, 3) # Set the new resistances - # logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) + # pyfalog.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) for i, attr in enumerate(( 'armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', 'armorExplosiveDamageResonance')): diff --git a/eos/gamedata.py b/eos/gamedata.py index d46f1032b..97df52292 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -30,6 +30,9 @@ try: except ImportError: from utils.compat import OrderedDict +from logbook import Logger +pyfalog = Logger(__name__) + class Effect(EqBase): """ @@ -67,6 +70,8 @@ class Effect(EqBase): if not self.__generated: self.__generateHandler() + pyfalog.debug("Generating effect: {0} ({1}) [runTime: {2}]", self.name, self.effectID, self.runTime) + return self.__handler @property @@ -141,7 +146,7 @@ class Effect(EqBase): @property def isImplemented(self): """ - Wether this effect is implemented in code or not, + Whether this effect is implemented in code or not, unimplemented effects simply do nothing at all when run """ return self.handler != effectDummy @@ -182,13 +187,16 @@ class Effect(EqBase): t = t if isinstance(t, tuple) or t is None else (t,) self.__type = t - except (ImportError, AttributeError): + except (ImportError, AttributeError) as e: self.__handler = effectDummy self.__runTime = "normal" self.__activeByDefault = True self.__type = None + pyfalog.debug("ImportError or AttributeError generating handler:") + pyfalog.warning(e) except Exception as e: - traceback.print_exc(e) + pyfalog.critical("Exception generating handler:") + pyfalog.critical(e) self.__generated = True diff --git a/eos/graph/fitDps.py b/eos/graph/fitDps.py index 221cbb3a1..3c039e903 100644 --- a/eos/graph/fitDps.py +++ b/eos/graph/fitDps.py @@ -21,6 +21,9 @@ from math import log, sin, radians, exp from eos.graph import Graph from eos.saveddata.module import State, Hardpoint +from logbook import Logger + +pyfalog = Logger(__name__) class FitDpsGraph(Graph): @@ -66,6 +69,7 @@ class FitDpsGraph(Graph): val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) data[attr] = val except: + pyfalog.warning("Caught exception in calcDPS.") pass for mod in fit.modules: diff --git a/eos/saveddata/booster.py b/eos/saveddata/booster.py index 0798175d7..a424ff75f 100644 --- a/eos/saveddata/booster.py +++ b/eos/saveddata/booster.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import reconstructor, validates @@ -25,7 +25,7 @@ import eos.db from eos.effectHandlerHelpers import HandledItem from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Booster(HandledItem, ItemAttrShortcut): @@ -47,11 +47,11 @@ class Booster(HandledItem, ItemAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: %d) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not a Booser", self.itemID) + pyfalog.error("Item (id: %d) is not a Booser", self.itemID) return self.build() diff --git a/eos/saveddata/cargo.py b/eos/saveddata/cargo.py index 70bb150c9..1f76063b0 100644 --- a/eos/saveddata/cargo.py +++ b/eos/saveddata/cargo.py @@ -18,7 +18,7 @@ # =============================================================================== import sys -import logging +from logbook import Logger from sqlalchemy.orm import validates, reconstructor @@ -26,7 +26,7 @@ import eos.db from eos.effectHandlerHelpers import HandledItem from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Cargo(HandledItem, ItemAttrShortcut): @@ -47,7 +47,7 @@ class Cargo(HandledItem, ItemAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: %d) does not exist", self.itemID) return self.__itemModifiedAttributes = ModifiedAttributeDict() diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 4332944c2..eea3e6b8f 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -18,7 +18,7 @@ # =============================================================================== -import logging +from logbook import Logger from itertools import chain from sqlalchemy.orm import validates, reconstructor @@ -27,7 +27,7 @@ import eos import eos.db from eos.effectHandlerHelpers import HandledItem, HandledImplantBoosterList -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Character(object): diff --git a/eos/saveddata/citadel.py b/eos/saveddata/citadel.py index 6e1654028..ccb68fc78 100644 --- a/eos/saveddata/citadel.py +++ b/eos/saveddata/citadel.py @@ -17,16 +17,17 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from eos.saveddata.ship import Ship -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Citadel(Ship): def validate(self, item): if item.category.name != "Structure": + pyfalog.error("Passed item '{0}' (category: {1}) is not under Structure category",item.name, item.category.name) raise ValueError( 'Passed item "%s" (category: (%s)) is not under Structure category' % (item.name, item.category.name)) diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index 9eaf9fc65..307d103a6 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import validates, reconstructor @@ -25,7 +25,7 @@ import eos.db from eos.effectHandlerHelpers import HandledItem, HandledCharge from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @@ -53,11 +53,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: %d) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not a Drone", self.itemID) + pyfalog.error("Item (id: %d) is not a Drone", self.itemID) return self.build() diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index cc363ef0f..1051a187d 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import validates, reconstructor @@ -27,7 +27,7 @@ from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, C from eos.saveddata.fighterAbility import FighterAbility from eos.saveddata.module import Slot -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @@ -61,11 +61,11 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: %d) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not a Fighter", self.itemID) + pyfalog.error("Item (id: %d) is not a Fighter", self.itemID) return self.build() diff --git a/eos/saveddata/fighterAbility.py b/eos/saveddata/fighterAbility.py index cb7c34912..54aad6f07 100644 --- a/eos/saveddata/fighterAbility.py +++ b/eos/saveddata/fighterAbility.py @@ -17,11 +17,11 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import reconstructor -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class FighterAbility(object): @@ -59,7 +59,7 @@ class FighterAbility(object): if self.effectID: self.__effect = next((x for x in self.fighter.item.effects.itervalues() if x.ID == self.effectID), None) if self.__effect is None: - logger.error("Effect (id: %d) does not exist", self.effectID) + pyfalog.error("Effect (id: %d) does not exist", self.effectID) return self.build() diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 8241f9579..b275aef5a 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -33,9 +33,9 @@ from eos.saveddata.character import Character from eos.saveddata.citadel import Citadel from eos.saveddata.module import Module, State, Slot, Hardpoint from utils.timer import Timer -import logging +from logbook import Logger -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class ImplantLocation(Enum): @@ -84,7 +84,7 @@ class Fit(object): if self.shipID: item = eos.db.getItem(self.shipID) if item is None: - logger.error("Item (id: %d) does not exist", self.shipID) + pyfalog.error("Item (id: {0}) does not exist", self.shipID) return try: @@ -97,7 +97,7 @@ class Fit(object): # change all instances in source). Remove this at some point self.extraAttributes = self.__ship.itemModifiedAttributes except ValueError: - logger.error("Item (id: %d) is not a Ship", self.shipID) + pyfalog.error("Item (id: {0}) is not a Ship", self.shipID) return if self.modeID and self.__ship: @@ -448,7 +448,7 @@ class Fit(object): self.commandBonuses[warfareBuffID] = (runTime, value, module, effect) def __runCommandBoosts(self, runTime="normal"): - logger.debug("Applying gang boosts for %r", self) + pyfalog.debug("Applying gang boosts for {0}", self) for warfareBuffID in self.commandBonuses.keys(): # Unpack all data required to run effect properly effect_runTime, value, thing, effect = self.commandBonuses[warfareBuffID] @@ -630,21 +630,23 @@ class Fit(object): del self.commandBonuses[warfareBuffID] def calculateModifiedAttributes(self, targetFit=None, withBoosters=False, dirtyStorage=None): - timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), logger) - logger.debug("Starting fit calculation on: %r, withBoosters: %s", self, withBoosters) + # TODO: Validate that remooving this doesn't break anything + # TODO: Keywords: logbook logging Ebag + # timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), logger) + pyfalog.debug("Starting fit calculation on: {0}, withBoosters: {1}", self, withBoosters) shadow = False if targetFit and not withBoosters: - logger.debug("Applying projections to target: %r", targetFit) + pyfalog.debug("Applying projections to target: {0}", targetFit) projectionInfo = self.getProjectionInfo(targetFit.ID) - logger.debug("ProjectionInfo: %s", projectionInfo) + pyfalog.debug("ProjectionInfo: {0}", projectionInfo) if self == targetFit: copied = self # original fit shadow = True # Don't inspect this, we genuinely want to reassign self # noinspection PyMethodFirstArgAssignment self = deepcopy(self) - logger.debug("Handling self projection - making shadow copy of fit. %r => %r", copied, self) + pyfalog.debug("Handling self projection - making shadow copy of fit. {0} => {1}", copied, self) # we delete the fit because when we copy a fit, flush() is # called to properly handle projection updates. However, we do # not want to save this fit to the database, so simply remove it @@ -679,7 +681,7 @@ class Fit(object): # projection have modifying stuff applied, such as gang boosts and other # local modules that may help if self.__calculated and not projected and not withBoosters: - logger.debug("Fit has already been calculated and is not projected, returning: %r", self) + pyfalog.debug("Fit has already been calculated and is not projected, returning: {0}", self) return for runTime in ("early", "normal", "late"): @@ -736,7 +738,8 @@ class Fit(object): if not withBoosters and self.commandBonuses: self.__runCommandBoosts(runTime) - timer.checkpoint('Done with runtime: %s' % runTime) + # TODO: Tied with timer above + # timer.checkpoint('Done with runtime: %s' % runTime) # Mark fit as calculated self.__calculated = True @@ -747,10 +750,11 @@ class Fit(object): if fit.getProjectionInfo(self.ID).active: fit.calculateModifiedAttributes(self, withBoosters=withBoosters, dirtyStorage=dirtyStorage) - timer.checkpoint('Done with fit calculation') + # TODO: Tied with timer above + # timer.checkpoint('Done with fit calculation') if shadow: - logger.debug("Delete shadow fit object") + pyfalog.debug("Delete shadow fit object") del self def fill(self): diff --git a/eos/saveddata/implant.py b/eos/saveddata/implant.py index b2185542a..e7174f2ff 100644 --- a/eos/saveddata/implant.py +++ b/eos/saveddata/implant.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import validates, reconstructor @@ -25,7 +25,7 @@ import eos.db from eos.effectHandlerHelpers import HandledItem from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Implant(HandledItem, ItemAttrShortcut): @@ -46,11 +46,11 @@ class Implant(HandledItem, ItemAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: %d) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not an Implant", self.itemID) + pyfalog.error("Item (id: %d) is not an Implant", self.itemID) return self.build() diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index ba41d559d..55d8c7ed1 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import validates, reconstructor @@ -28,7 +28,7 @@ from eos.mathUtils import floorFloat from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut from eos.saveddata.citadel import Citadel -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class State(Enum): @@ -94,11 +94,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: %d) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not a Module", self.itemID) + pyfalog.error("Item (id: %d) is not a Module", self.itemID) return if self.chargeID: diff --git a/eos/saveddata/override.py b/eos/saveddata/override.py index c4f8a1e02..f3f84425d 100644 --- a/eos/saveddata/override.py +++ b/eos/saveddata/override.py @@ -17,14 +17,14 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import reconstructor import eos.db from eos.eqBase import EqBase -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Override(EqBase): @@ -43,13 +43,13 @@ class Override(EqBase): if self.attrID: self.__attr = eos.db.getAttributeInfo(self.attrID) if self.__attr is None: - logger.error("Attribute (id: %d) does not exist", self.attrID) + pyfalog.error("Attribute (id: %d) does not exist", self.attrID) return if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: %d) does not exist", self.itemID) return @property diff --git a/eos/saveddata/ship.py b/eos/saveddata/ship.py index 4e4020cc7..1e3fd41f0 100644 --- a/eos/saveddata/ship.py +++ b/eos/saveddata/ship.py @@ -17,14 +17,14 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger import eos.db from eos.effectHandlerHelpers import HandledItem from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, cappingAttrKeyCache from eos.saveddata.mode import Mode -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Ship(ItemAttrShortcut, HandledItem): @@ -63,6 +63,7 @@ class Ship(ItemAttrShortcut, HandledItem): def validate(self, item): if item.category.name != "Ship": + pyfalog.error("Passed item '{0}' (category: {1}) is not under Ship category", item.name, item.category.name) raise ValueError( 'Passed item "%s" (category: (%s)) is not under Ship category' % (item.name, item.category.name)) diff --git a/gui/bitmapLoader.py b/gui/bitmapLoader.py index cb47e24bd..9ebfab114 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmapLoader.py @@ -27,6 +27,9 @@ import wx import config +from logbook import Logger +logging = Logger(__name__) + try: from collections import OrderedDict except ImportError: @@ -35,8 +38,10 @@ except ImportError: class BitmapLoader(object): try: + logging.info("Using zipped image files.") archive = zipfile.ZipFile(config.getPyfaPath('imgs.zip'), 'r') except IOError: + logging.info("Using local image files.") archive = None cachedBitmaps = OrderedDict() diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 2c6c926dc..1a6f81e48 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -33,7 +33,7 @@ from gui.builtinViewColumns.state import State from gui.bitmapLoader import BitmapLoader import gui.builtinViews.emptyView from gui.utils.exportHtml import exportHtml -from logging import getLogger +from logbook import Logger from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED from service.fit import Fit @@ -41,7 +41,7 @@ from service.market import Market import gui.globalEvents as GE -logger = getLogger(__name__) +pyfalog = Logger(__name__) # Tab spawning handler @@ -62,6 +62,7 @@ class FitSpawner(gui.multiSwitch.TabSpawner): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=event.fitID)) break except: + pyfalog.warning("Caught exception in fitSelected") pass if count < 0: startup = getattr(event, "startup", False) # see OpenFitsThread in gui.mainFrame @@ -278,6 +279,7 @@ class FittingView(d.Display): sFit.refreshFit(self.getActiveFit()) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) except wx._core.PyDeadObjectError: + pyfalog.warning("Caught dead object") pass event.Skip() @@ -414,7 +416,7 @@ class FittingView(d.Display): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) else: - logger.error("Missing module position for: %s", str(getattr(mod2, "ID", "Unknown"))) + pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown"))) def generateMods(self): """ @@ -483,7 +485,7 @@ class FittingView(d.Display): self.Show(self.activeFitID is not None and self.activeFitID == event.fitID) except wx._core.PyDeadObjectError: - pass + pyfalog.warning("Caught dead object") finally: event.Skip() @@ -637,14 +639,14 @@ class FittingView(d.Display): try: self.MakeSnapshot() except: - pass + pyfalog.warning("Failed to make snapshot") def OnShow(self, event): if event.GetShow(): try: self.MakeSnapshot() except: - pass + pyfalog.warning("Failed to make snapshot") event.Skip() def Snapshot(self): @@ -670,7 +672,7 @@ class FittingView(d.Display): try: fit = sFit.getFit(self.activeFitID) except: - return + pyfalog.warning("Failed to get fit") if fit is None: return diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 1c7044f3e..8006fa56a 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -33,6 +33,8 @@ from service.fit import Fit from service.character import Character from service.network import AuthenticationError, TimeoutError from service.market import Market +from logbook import Logger +pyfalog = Logger(__name__) class CharacterTextValidor(BaseValidator): @@ -55,6 +57,7 @@ class CharacterTextValidor(BaseValidator): return True except ValueError, e: + pyfalog.error(e) wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -628,11 +631,16 @@ class APIView(wx.Panel): try: activeChar = self.charEditor.entityEditor.getActiveEntity() list = sChar.apiCharList(activeChar.ID, self.inputID.GetLineText(0), self.inputKey.GetLineText(0)) - except AuthenticationError: - self.stStatus.SetLabel("Authentication failure. Please check keyID and vCode combination.") - except TimeoutError: - self.stStatus.SetLabel("Request timed out. Please check network connectivity and/or proxy settings.") + except AuthenticationError, e: + msg = "Authentication failure. Please check keyID and vCode combination." + pyfalog.info(msg) + self.stStatus.SetLabel(msg) + except TimeoutError, e: + msg = "Request timed out. Please check network connectivity and/or proxy settings." + pyfalog.info(msg) + self.stStatus.SetLabel(msg) except Exception, e: + pyfalog.error(e) self.stStatus.SetLabel("Error:\n%s" % e.message) else: self.charChoice.Clear() @@ -655,6 +663,7 @@ class APIView(wx.Panel): sChar.apiFetch(activeChar.ID, charName) self.stStatus.SetLabel("Successfully fetched %s\'s skills from EVE API." % charName) except Exception, e: + pyfalog.error("Unable to retrieve {0}\'s skills. Error message:\n{1}", charName, e) self.stStatus.SetLabel("Unable to retrieve %s\'s skills. Error message:\n%s" % (charName, e)) diff --git a/gui/characterSelection.py b/gui/characterSelection.py index e85cd081b..d00026173 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -24,6 +24,8 @@ import gui.globalEvents as GE import gui.mainFrame from service.character import Character from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) class CharacterSelection(wx.Panel): @@ -114,6 +116,7 @@ class CharacterSelection(wx.Panel): sChar.apiFetch(self.getActiveCharacter(), charName) except: # can we do a popup, notifying user of API error? + pyfalog.error("API fetch error") pass self.refreshCharacterList() diff --git a/gui/chromeTabs.py b/gui/chromeTabs.py index 3265b8d8a..f052f28a6 100644 --- a/gui/chromeTabs.py +++ b/gui/chromeTabs.py @@ -25,9 +25,11 @@ import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.fonts as fonts from gui.bitmapLoader import BitmapLoader - +from logbook import Logger from service.fit import Fit +pyfalog = Logger(__name__) + _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() _PageAdding, EVT_NOTEBOOK_PAGE_ADDING = wx.lib.newevent.NewEvent() @@ -1094,6 +1096,7 @@ class PFTabsContainer(wx.Panel): self.previewTimer.Start(500, True) break except: + pyfalog.warning("Exception caught in CheckTabPreview.") pass def CheckAddHighlighted(self, x, y): diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 7731c0120..848f02181 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -19,9 +19,9 @@ # noinspection PyPackageRequirements import wx -import logging +from logbook import Logger -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class ContextMenu(object): @@ -121,7 +121,7 @@ class ContextMenu(object): debug_end = len(cls._ids) if debug_end - debug_start: - logger.debug("%d new IDs created for this menu" % (debug_end - debug_start)) + pyfalog.debug("%d new IDs created for this menu" % (debug_end - debug_start)) return rootMenu if empty is False else None diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 181841712..19e6a22b0 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -14,6 +14,9 @@ from eos.db import getItem from gui.display import Display import gui.globalEvents as GE +from logbook import Logger +pyfalog = Logger(__name__) + if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): from service.crest import Crest, CrestModes @@ -147,7 +150,9 @@ class CrestFittings(wx.Frame): self.fitTree.populateSkillTree(fittings) del waitDialog except requests.exceptions.ConnectionError: - self.statusbar.SetStatusText("Connection error, please check your internet connection") + msg = "Connection error, please check your internet connection" + pyfalog.error(msg) + self.statusbar.SetStatusText(msg) def importFitting(self, event): selection = self.fitView.fitSelection @@ -173,7 +178,9 @@ class CrestFittings(wx.Frame): try: sCrest.delFitting(self.getActiveCharacter(), data['fittingID']) except requests.exceptions.ConnectionError: - self.statusbar.SetStatusText("Connection error, please check your internet connection") + msg = "Connection error, please check your internet connection" + pyfalog.error(msg) + self.statusbar.SetStatusText(msg) class ExportToEve(wx.Frame): @@ -281,9 +288,12 @@ class ExportToEve(wx.Frame): text = json.loads(res.text) self.statusbar.SetStatusText(text['message'], 1) except ValueError: + pyfalog.warning("Value error on loading JSON.") self.statusbar.SetStatusText("", 1) except requests.exceptions.ConnectionError: - self.statusbar.SetStatusText("Connection error, please check your internet connection", 1) + msg = "Connection error, please check your internet connection" + pyfalog.error(msg) + self.statusbar.SetStatusText(msg) class CrestMgmt(wx.Dialog): @@ -406,6 +416,7 @@ class FittingsTreeView(wx.Panel): cargo.amount = item['quantity'] list.append(cargo) except: + pyfalog.error("Exception caught in displayFit") pass self.parent.fitView.fitSelection = selection diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 843033c5b..1cf6e116d 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -18,7 +18,7 @@ # ============================================================================= import os -import logging +from logbook import Logger import imp # noinspection PyPackageRequirements @@ -42,7 +42,7 @@ except ImportError: mplImported = False -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class GraphFrame(wx.Frame): @@ -73,7 +73,7 @@ class GraphFrame(wx.Frame): self.legendFix = False if not graphFrame_enabled: - logger.info("Problems importing matplotlib; continuing without graphs") + pyfalog.info("Problems importing matplotlib; continuing without graphs") return try: diff --git a/gui/mainFrame.py b/gui/mainFrame.py index e2eb3a7c7..5cd31e83c 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -19,7 +19,7 @@ import sys import os.path -import logging +from logbook import Logger import sqlalchemy # noinspection PyPackageRequirements @@ -94,7 +94,7 @@ if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) disableOverrideEditor = True -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) # dummy panel(no paint no erasebk) @@ -145,6 +145,7 @@ class MainFrame(wx.Frame): return cls.__instance if cls.__instance is not None else MainFrame() def __init__(self, title="pyfa"): + pyfalog.debug("Initialize MainFrame") self.title = title wx.Frame.__init__(self, None, wx.ID_ANY, self.title) @@ -399,7 +400,7 @@ class MainFrame(wx.Frame): try: dlg.Destroy() except PyDeadObjectError: - logger.error("Tried to destroy an object that doesn't exist in .") + pyfalog.error("Tried to destroy an object that doesn't exist in .") def showImplantSetEditor(self, event): ImplantSetEditorDlg(self) @@ -427,7 +428,7 @@ class MainFrame(wx.Frame): try: dlg.Destroy() except PyDeadObjectError: - logger.error("Tried to destroy an object that doesn't exist in .") + pyfalog.error("Tried to destroy an object that doesn't exist in .") return with open(path, "w", encoding="utf-8") as openfile: @@ -437,7 +438,7 @@ class MainFrame(wx.Frame): try: dlg.Destroy() except PyDeadObjectError: - logger.error("Tried to destroy an object that doesn't exist in .") + pyfalog.error("Tried to destroy an object that doesn't exist in .") def showPreferenceDialog(self, event): dlg = PreferenceDialog(self) @@ -734,7 +735,7 @@ class MainFrame(wx.Frame): try: fits = Port().importFitFromBuffer(clipboard, self.getActiveFit()) except: - logger.error("Attempt to import failed:\n%s", clipboard) + pyfalog.error("Attempt to import failed:\n{0}", clipboard) else: self._openAfterImport(fits) @@ -754,7 +755,7 @@ class MainFrame(wx.Frame): try: dlg.Destroy() except PyDeadObjectError: - logger.error("Tried to destroy an object that doesn't exist in .") + pyfalog.error("Tried to destroy an object that doesn't exist in .") def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ @@ -811,7 +812,7 @@ class MainFrame(wx.Frame): try: dlg.Destroy() except PyDeadObjectError: - logger.error("Tried to destroy an object that doesn't exist in .") + pyfalog.error("Tried to destroy an object that doesn't exist in .") def backupToXml(self, event): """ Back up all fits to EVE XML file """ diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index a9b42cb4b..00402a7a3 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -26,6 +26,9 @@ import gui.graphFrame import gui.globalEvents as GE from gui.bitmapLoader import BitmapLoader +from logbook import Logger +pyfalog = Logger(__name__) + if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): from service.crest import Crest from service.crest import CrestModes @@ -33,6 +36,7 @@ if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION class MainMenuBar(wx.MenuBar): def __init__(self, mainFrame): + pyfalog.debug("Initialize MainMenuBar") self.characterEditorId = wx.NewId() self.damagePatternEditorId = wx.NewId() self.targetResistsEditorId = wx.NewId() @@ -166,6 +170,7 @@ class MainMenuBar(wx.MenuBar): self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) def fitChanged(self, event): + pyfalog.debug("fitChanged triggered") enable = event.fitID is not None self.Enable(wx.ID_SAVEAS, enable) self.Enable(wx.ID_COPY, enable) diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index 0ee1b008b..0afb2b3b4 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -26,6 +26,9 @@ import gui.PFSearchBox as SBox from gui.cachingImageList import CachingImageList from gui.contextMenu import ContextMenu from gui.bitmapLoader import BitmapLoader +from logbook import Logger + +pyfalog = Logger(__name__) ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent() @@ -56,6 +59,7 @@ class MetaButton(wx.ToggleButton): class MarketBrowser(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) + pyfalog.debug("Initialize marketBrowser") vbox = wx.BoxSizer(wx.VERTICAL) self.SetSizer(vbox) @@ -134,6 +138,7 @@ class SearchBox(SBox.PFSearchBox): class MarketTree(wx.TreeCtrl): def __init__(self, parent, marketBrowser): wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) + pyfalog.debug("Initialize marketTree") self.root = self.AddRoot("root") self.imageList = CachingImageList(16, 16) @@ -183,6 +188,7 @@ class MarketTree(wx.TreeCtrl): try: childId = self.AppendItem(root, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) except: + pyfalog.debug("Error appending item.") continue if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: self.AppendItem(childId, "dummy") @@ -226,6 +232,7 @@ class ItemView(Display): def __init__(self, parent, marketBrowser): Display.__init__(self, parent) + pyfalog.debug("Initialize ItemView") marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.selectionMade) self.unfilteredStore = set() @@ -252,6 +259,7 @@ class ItemView(Display): self.metaMap = self.makeReverseMetaMap() # Fill up recently used modules set + pyfalog.debug("Fill up recently used modules set") for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) diff --git a/gui/patternEditor.py b/gui/patternEditor.py index ed63fd9a3..879683373 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -25,6 +25,9 @@ from wx.lib.intctrl import IntCtrl from gui.utils.clipboard import toClipboard, fromClipboard from gui.builtinViews.entityEditor import EntityEditor, BaseValidator from service.damagePattern import DamagePattern, ImportError +from logbook import Logger + +pyfalog = Logger(__name__) class DmgPatternTextValidor(BaseValidator): @@ -47,6 +50,7 @@ class DmgPatternTextValidor(BaseValidator): return True except ValueError as e: + pyfalog.error(e) wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -256,9 +260,13 @@ class DmgPatternEditorDlg(wx.Dialog): sDP.importPatterns(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") except ImportError as e: + pyfalog.error(e) self.stNotice.SetLabel(str(e)) - except Exception: - self.stNotice.SetLabel("Could not import from clipboard: unknown errors") + except Exception as e: + msg = "Could not import from clipboard: unknown errors" + pyfalog.warning(msg) + pyfalog.error(e) + self.stNotice.SetLabel(msg) finally: self.entityEditor.refreshEntityList() else: diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index 81a305a11..3416d591a 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -1,5 +1,5 @@ import csv -import logging +from logbook import Logger # noinspection PyPackageRequirements import wx @@ -21,7 +21,7 @@ import gui.PFSearchBox as SBox from gui.marketBrowser import SearchBox from gui.bitmapLoader import BitmapLoader -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class AttributeEditor(wx.Frame): @@ -270,7 +270,7 @@ class AttributeGrid(wxpg.PropertyGrid): self.itemView.updateItems() - logger.debug('%s changed to "%s"' % (p.GetName(), p.GetValueAsString())) + pyfalog.debug('{0} changed to "{1}"', p.GetName(), p.GetValueAsString()) def OnPropGridSelect(self, event): pass diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 7e5e3cd62..c8bab84bb 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -23,6 +23,9 @@ from service.targetResists import TargetResists from gui.bitmapLoader import BitmapLoader from gui.utils.clipboard import toClipboard, fromClipboard from gui.builtinViews.entityEditor import EntityEditor, BaseValidator +from logbook import Logger + +pyfalog = Logger(__name__) class TargetResistsTextValidor(BaseValidator): @@ -45,6 +48,7 @@ class TargetResistsTextValidor(BaseValidator): return True except ValueError as e: + pyfalog.error(e) wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -230,10 +234,14 @@ class ResistsEditorDlg(wx.Dialog): except ValueError: editObj.SetForegroundColour(wx.RED) - self.stNotice.SetLabel("Incorrect Formatting (decimals only)") + msg = "Incorrect Formatting (decimals only)" + pyfalog.warning(msg) + self.stNotice.SetLabel(msg) except AssertionError: editObj.SetForegroundColour(wx.RED) - self.stNotice.SetLabel("Incorrect Range (must be 0-100)") + msg = "Incorrect Range (must be 0-100)" + pyfalog.warning(msg) + self.stNotice.SetLabel(msg) finally: # Refresh for color changes to take effect immediately self.Refresh() @@ -271,9 +279,13 @@ class ResistsEditorDlg(wx.Dialog): sTR.importPatterns(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") except ImportError as e: + pyfalog.error(e) self.stNotice.SetLabel(str(e)) - except Exception: - self.stNotice.SetLabel("Could not import from clipboard: unknown errors") + except Exception as e: + msg = "Could not import from clipboard:" + pyfalog.warning(msg) + pyfalog.error(e) + self.stNotice.SetLabel(msg) finally: self.entityEditor.refreshEntityList() else: diff --git a/gui/setEditor.py b/gui/setEditor.py index 036444e18..d199c1cc2 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -17,7 +17,7 @@ # along with pyfa. If not, see . # ============================================================================= -import logging +from logbook import Logger # noinspection PyPackageRequirements import wx @@ -26,7 +26,7 @@ from gui.builtinViews.implantEditor import BaseImplantEditorView from gui.utils.clipboard import toClipboard, fromClipboard from gui.builtinViews.entityEditor import EntityEditor, BaseValidator -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class ImplantTextValidor(BaseValidator): @@ -49,6 +49,7 @@ class ImplantTextValidor(BaseValidator): return True except ValueError as e: + pyfalog.error(e) wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -198,9 +199,10 @@ class ImplantSetEditorDlg(wx.Dialog): self.stNotice.SetLabel("Patterns successfully imported from clipboard") self.showInput(False) except ImportError as e: + pyfalog.error(e) self.stNotice.SetLabel(str(e)) except Exception as e: - logging.exception("Unhandled Exception") + pyfalog.error(e) self.stNotice.SetLabel("Could not import from clipboard: unknown errors") finally: self.updateChoices() diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 9822c699d..466e02e34 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -21,6 +21,8 @@ import gui.utils.animEffects as animEffects from gui.PFListPane import PFListPane from gui.contextMenu import ContextMenu from gui.bitmapLoader import BitmapLoader +from logbook import Logger +pyfalog = Logger(__name__) FitRenamed, EVT_FIT_RENAMED = wx.lib.newevent.NewEvent() FitSelected, EVT_FIT_SELECTED = wx.lib.newevent.NewEvent() @@ -682,6 +684,7 @@ class ShipBrowser(wx.Panel): self.lpane.Freeze() self.lpane.RemoveAllChildren() + pyfalog.debug("Populate ship category list.") if len(self.categoryList) == 0: # set cache of category list self.categoryList = list(sMkt.getShipRoot()) diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index 802a8358b..8e4fd3ad2 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -5,6 +5,9 @@ import wx from service.settings import HTMLExportSettings from service.fit import Fit from service.market import Market +from logbook import Logger + +pyfalog = Logger(__name__) class exportHtml(object): @@ -196,6 +199,7 @@ class exportHtmlThread(threading.Thread): HTMLgroup += '
  • ' + ship.name + ": " + \ fit[1] + '
  • \n' except: + pyfalog.warning("Failed to export line") pass finally: if self.callback: @@ -218,6 +222,7 @@ class exportHtmlThread(threading.Thread): HTMLship += '
  • ' + fit[ 1] + '
  • \n' except: + pyfalog.warning("Failed to export line") continue finally: if self.callback: @@ -270,6 +275,7 @@ class exportHtmlThread(threading.Thread): HTML += '' + ship.name + ': ' + \ fit[1] + '
    \n' except: + pyfalog.error("Failed to export line") continue finally: if self.callback: diff --git a/pyfa.py b/pyfa.py index 6453d373e..6526e4f18 100755 --- a/pyfa.py +++ b/pyfa.py @@ -19,11 +19,17 @@ # ============================================================================== import sys +import os +import os.path import re import config from optparse import OptionParser, BadOptionError, AmbiguousOptionError +from logbook import TimedRotatingFileHandler, Logger, StreamHandler, NestedSetup, FingersCrossedHandler, NullHandler, \ + CRITICAL, ERROR, WARNING, DEBUG, INFO +pyfalog = Logger(__name__) + class PassThroughOptionParser(OptionParser): """ @@ -36,9 +42,29 @@ class PassThroughOptionParser(OptionParser): try: OptionParser._process_args(self, largs, rargs, values) except (BadOptionError, AmbiguousOptionError) as e: + pyfalog.error("Bad startup option passed.") largs.append(e.opt_str) +class LoggerWriter: + def __init__(self, level): + # self.level is really like using log.debug(message) + # at least in my case + self.level = level + + def write(self, message): + # if statement reduces the amount of newlines that are + # printed to the logger + if message not in {'\n', ' '}: + self.level(message.replace("\n", "")) + + def flush(self): + # create a flush method so things can be flushed when + # the system wants to. Not sure if simply 'printing' + # sys.stderr is the correct way to do it, but it seemed + # to work properly for me. + self.level(sys.stderr) + # Parse command line options usage = "usage: %prog [--root]" parser = PassThroughOptionParser(usage=usage) @@ -47,9 +73,23 @@ parser.add_option("-w", "--wx28", action="store_true", dest="force28", help="For parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Set logger to debug level.", default=False) parser.add_option("-t", "--title", action="store", dest="title", help="Set Window Title", default=None) parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set the folder for savedata", default=None) +parser.add_option("-l", "--logginglevel", action="store", dest="logginglevel", help="Set the desired logging level (Critical, Error, Warning, Info, Debug)", default="Error") (options, args) = parser.parse_args() +if options.logginglevel == "Critical": + options.logginglevel = CRITICAL +elif options.logginglevel == "Error": + options.logginglevel = ERROR +elif options.logginglevel == "Warning": + options.logginglevel = WARNING +elif options.logginglevel == "Info": + options.logginglevel = INFO +elif options.logginglevel == "Debug": + options.logginglevel = DEBUG +else: + options.logginglevel = ERROR + if not hasattr(sys, 'frozen'): if sys.version_info < (2, 6) or sys.version_info > (3, 0): @@ -120,26 +160,119 @@ if __name__ == "__main__": config.defPaths(options.savepath) # Basic logging initialization - import logging - logging.basicConfig() - # Import everything - # noinspection PyPackageRequirements - import wx - import os - import os.path + # Logging levels: + ''' + logbook.CRITICAL + logbook.ERROR + logbook.WARNING + logbook.INFO + logbook.DEBUG + logbook.NOTSET + ''' - import eos.db - # noinspection PyUnresolvedReferences - import service.prefetch # noqa: F401 - from gui.mainFrame import MainFrame + if options.debug: + savePath_filename = "Pyfa_debug.log" + else: + savePath_filename = "Pyfa.log" - # Make sure the saveddata db exists - if not os.path.exists(config.savePath): - os.mkdir(config.savePath) + savePath_Destination = config.getSavePath(savePath_filename) - eos.db.saveddata_meta.create_all() + try: + if options.debug: + logging_mode = "Debug" + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + StreamHandler( + sys.stdout, + bubble=False, + level=options.logginglevel + ), + TimedRotatingFileHandler( + savePath_Destination, + level=0, + backup_count=3, + bubble=True, + date_format='%Y-%m-%d', + ), + ]) + else: + logging_mode = "User" + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + FingersCrossedHandler( + TimedRotatingFileHandler( + savePath_Destination, + level=0, + backup_count=3, + bubble=False, + date_format='%Y-%m-%d', + ), + action_level=ERROR, + buffer_size=1000, + # pull_information=True, + # reset=False, + ) + ]) + except: + logging_mode = "Console Only" + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + StreamHandler( + sys.stdout, + bubble=False + ) + ]) - pyfa = wx.App(False) - MainFrame(options.title) - pyfa.MainLoop() + with logging_setup.threadbound(): + # Don't redirect if frozen + if not hasattr(sys, 'frozen') and not options.debug: + # Output all stdout (print) messages as warnings + try: + sys.stdout = LoggerWriter(pyfalog.warning) + except ValueError, Exception: + pyfalog.critical("Cannot access log file. Continuing without writing stdout to log.") + # Output all stderr (stacktrace) messages as critical + try: + sys.stderr = LoggerWriter(pyfalog.critical) + except ValueError, Exception: + pyfalog.critical("Cannot access log file. Continuing without writing stderr to log.") + + pyfalog.info("Starting Pyfa") + pyfalog.info("Running in logging mode: {0}", logging_mode) + + if hasattr(sys, 'frozen') and options.debug: + pyfalog.critical("Running in frozen mode with debug turned on. Forcing all output to be written to log.") + + # Import everything + pyfalog.debug("Import wx") + # noinspection PyPackageRequirements + import wx + + pyfalog.debug("Import eos.db") + import eos.db + + pyfalog.debug("Run prefetch") + # noinspection PyUnresolvedReferences + import service.prefetch # noqa: F401 + + if not os.path.exists(config.savePath): + pyfalog.debug("Saveddata path does not exist, creating new path") + os.mkdir(config.savePath) + else: + pyfalog.debug("Using existing saveddata path: {0}", config.savePath) + + eos.db.saveddata_meta.create_all() + + pyfa = wx.App(False) + pyfalog.debug("Show GUI") + from gui.mainFrame import MainFrame + MainFrame(options.title) + pyfalog.debug("Run MainLoop()") + pyfa.MainLoop() diff --git a/requirements.txt b/requirements.txt index 2d8894113..ed301fe25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +logbook matplotlib PyYAML python-dateutil diff --git a/service/character.py b/service/character.py index 830eb5d57..dba74b2cb 100644 --- a/service/character.py +++ b/service/character.py @@ -20,7 +20,7 @@ import copy import itertools import json -import logging +from logbook import Logger import threading from codecs import open from xml.etree import ElementTree @@ -39,7 +39,7 @@ from eos.saveddata.character import Character as es_Character from eos.saveddata.module import Slot as es_Slot, Module as es_Module from eos.saveddata.fighter import Fighter as es_Fighter -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class CharacterImportThread(threading.Thread): @@ -72,7 +72,7 @@ class CharacterImportThread(threading.Thread): charFile = open(path, mode='r').read() doc = minidom.parseString(charFile) if doc.documentElement.tagName not in ("SerializableCCPCharacter", "SerializableUriCharacter"): - logger.error("Incorrect EVEMon XML sheet") + pyfalog.error("Incorrect EVEMon XML sheet") raise RuntimeError("Incorrect EVEMon XML sheet") name = doc.getElementsByTagName("name")[0].firstChild.nodeValue skill_els = doc.getElementsByTagName("skill") @@ -84,7 +84,7 @@ class CharacterImportThread(threading.Thread): "level": int(skill.getAttribute("level")), }) else: - logger.error("Attempted to import unknown skill %s (ID: %s) (Level: %s)", + pyfalog.error("Attempted to import unknown skill %s (ID: %s) (Level: %s)", skill.getAttribute("name"), skill.getAttribute("typeID"), skill.getAttribute("level"), @@ -92,8 +92,8 @@ class CharacterImportThread(threading.Thread): char = sCharacter.new(name + " (EVEMon)") sCharacter.apiUpdateCharSheet(char.ID, skills) except Exception, e: - logger.error("Exception on character import:") - logger.error(e) + pyfalog.error("Exception on character import:") + pyfalog.error(e) continue wx.CallAfter(self.callback) @@ -304,7 +304,7 @@ class Character(object): @staticmethod def rename(char, newName): if char.name in ("All 0", "All 5"): - logger.info("Cannot rename built in characters.") + pyfalog.info("Cannot rename built in characters.") else: char.name = newName eos.db.commit() @@ -404,7 +404,7 @@ class Character(object): def addImplant(charID, itemID): char = eos.db.getCharacter(charID) if char.ro: - logger.error("Trying to add implant to read-only character") + pyfalog.error("Trying to add implant to read-only character") return implant = es_Implant(eos.db.getItem(itemID)) diff --git a/service/crest.py b/service/crest.py index 39863fb31..c372365e6 100644 --- a/service/crest.py +++ b/service/crest.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx import thread -import logging +from logbook import Logger import threading import copy import uuid @@ -15,7 +15,7 @@ from service.settings import CRESTSettings from service.server import StoppableHTTPServer, AuthHandler from service.pycrest.eve import EVE -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Servers(Enum): @@ -153,17 +153,17 @@ class Crest(object): def logout(self): """Logout of implicit character""" - logging.debug("Character logout") + pyfalog.debug("Character logout") self.implicitCharacter = None wx.PostEvent(self.mainFrame, GE.SsoLogout(type=self.settings.get('mode'))) def stopServer(self): - logging.debug("Stopping Server") + pyfalog.debug("Stopping Server") self.httpd.stop() self.httpd = None def startServer(self): - logging.debug("Starting server") + pyfalog.debug("Starting server") if self.httpd: self.stopServer() time.sleep(1) @@ -179,10 +179,10 @@ class Crest(object): return if message['state'][0] != self.state: - logger.warn("OAUTH state mismatch") + pyfalog.warn("OAUTH state mismatch") return - logger.debug("Handling CREST login with: %s" % message) + pyfalog.debug("Handling CREST login with: %s" % message) if 'access_token' in message: # implicit eve = copy.deepcopy(self.eve) @@ -196,7 +196,7 @@ class Crest(object): eve() info = eve.whoami() - logger.debug("Got character info: %s" % info) + pyfalog.debug("Got character info: %s" % info) self.implicitCharacter = CrestChar(info['CharacterID'], info['CharacterName']) self.implicitCharacter.eve = eve @@ -209,7 +209,7 @@ class Crest(object): eve() info = eve.whoami() - logger.debug("Got character info: %s" % info) + pyfalog.debug("Got character info: %s" % info) # check if we have character already. If so, simply replace refresh_token char = self.getCrestCharacter(int(info['CharacterID'])) diff --git a/service/fit.py b/service/fit.py index bbd6fb334..c0bbfad62 100644 --- a/service/fit.py +++ b/service/fit.py @@ -18,7 +18,7 @@ # =============================================================================== import copy -import logging +from logbook import Logger import eos.db from eos.saveddata.booster import Booster as es_Booster @@ -36,7 +36,7 @@ from service.character import Character from service.damagePattern import DamagePattern from service.settings import SettingsProvider -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Fit(object): @@ -50,6 +50,7 @@ class Fit(object): return cls.instance def __init__(self): + pyfalog.debug("Initialize Fit class") self.pattern = DamagePattern.getInstance().getDamagePattern("Uniform") self.targetResists = None self.character = saveddata_Character.getAll5() @@ -1010,7 +1011,7 @@ class Fit(object): self.recalc(fit) def recalc(self, fit, withBoosters=True): - logger.debug("=" * 10 + "recalc" + "=" * 10) + pyfalog.debug("=" * 10 + "recalc" + "=" * 10) if fit.factorReload is not self.serviceFittingOptions["useGlobalForceReload"]: fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"] fit.clear() diff --git a/service/market.py b/service/market.py index 8a2abb107..287d84929 100644 --- a/service/market.py +++ b/service/market.py @@ -19,7 +19,7 @@ import re import threading -import logging +from logbook import Logger import Queue # noinspection PyPackageRequirements @@ -41,7 +41,7 @@ try: except ImportError: from utils.compat import OrderedDict -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) # Event which tells threads dependent on Market that it's initialized mktRdy = threading.Event() @@ -74,12 +74,12 @@ class ShipBrowserWorkerThread(threading.Thread): wx.CallAfter(callback, (id_, set_)) except: - pass + pyfalog.debug("Callback failed.") finally: try: queue.task_done() except: - pass + pyfalog.debug("Queue task done failed.") class PriceWorkerThread(threading.Thread): @@ -88,9 +88,11 @@ class PriceWorkerThread(threading.Thread): self.name = "PriceWorker" def run(self): + pyfalog.debug("Run start") self.queue = Queue.Queue() self.wait = {} self.processUpdates() + pyfalog.debug("Run end") def processUpdates(self): queue = self.queue @@ -440,7 +442,7 @@ class Market(object): else: raise TypeError("Need Item object, integer, float or string as argument") except: - logger.error("Could not get item: %s", identity) + pyfalog.error("Could not get item: %s", identity) raise return item @@ -834,7 +836,7 @@ class Market(object): try: callback(requests) except Exception: - pass + pyfalog.debug("Callback failed.") eos.db.commit() self.priceWorkerThread.trigger(requests, cb) @@ -850,7 +852,7 @@ class Market(object): try: callback(item) except: - pass + pyfalog.debug("Callback failed.") self.priceWorkerThread.setToWait(item.ID, cb) diff --git a/service/port.py b/service/port.py index 5a6b53fb5..5e182f38e 100644 --- a/service/port.py +++ b/service/port.py @@ -20,7 +20,7 @@ import re import os import xml.dom -import logging +from logbook import Logger import collections import json import threading @@ -50,7 +50,7 @@ from service.market import Market if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): from service.crest import Crest -logger = logging.getLogger("pyfa.service.port") +pyfalog = Logger(__name__) try: from collections import OrderedDict @@ -134,21 +134,21 @@ class Port(object): savebom = bom if codec_found is None: - logger.info("Unicode BOM not found in file %s.", path) + pyfalog.info("Unicode BOM not found in file %s.", path) attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252") for page in attempt_codecs: try: - logger.info("Attempting to decode file %s using %s page.", path, page) + pyfalog.info("Attempting to decode file %s using %s page.", path, page) srcString = unicode(srcString, page) codec_found = page - logger.info("File %s decoded using %s page.", path, page) + pyfalog.info("File %s decoded using %s page.", path, page) except UnicodeDecodeError: - logger.info("Error unicode decoding %s from page %s, trying next codec", path, page) + pyfalog.info("Error unicode decoding %s from page %s, trying next codec", path, page) else: break else: - logger.info("Unicode BOM detected in %s, using %s page.", path, codec_found) + pyfalog.info("Unicode BOM detected in %s, using %s page.", path, codec_found) srcString = unicode(srcString[len(savebom):], codec_found) else: @@ -167,7 +167,7 @@ class Port(object): except xml.parsers.expat.ExpatError: return False, "Malformed XML in %s" % path except Exception: - logger.exception("Unknown exception processing: %s", path) + pyfalog.exception("Unknown exception processing: %s", path) return False, "Unknown Error while processing %s" % path IDs = [] @@ -340,6 +340,7 @@ class Port(object): except ValueError: f.ship = Citadel(sMkt.getItem(fit['ship']['id'])) except: + pyfalog.warning("Caught exception in importCrest") return None items = fit['items'] @@ -365,6 +366,7 @@ class Port(object): m = Module(item) # When item can't be added to any slot (unknown item or just charge), ignore it except ValueError: + pyfalog.debug("Item can't be added to any slot (unknown item or just charge)") continue # Add subsystems before modules to make sure T3 cruisers have subsystems installed if item.category.name == "Subsystem": @@ -377,6 +379,7 @@ class Port(object): moduleList.append(m) except: + pyfalog.warning("Could not process module.") continue # Recalc to get slot numbers correct for T3 cruisers @@ -405,6 +408,7 @@ class Port(object): string = string[string.index(str(id_)):] break except: + pyfalog.warning("Exception caught in importDna") pass string = string[:string.index("::") + 2] info = string.split(":") @@ -422,7 +426,7 @@ class Port(object): return s_[:10] + "..." return s_ - logger.exception("Couldn't import ship data %r", [logtransform(s) for s in info]) + pyfalog.exception("Couldn't import ship data {0}", [logtransform(s) for s in info]) return None moduleList = [] @@ -449,6 +453,7 @@ class Port(object): try: m = Module(item) except: + pyfalog.warning("Exception caught in importDna") continue # Add subsystems before modules to make sure T3 cruisers have subsystems installed if item.category.name == "Subsystem": @@ -497,6 +502,7 @@ class Port(object): fit.ship = Citadel(ship) fit.name = fitName except: + pyfalog.warning("Exception caught in importEft") return # maintain map of drones and their quantities @@ -537,6 +543,7 @@ class Port(object): item = sMkt.getItem(modName, eager="group.category") except: # if no data can be found (old names) + pyfalog.warning("no data can be found (old names)") continue if item.category.name == "Drone": @@ -563,7 +570,7 @@ class Port(object): elif "boosterness" in item.attributes: fit.boosters.append(Booster(item)) else: - logger.error("Failed to import implant: %s", line) + pyfalog.error("Failed to import implant: {0}", line) # elif item.category.name == "Subsystem": # try: # subsystem = Module(item) @@ -689,6 +696,7 @@ class Port(object): try: droneItem = sMkt.getItem(droneName, eager="group.category") except: + pyfalog.warning("Cannot get item.") continue if droneItem.category.name == "Drone": # Add drone to the fitting @@ -710,6 +718,7 @@ class Port(object): try: implantItem = sMkt.getItem(entityData, eager="group.category") except: + pyfalog.warning("Cannot get item.") continue if implantItem.category.name != "Implant": continue @@ -725,6 +734,7 @@ class Port(object): try: boosterItem = sMkt.getItem(entityData, eager="group.category") except: + pyfalog.warning("Cannot get item.") continue # All boosters have implant category if boosterItem.category.name != "Implant": @@ -745,6 +755,7 @@ class Port(object): try: item = sMkt.getItem(cargoName) except: + pyfalog.warning("Cannot get item.") continue # Add Cargo to the fitting c = Cargo(item) @@ -758,6 +769,7 @@ class Port(object): try: modItem = sMkt.getItem(modName) except: + pyfalog.warning("Cannot get item.") continue # Create module @@ -779,6 +791,7 @@ class Port(object): if chargeItem.category.name == "Charge": m.charge = chargeItem except: + pyfalog.warning("Cannot get item.") pass # Append module to fit moduleList.append(m) @@ -797,6 +810,7 @@ class Port(object): wx.CallAfter(callback, None) # Skip fit silently if we get an exception except Exception: + pyfalog.error("Caught exception on fit.") pass return fits @@ -821,6 +835,7 @@ class Port(object): except ValueError: f.ship = Citadel(sMkt.getItem(shipType)) except: + pyfalog.warning("Caught exception on importXml") continue hardwares = fitting.getElementsByTagName("hardware") moduleList = [] @@ -830,6 +845,7 @@ class Port(object): try: item = sMkt.getItem(moduleName, eager="group.category") except: + pyfalog.warning("Caught exception on importXml") continue if item: if item.category.name == "Drone": @@ -852,6 +868,7 @@ class Port(object): m = Module(item) # When item can't be added to any slot (unknown item or just charge), ignore it except ValueError: + pyfalog.warning("item can't be added to any slot (unknown item or just charge), ignore it") continue # Add subsystems before modules to make sure T3 cruisers have subsystems installed if item.category.name == "Subsystem": @@ -865,6 +882,7 @@ class Port(object): moduleList.append(m) except KeyboardInterrupt: + pyfalog.warning("Keyboard Interrupt") continue # Recalc to get slot numbers correct for T3 cruisers @@ -1171,7 +1189,7 @@ class FitImportThread(threading.Thread): success, result = sPort.importFitFromFiles(self.paths, self.callback) if not success: # there was an error during processing - logger.error("Error while processing file import: %s", result) + pyfalog.error("Error while processing file import: {0}", result) wx.CallAfter(self.callback, -2, result) else: # Send done signal to GUI wx.CallAfter(self.callback, -1, result) diff --git a/service/prefetch.py b/service/prefetch.py index 06e6894c2..e73da9ff5 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -25,9 +25,9 @@ from eos.db import migration from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues from eos.db.saveddata.databaseRepair import DatabaseCleanup -import logging +from logbook import Logger -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) # Make sure the saveddata db exists if config.savePath and not os.path.exists(config.savePath): @@ -35,14 +35,16 @@ if config.savePath and not os.path.exists(config.savePath): if config.saveDB and os.path.isfile(config.saveDB): # If database exists, run migration after init'd database + pyfalog.debug("Run database migration.") db.saveddata_meta.create_all() migration.update(db.saveddata_engine) # Import default database values # Import values that must exist otherwise Pyfa breaks + pyfalog.debug("Import Required Database Values.") DefaultDatabaseValues.importRequiredDefaults() # Finds and fixes database corruption issues. - logging.debug("Starting database validation.") + pyfalog.debug("Starting database validation.") database_cleanup_instance = DatabaseCleanup() database_cleanup_instance.OrphanedCharacterSkills(db.saveddata_engine) database_cleanup_instance.OrphanedFitCharacterIDs(db.saveddata_engine) @@ -52,7 +54,7 @@ if config.saveDB and os.path.isfile(config.saveDB): database_cleanup_instance.OrphanedFitIDItemID(db.saveddata_engine) database_cleanup_instance.NullDamageTargetPatternValues(db.saveddata_engine) database_cleanup_instance.DuplicateSelectedAmmoName(db.saveddata_engine) - logging.debug("Completed database validation.") + pyfalog.debug("Completed database validation.") else: # If database does not exist, do not worry about migration. Simply diff --git a/service/price.py b/service/price.py index 32e2f20bd..ca2db812f 100644 --- a/service/price.py +++ b/service/price.py @@ -23,6 +23,8 @@ from xml.dom import minidom from eos import db from service.network import Network, TimeoutError +from logbook import Logger +pyfalog = Logger(__name__) VALIDITY = 24 * 60 * 60 # Price validity period, 24 hours REREQUEST = 4 * 60 * 60 # Re-request delay for failed fetches, 4 hours @@ -114,6 +116,7 @@ class Price(object): # If getting or processing data returned any errors except TimeoutError: # Timeout error deserves special treatment + pyfalog.warning("Price fetch timout") for typeID in priceMap.keys(): priceobj = priceMap[typeID] priceobj.time = time.time() + TIMEOUT @@ -121,6 +124,7 @@ class Price(object): del priceMap[typeID] except: # all other errors will pass and continue onward to the REREQUEST delay + pyfalog.warning("Caught exception in fetchPrices") pass # if we get to this point, then we've got an error. Set to REREQUEST delay diff --git a/service/pycrest/__init__.py b/service/pycrest/__init__.py index f083e1f84..5820a7290 100644 --- a/service/pycrest/__init__.py +++ b/service/pycrest/__init__.py @@ -1,12 +1 @@ -import logging - - -class NullHandler(logging.Handler): - def emit(self, record): - pass - - -logger = logging.getLogger('pycrest') -logger.addHandler(NullHandler()) - version = "0.0.1" diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index c96a10885..14e9d531c 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -1,5 +1,5 @@ import base64 -import logging +from logbook import Logger import os import re import time @@ -20,7 +20,7 @@ except ImportError: # pragma: no cover # noinspection PyPep8Naming import cPickle as pickle -logger = logging.getLogger("pycrest.eve") +pyfalog = Logger(__name__) cache_re = re.compile(r'max-age=([0-9]+)') @@ -58,6 +58,7 @@ class FileCache(APICache): with open(self._getpath(key), 'rb') as f: return pickle.loads(zlib.decompress(f.read())) except IOError as ex: + pyfalog.debug("IO error opening zip file. (May not exist yet)") if ex.errno == 2: # file does not exist (yet) return None else: @@ -69,6 +70,8 @@ class FileCache(APICache): try: os.unlink(self._getpath(key)) except OSError as ex: + pyfalog.debug("Caught exception in invalidate") + pyfalog.debug(ex) if ex.errno == 2: # does not exist pass else: @@ -116,7 +119,7 @@ class APIConnection(object): self.cache = DictCache() def get(self, resource, params=None): - logger.debug('Getting resource %s', resource) + pyfalog.debug('Getting resource %s', resource) if params is None: params = {} @@ -136,15 +139,15 @@ class APIConnection(object): key = (resource, frozenset(self._session.headers.items()), frozenset(prms.items())) cached = self.cache.get(key) if cached and cached['cached_until'] > time.time(): - logger.debug('Cache hit for resource %s (params=%s)', resource, prms) + pyfalog.debug('Cache hit for resource %s (params=%s)', resource, prms) return cached elif cached: - logger.debug('Cache stale for resource %s (params=%s)', resource, prms) + pyfalog.debug('Cache stale for resource %s (params=%s)', resource, prms) self.cache.invalidate(key) else: - logger.debug('Cache miss for resource %s (params=%s', resource, prms) + pyfalog.debug('Cache miss for resource %s (params=%s', resource, prms) - logger.debug('Getting resource %s (params=%s)', resource, prms) + pyfalog.debug('Getting resource %s (params=%s)', resource, prms) res = self._session.get(resource, params=prms) if res.status_code != 200: raise APIException("Got unexpected status code from server: %i" % res.status_code) diff --git a/service/server.py b/service/server.py index 9fc93f05f..c804db439 100644 --- a/service/server.py +++ b/service/server.py @@ -2,14 +2,13 @@ import BaseHTTPServer import urlparse import socket import thread -import logging +from logbook import Logger # noinspection PyPackageRequirements import wx from service.settings import CRESTSettings - -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) # noinspection PyPep8 HTML = ''' @@ -85,7 +84,7 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): # Allow listening for x seconds sec = self.settings.get('timeout') - logger.debug("Running server for %d seconds", sec) + pyfalog.debug("Running server for {0} seconds", sec) self.socket.settimeout(0.5) self.max_tries = sec / self.socket.gettimeout() @@ -99,16 +98,17 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): sock.settimeout(None) return sock, addr except socket.timeout: + pyfalog.warning("Server timed out waiting for connection") pass def stop(self): self.run = False def handle_timeout(self): - # logger.debug("Number of tries: %d"%self.tries) + # pyfalog.debug("Number of tries: %d"%self.tries) self.tries += 1 if self.tries == self.max_tries: - logger.debug("Server timed out waiting for connection") + pyfalog.debug("Server timed out waiting for connection") self.stop() def serve(self, callback): @@ -117,6 +117,7 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): try: self.handle_request() except TypeError: + pyfalog.debug("Caught exception in serve") pass self.server_close() diff --git a/service/update.py b/service/update.py index 969c78432..2cffd9cec 100644 --- a/service/update.py +++ b/service/update.py @@ -29,6 +29,9 @@ import dateutil.parser import config from service.network import Network from service.settings import UpdateSettings +from logbook import Logger + +pyfalog = Logger(__name__) class CheckUpdateThread(threading.Thread): @@ -83,6 +86,7 @@ class CheckUpdateThread(threading.Thread): wx.CallAfter(self.callback, release) # Singularity -> Singularity break except: + pyfalog.warning("Caught exception in run") pass @staticmethod