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
8 * Authors: Vincent Seguin <seguin@via.ecp.fr>
9 * Samuel Hocevar <sam@zoy.org>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
35 #include <stdarg.h> /* va_list for BSD */
39 #include <vlc_common.h>
40 #include <vlc_interface.h>
41 #include <vlc_charset.h>
42 #include <vlc_modules.h>
43 #include "../libvlc.h"
45 static void vlc_LogSpam(vlc_object_t
*obj
)
47 /* Announce who we are */
48 msg_Dbg(obj
, "VLC media player - %s", VERSION_MESSAGE
);
49 msg_Dbg(obj
, "%s", COPYRIGHT_MESSAGE
);
50 msg_Dbg(obj
, "revision %s", psz_vlc_changeset
);
51 msg_Dbg(obj
, "configured with %s", CONFIGURE_LINE
);
55 const struct vlc_logger_operations
*ops
;
58 static void vlc_vaLogCallback(vlc_logger_t
*logger
, int type
,
59 const vlc_log_t
*item
, const char *format
,
63 int canc
= vlc_savecancel();
65 logger
->ops
->log(logger
, type
, item
, format
, ap
);
66 vlc_restorecancel(canc
);
70 static void vlc_LogCallback(vlc_logger_t
*logger
, int type
,
71 const vlc_log_t
*item
, const char *format
, ...)
76 vlc_vaLogCallback(logger
, type
, item
, format
, ap
);
81 static void Win32DebugOutputMsg (void *, int , const vlc_log_t
*,
82 const char *, va_list);
85 void vlc_vaLog(struct vlc_logger
*const *loggerp
, int type
,
86 const char *typename
, const char *module
,
87 const char *file
, unsigned line
, const char *func
,
88 const char *format
, va_list args
)
90 struct vlc_logger
*logger
= *loggerp
;
91 /* Get basename from the module filename */
92 char *p
= strrchr(module
, '/');
95 p
= strchr(module
, '.');
97 size_t modlen
= (p
!= NULL
) ? (p
- module
) : 0;
98 char modulebuf
[modlen
+ 1];
101 memcpy(modulebuf
, module
, modlen
);
102 modulebuf
[modlen
] = '\0';
106 /* Fill message information fields */
109 msg
.i_object_id
= (uintptr_t)(void *)loggerp
;
110 msg
.psz_object_type
= typename
;
111 msg
.psz_module
= module
;
112 msg
.psz_header
= NULL
;
116 msg
.tid
= vlc_thread_id();
122 Win32DebugOutputMsg (NULL
, type
, &msg
, format
, ap
);
126 /* Pass message to the callback */
128 vlc_vaLogCallback(logger
, type
, &msg
, format
, args
);
131 void vlc_Log(struct vlc_logger
*const *logger
, int type
,
132 const char *typename
, const char *module
,
133 const char *file
, unsigned line
, const char *func
,
134 const char *format
, ... )
138 va_start(ap
, format
);
139 vlc_vaLog(logger
, type
, typename
, module
, file
, line
, func
, format
, ap
);
144 static const char msg_type
[4][9] = { "", " error", " warning", " debug" };
146 static void Win32DebugOutputMsg (void* d
, int type
, const vlc_log_t
*p_item
,
147 const char *format
, va_list dol
)
151 const signed char *pverbose
= d
;
152 if (pverbose
&& (*pverbose
< 0 || *pverbose
< (type
- VLC_MSG_ERR
)))
157 int msg_len
= vsnprintf(NULL
, 0, format
, dol2
);
163 char *msg
= malloc(msg_len
+ 1 + 1);
167 msg_len
= vsnprintf(msg
, msg_len
+1, format
, dol
);
169 if (msg
[msg_len
-1] != '\n') {
171 msg
[msg_len
+ 1] = '\0';
173 char* psz_msg
= NULL
;
174 if (asprintf(&psz_msg
, "%s %s%s: %s", p_item
->psz_module
,
175 p_item
->psz_object_type
, msg_type
[type
], msg
) > 0) {
176 wchar_t* wmsg
= ToWide(psz_msg
);
177 if (likely(wmsg
!= NULL
))
179 OutputDebugStringW(wmsg
);
190 * Early (latched) message log.
192 * A message log that stores messages in memory until another log is available.
194 typedef struct vlc_log_early_t
196 struct vlc_log_early_t
*next
;
202 typedef struct vlc_logger_early
{
204 vlc_log_early_t
*head
;
205 vlc_log_early_t
**tailp
;
207 struct vlc_logger logger
;
208 } vlc_logger_early_t
;
210 static void vlc_vaLogEarly(void *d
, int type
, const vlc_log_t
*item
,
211 const char *format
, va_list ap
)
213 struct vlc_logger
*logger
= d
;
214 struct vlc_logger_early
*early
=
215 container_of(logger
, struct vlc_logger_early
, logger
);
217 vlc_log_early_t
*log
= malloc(sizeof (*log
));
218 if (unlikely(log
== NULL
))
223 log
->meta
.i_object_id
= item
->i_object_id
;
224 /* NOTE: Object types MUST be static constant - no need to copy them. */
225 log
->meta
.psz_object_type
= item
->psz_object_type
;
226 log
->meta
.psz_module
= item
->psz_module
; /* Ditto. */
227 log
->meta
.psz_header
= item
->psz_header
? strdup(item
->psz_header
) : NULL
;
228 log
->meta
.file
= item
->file
;
229 log
->meta
.line
= item
->line
;
230 log
->meta
.func
= item
->func
;
232 if (vasprintf(&log
->msg
, format
, ap
) == -1)
235 vlc_mutex_lock(&early
->lock
);
236 assert(early
->tailp
!= NULL
);
237 assert(*(early
->tailp
) == NULL
);
238 *(early
->tailp
) = log
;
239 early
->tailp
= &log
->next
;
240 vlc_mutex_unlock(&early
->lock
);
243 static void vlc_LogEarlyClose(void *d
)
245 struct vlc_logger
*logger
= d
;
246 struct vlc_logger_early
*early
=
247 container_of(logger
, struct vlc_logger_early
, logger
);
248 vlc_logger_t
*sink
= early
->sink
;
250 /* Drain early log messages */
251 for (vlc_log_early_t
*log
= early
->head
, *next
; log
!= NULL
; log
= next
)
253 vlc_LogCallback(sink
, log
->type
, &log
->meta
, "%s",
254 (log
->msg
!= NULL
) ? log
->msg
: "message lost");
263 static const struct vlc_logger_operations early_ops
= {
268 static struct vlc_logger
*vlc_LogEarlyOpen(struct vlc_logger
*logger
)
270 vlc_logger_early_t
*early
= malloc(sizeof (*early
));
271 if (unlikely(early
== NULL
))
274 early
->logger
.ops
= &early_ops
;
275 vlc_mutex_init(&early
->lock
);
277 early
->tailp
= &early
->head
;
278 early
->sink
= logger
;
279 return &early
->logger
;
282 static void vlc_vaLogDiscard(void *d
, int type
, const vlc_log_t
*item
,
283 const char *format
, va_list ap
)
285 (void) d
; (void) type
; (void) item
; (void) format
; (void) ap
;
288 static void vlc_LogDiscardClose(void *d
)
293 static const struct vlc_logger_operations discard_ops
= {
298 static struct vlc_logger discard_log
= { &discard_ops
};
301 * Switchable message log.
303 * A message log that can be redirected live.
305 struct vlc_logger_switch
{
306 struct vlc_logger
*backend
;
307 struct vlc_logger frontend
;
311 static void vlc_vaLogSwitch(void *d
, int type
, const vlc_log_t
*item
,
312 const char *format
, va_list ap
)
314 struct vlc_logger
*logger
= d
;
315 struct vlc_logger_switch
*logswitch
=
316 container_of(logger
, struct vlc_logger_switch
, frontend
);
317 struct vlc_logger
*backend
;
319 vlc_rwlock_rdlock(&logswitch
->lock
);
320 backend
= logswitch
->backend
;
321 backend
->ops
->log(backend
, type
, item
, format
, ap
);
322 vlc_rwlock_unlock(&logswitch
->lock
);
325 static void vlc_LogSwitchClose(void *d
)
327 struct vlc_logger
*logger
= d
;
328 struct vlc_logger_switch
*logswitch
=
329 container_of(logger
, struct vlc_logger_switch
, frontend
);
330 struct vlc_logger
*backend
= logswitch
->backend
;
332 logswitch
->backend
= &discard_log
;
333 backend
->ops
->destroy(backend
);
335 vlc_rwlock_destroy(&logswitch
->lock
);
339 static const struct vlc_logger_operations switch_ops
= {
344 static void vlc_LogSwitch(vlc_logger_t
*logger
, vlc_logger_t
*new_logger
)
346 struct vlc_logger_switch
*logswitch
=
347 container_of(logger
, struct vlc_logger_switch
, frontend
);
348 struct vlc_logger
*old_logger
;
350 assert(logger
->ops
== &switch_ops
);
352 if (new_logger
== NULL
)
353 new_logger
= &discard_log
;
355 vlc_rwlock_wrlock(&logswitch
->lock
);
356 old_logger
= logswitch
->backend
;
357 logswitch
->backend
= new_logger
;
358 vlc_rwlock_unlock(&logswitch
->lock
);
360 old_logger
->ops
->destroy(old_logger
);
363 static struct vlc_logger
*vlc_LogSwitchCreate(void)
365 struct vlc_logger_switch
*logswitch
= malloc(sizeof (*logswitch
));
366 if (unlikely(logswitch
== NULL
))
369 logswitch
->frontend
.ops
= &switch_ops
;
370 logswitch
->backend
= &discard_log
;
371 vlc_rwlock_init(&logswitch
->lock
);
372 return &logswitch
->frontend
;
376 * Module-based message log.
378 struct vlc_logger_module
{
379 struct vlc_object_t obj
;
380 struct vlc_logger frontend
;
381 const struct vlc_logger_operations
*ops
;
385 static int vlc_logger_load(void *func
, bool forced
, va_list ap
)
387 const struct vlc_logger_operations
*(*activate
)(vlc_object_t
*,
389 struct vlc_logger_module
*module
= va_arg(ap
, struct vlc_logger_module
*);
392 module
->ops
= activate(VLC_OBJECT(module
), &module
->opaque
);
393 return (module
->ops
!= NULL
) ? VLC_SUCCESS
: VLC_EGENERIC
;
396 static void vlc_vaLogModule(void *d
, int type
, const vlc_log_t
*item
,
397 const char *format
, va_list ap
)
399 struct vlc_logger
*logger
= d
;
400 struct vlc_logger_module
*module
=
401 container_of(logger
, struct vlc_logger_module
, frontend
);
403 module
->ops
->log(module
->opaque
, type
, item
, format
, ap
);
406 static void vlc_LogModuleClose(void *d
)
408 struct vlc_logger
*logger
= d
;
409 struct vlc_logger_module
*module
=
410 container_of(logger
, struct vlc_logger_module
, frontend
);
412 if (module
->ops
->destroy
!= NULL
)
413 module
->ops
->destroy(module
->opaque
);
415 vlc_object_delete(VLC_OBJECT(module
));
418 static const struct vlc_logger_operations module_ops
= {
423 static struct vlc_logger
*vlc_LogModuleCreate(vlc_object_t
*parent
)
425 struct vlc_logger_module
*module
;
427 module
= vlc_custom_create(parent
, sizeof (*module
), "logger");
428 if (unlikely(module
== NULL
))
431 /* TODO: module configuration item */
432 if (vlc_module_load(VLC_OBJECT(module
), "logger", NULL
, false,
433 vlc_logger_load
, module
) == NULL
) {
434 vlc_object_delete(VLC_OBJECT(module
));
438 module
->frontend
.ops
= &module_ops
;
439 return &module
->frontend
;
443 * Initializes the messages logging subsystem and drain the early messages to
444 * the configured log.
446 void vlc_LogInit(libvlc_int_t
*vlc
)
448 struct vlc_logger
*logger
= vlc_LogModuleCreate(VLC_OBJECT(vlc
));
450 logger
= &discard_log
;
452 vlc_LogSwitch(vlc
->obj
.logger
, logger
);
456 * Performs preinitialization of the messages logging subsystem.
458 * Early log messages will be stored in memory until the subsystem is fully
459 * initialized with vlc_LogInit(). This enables logging before the
460 * configuration and modules bank are ready.
462 * \return 0 on success, -1 on error.
464 int vlc_LogPreinit(libvlc_int_t
*vlc
)
466 vlc_logger_t
*logger
= vlc_LogSwitchCreate();
467 if (unlikely(logger
== NULL
))
469 vlc
->obj
.logger
= logger
;
471 struct vlc_logger
*early
= vlc_LogEarlyOpen(logger
);
473 vlc_LogSwitch(logger
, early
);
474 vlc_LogSpam(VLC_OBJECT(vlc
));
480 * Message log with "header".
482 struct vlc_logger_header
{
483 struct vlc_logger logger
;
484 struct vlc_logger
*parent
;
488 static void vlc_vaLogHeader(void *d
, int type
, const vlc_log_t
*item
,
489 const char *format
, va_list ap
)
491 struct vlc_logger
*logger
= d
;
492 struct vlc_logger_header
*header
=
493 container_of(logger
, struct vlc_logger_header
, logger
);
494 struct vlc_logger
*parent
= header
->parent
;
495 vlc_log_t hitem
= *item
;
497 hitem
.psz_header
= header
->header
;
498 parent
->ops
->log(parent
, type
, &hitem
, format
, ap
);
501 static const struct vlc_logger_operations header_ops
= {
506 struct vlc_logger
*vlc_LogHeaderCreate(struct vlc_logger
*parent
,
509 size_t len
= strlen(str
) + 1;
510 struct vlc_logger_header
*header
= malloc(sizeof (*header
) + len
);
511 if (unlikely(header
== NULL
))
514 header
->logger
.ops
= &header_ops
;
515 header
->parent
= parent
;
516 memcpy(header
->header
, str
, len
);
517 return &header
->logger
;
521 * External custom log callback
523 struct vlc_logger_external
{
524 struct vlc_logger logger
;
525 const struct vlc_logger_operations
*ops
;
529 static void vlc_vaLogExternal(void *d
, int type
, const vlc_log_t
*item
,
530 const char *format
, va_list ap
)
532 struct vlc_logger_external
*ext
= d
;
534 ext
->ops
->log(ext
->opaque
, type
, item
, format
, ap
);
537 static void vlc_LogExternalClose(void *d
)
539 struct vlc_logger_external
*ext
= d
;
541 if (ext
->ops
->destroy
!= NULL
)
542 ext
->ops
->destroy(ext
->opaque
);
546 static const struct vlc_logger_operations external_ops
= {
548 vlc_LogExternalClose
,
551 static struct vlc_logger
*
552 vlc_LogExternalCreate(const struct vlc_logger_operations
*ops
, void *opaque
)
554 struct vlc_logger_external
*ext
= malloc(sizeof (*ext
));
555 if (unlikely(ext
== NULL
))
558 ext
->logger
.ops
= &external_ops
;
560 ext
->opaque
= opaque
;
565 * Sets the message logging callback.
566 * \param ops message callback, or NULL to clear
567 * \param data data pointer for the message callback
569 void vlc_LogSet(libvlc_int_t
*vlc
, const struct vlc_logger_operations
*ops
,
572 struct vlc_logger
*logger
;
575 logger
= vlc_LogExternalCreate(ops
, opaque
);
580 logger
= &discard_log
;
582 vlc_LogSwitch(vlc
->obj
.logger
, logger
);
583 vlc_LogSpam(VLC_OBJECT(vlc
));
586 void vlc_LogDestroy(vlc_logger_t
*logger
)
588 logger
->ops
->destroy(logger
);