No issues found
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.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
43 #if GLIB_CHECK_VERSION (2,31,0)
44 static GMutex mutex;
45 #else
46 static GMutex *mutex;
47 #endif
48
49 static inline void
50 log_output (const gchar *domain,
51 GLogLevelFlags log_level,
52 const gchar *message)
53 {
54 time_t now;
55 gchar time_str[64];
56 gchar *output;
57 struct tm *local_time;
58 const gchar *log_level_str;
59 static gsize size = 0;
60
61 g_return_if_fail (initialized == TRUE);
62 g_return_if_fail (message != NULL && message[0] != '\0');
63
64 /* Ensure file logging is thread safe */
65 #if GLIB_CHECK_VERSION (2,31,0)
66 g_mutex_lock (&mutex);
67 #else
68 g_mutex_lock (mutex);
69 #endif
70
71 /* Check log size, 10MiB limit */
72 if (size > (10 << 20) && fd) {
73 rewind (fd);
74
75 if (ftruncate (fileno (fd), 0) != 0) {
76 /* FIXME: What should we do if this fails? */
77 }
78
79 size = 0;
80 }
81
82 now = time ((time_t *) NULL);
83 local_time = localtime (&now);
84 strftime (time_str, 64, "%d %b %Y, %H:%M:%S:", local_time);
85
86 switch (log_level) {
87 case G_LOG_LEVEL_WARNING:
88 log_level_str = "-Warning **";
89 break;
90
91 case G_LOG_LEVEL_CRITICAL:
92 log_level_str = "-Critical **";
93 break;
94
95 case G_LOG_LEVEL_ERROR:
96 log_level_str = "-Error **";
97 break;
98 case G_LOG_FLAG_RECURSION:
99 case G_LOG_FLAG_FATAL:
100 case G_LOG_LEVEL_MESSAGE:
101 case G_LOG_LEVEL_INFO:
102 case G_LOG_LEVEL_DEBUG:
103 case G_LOG_LEVEL_MASK:
104 default:
105 log_level_str = NULL;
106 break;
107 }
108
109 output = g_strdup_printf ("%s%s %s%s: %s",
110 log_level_str ? "\n" : "",
111 time_str,
112 domain,
113 log_level_str ? log_level_str : "",
114 message);
115
116 if (G_UNLIKELY (fd == NULL)) {
117 FILE *f;
118
119 if (log_level == G_LOG_LEVEL_WARNING ||
120 log_level == G_LOG_LEVEL_CRITICAL ||
121 log_level == G_LOG_LEVEL_ERROR) {
122 f = stderr;
123 } else {
124 f = stdout;
125 }
126
127 g_fprintf (f, "%s\n", output);
128 fflush (f);
129 } else {
130 size += g_fprintf (fd, "%s\n", output);
131 fflush (fd);
132 }
133
134 g_free (output);
135
136 #if GLIB_CHECK_VERSION (2,31,0)
137 g_mutex_unlock (&mutex);
138 #else
139 g_mutex_unlock (mutex);
140 #endif
141 }
142
143 static void
144 tracker_log_handler (const gchar *domain,
145 GLogLevelFlags log_level,
146 const gchar *message,
147 gpointer user_data)
148 {
149 /* Unless enabled, we don't log to file by default */
150 if (use_log_files) {
151 log_output (domain, log_level, message);
152 }
153
154 /* Now show the message through stdout/stderr as usual */
155 g_log_default_handler (domain, log_level, message, user_data);
156 }
157
158 static void
159 hide_log_handler (const gchar *domain,
160 GLogLevelFlags log_level,
161 const gchar *message,
162 gpointer user_data)
163 {
164 /* do nothing */
165 }
166
167 gboolean
168 tracker_log_init (gint this_verbosity,
169 gchar **used_filename)
170 {
171 const gchar *env_use_log_files;
172 const gchar *env_verbosity;
173 GLogLevelFlags hide_levels = 0;
174
175 if (initialized) {
176 return TRUE;
177 }
178
179 env_use_log_files = g_getenv ("TRACKER_USE_LOG_FILES");
180 if (env_use_log_files != NULL) {
181 /* When set we use:
182 * ~/.local/share/Tracker/
183 * Otherwise, we either of the following:
184 * ~/.xsession-errors
185 * ~/.cache/gdm/session.log
186 * systemd journal
187 * Depending on the system.
188 */
189 use_log_files = TRUE;
190 }
191
192 env_verbosity = g_getenv ("TRACKER_VERBOSITY");
193 if (env_verbosity != NULL) {
194 this_verbosity = atoi (env_verbosity);
195 } else {
196 gchar *verbosity_string;
197
198 /* make sure libtracker-sparql uses the same verbosity setting */
199
200 verbosity_string = g_strdup_printf ("%d", this_verbosity);
201 g_setenv ("TRACKER_VERBOSITY", verbosity_string, FALSE);
202 g_free (verbosity_string);
203 }
204
205 /* If we have debug enabled, we imply G_MESSAGES_DEBUG or we
206 * see nothing, this came in since GLib 2.32.
207 */
208 if (this_verbosity > 2) {
209 g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
210 }
211
212 if (use_log_files) {
213 gchar *basename;
214 gchar *filename;
215
216 basename = g_strdup_printf ("%s.log", g_get_application_name ());
217 filename = g_build_filename (g_get_user_data_dir (),
218 "tracker",
219 basename,
220 NULL);
221 g_free (basename);
222
223 /* Open file */
224 fd = g_fopen (filename, "a");
225 if (!fd) {
226 const gchar *error_string;
227
228 error_string = g_strerror (errno);
229 g_fprintf (stderr,
230 "Could not open log:'%s', %s\n",
231 filename,
232 error_string);
233 g_fprintf (stderr,
234 "All logging will go to stderr\n");
235
236 use_log_files = TRUE;
237 }
238
239 if (used_filename) {
240 *used_filename = filename;
241 } else {
242 g_free (filename);
243 }
244 } else {
245 *used_filename = NULL;
246 }
247
248 verbosity = CLAMP (this_verbosity, 0, 3);
249
250 #if GLIB_CHECK_VERSION (2,31,0)
251 g_mutex_init (&mutex);
252 #else
253 mutex = g_mutex_new ();
254 #endif
255
256 switch (this_verbosity) {
257 /* Log level 3: EVERYTHING */
258 case 3:
259 break;
260
261 /* Log level 2: CRITICAL/ERROR/WARNING/INFO/MESSAGE only */
262 case 2:
263 hide_levels = G_LOG_LEVEL_DEBUG;
264 break;
265
266 /* Log level 1: CRITICAL/ERROR/WARNING/INFO only */
267 case 1:
268 hide_levels = G_LOG_LEVEL_DEBUG |
269 G_LOG_LEVEL_MESSAGE;
270 break;
271
272 /* Log level 0: CRITICAL/ERROR/WARNING only (default) */
273 default:
274 case 0:
275 hide_levels = G_LOG_LEVEL_DEBUG |
276 G_LOG_LEVEL_MESSAGE |
277 G_LOG_LEVEL_INFO;
278 break;
279 }
280
281 if (hide_levels) {
282 /* Hide log levels according to configuration */
283 log_handler_id = g_log_set_handler (G_LOG_DOMAIN,
284 hide_levels,
285 hide_log_handler,
286 NULL);
287 }
288
289 /* Set log handler function for the rest */
290 g_log_set_default_handler (tracker_log_handler, NULL);
291
292 initialized = TRUE;
293
294 /* log binary name and version */
295 g_message ("Starting %s %s", g_get_application_name (), PACKAGE_VERSION);
296
297 return TRUE;
298 }
299
300 void
301 tracker_log_shutdown (void)
302 {
303 if (!initialized) {
304 return;
305 }
306
307 g_message ("Stopping %s %s", g_get_application_name (), PACKAGE_VERSION);
308
309 /* Reset default log handler */
310 g_log_set_default_handler (g_log_default_handler, NULL);
311
312 if (log_handler_id) {
313 g_log_remove_handler (G_LOG_DOMAIN, log_handler_id);
314 log_handler_id = 0;
315 }
316
317 if (use_log_files && fd != NULL) {
318 fclose (fd);
319 }
320
321 #if GLIB_CHECK_VERSION (2,31,0)
322 g_mutex_clear (&mutex);
323 #else
324 g_mutex_free (mutex);
325 #endif
326
327 initialized = FALSE;
328 }