Package: pyoslog
PyoslogPyoslog allows you to send messages to the macOSunified logging systemusing Python.frompyoslogimportos_log,OS_LOG_DEFAULTos_log(OS_LOG_DEFAULT,'Hello from Python!')InstallationPyoslog requires macOS 10.12 or later and Python 3.6 or later.
Install usingpip:python-mpipinstallpyoslogThe module will install and import without error on earlier macOS versions, or on unsupported Operating Systems or incompatible Python versions.
Usepyoslog.is_supported()if you need to support incompatible environments and want to know at runtime whether to use pyoslog.
Please note that ifis_supported()returnsFalsethen none of the module's other methods or constants will exist.Usageimportpyoslogifpyoslog.is_supported():pyoslog.log('This is an OS_LOG_TYPE_DEFAULT message via pyoslog')Available methodsPyoslog provides the following methods from Apple'sunified logging header:os_log_createos_log_type_enabled(andinfo/debugvariants)os_log_with_typeos_log(andinfo/debug/error/faultvariants).All the pyoslog methods have the same signatures as their native versions,exceptfor where a method requires aformatparameter.
Theos_logsystem requires a constant (static) format specifier, and it is not possible to achieve this via Python.
As a result, all instances of format strings use"%{public}s", and all messages are converted to a string before passing to the native methods.Pyoslog also offers a helper method –log– that by default posts a message of typeOS_LOG_TYPE_DEFAULTtoOS_LOG_DEFAULT.
For example, the shortcutlog('message')is equivalent toos_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, 'message').TheHandlerclass is designed for use with Python's inbuiltloggingmodule.
It works as a drop-in replacement for other Handler varieties.Seepyoslog's method documentationfor a full reference.Labelling subsystem and categoryCreate a log object usingos_log_createand pass it to any of the log methods to add your own subsystem and category labels:importpyosloglog=pyoslog.os_log_create('ac.robinson.pyoslog','custom-category')pyoslog.os_log_with_type(log,pyoslog.OS_LOG_TYPE_DEBUG,'Message to log object',log,'of type',pyoslog.OS_LOG_TYPE_DEBUG)Enabling and disabling log outputLog output can be enabled or disabled globally by switching between the desired log object andpyoslog.OS_LOG_DISABLED:importpyosloglog=pyoslog.OS_LOG_DEFAULTpyoslog.os_log(log,'Log output enabled')log=pyoslog.OS_LOG_DISABLEDpyoslog.os_log(log,'Log output disabled')It is also possible to check whether individual log types are enabled for a particular log object:importpyoslogpyoslog.os_log_type_enabled(pyoslog.OS_LOG_DEFAULT,pyoslog.OS_LOG_TYPE_DEBUG)It is not possible to directly set a log object's mode from Python, but see theconfigsection ofman logfor documentation about doing this insudomode.Integration with the logging moduleUse the pyoslogHandlerto direct messages to pyoslog:importlogging,pyosloghandler=pyoslog.Handler()handler.setSubsystem('org.example.your-app','filter-category')logger=logging.getLogger()logger.addHandler(handler)logger.error('message')Logger levels are mapped internally to theOS_LOG_TYPE_*values – for example,logger.debug('message')will generate a message of typeOS_LOG_TYPE_DEBUG.Receiving log messagesLogs can be viewed using Console.app or thelogcommand.
For example, messages sent using the default configuration can be streamed using:logstream--predicate'processImagePath CONTAINS [c] "python"'Messages sent using custom log objects can be filtered more precisely.
For example, to receive messages from the labelled subsystem used in the example above:logstream--predicate'subsystem == "ac.robinson.pyoslog"'--leveldebugSeeman logfor further details about the available options and filters.Handling cleanupWhen labelling subsystem and category using the native C methods there is a requirement to free the log object after use (usingos_release).
The pyoslog module handles this for you – there is no need todelor release these objects.LimitationsAs noted above, while the macOSos_logAPI allows use of a format string with many methods, this name is required to be a C string literal.
As a result, pyoslog hardcodes all format strings to"%{public}s".TestingThe pyoslog module's tests require thepyobjc OSLog framework wrappersand thestoreWithScope initialiserin order to verify output so, as a result, can only be run on macOS 12 or later.After installing the OSLog wrappers (viapython -m pip install pyobjc-framework-OSLog), navigate to thetestsdirectory and run:python-munittestAll of pyoslog's code is covered by tests, but please note that if Console.app is live-streaming messages, some tests may fail.
Seetest_logging.pyfor discussion about why this is the case.AlternativesAt the time this module was created there were no alternatives available onPyPi.
Since then, themacos-oslogmodule has been released, with broadly equivalent functionality to pyoslog, except for the need to manually release the log object.
There is alsoos-signpost, which usescythonto provide theOSSignposterAPI, and could easily be extended to provideos_logfunctionality.In addition, there are other options available if PyPi access is not seen as a constraint:apple_os_log_pypymacoslogloggyNote that thepyobjcmoduleOSLogis forreadingfrom the unified logging system rather than writing to it (and as a result is used for testing pyoslog).
Alog.hbinding is on that project'sroadmap, but not yet implemented.LicenseApache 2.0