Skip to content

Commit cb2024e

Browse files
committed
Version 0.2.0 - add logging.Handler and improve documentation
1 parent 9fb4147 commit cb2024e

File tree

6 files changed

+114
-38
lines changed

6 files changed

+114
-38
lines changed

README.md

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,21 @@ from pyoslog import os_log, OS_LOG_DEFAULT
66
os_log(OS_LOG_DEFAULT, 'Hello from Python!')
77
```
88

9+
910
## Installation
10-
`python -m pip install pyoslog`
11+
```shell
12+
python -m pip install pyoslog
13+
```
14+
15+
Pyoslog requires macOS 10.12 or later.
1116

1217

1318
## Usage
14-
Pyoslog currently provides the methods [`os_log_create`](https://developer.apple.com/documentation/os/1643744-os_log_create), [`os_log_wih_type`](https://developer.apple.com/documentation/os/os_log_with_type) and [`os_log`](https://developer.apple.com/documentation/os/os_log) with the same signatures as their native versions.
19+
Pyoslog currently provides the methods [`os_log_create`](https://developer.apple.com/documentation/os/1643744-os_log_create), [`os_log_with_type`](https://developer.apple.com/documentation/os/os_log_with_type) and [`os_log`](https://developer.apple.com/documentation/os/os_log), each with the same signatures as their native versions.
1520

16-
The module also offers a helper method, `log` that by default posts a message of type `OS_LOG_TYPE_DEFAULT` to `OS_LOG_DEFAULT`. For example, the shortcut `pyoslog.log('message')` is equivalent to `pyoslog.os_log_with_type(pyoslog.OS_LOG_DEFAULT, pyoslog.OS_LOG_TYPE_DEFAULT, 'message')`.
21+
The module also offers a helper method`log` that by default posts a message of type `OS_LOG_TYPE_DEFAULT` to `OS_LOG_DEFAULT`. For example, the shortcut `log('message')` is equivalent to `os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, 'message')`.
1722

23+
The `Handler` class is designed for use with Python's inbuilt [logging](https://docs.python.org/3/library/logging.html) module.
1824

1925
### Labelling subsystem and category
2026
Create a log object using `os_log_create` and pass it to any of the log methods to add your own subsystem and category labels:
@@ -25,15 +31,46 @@ log = pyoslog.os_log_create('ac.robinson.pyoslog', 'custom-category')
2531
pyoslog.os_log_with_type(log, pyoslog.OS_LOG_TYPE_DEBUG, 'Message to log object', log, 'of type', pyoslog.OS_LOG_TYPE_DEBUG)
2632
```
2733

34+
35+
### Integration with the logging module
36+
Use the pyoslog `Handler` to direct messages to pyoslog:
37+
38+
```python
39+
import logging, pyoslog
40+
logger = logging.getLogger('My app name')
41+
logger.setLevel(logging.DEBUG)
42+
handler = pyoslog.Handler()
43+
handler.setSubsystem('org.example.your-app', 'filter-category')
44+
logger.addHandler(handler)
45+
logger.debug('message')
46+
```
47+
48+
2849
### Receiving log messages
2950
Logs can be viewed using Console.app or the `log` command. For example, messages sent using the default configuration can be monitored using:
3051

31-
`log stream --predicate 'processImagePath CONTAINS "Python"'`
52+
```shell
53+
log stream --predicate 'processImagePath CONTAINS "Python"'
54+
```
55+
56+
Messages sent using custom configurations can be filtered more precisely. For example, to receive messages from the labelled subsystem used in the example above:
57+
58+
```shell
59+
log stream --predicate 'subsystem == "ac.robinson.pyoslog"' --level=debug
60+
```
61+
62+
See `man log` for further details about the available options and filters.
63+
64+
65+
## Alternatives
66+
At the time this module was created there were no alternatives available on [PyPi](https://pypi.org/). There are, however, other options available if this is not seen as a constraint:
3267

33-
Messages sent using custom configurations can be filtered more precisely. For example, to receive messages from the labelled subsystem example above:
68+
- [apple_os_log_py](https://github.com/cedar101/apple_os_log_py)
69+
- [pymacoslog](https://github.com/douglas-carmichael/pymacoslog)
70+
- [loggy](https://github.com/pointy-tools/loggy)
3471

35-
`log stream --predicate 'subsystem == "ac.robinson.pyoslog"' --level=debug`
72+
Note that the [pyobjc](https://pyobjc.readthedocs.io/) module [OSLog](https://pypi.org/project/pyobjc-framework-OSLog/) is for _reading_ from the unified logging system rather than writing to it. A `log.h` binding is on that project's [roadmap](https://github.com/ronaldoussoren/pyobjc/issues/377), but not yet implemented.
3673

3774

3875
## License
39-
[Apache 2.0](LICENSE)
76+
[Apache 2.0](LICENSE)

pyoslog/__init__.py

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,3 @@
11
""""Send messages to the macOS unified logging system. See: https://developer.apple.com/documentation/os/os_log"""
2-
import _pyoslog
3-
4-
OS_LOG_DEFAULT = _pyoslog.OS_LOG_DEFAULT
5-
6-
OS_LOG_TYPE_DEFAULT = _pyoslog.OS_LOG_TYPE_DEFAULT
7-
OS_LOG_TYPE_INFO = _pyoslog.OS_LOG_TYPE_INFO
8-
OS_LOG_TYPE_DEBUG = _pyoslog.OS_LOG_TYPE_DEBUG
9-
OS_LOG_TYPE_ERROR = _pyoslog.OS_LOG_TYPE_ERROR
10-
OS_LOG_TYPE_FAULT = _pyoslog.OS_LOG_TYPE_FAULT
11-
12-
13-
def os_log_create(subsystem, category):
14-
"""Creates a custom log object. See: https://developer.apple.com/documentation/os/1643744-os_log_create"""
15-
return _pyoslog.os_log_create(subsystem, category)
16-
17-
18-
def os_log_with_type(log_object, log_type, *message):
19-
"""Sends a message at a specific logging level, such as default, info, debug, error, or fault, to the logging
20-
system. See: https://developer.apple.com/documentation/os/os_log_with_type """
21-
return _pyoslog.os_log_with_type(log_object, log_type, ' '.join(map(str, message)))
22-
23-
24-
def os_log(log_object, *message):
25-
"""Sends a default-level message to the logging system. See: https://developer.apple.com/documentation/os/os_log"""
26-
return os_log_with_type(log_object, OS_LOG_TYPE_DEFAULT, *message)
27-
28-
29-
def log(*message, log_object=OS_LOG_DEFAULT, log_type=OS_LOG_TYPE_DEFAULT):
30-
"""Equivalent to os_log_with_type(log_object, log_type, *message) with the default log object and type"""
31-
return os_log_with_type(log_object, log_type, *message)
2+
from .core import *
3+
from .handler import *

pyoslog/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__title__ = 'pyoslog'
2-
__version__ = '0.1.0'
2+
__version__ = '0.2.0'
33
__description__ = 'Send messages to the macOS unified logging system'
44
__author__ = 'Simon Robinson'
55
__author_email__ = 'simon@robinson.ac'

pyoslog/core.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import _pyoslog
2+
3+
OS_LOG_DEFAULT = _pyoslog.OS_LOG_DEFAULT
4+
5+
OS_LOG_TYPE_DEFAULT = _pyoslog.OS_LOG_TYPE_DEFAULT
6+
OS_LOG_TYPE_INFO = _pyoslog.OS_LOG_TYPE_INFO
7+
OS_LOG_TYPE_DEBUG = _pyoslog.OS_LOG_TYPE_DEBUG
8+
OS_LOG_TYPE_ERROR = _pyoslog.OS_LOG_TYPE_ERROR
9+
OS_LOG_TYPE_FAULT = _pyoslog.OS_LOG_TYPE_FAULT
10+
11+
12+
def os_log_create(subsystem, category):
13+
"""Creates a custom log object. See: https://developer.apple.com/documentation/os/1643744-os_log_create"""
14+
return _pyoslog.os_log_create(subsystem, category)
15+
16+
17+
def os_log_with_type(log_object, log_type, *message):
18+
"""Sends a message at a specific logging level, such as default, info, debug, error, or fault, to the logging
19+
system. See: https://developer.apple.com/documentation/os/os_log_with_type """
20+
return _pyoslog.os_log_with_type(log_object, log_type, ' '.join(map(str, message)))
21+
22+
23+
def os_log(log_object, *message):
24+
"""Sends a default-level message to the logging system. See: https://developer.apple.com/documentation/os/os_log"""
25+
return os_log_with_type(log_object, OS_LOG_TYPE_DEFAULT, *message)
26+
27+
28+
def log(*message, log_object=OS_LOG_DEFAULT, log_type=OS_LOG_TYPE_DEFAULT):
29+
"""Equivalent to os_log_with_type(log_object, log_type, *message) with the default log object and type"""
30+
return os_log_with_type(log_object, log_type, *message)

pyoslog/handler.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from .core import *
2+
import logging
3+
4+
5+
class Handler(logging.Handler):
6+
"""This logging Handler simply forwards all messages to pyoslog"""
7+
8+
def __init__(self):
9+
"""Initialise a Handler instance, logging to OS_LOG_DEFAULT at OS_LOG_TYPE_DEFAULT"""
10+
logging.Handler.__init__(self)
11+
12+
self.log_object = OS_LOG_DEFAULT
13+
self.log_type = OS_LOG_TYPE_DEFAULT
14+
15+
self._levelToType = {
16+
logging.CRITICAL: OS_LOG_TYPE_FAULT,
17+
logging.ERROR: OS_LOG_TYPE_ERROR,
18+
logging.WARNING: OS_LOG_TYPE_DEFAULT,
19+
logging.INFO: OS_LOG_TYPE_INFO,
20+
logging.DEBUG: OS_LOG_TYPE_DEBUG,
21+
logging.NOTSET: OS_LOG_TYPE_DEFAULT
22+
}
23+
24+
def setLevel(self, level):
25+
"""Sets the log level, mapping logging.<level> to pyoslog.OS_LOG_TYPE_<equivalent level>"""
26+
self.log_type = self._levelToType.get(level)
27+
super().setLevel(level)
28+
29+
# noinspection PyPep8Naming
30+
def setSubsystem(self, subsystem, category='default'):
31+
"""Sets the subsystem (in reverse DNS notation), and optionally a category to allow further filtering"""
32+
self.log_object = os_log_create(subsystem, category)
33+
34+
def emit(self, record):
35+
"""Emit a record, sending its contents to pyoslog"""
36+
os_log_with_type(self.log_object, self.log_type, self.format(record))

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
url=about['__url__'],
2424

2525
platforms=['darwin'],
26+
packages=[NAME],
2627
ext_modules=[Extension('_' + NAME, ['%s/_%s.c' % (NAME, NAME)])],
2728

2829
package_data={'': ['LICENSE']},

0 commit comments

Comments
 (0)