3636import glob
3737import platform
3838import ast
39+ import logging
3940from threading import Thread , Lock , Event
4041from ctypes import *
4142from .WeaveUtility import WeaveUtility
@@ -90,8 +91,57 @@ def __init__(self, profileId, statusCode, systemErrorCode, msg=None):
9091 def __str__ (self ):
9192 return "Device Error: " + self .message
9293
94+ class LogCategory (object ):
95+ '''Debug logging categories used by openweave.'''
96+
97+ # NOTE: These values must correspond to those used in the openweave C++ code.
98+ Disabled = 0
99+ Error = 1
100+ Progress = 2
101+ Detail = 3
102+ Retain = 4
103+
104+ @staticmethod
105+ def categoryToLogLevel (cat ):
106+ if cat == LogCategory .Error :
107+ return logging .ERROR
108+ elif cat == LogCategory .Progress :
109+ return logging .INFO
110+ elif cat == LogCategory .Detail :
111+ return logging .DEBUG
112+ elif cat == LogCategory .Retain :
113+ return logging .CRITICAL
114+ else :
115+ return logging .NOTSET
116+
117+ class WeaveLogFormatter (logging .Formatter ):
118+ '''A custom logging.Formatter for logging openweave library messages.'''
119+ def __init__ (self , datefmt = None , logModule = True , logLevel = False , logTimestamp = False , logMSecs = True ):
120+ fmt = '%(message)s'
121+ if logModule :
122+ fmt = 'WEAVE:%(weave-module)s: ' + fmt
123+ if logLevel :
124+ fmt = '%(levelname)s:' + fmt
125+ if datefmt is not None or logTimestamp :
126+ fmt = '%(asctime)s ' + fmt
127+ super (WeaveLogFormatter , self ).__init__ (fmt = fmt , datefmt = datefmt )
128+ self .logMSecs = logMSecs
129+
130+ def formatTime (self , record , datefmt = None ):
131+ timestamp = record .__dict__ .get ('timestamp' , time .time ())
132+ timestamp = time .localtime (timestamp )
133+ if datefmt is None :
134+ datefmt = '%Y-%m-%d %H:%M:%S%z'
135+ timestampStr = time .strftime ('%Y-%m-%d %H:%M:%S%z' )
136+ if self .logMSecs :
137+ timestampUS = record .__dict__ .get ('timestamp-usec' , 0 )
138+ timestampStr = '%s.%03ld' % (timestampStr , timestampUS / 1000 )
139+ return timestampStr
140+
141+
93142_CompleteFunct = CFUNCTYPE (None , c_void_p , c_void_p )
94143_ErrorFunct = CFUNCTYPE (None , c_void_p , c_void_p , c_ulong , POINTER (DeviceStatusStruct ))
144+ _LogMessageFunct = CFUNCTYPE (None , c_int64 , c_int64 , c_char_p , c_uint8 , c_char_p )
95145
96146@_singleton
97147class WeaveStack (object ):
@@ -102,8 +152,33 @@ def __init__(self):
102152 self ._weaveDLLPath = None
103153 self .devMgr = None
104154 self .callbackRes = None
155+ self ._activeLogFunct = None
105156
106- self ._InitLib ()
157+ # Locate and load the openweave shared library.
158+ self ._loadLib ()
159+
160+ # By default, configure the openweave library to log to a python logger object with the
161+ # name 'openweave.WeaveStack'. If this logger has not already been initialized by
162+ # the application, automatically configure it to log to stdout.
163+ #
164+ # Applications can override this behavior in any one the following ways:
165+ # - Setting self.logger to a different python logger object.
166+ # - Replacing the StreamHandler on self.logger with a different handler object.
167+ # - Setting a different Formatter object on the existing StreamHandler object.
168+ # - Reconfiguring the existing LogMessageFormatter object, e.g. to enable logging
169+ # of timestamps.
170+ # - Configuring openwave to call an application-specific logging function by
171+ # calling self.setLogFunct().
172+ # - Calling self.setLogFunct(None), which will configure the openweave library
173+ # to log directly to stdout, bypassing python altogether.
174+ #
175+ self .logger = logging .getLogger (__name__ )
176+ if len (self .logger .handlers ) == 0 :
177+ logHandler = logging .StreamHandler (stream = sys .stdout )
178+ logHandler .setFormatter (WeaveLogFormatter ())
179+ self .logger .addHandler (logHandler )
180+ self .logger .setLevel (logging .DEBUG )
181+ self .setLogFunct (self .defaultLogFunct )
107182
108183 def HandleComplete (appState , reqState ):
109184 self .callbackRes = True
@@ -117,6 +192,49 @@ def HandleError(appState, reqState, err, devStatusPtr):
117192 self .cbHandleError = _ErrorFunct (HandleError )
118193 self .blockingCB = None # set by other modules(BLE) that require service by thread while thread blocks.
119194
195+ # Initialize the openweave library
196+ res = self ._weaveStackLib .nl_Weave_Stack_Init ()
197+ if (res != 0 ):
198+ raise self ._weaveStack .ErrorToException (res )
199+
200+ @property
201+ def defaultLogFunct (self ):
202+ '''Returns a python callable which, when called, logs a message to the python logger object
203+ currently associated with the WeaveStack object.
204+ The returned function is suitable for passing to the setLogFunct() method.'''
205+ def logFunct (timestamp , timestampUSec , moduleName , logCat , message ):
206+ moduleName = WeaveUtility .CStringToString (moduleName )
207+ message = WeaveUtility .CStringToString (message )
208+ logLevel = LogCategory .categoryToLogLevel (logCat )
209+ msgAttrs = {
210+ 'weave-module' : moduleName ,
211+ 'timestamp' : timestamp ,
212+ 'timestamp-usec' : timestampUSec
213+ }
214+ self .logger .log (logLevel , message , extra = msgAttrs )
215+ return logFunct
216+
217+ def setLogFunct (self , logFunct ):
218+ '''Set the function used by the openweave library to log messages.
219+ The supplied object must be a python callable that accepts the following
220+ arguments:
221+ timestamp (integer)
222+ timestampUS (integer)
223+ module name (encoded UTF-8 string)
224+ log category (integer)
225+ message (encoded UTF-8 string)
226+ Specifying None configures the openweave library to log directly to stdout.'''
227+ if logFunct is None :
228+ logFunct = 0
229+ if not isinstance (logFunct , _LogMessageFunct ):
230+ logFunct = _LogMessageFunct (logFunct )
231+ with self .networkLock :
232+ # NOTE: WeaveStack must hold a reference to the CFUNCTYPE object while it is
233+ # set. Otherwise it may get garbage collected, and logging calls from the
234+ # openweave library will fail.
235+ self ._activeLogFunct = logFunct
236+ self ._weaveStackLib .nl_Weave_Stack_SetLogFunct (logFunct )
237+
120238 def Shutdown (self ):
121239 self ._weaveStack .Call (
122240 lambda : self ._dmLib .nl_Weave_Stack_Shutdown ()
@@ -208,7 +326,7 @@ def _AllDirsToRoot(self, dir):
208326 break
209327 dir = parent
210328
211- def _InitLib (self ):
329+ def _loadLib (self ):
212330 if (self ._weaveStackLib == None ):
213331 self ._weaveStackLib = CDLL (self .LocateWeaveDLL ())
214332 self ._weaveStackLib .nl_Weave_Stack_Init .argtypes = [ ]
@@ -219,7 +337,5 @@ def _InitLib(self):
219337 self ._weaveStackLib .nl_Weave_Stack_StatusReportToString .restype = c_char_p
220338 self ._weaveStackLib .nl_Weave_Stack_ErrorToString .argtypes = [ c_uint32 ]
221339 self ._weaveStackLib .nl_Weave_Stack_ErrorToString .restype = c_char_p
222-
223- res = self ._weaveStackLib .nl_Weave_Stack_Init ()
224- if (res != 0 ):
225- raise self ._weaveStack .ErrorToException (res )
340+ self ._weaveStackLib .nl_Weave_Stack_SetLogFunct .argtypes = [ _LogMessageFunct ]
341+ self ._weaveStackLib .nl_Weave_Stack_SetLogFunct .restype = c_uint32
0 commit comments