]> sjero.net Git - linphone/blob - gtk-glade/logging.c
9bdf1dc0ea5da35e983c36b5e836949eddd78349
[linphone] / gtk-glade / logging.c
1 /*
2 linphone, gtk-glade interface.
3 Copyright (C) 2008  Simon MORLAT (simon.morlat@linphone.org)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20 #include "linphone.h"
21
22 #ifndef WIN32
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #endif
26
27
28 static GtkWidget *log_window=NULL;
29 static GStaticMutex log_mutex=G_STATIC_MUTEX_INIT;
30 static GList *log_queue=NULL;
31
32 typedef struct _LinphoneGtkLog{
33         OrtpLogLevel lev;
34         gchar *msg;
35 }LinphoneGtkLog;
36
37
38 /******
39  * Module to log to a file
40  ******/
41
42 /* Marker to insert as a line at the start of every log file */
43 #define LOGFILE_MARKER_START  "----<start>----"
44 /* Marker to insert as a line at the end of every log file */
45 #define LOGFILE_MARKER_STOP  "----<end>----"
46 /* Number of files to keep in history, log file rotation will be
47    performed. */
48 #define LOGFILE_ROTATION 4
49 /* Pointer to opened log file */
50 static FILE *_logfile = NULL;
51
52
53 /* Called on exit, print out the marker, close the file and avoid to
54    continue logging. */
55 static void linphone_gtk_log_uninit()
56 {
57         if (_logfile != NULL) {
58                 fprintf(_logfile, "%s\n", LOGFILE_MARKER_STOP);
59                 fclose(_logfile);
60                 _logfile = NULL;
61         }
62 }
63
64 /* Called when we start logging, find a good place for the log files,
65    perform rotation, insert the start marker and return the pointer to
66    the file that should be used for logging, or NULL on errors or if
67    disabled. */
68 static FILE *linphone_gtk_log_init()
69 {
70         static char _logdir[1024];
71         static char _logfname[1024];
72         static gboolean _log_init = FALSE;
73         const char *dst_fname;
74
75         if (!_log_init) {
76                 dst_fname = linphone_gtk_get_ui_config("logfile",NULL);
77                 /* For anything to happen, we need a logfile configuration variable,
78                  this is our trigger */
79                 if (dst_fname) {
80                         /* arrange for _logdir to contain a
81                          directory that has been created and _logfname to contain the
82                          path to a file to which we will log */
83 #ifdef WIN32
84                         const char *appdata=getenv("LOCALAPPDATA");
85                         if (appdata) {
86                                 snprintf(_logdir, sizeof(_logdir),"%s\\Linphone", appdata);
87                                 mkdir(_logdir);
88                         } else {
89                                 _logdir[0] = '\0';
90                         }
91 #define PATH_SEPARATOR '\\'
92 #else
93                         const char *home=getenv("HOME");
94                         if (home) {
95                                 snprintf(_logdir, sizeof(_logdir),"%s/.linphone", home);
96                                 mkdir(_logdir,S_IRUSR | S_IWUSR | S_IRGRP);
97                         } else {
98                                 _logdir[0] = '\0';
99                         }
100 #define PATH_SEPARATOR '/'
101 #endif
102                         /* We have a directory, fix the path to the log file in it and
103                          open the file so that we will be appending to it. */
104                         if (_logdir[0] != '\0') {
105                                 snprintf(_logfname, sizeof(_logfname), "%s%c%s",
106                                         _logdir, PATH_SEPARATOR, dst_fname);
107                                 /* If the constant LOGFILE_ROTATION is greater than zero, then
108                                  we kick away a simple rotation that will ensure that there
109                                  are never more than LOGFILE_ROTATION+1 old copies of the
110                                  log file on the disk.  The oldest file is always rotated
111                                  "away" as expected.  Rotated files have the same name as
112                                  the main log file, though with a number 0..LOGFILE_ROTATION
113                                  at the end, where the greater the number is, the older the
114                                  file is. */
115                                 if (ortp_file_exist(_logfname)==0 && LOGFILE_ROTATION > 0) {
116                                         int i;
117                                         char old_fname[1024];
118                                         char new_fname[1024];
119
120                                         /* Rotate away existing files.  We make sure to remove the
121                                          old files otherwise rename() would not work properly.  We
122                                          have to loop in reverse here. */
123                                         for (i=LOGFILE_ROTATION-1;i>=0;i--) {
124                                                 snprintf(old_fname, sizeof(old_fname), "%s%c%s.%d",
125                                                         _logdir, PATH_SEPARATOR, dst_fname, i);
126                                                 snprintf(new_fname, sizeof(new_fname), "%s%c%s.%d",
127                                                         _logdir, PATH_SEPARATOR, dst_fname, i+1);
128                                                 if (ortp_file_exist(old_fname)==0) {
129                                                         if (ortp_file_exist(new_fname)==0)
130                                                                 unlink(new_fname);
131                                                         rename(old_fname, new_fname);
132                                                 }
133                                         }
134                                         /* Move current log file as the first of the rotation.  Make
135                                          sure to remove the old .0 also, since otherwise rename()
136                                          would not work as expected. */
137                                         snprintf(new_fname, sizeof(new_fname), "%s%c%s.%d",
138                                                 _logdir, PATH_SEPARATOR, dst_fname, 0);
139                                         if (ortp_file_exist(new_fname)==0)
140                                                 unlink(new_fname);
141                                         rename(_logfname, new_fname);
142                                 }
143                                 /* Start a new log file and mark that we have now initialised */
144                                 _logfile = fopen(_logfname, "w");
145                                 fprintf(_logfile, "%s\n", LOGFILE_MARKER_START);
146                         }
147                 }
148                 _log_init = TRUE;
149         }
150         return _logfile;
151 }
152
153 static void linphone_gtk_log_file(OrtpLogLevel lev, const char *msg)
154 {
155         LinphoneCore *lc;
156         time_t now;
157         FILE *outlog;
158
159         lc = linphone_gtk_get_core();
160         /* Nothing to do until the core has initialised */
161         if (lc == NULL)
162                 return;
163
164         /* lc->config will turn NULL at exit, close the file to flush and
165          return to stop logging */
166         if (linphone_core_get_config(lc) == NULL) {
167                 linphone_gtk_log_uninit();
168                 return;
169         }
170
171         outlog = linphone_gtk_log_init();
172         if (outlog != NULL) {
173                 /* We have an opened file and we have initialised properly, it's
174                  time to write all these log messages. We convert the log level
175                  from oRTP into something readable and timestamp each log
176                  message.  The format of the timestamp can be controlled by
177                  logfile_date_format in the GtkUi section of the config file,
178                  but it defaults to something compact, but yet readable. */
179                 const char *lname="undef";
180                 const char *dateformat=linphone_gtk_get_ui_config("logfile_date_format",
181                     "%Y%m%d-%H:%M:%S");
182                 char date[256];
183
184                 /* Convert level constant to text */
185                 switch(lev){
186                         case ORTP_DEBUG:
187                                 lname="debug";
188                                 break;
189                         case ORTP_MESSAGE:
190                                 lname="message";
191                                 break;
192                         case ORTP_WARNING:
193                                 lname="warning";
194                                 break;
195                         case ORTP_ERROR:
196                                 lname="error";
197                                 break;
198                         case ORTP_FATAL:
199                                 lname="fatal";
200                                 break;
201                         default:
202                                 lname="undef";
203                                 break;
204                 }
205                 /* Get current time and format it properly */
206                 now = time(NULL);
207                 strftime(date, sizeof(date), dateformat, localtime(&now));
208                 /* Now print out the message to the logfile.  We don't flush,
209                  maybe we should do to ensure that we have all the messages in
210                  case of a crash (which is one of the main reasons we have a
211                      log facility in the first place). */
212                 fprintf(outlog, "[%s] [%s] %s\n", date, lname, msg);
213         }
214 }
215
216
217
218 static gboolean delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
219 {
220         gtk_widget_hide (widget);
221         return TRUE;
222 }
223
224 void linphone_gtk_log_hide(){
225         if (log_window)
226                 gtk_widget_hide(log_window);
227 }
228
229 void linphone_gtk_create_log_window(void){
230         GtkTextBuffer *b;
231         log_window=linphone_gtk_create_window("log");
232         b=gtk_text_view_get_buffer(GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview")));
233         gtk_text_buffer_create_tag(b,"red","foreground","red",NULL);
234         gtk_text_buffer_create_tag(b,"orange","foreground","orange",NULL);
235         /*prevent the log window from being destroyed*/
236         g_signal_connect (G_OBJECT (log_window), "delete-event",
237                 G_CALLBACK (delete_event_cb), NULL);
238
239 }
240
241 void linphone_gtk_destroy_log_window(void){
242         GtkWidget *w=log_window;
243         g_static_mutex_lock(&log_mutex);
244         log_window=NULL;
245         gtk_widget_destroy(w);
246         g_static_mutex_unlock(&log_mutex);
247 }
248
249 void linphone_gtk_log_show(void){
250         gtk_widget_show(log_window);
251         gtk_window_present(GTK_WINDOW(log_window));
252 }
253
254 static void linphone_gtk_display_log(OrtpLogLevel lev, const char *msg){
255         GtkTextIter iter,begin;
256         int off;
257         static GtkTextView *v=NULL;
258         GtkTextBuffer *b;
259         const char *lname="undef";
260
261         if (log_window==NULL) {
262                 return;
263         }
264
265         if (v==NULL) v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
266         b=gtk_text_view_get_buffer(v);
267         switch(lev){
268                 case ORTP_DEBUG:
269                         lname="debug";
270                         break;
271                 case ORTP_MESSAGE:
272                         lname="message";
273                         break;
274                 case ORTP_WARNING:
275                         lname="warning";
276                         break;
277                 case ORTP_ERROR:
278                         lname="error";
279                         break;
280                 case ORTP_FATAL:
281                         lname="fatal";
282                         break;
283                 default:
284                         g_error("Bad level !");
285         }
286         gtk_text_buffer_get_end_iter(b,&iter);
287         off=gtk_text_iter_get_offset(&iter);
288         gtk_text_buffer_insert(b,&iter,lname,-1);
289         gtk_text_buffer_get_end_iter(b,&iter);
290         gtk_text_buffer_insert(b,&iter,": ",-1);
291         gtk_text_buffer_get_end_iter(b,&iter);
292         gtk_text_buffer_insert(b,&iter,msg,-1);
293         gtk_text_buffer_get_end_iter(b,&iter);
294         gtk_text_buffer_insert(b,&iter,"\n",-1);
295         gtk_text_buffer_get_end_iter(b,&iter);
296         gtk_text_buffer_get_iter_at_offset(b,&begin,off);
297         if (lev==ORTP_ERROR || lev==ORTP_FATAL) gtk_text_buffer_apply_tag_by_name(b,"red",&begin,&iter);
298         else if (lev==ORTP_WARNING) gtk_text_buffer_apply_tag_by_name(b,"orange",&begin,&iter);
299         gtk_text_buffer_get_end_iter(b,&iter);
300         //gtk_text_view_scroll_to_iter(v,&iter,0,FALSE,0,0);
301 }
302
303 gboolean linphone_gtk_check_logs(){
304         GList *elem;
305         g_static_mutex_lock(&log_mutex);
306         for(elem=log_queue;elem!=NULL;elem=elem->next){
307                 LinphoneGtkLog *lgl=(LinphoneGtkLog*)elem->data;
308                 linphone_gtk_display_log(lgl->lev,lgl->msg);
309                 g_free(lgl->msg);
310                 g_free(lgl);
311         }
312         if (log_queue) g_list_free(log_queue);
313         log_queue=NULL;
314         g_static_mutex_unlock(&log_mutex);
315         return TRUE;
316 }
317
318 void linphone_gtk_log_push(OrtpLogLevel lev, const char *fmt, va_list args){
319         gchar *msg=g_strdup_vprintf(fmt,args);
320         LinphoneGtkLog *lgl=g_new(LinphoneGtkLog,1);
321         lgl->lev=lev;
322         lgl->msg=msg;
323         g_static_mutex_lock(&log_mutex);
324         log_queue=g_list_append(log_queue,lgl);
325         linphone_gtk_log_file(lev, msg);
326         g_static_mutex_unlock(&log_mutex);
327 }
328