]> git.sesse.net Git - vlc/blob - src/misc/messages.c
console: convert console logger to module
[vlc] / src / misc / messages.c
1 /*****************************************************************************
2  * messages.c: messages interface
3  * This library provides an interface to the message queue to be used by other
4  * modules, especially intf modules. See vlc_config.h for output configuration.
5  *****************************************************************************
6  * Copyright (C) 1998-2005 VLC authors and VideoLAN
7  * $Id$
8  *
9  * Authors: Vincent Seguin <seguin@via.ecp.fr>
10  *          Samuel Hocevar <sam@zoy.org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <stdlib.h>
36 #include <stdarg.h>                                       /* va_list for BSD */
37 #include <unistd.h>
38 #include <assert.h>
39
40 #include <vlc_common.h>
41 #include <vlc_interface.h>
42 #include <vlc_charset.h>
43 #include <vlc_modules.h>
44 #include "../libvlc.h"
45
46 #ifdef __ANDROID__
47 #include <android/log.h>
48 #endif
49
50 struct vlc_logger_t
51 {
52     VLC_COMMON_MEMBERS
53     vlc_rwlock_t lock;
54     vlc_log_cb log;
55     void *sys;
56     module_t *module;
57 };
58
59 static void vlc_vaLogCallback(libvlc_int_t *vlc, int type,
60                               const vlc_log_t *item, const char *format,
61                               va_list ap)
62 {
63     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
64
65     assert(logger != NULL);
66     vlc_rwlock_rdlock(&logger->lock);
67     logger->log(logger->sys, type, item, format, ap);
68     vlc_rwlock_unlock(&logger->lock);
69 }
70
71 static void vlc_LogCallback(libvlc_int_t *vlc, int type, const vlc_log_t *item,
72                             const char *format, ...)
73 {
74     va_list ap;
75
76     va_start(ap, format);
77     vlc_vaLogCallback(vlc, type, item, format, ap);
78     va_end(ap);
79 }
80
81 #ifdef _WIN32
82 static void Win32DebugOutputMsg (void *, int , const vlc_log_t *,
83                                  const char *, va_list);
84 #endif
85
86 /**
87  * Emit a log message. This function is the variable argument list equivalent
88  * to vlc_Log().
89  */
90 void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
91                 const char *format, va_list args)
92 {
93     if (obj != NULL && obj->i_flags & OBJECT_FLAGS_QUIET)
94         return;
95
96     /* Get basename from the module filename */
97     char *p = strrchr(module, '/');
98     if (p != NULL)
99         module = p;
100     p = strchr(module, '.');
101
102     size_t modlen = (p != NULL) ? (p - module) : 0;
103     char modulebuf[modlen + 1];
104     if (p != NULL)
105     {
106         memcpy(modulebuf, module, modlen);
107         modulebuf[modlen] = '\0';
108         module = modulebuf;
109     }
110
111     /* Fill message information fields */
112     vlc_log_t msg;
113
114     msg.i_object_id = (uintptr_t)obj;
115     msg.psz_object_type = (obj != NULL) ? obj->psz_object_type : "generic";
116     msg.psz_module = module;
117     msg.psz_header = NULL;
118
119     for (vlc_object_t *o = obj; o != NULL; o = o->p_parent)
120         if (o->psz_header != NULL)
121         {
122             msg.psz_header = o->psz_header;
123             break;
124         }
125
126 #ifdef _WIN32
127     va_list ap;
128
129     va_copy (ap, args);
130     Win32DebugOutputMsg (NULL, type, &msg, format, ap);
131     va_end (ap);
132 #endif
133
134     /* Pass message to the callback */
135     if (obj != NULL)
136         vlc_vaLogCallback(obj->p_libvlc, type, &msg, format, args);
137 }
138
139 /**
140  * Emit a log message.
141  * \param obj VLC object emitting the message or NULL
142  * \param type VLC_MSG_* message type (info, error, warning or debug)
143  * \param module name of module from which the message come
144  *               (normally MODULE_STRING)
145  * \param format printf-like message format
146  */
147 void vlc_Log(vlc_object_t *obj, int type, const char *module,
148              const char *format, ... )
149 {
150     va_list ap;
151
152     va_start(ap, format);
153     vlc_vaLog(obj, type, module, format, ap);
154     va_end(ap);
155 }
156
157 #ifdef _WIN32
158 static const char msg_type[4][9] = { "", " error", " warning", " debug" };
159
160 static void Win32DebugOutputMsg (void* d, int type, const vlc_log_t *p_item,
161                                  const char *format, va_list dol)
162 {
163     VLC_UNUSED(p_item);
164
165     const signed char *pverbose = d;
166     if (pverbose && (*pverbose < 0 || *pverbose < (type - VLC_MSG_ERR)))
167         return;
168
169     va_list dol2;
170     va_copy (dol2, dol);
171     int msg_len = vsnprintf(NULL, 0, format, dol2);
172     va_end (dol2);
173
174     if(msg_len <= 0)
175         return;
176
177     char *msg = malloc(msg_len + 1 + 1);
178     if (!msg)
179         return;
180
181     msg_len = vsnprintf(msg, msg_len+1, format, dol);
182     if (msg_len > 0){
183         if(msg[msg_len-1] != '\n'){
184             msg[msg_len] = '\n';
185             msg[msg_len + 1] = '\0';
186         }
187         char* psz_msg = NULL;
188         if(asprintf(&psz_msg, "%s %s%s: %s", p_item->psz_module,
189                     p_item->psz_object_type, msg_type[type], msg) > 0) {
190             wchar_t* wmsg = ToWide(psz_msg);
191             OutputDebugStringW(wmsg);
192             free(wmsg);
193             free(psz_msg);
194         }
195     }
196     free(msg);
197 }
198 #endif
199
200 #ifdef __ANDROID__
201 static void AndroidPrintMsg (void *d, int type, const vlc_log_t *p_item,
202                              const char *format, va_list ap)
203 {
204     int verbose = (intptr_t)d;
205     int prio;
206
207     if (verbose < 0 || verbose < (type - VLC_MSG_ERR))
208         return;
209
210     int canc = vlc_savecancel ();
211
212     char *format2;
213     if (asprintf (&format2, "[%0*"PRIxPTR"] %s %s: %s",
214                   ptr_width, p_item->i_object_id, p_item->psz_module,
215                   p_item->psz_object_type, format) < 0)
216         return;
217     switch (type) {
218         case VLC_MSG_INFO:
219             prio = ANDROID_LOG_INFO;
220             break;
221         case VLC_MSG_ERR:
222             prio = ANDROID_LOG_ERROR;
223             break;
224         case VLC_MSG_WARN:
225             prio = ANDROID_LOG_WARN;
226             break;
227         default:
228         case VLC_MSG_DBG:
229             prio = ANDROID_LOG_DEBUG;
230     }
231     __android_log_vprint (prio, "VLC", format2, ap);
232     free (format2);
233     vlc_restorecancel (canc);
234 }
235 #endif
236
237 typedef struct vlc_log_early_t
238 {
239     struct vlc_log_early_t *next;
240     int type;
241     vlc_log_t meta;
242     char *msg;
243 } vlc_log_early_t;
244
245 typedef struct
246 {
247     vlc_mutex_t lock;
248     vlc_log_early_t *head;
249     vlc_log_early_t **tailp;
250 } vlc_logger_early_t;
251
252 static void vlc_vaLogEarly(void *d, int type, const vlc_log_t *item,
253                            const char *format, va_list ap)
254 {
255     vlc_logger_early_t *sys = d;
256
257     vlc_log_early_t *log = malloc(sizeof (*log));
258     if (unlikely(log == NULL))
259         return;
260
261     log->next = NULL;
262     log->type = type;
263     log->meta.i_object_id = item->i_object_id;
264     /* NOTE: Object types MUST be static constant - no need to copy them. */
265     log->meta.psz_object_type = item->psz_object_type;
266     log->meta.psz_module = item->psz_module; /* Ditto. */
267     log->meta.psz_header = item->psz_header ? strdup(item->psz_header) : NULL;
268
269     int canc = vlc_savecancel(); /* XXX: needed for vasprintf() ? */
270     if (vasprintf(&log->msg, format, ap) == -1)
271         log->msg = NULL;
272     vlc_restorecancel(canc);
273
274     vlc_mutex_lock(&sys->lock);
275     assert(sys->tailp != NULL);
276     assert(*(sys->tailp) == NULL);
277     *(sys->tailp) = log;
278     sys->tailp = &log->next;
279     vlc_mutex_unlock(&sys->lock);
280 }
281
282 static int vlc_LogEarlyOpen(vlc_logger_t *logger)
283 {
284     vlc_logger_early_t *sys = malloc(sizeof (*sys));
285
286     if (unlikely(sys == NULL))
287         return -1;
288
289     vlc_mutex_init(&sys->lock);
290     sys->head = NULL;
291     sys->tailp = &sys->head;
292
293     logger->log = vlc_vaLogEarly;
294     logger->sys = sys;
295     return 0;
296 }
297
298 static void vlc_LogEarlyClose(vlc_logger_t *logger, void *d)
299 {
300     libvlc_int_t *vlc = logger->p_libvlc;
301     vlc_logger_early_t *sys = d;
302
303     /* Drain early log messages */
304     for (vlc_log_early_t *log = sys->head, *next; log != NULL; log = next)
305     {
306         vlc_LogCallback(vlc, log->type, &log->meta, "%s",
307                         (log->msg != NULL) ? log->msg : "message lost");
308         free(log->msg);
309         next = log->next;
310         free(log);
311     }
312
313     vlc_mutex_destroy(&sys->lock);
314     free(sys);
315 }
316
317 static void vlc_vaLogDiscard(void *d, int type, const vlc_log_t *item,
318                              const char *format, va_list ap)
319 {
320     (void) d; (void) type; (void) item; (void) format; (void) ap;
321 }
322
323 static int vlc_logger_load(void *func, va_list ap)
324 {
325     vlc_log_cb (*activate)(vlc_object_t *, void **) = func;
326     vlc_logger_t *logger = va_arg(ap, vlc_logger_t *);
327     vlc_log_cb *cb = va_arg(ap, vlc_log_cb *);
328     void **sys = va_arg(ap, void **);
329
330     *cb = activate(VLC_OBJECT(logger), sys);
331     return (*cb != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
332 }
333
334 static void vlc_logger_unload(void *func, va_list ap)
335 {
336     void (*deactivate)(vlc_logger_t *) = func;
337     void *sys = va_arg(ap, void *);
338
339     deactivate(sys);
340 }
341
342 /**
343  * Performs preinitialization of the messages logging subsystem.
344  *
345  * Early log messages will be stored in memory until the subsystem is fully
346  * initialized with vlc_LogInit(). This enables logging before the
347  * configuration and modules bank are ready.
348  *
349  * \return 0 on success, -1 on error.
350  */
351 int vlc_LogPreinit(libvlc_int_t *vlc)
352 {
353     vlc_logger_t *logger = vlc_custom_create(vlc, sizeof (*logger), "logger");
354
355     libvlc_priv(vlc)->logger = logger;
356
357     if (unlikely(logger == NULL))
358         return -1;
359
360     vlc_rwlock_init(&logger->lock);
361
362     if (vlc_LogEarlyOpen(logger))
363     {
364         logger->log = vlc_vaLogDiscard;
365         return -1;
366     }
367
368     /* Announce who we are */
369     msg_Dbg(vlc, "VLC media player - %s", VERSION_MESSAGE);
370     msg_Dbg(vlc, "%s", COPYRIGHT_MESSAGE);
371     msg_Dbg(vlc, "revision %s", psz_vlc_changeset);
372     msg_Dbg(vlc, "configured with %s", CONFIGURE_LINE);
373     return 0;
374 }
375
376 /**
377  * Initializes the messages logging subsystem and drain the early messages to
378  * the configured log.
379  *
380  * \return 0 on success, -1 on error.
381  */
382 int vlc_LogInit(libvlc_int_t *vlc)
383 {
384     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
385     if (unlikely(logger == NULL))
386         return -1;
387
388     vlc_log_cb cb;
389     void *sys, *early_sys = NULL;
390
391     /* TODO: module configuration item */
392     module_t *module = vlc_module_load(logger, "logger", NULL, false,
393                                        vlc_logger_load, logger, &cb, &sys);
394     if (module == NULL)
395 #ifdef __ANDROID__
396         cb = AndroidPrintMsg, sys = NULL;
397 #else
398         cb = vlc_vaLogDiscard;
399 #endif
400
401     vlc_rwlock_wrlock(&logger->lock);
402     if (logger->log == vlc_vaLogEarly)
403         early_sys = logger->sys;
404
405     logger->log = cb;
406     logger->sys = sys;
407     assert(logger->module == NULL); /* Only one call to vlc_LogInit()! */
408     logger->module = module;
409     vlc_rwlock_unlock(&logger->lock);
410
411     if (early_sys != NULL)
412         vlc_LogEarlyClose(logger, early_sys);
413
414     return 0;
415 }
416
417 /**
418  * Sets the message logging callback.
419  * \param cb message callback, or NULL to clear
420  * \param data data pointer for the message callback
421  */
422 void vlc_LogSet(libvlc_int_t *vlc, vlc_log_cb cb, void *opaque)
423 {
424     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
425
426     if (unlikely(logger == NULL))
427         return;
428
429     module_t *module;
430     void *sys;
431
432     if (cb == NULL)
433         cb = vlc_vaLogDiscard;
434
435     vlc_rwlock_wrlock(&logger->lock);
436     sys = logger->sys;
437     module = logger->module;
438
439     logger->log = cb;
440     logger->sys = opaque;
441     logger->module = NULL;
442     vlc_rwlock_unlock(&logger->lock);
443
444     if (module != NULL)
445         vlc_module_unload(module, vlc_logger_unload, sys);
446
447     /* Announce who we are */
448     msg_Dbg (vlc, "VLC media player - %s", VERSION_MESSAGE);
449     msg_Dbg (vlc, "%s", COPYRIGHT_MESSAGE);
450     msg_Dbg (vlc, "revision %s", psz_vlc_changeset);
451     msg_Dbg (vlc, "configured with %s", CONFIGURE_LINE);
452 }
453
454 void vlc_LogDeinit(libvlc_int_t *vlc)
455 {
456     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
457
458     if (unlikely(logger == NULL))
459         return;
460
461     if (logger->module != NULL)
462         vlc_module_unload(logger->module, vlc_logger_unload, logger->sys);
463     else
464     /* Flush early log messages (corner case: no call to vlc_LogInit()) */
465     if (logger->log == vlc_vaLogEarly)
466     {
467         logger->log = vlc_vaLogDiscard;
468         vlc_LogEarlyClose(logger, logger->sys);
469     }
470
471     vlc_rwlock_destroy(&logger->lock);
472     vlc_object_release(logger);
473     libvlc_priv(vlc)->logger = NULL;
474 }