Skip to content

Commit 6896d1f

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 6d73c64 commit 6896d1f

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class Instance:
77
- a list of strings as first parameters
88
- the parameters given as the constructor parameters (must be strings)
99
"""
10+
1011
def __new__(cls, *args):
1112
if len(args) == 1:
1213
# Only 1 arg. It is either a C pointer, or an arg string,
@@ -142,6 +143,52 @@ def video_filter_list_get(self):
142143
"""
143144
return module_description_list(libvlc_video_filter_list_get(self))
144145

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

0 commit comments

Comments
 (0)