]> git.sesse.net Git - vlc/blob - modules/misc/logger.c
All: missing #include "charset.h"
[vlc] / modules / misc / logger.c
1 /*****************************************************************************
2  * logger.c : file logging plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
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.
13  *
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.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdio.h>
32
33 #ifdef UNDER_CE
34 #   define _IONBF 0x0004
35 #endif
36
37 #include <vlc/vlc.h>
38 #include <vlc/intf.h>
39 #include "charset.h"
40
41 #define MODE_TEXT 0
42 #define MODE_HTML 1
43 #define MODE_SYSLOG 2
44
45 #ifdef __APPLE__
46 #define LOG_DIR "Library/Logs/"
47 #endif
48
49 #define LOG_FILE_TEXT "vlc-log.txt"
50 #define LOG_FILE_HTML "vlc-log.html"
51
52 #define LOG_STRING( msg, file ) fwrite( msg, strlen( msg ), 1, file );
53
54 #define TEXT_HEADER "-- logger module started --\n"
55 #define TEXT_FOOTER "-- logger module stopped --\n"
56
57 #define HTML_HEADER \
58     "<html>\n" \
59     "  <head>\n" \
60     "    <title>vlc log</title>\n" \
61     "  </head>\n" \
62     "  <body bgcolor=\"#000000\" text=\"#aaaaaa\">\n" \
63     "    <pre>\n" \
64     "      <b>-- logger module started --</b>\n"
65 #define HTML_FOOTER \
66     "      <b>-- logger module stopped --</b>\n" \
67     "    </pre>\n" \
68     "  </body>\n" \
69     "</html>\n"
70
71 #if HAVE_SYSLOG_H
72 #include <syslog.h>
73 #endif
74
75 /*****************************************************************************
76  * intf_sys_t: description and status of log interface
77  *****************************************************************************/
78 struct intf_sys_t
79 {
80     int i_mode;
81     FILE *p_rrd;
82     mtime_t last_update;
83
84     FILE *    p_file; /* The log file */
85     msg_subscription_t *p_sub;
86 };
87
88 /*****************************************************************************
89  * Local prototypes
90  *****************************************************************************/
91 static int  Open    ( vlc_object_t * );
92 static void Close   ( vlc_object_t * );
93 static void Run     ( intf_thread_t * );
94
95 static void FlushQueue        ( msg_subscription_t *, FILE *, int );
96 static void TextPrint         ( const msg_item_t *, FILE * );
97 static void HtmlPrint         ( const msg_item_t *, FILE * );
98 #ifdef HAVE_SYSLOG_H
99 static void SyslogPrint       ( const msg_item_t *);
100 #endif
101
102 static void DoRRD( intf_thread_t *p_intf );
103
104 /*****************************************************************************
105  * Module descriptor
106  *****************************************************************************/
107 static char *mode_list[] = { "text", "html"
108 #ifdef HAVE_SYSLOG_H
109 ,"syslog"
110 #endif
111 };
112 static char *mode_list_text[] = { N_("Text"), "HTML"
113 #ifdef HAVE_SYSLOG_H
114 , "syslog"
115 #endif
116 };
117
118 #define LOGMODE_TEXT N_("Log format")
119 #ifdef HAVE_SYSLOG_H
120 #define LOGMODE_LONGTEXT N_("Specify the log format. Available choices are \"text\" (default), \"html\", and \"syslog\".")
121 #else
122 #define LOGMODE_LONGTEXT N_("Specify the log format. Available choices are \"text\" (default) and \"html\".")
123 #endif
124
125 vlc_module_begin();
126     set_shortname( N_( "Logging" ) );
127     set_description( _("File logging") );
128
129     add_file( "logfile", NULL, NULL,
130              N_("Log filename"), N_("Specify the log filename."), VLC_FALSE );
131     add_string( "logmode", "text", NULL, LOGMODE_TEXT, LOGMODE_LONGTEXT,
132                 VLC_FALSE );
133         change_string_list( mode_list, mode_list_text, 0 );
134
135     add_string( "rrd-file", NULL, NULL, N_("RRD output file") ,
136                     N_("Output data for RRDTool in this file" ), VLC_TRUE );
137
138     set_capability( "interface", 0 );
139     set_callbacks( Open, Close );
140 vlc_module_end();
141
142 /*****************************************************************************
143  * Open: initialize and create stuff
144  *****************************************************************************/
145 static int Open( vlc_object_t *p_this )
146 {
147     intf_thread_t *p_intf = (intf_thread_t *)p_this;
148     char *psz_mode, *psz_file, *psz_rrd_file;
149
150     CONSOLE_INTRO_MSG;
151     msg_Info( p_intf, "Using logger..." );
152
153     /* Allocate instance and initialize some members */
154     p_intf->p_sys = (intf_sys_t *)malloc( sizeof( intf_sys_t ) );
155     if( p_intf->p_sys == NULL )
156     {
157         msg_Err( p_intf, "out of memory" );
158         return -1;
159     }
160
161     psz_mode = var_CreateGetString( p_intf, "logmode" );
162     if( psz_mode )
163     {
164         if( !strcmp( psz_mode, "text" ) )
165         {
166             p_intf->p_sys->i_mode = MODE_TEXT;
167         }
168         else if( !strcmp( psz_mode, "html" ) )
169         {
170             p_intf->p_sys->i_mode = MODE_HTML;
171         }
172 #ifdef HAVE_SYSLOG_H
173         else if( !strcmp( psz_mode, "syslog" ) )
174         {
175             p_intf->p_sys->i_mode = MODE_SYSLOG;
176         }
177 #endif
178         else
179         {
180             msg_Err( p_intf, "invalid log mode `%s', using `text'", psz_mode );
181             p_intf->p_sys->i_mode = MODE_TEXT;
182         }
183
184         free( psz_mode );
185     }
186     else
187     {
188         msg_Warn( p_intf, "no log mode specified, using `text'" );
189         p_intf->p_sys->i_mode = MODE_TEXT;
190     }
191
192     if( p_intf->p_sys->i_mode != MODE_SYSLOG )
193     {
194         psz_file = config_GetPsz( p_intf, "logfile" );
195         if( !psz_file )
196         {
197 #ifdef __APPLE__
198             char *psz_homedir = p_this->p_vlc->psz_homedir;
199
200             if( !psz_homedir )
201             {
202                 msg_Err( p_this, "psz_homedir is null" );
203                 return -1;
204             }
205             psz_file = (char *)malloc( sizeof("/" LOG_DIR "/" LOG_FILE_HTML) +
206                                            strlen(psz_homedir) );
207             if( psz_file )
208             {
209                 switch( p_intf->p_sys->i_mode )
210                 {
211                 case MODE_HTML:
212                     sprintf( psz_file, "%s/" LOG_DIR "/" LOG_FILE_HTML,
213                          psz_homedir );
214                     break;
215                 case MODE_TEXT:
216                 default:
217                     sprintf( psz_file, "%s/" LOG_DIR "/" LOG_FILE_TEXT,
218                          psz_homedir );
219                     break;
220                 }
221             }
222 #else
223             switch( p_intf->p_sys->i_mode )
224             {
225             case MODE_HTML:
226                 psz_file = strdup( LOG_FILE_HTML );
227                 break;
228             case MODE_TEXT:
229             default:
230                 psz_file = strdup( LOG_FILE_TEXT );
231                 break;
232             }
233 #endif
234             msg_Warn( p_intf, "no log filename provided, using `%s'",
235                                psz_file );
236         }
237
238         /* Open the log file and remove any buffering for the stream */
239         msg_Dbg( p_intf, "opening logfile `%s'", psz_file );
240         p_intf->p_sys->p_file = utf8_fopen( psz_file, "wt" );
241         if( p_intf->p_sys->p_file == NULL )
242         {
243             msg_Err( p_intf, "error opening logfile `%s'", psz_file );
244             free( p_intf->p_sys );
245             free( psz_file );
246             return -1;
247         }
248         setvbuf( p_intf->p_sys->p_file, NULL, _IONBF, 0 );
249
250         free( psz_file );
251
252         switch( p_intf->p_sys->i_mode )
253         {
254         case MODE_HTML:
255             LOG_STRING( HTML_HEADER, p_intf->p_sys->p_file );
256             break;
257         case MODE_TEXT:
258         default:
259             LOG_STRING( TEXT_HEADER, p_intf->p_sys->p_file );
260             break;
261         }
262
263     }
264     else
265     {
266         p_intf->p_sys->p_file = NULL;
267 #ifdef HAVE_SYSLOG_H
268         openlog( "VLC", 0, LOG_DAEMON );
269 #endif
270     }
271
272     p_intf->p_sys->last_update = 0;
273     p_intf->p_sys->p_rrd = NULL;
274
275     psz_rrd_file = config_GetPsz( p_intf, "rrd-file" );
276     if( psz_rrd_file && *psz_rrd_file )
277     {
278         p_intf->p_sys->p_rrd = utf8_fopen( psz_rrd_file, "w" );
279     }
280
281     p_intf->p_sys->p_sub = msg_Subscribe( p_intf , MSG_QUEUE_NORMAL );
282     p_intf->pf_run = Run;
283
284     return 0;
285 }
286
287 /*****************************************************************************
288  * Close: destroy interface stuff
289  *****************************************************************************/
290 static void Close( vlc_object_t *p_this )
291 {
292     intf_thread_t *p_intf = (intf_thread_t *)p_this;
293
294     /* Flush the queue and unsubscribe from the message queue */
295     FlushQueue( p_intf->p_sys->p_sub, p_intf->p_sys->p_file,
296                 p_intf->p_sys->i_mode );
297     msg_Unsubscribe( p_intf, p_intf->p_sys->p_sub );
298
299     switch( p_intf->p_sys->i_mode )
300     {
301     case MODE_HTML:
302         LOG_STRING( HTML_FOOTER, p_intf->p_sys->p_file );
303         break;
304     case MODE_TEXT:
305 #ifdef HAVE_SYSLOG_H
306     case MODE_SYSLOG:
307         closelog();
308         break;
309 #endif
310     default:
311         LOG_STRING( TEXT_FOOTER, p_intf->p_sys->p_file );
312         break;
313     }
314
315     /* Close the log file */
316     if( p_intf->p_sys->i_mode != MODE_SYSLOG )
317         fclose( p_intf->p_sys->p_file );
318
319     /* Destroy structure */
320     free( p_intf->p_sys );
321 }
322
323 /*****************************************************************************
324  * Run: rc thread
325  *****************************************************************************
326  * This part of the interface is in a separate thread so that we can call
327  * exec() from within it without annoying the rest of the program.
328  *****************************************************************************/
329 static void Run( intf_thread_t *p_intf )
330 {
331     while( !p_intf->b_die )
332     {
333         FlushQueue( p_intf->p_sys->p_sub, p_intf->p_sys->p_file,
334                     p_intf->p_sys->i_mode );
335
336         if( p_intf->p_sys->p_rrd )
337             DoRRD( p_intf );
338
339         msleep( INTF_IDLE_SLEEP );
340     }
341 }
342
343 /*****************************************************************************
344  * FlushQueue: flush the message queue into the log
345  *****************************************************************************/
346 static void FlushQueue( msg_subscription_t *p_sub, FILE *p_file, int i_mode )
347 {
348     int i_start, i_stop;
349
350     vlc_mutex_lock( p_sub->p_lock );
351     i_stop = *p_sub->pi_stop;
352     vlc_mutex_unlock( p_sub->p_lock );
353
354     if( p_sub->i_start != i_stop )
355     {
356         /* Append all messages to log file */
357         for( i_start = p_sub->i_start;
358              i_start != i_stop;
359              i_start = (i_start+1) % VLC_MSG_QSIZE )
360         {
361             switch( i_mode )
362             {
363             case MODE_HTML:
364                 HtmlPrint( &p_sub->p_msg[i_start], p_file );
365                 break;
366 #ifdef HAVE_SYSLOG_H
367             case MODE_SYSLOG:
368                 SyslogPrint( &p_sub->p_msg[i_start] );
369                 break;
370 #endif
371             case MODE_TEXT:
372             default:
373                 TextPrint( &p_sub->p_msg[i_start], p_file );
374                 break;
375             }
376         }
377
378         vlc_mutex_lock( p_sub->p_lock );
379         p_sub->i_start = i_start;
380         vlc_mutex_unlock( p_sub->p_lock );
381     }
382 }
383
384 static const char *ppsz_type[4] = { ": ", " error: ",
385                                     " warning: ", " debug: " };
386
387 static void TextPrint( const msg_item_t *p_msg, FILE *p_file )
388 {
389     LOG_STRING( p_msg->psz_module, p_file );
390     LOG_STRING( ppsz_type[p_msg->i_type], p_file );
391     LOG_STRING( p_msg->psz_msg, p_file );
392     LOG_STRING( "\n", p_file );
393 }
394
395 #ifdef HAVE_SYSLOG_H
396 static void SyslogPrint( const msg_item_t *p_msg )
397 {
398     int i_priority = LOG_INFO;
399
400     if( p_msg->i_type  == 0 ) i_priority = LOG_INFO;
401     if( p_msg->i_type  == 1 ) i_priority = LOG_ERR;
402     if( p_msg->i_type  == 2 ) i_priority = LOG_WARNING;
403     if( p_msg->i_type  == 3 ) i_priority = LOG_DEBUG;
404
405     if( p_msg->psz_header )
406         syslog( i_priority, "%s %s: %s", p_msg->psz_header,
407                 p_msg->psz_module, p_msg->psz_msg );
408     else
409         syslog( i_priority, "%s: %s", p_msg->psz_module, p_msg->psz_msg );
410         
411 }
412 #endif
413
414 static void HtmlPrint( const msg_item_t *p_msg, FILE *p_file )
415 {
416     static const char *ppsz_color[4] = { "<font color=\"#ffffff\">",
417                                          "<font color=\"#ff6666\">",
418                                          "<font color=\"#ffff66\">",
419                                          "<font color=\"#aaaaaa\">" };
420
421     LOG_STRING( p_msg->psz_module, p_file );
422     LOG_STRING( ppsz_type[p_msg->i_type], p_file );
423     LOG_STRING( ppsz_color[p_msg->i_type], p_file );
424     LOG_STRING( p_msg->psz_msg, p_file );
425     LOG_STRING( "</font>\n", p_file );
426 }
427
428 static void DoRRD( intf_thread_t *p_intf )
429 {
430     playlist_t *p_playlist;
431     float f_input_bitrate;
432     if( mdate() - p_intf->p_sys->last_update < 1000000 )
433         return;
434     p_intf->p_sys->last_update = mdate();
435
436     p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
437                                                 FIND_ANYWHERE );
438     if( p_playlist && p_playlist->p_stats )
439     {
440         fprintf( p_intf->p_sys->p_rrd, I64Fi":%f:%f:%f\n",
441                    p_intf->p_sys->last_update/1000000,
442                    (float)(p_playlist->p_stats->f_input_bitrate)*1000,
443                    (float)(p_playlist->p_stats->f_demux_bitrate)*1000,
444                    (float)(p_playlist->p_stats->f_output_bitrate)*1000 );
445         fflush( p_intf->p_sys->p_rrd );
446         vlc_object_release( p_playlist );
447     }
448 }