1 /*
2 * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config-miners.h"
21
22 #include <sys/time.h>
23 #include <sys/resource.h>
24 #include <sys/types.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <errno.h>
31
32 #include <glib/gstdio.h>
33
34 #include "tracker-log.h"
35 #include "tracker-file-utils.h"
36
37 static gboolean initialized;
38 static FILE *fd;
39 static gint verbosity;
40 static guint log_handler_id;
41 static gboolean use_log_files;
42 static GMutex mutex;
43
44 static inline void
log_output(const gchar * domain,GLogLevelFlags log_level,const gchar * message)45 log_output (const gchar *domain,
46 GLogLevelFlags log_level,
47 const gchar *message)
48 {
49 time_t now;
50 gchar time_str[64];
51 gchar *output;
52 struct tm *local_time;
53 const gchar *log_level_str;
54 static gsize size = 0;
55
56 g_return_if_fail (initialized == TRUE);
57 g_return_if_fail (message != NULL && message[0] != '\0');
58
59 /* Ensure file logging is thread safe */
60 g_mutex_lock (&mutex);
61
62 /* Check log size, 10MiB limit */
63 if (size > (10 << 20) && fd) {
64 rewind (fd);
65
66 if (ftruncate (fileno (fd), 0) != 0) {
67 /* FIXME: What should we do if this fails? */
68 }
69
70 size = 0;
71 }
72
73 now = time ((time_t *) NULL);
74 local_time = localtime (&now);
75 strftime (time_str, 64, "%d %b %Y, %H:%M:%S:", local_time);
76
77 switch (log_level) {
78 case G_LOG_LEVEL_WARNING:
79 log_level_str = "-Warning **";
80 break;
81
82 case G_LOG_LEVEL_CRITICAL:
83 log_level_str = "-Critical **";
84 break;
85
86 case G_LOG_LEVEL_ERROR:
87 log_level_str = "-Error **";
88 break;
89 case G_LOG_FLAG_RECURSION:
90 case G_LOG_FLAG_FATAL:
91 case G_LOG_LEVEL_MESSAGE:
92 case G_LOG_LEVEL_INFO:
93 case G_LOG_LEVEL_DEBUG:
94 case G_LOG_LEVEL_MASK:
95 default:
96 log_level_str = NULL;
97 break;
98 }
99
100 output = g_strdup_printf ("%s%s %s%s: %s",
101 log_level_str ? "\n" : "",
102 time_str,
103 domain,
104 log_level_str ? log_level_str : "",
105 message);
106
107 if (G_UNLIKELY (fd == NULL)) {
108 FILE *f;
109
110 if (log_level == G_LOG_LEVEL_WARNING ||
111 log_level == G_LOG_LEVEL_CRITICAL ||
112 log_level == G_LOG_LEVEL_ERROR) {
113 f = stderr;
114 } else {
115 f = stdout;
116 }
117
118 g_fprintf (f, "%s\n", output);
119 fflush (f);
120 } else {
121 size += g_fprintf (fd, "%s\n", output);
122 fflush (fd);
123 }
124
125 g_free (output);
126
127 g_mutex_unlock (&mutex);
128 }
129
130 static void
tracker_log_handler(const gchar * domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)131 tracker_log_handler (const gchar *domain,
132 GLogLevelFlags log_level,
133 const gchar *message,
134 gpointer user_data)
135 {
136 /* Unless enabled, we don't log to file by default */
137 if (use_log_files) {
138 log_output (domain, log_level, message);
139 }
140
141 /* Now show the message through stdout/stderr as usual */
142 g_log_default_handler (domain, log_level, message, user_data);
143 }
144
145 static void
hide_log_handler(const gchar * domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)146 hide_log_handler (const gchar *domain,
147 GLogLevelFlags log_level,
148 const gchar *message,
149 gpointer user_data)
150 {
151 /* do nothing */
152 }
153
154 gboolean
tracker_log_init(gint this_verbosity,gchar ** used_filename)155 tracker_log_init (gint this_verbosity,
156 gchar **used_filename)
157 {
158 const gchar *env_use_log_files;
159 const gchar *env_verbosity;
160 GLogLevelFlags hide_levels = 0;
161
162 if (initialized) {
163 return TRUE;
164 }
165
166 env_use_log_files = g_getenv ("TRACKER_USE_LOG_FILES");
167 if (env_use_log_files != NULL) {
168 /* When set we use:
169 * ~/.local/share/Tracker/
170 * Otherwise, we either of the following:
171 * ~/.xsession-errors
172 * ~/.cache/gdm/session.log
173 * systemd journal
174 * Depending on the system.
175 */
176 use_log_files = TRUE;
177 }
178
179 env_verbosity = g_getenv ("TRACKER_VERBOSITY");
180 if (env_verbosity != NULL) {
181 this_verbosity = atoi (env_verbosity);
182 } else {
183 gchar *verbosity_string;
184
185 /* make sure libtracker-sparql uses the same verbosity setting */
186
187 verbosity_string = g_strdup_printf ("%d", this_verbosity);
188 g_setenv ("TRACKER_VERBOSITY", verbosity_string, FALSE);
189 g_free (verbosity_string);
190 }
191
192 /* If we have debug enabled, we imply G_MESSAGES_DEBUG or we
193 * see nothing, this came in since GLib 2.32.
194 */
195 if (this_verbosity > 1) {
196 g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
197 }
198
199 if (use_log_files) {
200 gchar *basename;
201 gchar *filename;
202
203 basename = g_strdup_printf ("%s.log", g_get_application_name ());
204 filename = g_build_filename (g_get_user_data_dir (),
205 "tracker",
206 basename,
207 NULL);
208 g_free (basename);
209
210 /* Open file */
211 fd = g_fopen (filename, "a");
212 if (!fd) {
213 const gchar *error_string;
214
215 error_string = g_strerror (errno);
216 g_fprintf (stderr,
217 "Could not open log:'%s', %s\n",
218 filename,
219 error_string);
220 g_fprintf (stderr,
221 "All logging will go to stderr\n");
222
223 use_log_files = TRUE;
224 }
225
226 if (used_filename) {
227 *used_filename = filename;
228 } else {
229 g_free (filename);
230 }
231 } else {
232 *used_filename = NULL;
233 }
234
235 verbosity = CLAMP (this_verbosity, 0, 3);
236
237 g_mutex_init (&mutex);
238
239 switch (this_verbosity) {
240 /* Log level 3: EVERYTHING */
241 case 3:
242 break;
243
244 /* Log level 2: CRITICAL/ERROR/WARNING/INFO/MESSAGE only */
245 case 2:
246 hide_levels = G_LOG_LEVEL_DEBUG;
247 break;
248
249 /* Log level 1: CRITICAL/ERROR/WARNING/INFO only */
250 case 1:
251 hide_levels = G_LOG_LEVEL_DEBUG |
252 G_LOG_LEVEL_MESSAGE;
253 break;
254
255 /* Log level 0: CRITICAL/ERROR/WARNING only (default) */
256 default:
257 case 0:
258 hide_levels = G_LOG_LEVEL_DEBUG |
259 G_LOG_LEVEL_MESSAGE |
260 G_LOG_LEVEL_INFO;
261 break;
262 }
263
264 if (hide_levels) {
265 /* Hide log levels according to configuration */
266 log_handler_id = g_log_set_handler (G_LOG_DOMAIN,
267 hide_levels,
268 hide_log_handler,
269 NULL);
270 }
271
272 /* Set log handler function for the rest */
273 g_log_set_default_handler (tracker_log_handler, NULL);
274
275 initialized = TRUE;
276
277 /* log binary name and version */
278 g_message ("Starting %s %s", g_get_application_name (), PACKAGE_VERSION);
279
280 return TRUE;
281 }
282
283 void
tracker_log_shutdown(void)284 tracker_log_shutdown (void)
285 {
286 if (!initialized) {
287 return;
288 }
289
290 g_message ("Stopping %s %s", g_get_application_name (), PACKAGE_VERSION);
291
292 /* Reset default log handler */
293 g_log_set_default_handler (g_log_default_handler, NULL);
294
295 if (log_handler_id) {
296 g_log_remove_handler (G_LOG_DOMAIN, log_handler_id);
297 log_handler_id = 0;
298 }
299
300 if (use_log_files && fd != NULL) {
301 fclose (fd);
302 }
303
304 g_mutex_clear (&mutex);
305
306 initialized = FALSE;
307 }
308