Limit Plugin Logging

Posted on
Tue Sep 10, 2019 6:49 am
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: Limit Plugin Logging

DaveL17 wrote:
... then I'd wrap the rest of my logging in a method and call that as needed..
Code: Select all
self.log_me(error_state=False, level=20, msg="something important")
Then you could select the proper logging formatter in the call to the method by setting error_state. Based on my very quick tests, this will adjust the output to the private debug log, but leave Indigo's Event Log unchanged. Is that even close?...

After re-reading your post, I think I get it, maybe... Something like
Code: Select all
def my_log_method(level, message):
    if level == "info":
        self.indigo_log_handler.setFormatter(logging.Formatter('%(msg)s'))
        self.logger.info(u"{}'.format(message))
    elif level == "debug:
        self.indigo_log_handler.setFormatter(logging.Formatter('%(funcName)s: %(msg)s'))
        self.logger.debug(u"{}".format(message))
       

Posted on
Tue Sep 10, 2019 9:16 am
jay (support) offline
Site Admin
User avatar
Posts: 18200
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Limit Plugin Logging

You can definitely do what you're looking to do: Python handlers can specify at what level they log, and what associated formatters to use. By default, Indigo defines the following handler:

Code: Select all
################################################################################
# Finally, we define a logging handler that emits log messages into the Indigo
# Event Log.
################################################################################
class IndigoLogHandler(logging.Handler, object):
   def __init__(self, displayName, level=logging.NOTSET):
      super(IndigoLogHandler, self).__init__(level)
      self.displayName = displayName

   def emit(self, record):
      # First, determine if it needs to be an Indigo error
      is_error = False
      if record.levelno in (logging.ERROR, logging.CRITICAL):
         is_error = True
      type_string = self.displayName
      # For any level besides INFO and ERROR (which Indigo handles), we append
      # the debug level (i.e. Critical, Warning, Debug, etc) to the type string
      if record.levelno not in (logging.INFO, logging.ERROR):
         type_string += u" %s" % record.levelname.title()
      # Then we write the message
      indigo.server.log(message=self.format(record), type=type_string, isError=is_error)


As you can see, we take any string (passed into the record param as record.message) and pass it to the handler's format() method (which calls the formatters that you add to the handler and which we define by default as '%(message)s') to generate the string we pass as the message to the server's log() method (and we add an extra type string for anything other than INFO and ERROR).

We then add this handler to the standard logger that we create for the plugin (self.logger) and set it's level to INFO (many developers will change this in their init() method based on debugging, etc).

However, what you can do is to create your own handler (using the one above as a template) that would only emit messages of DEBUG or lower and then add that handler to the self.logger object (loggers can have an unlimited number of handlers). Make sure to set the handler's level to DEBUG (or THREADDEBUG if you're using that Indigo created level) so that it emits the different format, and associate the format with the handler.

So, in pseudo code:

Code: Select all
# Define the handler class
class MyDebugHandler(logging.Handler, object):
   def __init__(self, displayName, level=logging.NOTSET):
      super(IndigoLogHandler, self).__init__(level)
      self.displayName = displayName

   def emit(self, record):
      # add whatever stuff is specific to your handler, but the most important thing
      # is to only to log things that match record.levelno <= logging.DEBUG
      # (that might not be exactly right, but it's close I think)

# In the init method for your plugin, do these
my_debug_handler = MyDebugHandler(pluginDisplayName, logging.THREADDEBUG)
my_debug_handler.setFormatter(some_format_here)
self.logger.addHandler(my_debug_handler)


I probably got some of the details wrong, but that should get you started.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Tue Sep 10, 2019 1:40 pm
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: Limit Plugin Logging

jay (support) wrote:
You can definitely do what you're looking to do: ...

Thanks Jay. This should keep me busy. I'll report back when I have something (or, more likely, when I get stuck.)

Posted on
Fri Sep 13, 2019 2:02 am
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: Limit Plugin Logging

I previously wrote:
Thanks Jay. This should keep me busy. I'll report back when I have something (or, more likely, when I get stuck.)

It may not be pretty, but I got it working. Two key things were necessary: removing the default Indigo log handler and importing plugin_base. It actually worked with the Indigo log handler enabled, but I got double logging of all messages. I suspect there might be a better way to do this, but for now, at least it is working as I wanted.

Thanks Jay and Dave

Code: Select all
import logging
from plugin_base import IndigoLogHandler

################################################################################
class MyDebugHandler(IndigoLogHandler, object):
        ########################################
    """ define a log handler to format log messages by level """
    def __init__(self, displayName, level=logging.NOTSET, debug=False):
        super(IndigoLogHandler, self).__init__(level)
        self.displayName = displayName
        self.debug = debug

    def setDebug(self, debug):
        self.debug = debug

    def emit(self, record):
        level = record.levelname
        is_error = False
        if (level == 'THREADDEBUG' or level == 'DEBUG') and self.debug:   # 5 & 10
            logmessage = '{}, {}, {}: {} {}'.format(record.pathname, record.funcName, record.lineno, record.args, record.msg)
        elif level == 'INFO':      # 20
            logmessage = record.getMessage()
        elif level == 'WARNING':   # 30
            logmessage = '{}, {}, {}: {} {}'.format(record.pathname, record.funcName, record.lineno, record.args, record.msg)
        elif level == 'ERROR':      # 40
            logmessage = '{}, {}, {}: {} {}'.format(record.pathname, record.funcName, record.lineno, record.args, record.msg)
            is_error = True
        else:
            return

        indigo.server.log(message=logmessage, type=self.displayName, isError=is_error)


################################################################################
class Plugin(indigo.PluginBase):
    ########################################
    def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs):
        super(Plugin, self).__init__(pluginId, pluginDisplayName, pluginVersion, pluginPrefs)

        my_debug_handler = MyDebugHandler(pluginDisplayName, logging.THREADDEBUG)
        self.logger.addHandler(my_debug_handler)
        self.logger.removeHandler(self.indigo_log_handler)
        self.debug = pluginPrefs.get("showDebugInfo", False)
        self.my_debug_handler.setDebug(self.debug)

Posted on
Fri Sep 13, 2019 9:11 am
jay (support) offline
Site Admin
User avatar
Posts: 18200
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Limit Plugin Logging

Since you want to customize all messages that aren't logger.INFO, then your approach is correct. If you'd only wanted to customize logger.DEBUG or lower, then you could have done that and leave the IndigoLogHandler that's created automatically.

I don't think you really needed to import IndigoLogHandler since your handler is overriding everything it defined anyway (and you're removing it from self.logger), so logging.Handler would have worked as your base class. But that doesn't really matter, just an FYI.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Who is online

Users browsing this forum: No registered users and 2 guests