1 /*****************************************************************************
2 * logger.c : file logging plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002-2008 the VideoLAN team
7 * Authors: Samuel Hocevar <sam@zoy.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_interface.h>
36 #include <vlc_charset.h>
42 # include <android/log.h>
50 #define LOG_DIR "Library/Logs/"
53 #define LOG_FILE_TEXT "vlc-log.txt"
54 #define LOG_FILE_HTML "vlc-log.html"
56 #define TEXT_HEADER "\xEF\xBB\xBF-- logger module started --\n"
57 #define TEXT_FOOTER "-- logger module stopped --\n"
60 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n" \
61 " \"http://www.w3.org/TR/html4/strict.dtd\">\n" \
64 " <title>vlc log</title>\n" \
65 " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" \
67 " <body style=\"background-color: #000000; color: #aaaaaa;\">\n" \
69 " <strong>-- logger module started --</strong>\n"
71 " <strong>-- logger module stopped --</strong>\n" \
80 /*****************************************************************************
81 * intf_sys_t: description and status of log interface
82 *****************************************************************************/
85 msg_subscription_t *p_sub;
90 /*****************************************************************************
92 *****************************************************************************/
93 static int Open ( vlc_object_t * );
94 static void Close ( vlc_object_t * );
96 static void TextPrint(void *, int, const msg_item_t *, const char *, va_list);
97 static void HtmlPrint(void *, int, const msg_item_t *, const char *, va_list);
99 static void SyslogPrint(void *, int, const msg_item_t *, const char *,
103 static void AndroidPrint(void *, int, const msg_item_t *, const char *, va_list);
106 /*****************************************************************************
108 *****************************************************************************/
109 static const char *const mode_list[] = { "text", "html"
117 static const char *const mode_list_text[] = { N_("Text"), "HTML"
126 #define LOGMODE_TEXT N_("Log format")
127 #define LOGMODE_LONGTEXT N_("Specify the logging format.")
130 #define SYSLOG_FACILITY_TEXT N_("Syslog facility")
131 #define SYSLOG_FACILITY_LONGTEXT N_("Select the syslog facility where logs " \
132 "will be forwarded.")
134 /* First in list is the default facility used. */
135 #define DEFINE_SYSLOG_FACILITY \
136 DEF( "user", LOG_USER ), \
137 DEF( "daemon", LOG_DAEMON ), \
138 DEF( "local0", LOG_LOCAL0 ), \
139 DEF( "local1", LOG_LOCAL1 ), \
140 DEF( "local2", LOG_LOCAL2 ), \
141 DEF( "local3", LOG_LOCAL3 ), \
142 DEF( "local4", LOG_LOCAL4 ), \
143 DEF( "local5", LOG_LOCAL5 ), \
144 DEF( "local6", LOG_LOCAL6 ), \
145 DEF( "local7", LOG_LOCAL7 )
147 #define DEF( a, b ) a
148 static const char *const fac_name[] = { DEFINE_SYSLOG_FACILITY };
150 #define DEF( a, b ) b
151 static const int fac_number[] = { DEFINE_SYSLOG_FACILITY };
153 enum { fac_entries = sizeof(fac_name)/sizeof(fac_name[0]) };
154 #undef DEFINE_SYSLOG_FACILITY
158 #define LOGVERBOSE_TEXT N_("Verbosity")
159 #define LOGVERBOSE_LONGTEXT N_("Select the verbosity to use for log or -1 to " \
160 "use the same verbosity given by --verbose.")
163 set_shortname( N_( "Logging" ) )
164 set_description( N_("File logging") )
166 set_category( CAT_ADVANCED )
167 set_subcategory( SUBCAT_ADVANCED_MISC )
169 add_savefile( "logfile", NULL,
170 N_("Log filename"), N_("Specify the log filename."), false )
171 add_string( "logmode", "text", LOGMODE_TEXT, LOGMODE_LONGTEXT,
173 change_string_list( mode_list, mode_list_text, 0 )
175 add_string( "syslog-facility", fac_name[0], SYSLOG_FACILITY_TEXT,
176 SYSLOG_FACILITY_LONGTEXT, true )
177 change_string_list( fac_name, fac_name, 0 )
179 add_integer( "log-verbose", -1, LOGVERBOSE_TEXT, LOGVERBOSE_LONGTEXT,
182 add_obsolete_string( "rrd-file" )
184 set_capability( "interface", 0 )
185 set_callbacks( Open, Close )
188 /*****************************************************************************
189 * Open: initialize and create stuff
190 *****************************************************************************/
191 static int Open( vlc_object_t *p_this )
193 intf_thread_t *p_intf = (intf_thread_t *)p_this;
197 msg_Info( p_intf, "using logger." );
199 /* Allocate instance and initialize some members */
200 p_sys = p_intf->p_sys = (intf_sys_t *)malloc( sizeof( intf_sys_t ) );
204 msg_callback_t cb = TextPrint;
205 const char *filename = LOG_FILE_TEXT, *header = TEXT_HEADER;
206 p_sys->footer = TEXT_FOOTER;
208 char *mode = var_InheritString( p_intf, "logmode" );
211 if( !strcmp( mode, "html" ) )
213 p_sys->footer = HTML_FOOTER;
214 header = HTML_HEADER;
218 else if( !strcmp( mode, "syslog" ) )
222 else if( !strcmp( mode, "android" ) )
225 else if( strcmp( mode, "text" ) )
226 msg_Warn( p_intf, "invalid log mode `%s', using `text'", mode );
231 if( cb == SyslogPrint )
234 char *psz_facility = var_InheritString( p_intf, "syslog-facility" );
238 for( size_t i = 0; i < fac_entries; ++i )
240 if( !strcmp( psz_facility, fac_name[i] ) )
242 i_facility = fac_number[i];
249 msg_Warn( p_intf, "invalid syslog facility `%s', using `%s'",
250 psz_facility, fac_name[0] );
251 i_facility = fac_number[0];
253 free( psz_facility );
257 msg_Warn( p_intf, "no syslog facility specified, using `%s'",
259 i_facility = fac_number[0];
262 openlog( "vlc", LOG_PID|LOG_NDELAY, i_facility );
263 p_sys->p_file = NULL;
268 if( cb != AndroidPrint )
271 char *psz_file = var_InheritString( p_intf, "logfile" );
275 char *home = config_GetUserDir(VLC_DOCUMENTS_DIR);
277 || asprintf( &psz_file, "%s/"LOG_DIR"/%s", home,
283 msg_Warn( p_intf, "no log filename provided, using `%s'",
289 /* Open the log file and remove any buffering for the stream */
290 msg_Dbg( p_intf, "opening logfile `%s'", filename );
291 p_sys->p_file = vlc_fopen( filename, "at" );
293 if( p_sys->p_file == NULL )
295 msg_Err( p_intf, "error opening logfile `%s': %m", filename );
299 setvbuf( p_sys->p_file, NULL, _IONBF, 0 );
300 fputs( header, p_sys->p_file );
303 p_sys->p_sub = vlc_Subscribe( cb, p_intf );
307 /*****************************************************************************
308 * Close: destroy interface stuff
309 *****************************************************************************/
310 static void Close( vlc_object_t *p_this )
312 intf_thread_t *p_intf = (intf_thread_t *)p_this;
313 intf_sys_t *p_sys = p_intf->p_sys;
315 /* Flush the queue and unsubscribe from the message queue */
316 vlc_Unsubscribe( p_sys->p_sub );
318 /* Close the log file */
320 if( p_sys->p_file == NULL )
325 fputs( p_sys->footer, p_sys->p_file );
326 fclose( p_sys->p_file );
329 /* Destroy structure */
333 static bool IgnoreMessage( intf_thread_t *p_intf, int type )
335 /* TODO: cache value... */
336 int verbosity = var_InheritInteger( p_intf, "log-verbose" );
338 verbosity = var_InheritInteger( p_intf, "verbose" );
340 return verbosity < 0 || verbosity < (type - VLC_MSG_ERR);
347 static const char ppsz_type[4][9] = {
355 static const android_LogPriority prioritytype[4] = {
362 static void AndroidPrint( void *opaque, int type, const msg_item_t *item,
363 const char *fmt, va_list ap )
366 intf_thread_t *p_intf = opaque;
368 if( IgnoreMessage( p_intf, type ) )
371 int canc = vlc_savecancel();
372 __android_log_vprint(prioritytype[type], "vlc", fmt, ap);
373 vlc_restorecancel( canc );
377 static void TextPrint( void *opaque, int type, const msg_item_t *item,
378 const char *fmt, va_list ap )
380 intf_thread_t *p_intf = opaque;
381 FILE *stream = p_intf->p_sys->p_file;
383 if( IgnoreMessage( p_intf, type ) )
386 int canc = vlc_savecancel();
388 fprintf( stream, "%s%s: ", item->psz_module, ppsz_type[type] );
389 vfprintf( stream, fmt, ap );
390 putc_unlocked( '\n', stream );
391 funlockfile( stream );
392 vlc_restorecancel( canc );
396 static void SyslogPrint( void *opaque, int type, const msg_item_t *item,
397 const char *fmt, va_list ap )
399 static const int i_prio[4] = { LOG_INFO, LOG_ERR, LOG_WARNING, LOG_DEBUG };
401 intf_thread_t *p_intf = opaque;
403 int i_priority = i_prio[type];
405 if( IgnoreMessage( p_intf, type )
406 || unlikely(vasprintf( &str, fmt, ap ) == -1) )
409 int canc = vlc_savecancel();
410 if( item->psz_header != NULL )
411 syslog( i_priority, "[%s] %s%s: %s", item->psz_header,
412 item->psz_module, ppsz_type[type], str );
414 syslog( i_priority, "%s%s: %s",
415 item->psz_module, ppsz_type[type], str );
416 vlc_restorecancel( canc );
421 static void HtmlPrint( void *opaque, int type, const msg_item_t *item,
422 const char *fmt, va_list ap )
424 static const unsigned color[4] = {
425 0xffffff, 0xff6666, 0xffff66, 0xaaaaaa,
428 intf_thread_t *p_intf = opaque;
429 FILE *stream = p_intf->p_sys->p_file;
431 if( IgnoreMessage( p_intf, type ) )
434 int canc = vlc_savecancel();
436 fprintf( stream, "%s%s: <span style=\"color: #%06x\">",
437 item->psz_module, ppsz_type[type], color[type] );
438 /* FIXME: encode special ASCII characters */
439 fprintf( stream, fmt, ap );
440 fputs( "</span>\n", stream );
441 funlockfile( stream );
442 vlc_restorecancel( canc );