logger.c 12.3 KB
Newer Older
1
2
3
/*****************************************************************************
 * logger.c : file logging plugin for vlc
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
4
 * Copyright (C) 2002-2008 the VideoLAN team
5
 * $Id$
6
7
8
9
10
11
12
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14
15
16
17
18
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
19
20
21
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

28
29
30
31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
33
#include <vlc_plugin.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34
#include <vlc_interface.h>
35
#include <vlc_fs.h>
36
#include <vlc_charset.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
37

38
#include <stdarg.h>
39
#include <assert.h>
40
41
42

#define MODE_TEXT 0
#define MODE_HTML 1
43
#define MODE_SYSLOG 2
44

45
#ifdef __APPLE__
hartman's avatar
hartman committed
46
47
48
49
50
51
#define LOG_DIR "Library/Logs/"
#endif

#define LOG_FILE_TEXT "vlc-log.txt"
#define LOG_FILE_HTML "vlc-log.html"

52
53
54
55
#define TEXT_HEADER "-- logger module started --\n"
#define TEXT_FOOTER "-- logger module stopped --\n"

#define HTML_HEADER \
56
57
    "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n" \
    "  \"http://www.w3.org/TR/html4/strict.dtd\">\n" \
58
59
60
    "<html>\n" \
    "  <head>\n" \
    "    <title>vlc log</title>\n" \
61
    "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" \
62
    "  </head>\n" \
63
    "  <body style=\"background-color: #000000; color: #aaaaaa;\">\n" \
64
65
66
67
68
69
70
71
    "    <pre>\n" \
    "      <b>-- logger module started --</b>\n"
#define HTML_FOOTER \
    "      <b>-- logger module stopped --</b>\n" \
    "    </pre>\n" \
    "  </body>\n" \
    "</html>\n"

Pierre Ynard's avatar
Pierre Ynard committed
72
#ifdef HAVE_SYSLOG_H
73
74
75
#include <syslog.h>
#endif

76
77
78
79
80
81
/*****************************************************************************
 * intf_sys_t: description and status of log interface
 *****************************************************************************/
struct intf_sys_t
{
    msg_subscription_t *p_sub;
82
    FILE *p_file;
83
    const char *footer;
84
85
86
87
88
};

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
89
static int  Open    ( vlc_object_t * );
90
91
static void Close   ( vlc_object_t * );

92
93
static void TextPrint(void *, int, const msg_item_t *, const char *, va_list);
static void HtmlPrint(void *, int, const msg_item_t *, const char *, va_list);
94
#ifdef HAVE_SYSLOG_H
95
96
static void SyslogPrint(void *, int, const msg_item_t *, const char *,
                        va_list);
97
#endif
98
99
100
101

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
102
static const char *const mode_list[] = { "text", "html"
103
104
105
106
#ifdef HAVE_SYSLOG_H
,"syslog"
#endif
};
107
static const char *const mode_list_text[] = { N_("Text"), "HTML"
108
109
110
111
#ifdef HAVE_SYSLOG_H
, "syslog"
#endif
};
112

Christophe Massiot's avatar
Christophe Massiot committed
113
#define LOGMODE_TEXT N_("Log format")
114
#ifndef HAVE_SYSLOG_H
115
#define LOGMODE_LONGTEXT N_("Specify the log format. Available choices are " \
Christophe Mutricy's avatar
Christophe Mutricy committed
116
  "\"text\" (default) and \"html\".")
117
#else
Christophe Massiot's avatar
Christophe Massiot committed
118

119
120
121
#define LOGMODE_LONGTEXT N_("Specify the log format. Available choices are " \
  "\"text\" (default), \"html\", and \"syslog\" (special mode to send to " \
  "syslog instead of file.")
122
123
124

#define SYSLOG_FACILITY_TEXT N_("Syslog facility")
#define SYSLOG_FACILITY_LONGTEXT N_("Select the syslog facility where logs " \
125
  "will be forwarded. Available choices are \"user\" (default), \"daemon\", " \
126
  "and \"local0\" through \"local7\".")
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

/* First in list is the default facility used. */
#define DEFINE_SYSLOG_FACILITY \
  DEF( "user",   LOG_USER ), \
  DEF( "daemon", LOG_DAEMON ), \
  DEF( "local0", LOG_LOCAL0 ), \
  DEF( "local1", LOG_LOCAL1 ), \
  DEF( "local2", LOG_LOCAL2 ), \
  DEF( "local3", LOG_LOCAL3 ), \
  DEF( "local4", LOG_LOCAL4 ), \
  DEF( "local5", LOG_LOCAL5 ), \
  DEF( "local6", LOG_LOCAL6 ), \
  DEF( "local7", LOG_LOCAL7 )

#define DEF( a, b ) a
static const char *const fac_name[]   = { DEFINE_SYSLOG_FACILITY };
#undef  DEF
#define DEF( a, b ) b
static const int         fac_number[] = { DEFINE_SYSLOG_FACILITY };
#undef  DEF
enum                   { fac_entries = sizeof(fac_name)/sizeof(fac_name[0]) };
#undef  DEFINE_SYSLOG_FACILITY

150
151
#endif

Pierre Ynard's avatar
Pierre Ynard committed
152
153
154
155
#define LOGVERBOSE_TEXT N_("Verbosity")
#define LOGVERBOSE_LONGTEXT N_("Select the verbosity to use for log or -1 to " \
"use the same verbosity given by --verbose.")

156
157
158
vlc_module_begin ()
    set_shortname( N_( "Logging" ) )
    set_description( N_("File logging") )
gbazin's avatar
   
gbazin committed
159

160
161
    set_category( CAT_ADVANCED )
    set_subcategory( SUBCAT_ADVANCED_MISC )
162

163
    add_savefile( "logfile", NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
164
             N_("Log filename"), N_("Specify the log filename."), false )
165
    add_string( "logmode", "text", LOGMODE_TEXT, LOGMODE_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
166
                false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
167
        change_string_list( mode_list, mode_list_text, 0 )
168
#ifdef HAVE_SYSLOG_H
169
    add_string( "syslog-facility", fac_name[0], SYSLOG_FACILITY_TEXT,
170
                SYSLOG_FACILITY_LONGTEXT, true )
171
        change_string_list( fac_name, fac_name, 0 )
172
#endif
173
    add_integer( "log-verbose", -1, LOGVERBOSE_TEXT, LOGVERBOSE_LONGTEXT,
174
175
           false )
    
176
    add_obsolete_string( "rrd-file" )
zorglub's avatar
zorglub committed
177

178
179
180
    set_capability( "interface", 0 )
    set_callbacks( Open, Close )
vlc_module_end ()
181
182
183
184
185

/*****************************************************************************
 * Open: initialize and create stuff
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
186
{
187
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
188
    intf_sys_t *p_sys;
189

gbazin's avatar
   
gbazin committed
190
    CONSOLE_INTRO_MSG;
191
    msg_Info( p_intf, "using logger." );
192
193

    /* Allocate instance and initialize some members */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
194
195
196
    p_sys = p_intf->p_sys = (intf_sys_t *)malloc( sizeof( intf_sys_t ) );
    if( p_sys == NULL )
        return VLC_ENOMEM;
197

198
199
200
201
202
203
    msg_callback_t cb = TextPrint;
    const char *filename = LOG_FILE_TEXT, *header = TEXT_HEADER;
    p_sys->footer = TEXT_FOOTER;

    char *mode = var_InheritString( p_intf, "logmode" );
    if( mode != NULL )
204
    {
205
        if( !strcmp( mode, "html" ) )
206
        {
207
208
209
            p_sys->footer = HTML_FOOTER;
            header = HTML_HEADER;
            cb = HtmlPrint;
210
        }
211
#ifdef HAVE_SYSLOG_H
212
213
        else if( !strcmp( mode, "syslog" ) )
            cb = SyslogPrint;
214
#endif
215
216
217
        else if( strcmp( mode, "text" ) )
            msg_Warn( p_intf, "invalid log mode `%s', using `text'", mode );
        free( mode );
218
    }
219
220

#ifdef HAVE_SYSLOG_H
221
222
    if( cb == SyslogPrint )
    {
223
        int i_facility;
ivoire's avatar
ivoire committed
224
        char *psz_facility = var_InheritString( p_intf, "syslog-facility" );
225
226
227
        if( psz_facility )
        {
            bool b_valid = 0;
228
            for( size_t i = 0; i < fac_entries; ++i )
229
            {
230
                if( !strcmp( psz_facility, fac_name[i] ) )
231
                {
232
                    i_facility = fac_number[i];
233
234
235
236
237
238
                    b_valid = 1;
                    break;
                }
            }
            if( !b_valid )
            {
239
240
241
                msg_Warn( p_intf, "invalid syslog facility `%s', using `%s'",
                          psz_facility, fac_name[0] );
                i_facility = fac_number[0];
242
243
244
245
246
            }
            free( psz_facility );
        }
        else
        {
247
248
249
            msg_Warn( p_intf, "no syslog facility specified, using `%s'",
                      fac_name[0] );
            i_facility = fac_number[0];
250
251
252
        }

        openlog( "vlc", LOG_PID|LOG_NDELAY, i_facility );
253
        p_sys->p_file = NULL;
254
    }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
    else
#endif
    {
        char *psz_file = var_InheritString( p_intf, "logfile" );
        if( !psz_file )
        {
#ifdef __APPLE__
            char *home = config_GetUserDir(VLC_DOCUMENTS_DIR);
            if( home == NULL
             || asprintf( &psz_file, "%s/"LOG_DIR"/%s", home,
                          filename ) == -1 )
                psz_file = NULL;
            free(home);
            filename = psz_file;
#endif
            msg_Warn( p_intf, "no log filename provided, using `%s'",
                      filename );
        }
        else
            filename = psz_file;
275

276
277
278
279
280
281
282
283
284
285
286
287
288
        /* Open the log file and remove any buffering for the stream */
        msg_Dbg( p_intf, "opening logfile `%s'", filename );
        p_sys->p_file = vlc_fopen( filename, "at" );
        free( psz_file );
        if( p_sys->p_file == NULL )
        {
            msg_Err( p_intf, "error opening logfile `%s': %m", filename );
            free( p_sys );
            return VLC_EGENERIC;
        }
        setvbuf( p_sys->p_file, NULL, _IONBF, 0 );
        fputs( header, p_sys->p_file );
    }
gbazin's avatar
   
gbazin committed
289

290
291
    p_sys->p_sub = vlc_Subscribe( cb, p_intf );
    return VLC_SUCCESS;
292
293
294
295
296
297
}

/*****************************************************************************
 * Close: destroy interface stuff
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
298
{
299
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
300
301
    intf_sys_t *p_sys = p_intf->p_sys;

302
    /* Flush the queue and unsubscribe from the message queue */
303
    vlc_Unsubscribe( p_sys->p_sub );
304

305
    /* Close the log file */
306
#ifdef HAVE_SYSLOG_H
307
    if( p_sys->p_file == NULL )
308
        closelog();
309
    else
310
#endif
311
312
    {
        fputs( p_sys->footer, p_sys->p_file );
313
        fclose( p_sys->p_file );
314
    }
315
316

    /* Destroy structure */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
317
    free( p_sys );
318
319
}

320
static bool IgnoreMessage( intf_thread_t *p_intf, int type )
321
{
322
    /* TODO: cache value... */
323
    int verbosity = var_InheritInteger( p_intf, "log-verbose" );
324
    if (verbosity == -1)
325
        verbosity = var_InheritInteger( p_intf, "verbose" );
326

327
    return verbosity < 0 || verbosity < (type - VLC_MSG_ERR);
328
329
}

330
331
332
333
334
335
336
337
338
/*
 * Logging callbacks
 */

static const char ppsz_type[4][9] = {
    "",
    " error",
    " warning",
    " debug",
339
};
340

341
342
static void TextPrint( void *opaque, int type, const msg_item_t *item,
                       const char *fmt, va_list ap )
343
{
344
345
346
347
348
349
350
351
352
353
    intf_thread_t *p_intf = opaque;
    FILE *stream = p_intf->p_sys->p_file;

    if( IgnoreMessage( p_intf, type ) )
        return;

    int canc = vlc_savecancel();
    flockfile( stream );
    utf8_fprintf( stream, "%s%s: ", item->psz_module, ppsz_type[type] );
    utf8_fprintf( stream, fmt, ap );
354
    putc_unlocked( '\n', stream );
355
356
    funlockfile( stream );
    vlc_restorecancel( canc );
357
358
}

359
#ifdef HAVE_SYSLOG_H
360
361
static void SyslogPrint( void *opaque, int type, const msg_item_t *item,
                         const char *fmt, va_list ap )
362
{
363
    static const int i_prio[4] = { LOG_INFO, LOG_ERR, LOG_WARNING, LOG_DEBUG };
364
365
366

    intf_thread_t *p_intf = opaque;
    char *str;
367
    int i_priority = i_prio[type];
368

369
370
371
372
373
    if( IgnoreMessage( p_intf, type )
     || unlikely(vasprintf( &str, fmt, ap ) == -1) )
        return;

    int canc = vlc_savecancel();
374
    if( item->psz_header != NULL )
375
        syslog( i_priority, "[%s] %s%s: %s", item->psz_header,
376
                item->psz_module, ppsz_type[type], str );
377
    else
378
        syslog( i_priority, "%s%s: %s",
379
                item->psz_module, ppsz_type[type], str );
380
381
    vlc_restorecancel( canc );
    free( str );
382
383
384
}
#endif

385
386
static void HtmlPrint( void *opaque, int type, const msg_item_t *item,
                       const char *fmt, va_list ap )
387
{
388
389
    static const unsigned color[4] = {
        0xffffff, 0xff6666, 0xffff66, 0xaaaaaa,
390
391
    };

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
    intf_thread_t *p_intf = opaque;
    FILE *stream = p_intf->p_sys->p_file;

    if( IgnoreMessage( p_intf, type ) )
        return;

    int canc = vlc_savecancel();
    flockfile( stream );
    fprintf( stream, "%s%s: <span style=\"color: #%06x\">",
             item->psz_module, ppsz_type[type], color[type] );
    /* FIXME: encode special ASCII characters */
    fprintf( stream, fmt, ap );
    fputs( "</span>\n", stream );
    funlockfile( stream );
    vlc_restorecancel( canc );
407
}