Skip to content

Commit fac5d12

Browse files
committed
Instance: add a set_logger method
Adds a way to link libVLC log system to a python logging.Logger object. The logger and log callback is attached to the libVLC Instance object to avoid the python garbage collector to destroy them too soon. NOTE: as libVLC log messages are based on a printf + va_list format, we need to use the standard C `vsnprintf` function with a max fixed size output.
1 parent f76d986 commit fac5d12

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

generator/templates/header.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,20 @@ class EventUnion(ctypes.Union):
489489
('new_length', ctypes.c_longlong),
490490
]
491491

492+
def loglevel_to_logging(level):
493+
"""Converts VLC log level to python logging Log level
494+
"""
495+
if level == LogLevel.DEBUG:
496+
return logging.DEBUG
497+
elif level == LogLevel.ERROR:
498+
return logging.ERROR
499+
elif level == LogLevel.NOTICE:
500+
return logging.INFO
501+
elif level == LogLevel.WARNING:
502+
return logging.WARNING
503+
else:
504+
return logging.INFO
505+
492506
# Generated structs #
493507
# GENERATED_STRUCTS go here # see generate.py
494508
# End of generated structs #

generator/templates/override.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ class Instance:
77
- a list of strings as first parameters
88
- the parameters given as the constructor parameters (must be strings)
99
"""
10+
11+
# logging related private fields
12+
_vsnprintf = None
13+
_max_log_message_size = 4096
14+
_logger = None
15+
_log_handler = None
16+
1017
def __new__(cls, *args):
1118
if len(args) == 1:
1219
# Only 1 arg. It is either a C pointer, or an arg string,
@@ -142,6 +149,54 @@ def video_filter_list_get(self):
142149
"""
143150
return module_description_list(libvlc_video_filter_list_get(self))
144151

152+
def set_logger(self, logger, max_log_message_size=4096):
153+
"""Links a logging.Logger object to the libVLC Instance
154+
155+
Along with the log level and message, each libVLC log will also include
156+
The following extra info:
157+
- vlc_module: the name of the VLC module (str).
158+
- file: the VLC source filename (str).
159+
- line: the VLC source file line number (int).
160+
These variables can be used in the logger formatter.
161+
162+
@param logger: a logging.Logger object
163+
@param max_log_message_size: defines the maximum size that will be
164+
copied from VLC log messages. If you experience truncated log
165+
messages, raise this number (default 4096).
166+
"""
167+
# libVLC provides the log message through a printf format + va_list.
168+
# Unfortunately, there is no simple way to use a
169+
# printf format + va_list in Python outside of the use of a C format
170+
# function.
171+
# As there is no guarantee to have access to a C `vasprintf`, we use
172+
# `vsnprintf` with a log message max size.
173+
libc = find_libc()
174+
if libc is None:
175+
raise Exception("Cannot find a proper standard C library (needed to retrieve log messages)")
176+
self._vsnprintf = libc.vsnprintf
177+
self._max_log_message_size = max_log_message_size
178+
self._logger = logger
179+
# The log_handler is meant to be the "real" callback for libvlc_log_set().
180+
@CallbackDecorators.LogCb
181+
def log_handler(instance, log_level, ctx, fmt, va_list):
182+
bufferString = ctypes.create_string_buffer(self._max_log_message_size)
183+
self._vsnprintf(bufferString, self._max_log_message_size, fmt,
184+
ctypes.cast(va_list, ctypes.c_void_p))
185+
msg = bufferString.value.decode('utf-8')
186+
module, file, line = libvlc_log_get_context(ctx)
187+
module = module.decode('utf-8')
188+
file = file.decode('utf-8')
189+
self._logger.log(loglevel_to_logging(LogLevel(log_level)),
190+
msg, extra={"vlc_module": module, "file": file, "line": line})
191+
# We need to keep a reference to the log_handler function that persists
192+
# after the end of the set_logger.
193+
# If we do not do that, there is a (high) chance the python garbage
194+
# collector will destroy the callback function and produce segfault in
195+
# libVLC.
196+
# To avoid that, link the log_handler lifetime to the Instance's one.
197+
self._log_handler = log_handler
198+
self.log_set(self._log_handler, None)
199+
145200
class Media:
146201
"""Create a new Media instance.
147202

0 commit comments

Comments
 (0)