1 /*
2 * This file Copyright (C) 2010-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 */
8
9 #include <errno.h>
10 #include <stdio.h>
11
12 #include <event2/buffer.h>
13
14 #include "transmission.h"
15 #include "file.h"
16 #include "log.h"
17 #include "platform.h" /* tr_lock */
18 #include "tr-assert.h"
19 #include "utils.h"
20
21 tr_log_level __tr_message_level = TR_LOG_ERROR;
22
23 static bool myQueueEnabled = false;
24 static tr_log_message* myQueue = NULL;
25 static tr_log_message** myQueueTail = &myQueue;
26 static int myQueueLength = 0;
27
28 #ifndef _WIN32
29
30 /* make null versions of these win32 functions */
IsDebuggerPresent(void)31 static inline bool IsDebuggerPresent(void)
32 {
33 return false;
34 }
35
OutputDebugStringA(void const * unused UNUSED)36 static inline void OutputDebugStringA(void const* unused UNUSED)
37 {
38 }
39
40 #endif
41
42 /***
43 ****
44 ***/
45
tr_logGetLevel(void)46 tr_log_level tr_logGetLevel(void)
47 {
48 return __tr_message_level;
49 }
50
51 /***
52 ****
53 ***/
54
getMessageLock(void)55 static tr_lock* getMessageLock(void)
56 {
57 static tr_lock* l = NULL;
58
59 if (l == NULL)
60 {
61 l = tr_lockNew();
62 }
63
64 return l;
65 }
66
tr_logGetFile(void)67 tr_sys_file_t tr_logGetFile(void)
68 {
69 static bool initialized = false;
70 static tr_sys_file_t file = TR_BAD_SYS_FILE;
71
72 if (!initialized)
73 {
74 int const fd = tr_env_get_int("TR_DEBUG_FD", 0);
75
76 switch (fd)
77 {
78 case 1:
79 file = tr_sys_file_get_std(TR_STD_SYS_FILE_OUT, NULL);
80 break;
81
82 case 2:
83 file = tr_sys_file_get_std(TR_STD_SYS_FILE_ERR, NULL);
84 break;
85 }
86
87 initialized = true;
88 }
89
90 return file;
91 }
92
tr_logSetLevel(tr_log_level level)93 void tr_logSetLevel(tr_log_level level)
94 {
95 __tr_message_level = level;
96 }
97
tr_logSetQueueEnabled(bool isEnabled)98 void tr_logSetQueueEnabled(bool isEnabled)
99 {
100 myQueueEnabled = isEnabled;
101 }
102
tr_logGetQueueEnabled(void)103 bool tr_logGetQueueEnabled(void)
104 {
105 return myQueueEnabled;
106 }
107
tr_logGetQueue(void)108 tr_log_message* tr_logGetQueue(void)
109 {
110 tr_log_message* ret;
111 tr_lockLock(getMessageLock());
112
113 ret = myQueue;
114 myQueue = NULL;
115 myQueueTail = &myQueue;
116 myQueueLength = 0;
117
118 tr_lockUnlock(getMessageLock());
119 return ret;
120 }
121
tr_logFreeQueue(tr_log_message * list)122 void tr_logFreeQueue(tr_log_message* list)
123 {
124 tr_log_message* next;
125
126 while (list != NULL)
127 {
128 next = list->next;
129 tr_free(list->message);
130 tr_free(list->name);
131 tr_free(list);
132 list = next;
133 }
134 }
135
136 /**
137 ***
138 **/
139
tr_logGetTimeStr(char * buf,size_t buflen)140 char* tr_logGetTimeStr(char* buf, size_t buflen)
141 {
142 char tmp[64];
143 struct tm now_tm;
144 struct timeval tv;
145 time_t seconds;
146 int milliseconds;
147
148 tr_gettimeofday(&tv);
149
150 seconds = tv.tv_sec;
151 tr_localtime_r(&seconds, &now_tm);
152 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S.%%03d", &now_tm);
153 milliseconds = tv.tv_usec / 1000;
154 tr_snprintf(buf, buflen, tmp, milliseconds);
155
156 return buf;
157 }
158
tr_logGetDeepEnabled(void)159 bool tr_logGetDeepEnabled(void)
160 {
161 static int8_t deepLoggingIsActive = -1;
162
163 if (deepLoggingIsActive < 0)
164 {
165 deepLoggingIsActive = (int8_t)(IsDebuggerPresent() || tr_logGetFile() != TR_BAD_SYS_FILE);
166 }
167
168 return deepLoggingIsActive != 0;
169 }
170
tr_logAddDeep(char const * file,int line,char const * name,char const * fmt,...)171 void tr_logAddDeep(char const* file, int line, char const* name, char const* fmt, ...)
172 {
173 tr_sys_file_t const fp = tr_logGetFile();
174
175 if (fp != TR_BAD_SYS_FILE || IsDebuggerPresent())
176 {
177 va_list args;
178 char timestr[64];
179 char* message;
180 size_t message_len;
181 struct evbuffer* buf = evbuffer_new();
182 char* base = tr_sys_path_basename(file, NULL);
183
184 evbuffer_add_printf(buf, "[%s] ", tr_logGetTimeStr(timestr, sizeof(timestr)));
185
186 if (name != NULL)
187 {
188 evbuffer_add_printf(buf, "%s ", name);
189 }
190
191 va_start(args, fmt);
192 evbuffer_add_vprintf(buf, fmt, args);
193 va_end(args);
194 evbuffer_add_printf(buf, " (%s:%d)" TR_NATIVE_EOL_STR, base, line);
195 /* FIXME (libevent2) ifdef this out for nonwindows platforms */
196 message = evbuffer_free_to_str(buf, &message_len);
197 OutputDebugStringA(message);
198
199 if (fp != TR_BAD_SYS_FILE)
200 {
201 tr_sys_file_write(fp, message, message_len, NULL, NULL);
202 }
203
204 tr_free(message);
205 tr_free(base);
206 }
207 }
208
209 /***
210 ****
211 ***/
212
tr_logAddMessage(char const * file,int line,tr_log_level level,char const * name,char const * fmt,...)213 void tr_logAddMessage(char const* file, int line, tr_log_level level, char const* name, char const* fmt, ...)
214 {
215 int const err = errno; /* message logging shouldn't affect errno */
216 char buf[1024];
217 int buf_len;
218 va_list ap;
219 tr_lockLock(getMessageLock());
220
221 /* build the text message */
222 *buf = '\0';
223 va_start(ap, fmt);
224 buf_len = evutil_vsnprintf(buf, sizeof(buf), fmt, ap);
225 va_end(ap);
226
227 if (buf_len < 0)
228 {
229 goto finish;
230 }
231
232 #ifdef _WIN32
233
234 if ((size_t)buf_len < sizeof(buf) - 3)
235 {
236 buf[buf_len + 0] = '\r';
237 buf[buf_len + 1] = '\n';
238 buf[buf_len + 2] = '\0';
239 OutputDebugStringA(buf);
240 buf[buf_len + 0] = '\0';
241 }
242 else
243 {
244 OutputDebugStringA(buf);
245 }
246
247 #endif
248
249 if (!tr_str_is_empty(buf))
250 {
251 if (tr_logGetQueueEnabled())
252 {
253 tr_log_message* newmsg;
254 newmsg = tr_new0(tr_log_message, 1);
255 newmsg->level = level;
256 newmsg->when = tr_time();
257 newmsg->message = tr_strdup(buf);
258 newmsg->file = file;
259 newmsg->line = line;
260 newmsg->name = tr_strdup(name);
261
262 *myQueueTail = newmsg;
263 myQueueTail = &newmsg->next;
264 ++myQueueLength;
265
266 if (myQueueLength > TR_LOG_MAX_QUEUE_LENGTH)
267 {
268 tr_log_message* old = myQueue;
269 myQueue = old->next;
270 old->next = NULL;
271 tr_logFreeQueue(old);
272 --myQueueLength;
273 TR_ASSERT(myQueueLength == TR_LOG_MAX_QUEUE_LENGTH);
274 }
275 }
276 else
277 {
278 tr_sys_file_t fp;
279 char timestr[64];
280
281 fp = tr_logGetFile();
282
283 if (fp == TR_BAD_SYS_FILE)
284 {
285 fp = tr_sys_file_get_std(TR_STD_SYS_FILE_ERR, NULL);
286 }
287
288 tr_logGetTimeStr(timestr, sizeof(timestr));
289
290 if (name != NULL)
291 {
292 tr_sys_file_write_fmt(fp, "[%s] %s: %s" TR_NATIVE_EOL_STR, NULL, timestr, name, buf);
293 }
294 else
295 {
296 tr_sys_file_write_fmt(fp, "[%s] %s" TR_NATIVE_EOL_STR, NULL, timestr, buf);
297 }
298
299 tr_sys_file_flush(fp, NULL);
300 }
301 }
302
303 finish:
304 tr_lockUnlock(getMessageLock());
305 errno = err;
306 }
307