ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/logger.C
Revision: 1.31
Committed: Sat Nov 17 23:40:00 2018 UTC (5 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.30: +1 -0 lines
Log Message:
copyright update 2018

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.16 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 root 1.29 *
4 root 1.31 * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team
5 root 1.30 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
6 root 1.29 *
7 root 1.20 * Deliantra is free software: you can redistribute it and/or modify it under
8     * the terms of the Affero GNU General Public License as published by the
9     * Free Software Foundation, either version 3 of the License, or (at your
10     * option) any later version.
11 root 1.29 *
12 root 1.15 * This program is distributed in the hope that it will be useful,
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     * GNU General Public License for more details.
16 root 1.29 *
17 root 1.20 * You should have received a copy of the Affero GNU General Public License
18     * and the GNU General Public License along with this program. If not, see
19     * <http://www.gnu.org/licenses/>.
20 root 1.29 *
21 root 1.16 * The authors can be reached via e-mail to <support@deliantra.net>
22 pippijn 1.11 */
23 elmex 1.1
24 root 1.12 #include <cstdarg>
25 root 1.17 #include <cstring>
26    
27     #include <vector>
28    
29     #include <sys/uio.h>
30     #include <pthread.h>
31    
32     #include "global.h"
33     #include "dynbuf.h"
34     #include "util.h"
35    
36     struct logline
37     {
38     struct timeval tv;
39     char *buf; // includes PREFIX_LEN garbage bytes
40     int len;
41     int flags;
42     };
43    
44 root 1.25 typedef std::vector<logline, slice_allocator<logline> > logvector;
45    
46 root 1.17 static SMUTEX(mutex);
47 root 1.24 static SMUTEX(fdlock);
48 root 1.17 static SCOND(cond);
49     static int logfd = STDERR_FILENO;
50     static int logsync = 1;
51 root 1.25 static logvector queue;
52 root 1.17
53 root 1.24 int log_setfd (int fd)
54 root 1.17 {
55 root 1.24 SMUTEX_LOCK (fdlock);
56     int old = logfd;
57 root 1.17 logfd = fd < 0 ? STDERR_FILENO : fd;
58 root 1.24 SMUTEX_UNLOCK (fdlock);
59    
60     return old;
61 root 1.17 }
62    
63 root 1.26 #define PREFIX_LEN sizeof ("0000-00-00 00:00:00.0000 L+") - 1
64 root 1.17
65     static void
66     log_sync (logline &line)
67     {
68 root 1.26 static const char levelchar [16+1] = "EWIDt???????????";
69 root 1.18 struct tm lt;
70 root 1.17 char pfx [PREFIX_LEN];
71    
72 root 1.18 localtime_r (&line.tv.tv_sec, &lt);
73    
74 root 1.26 sprintf (pfx, "%04d-%02d-%02d %02d:%02d:%02d.%04d %c",
75 root 1.18 lt.tm_year + 1900,
76     lt.tm_mon + 1,
77     lt.tm_mday,
78     lt.tm_hour,
79     lt.tm_min,
80     lt.tm_sec,
81 root 1.26 (int)(line.tv.tv_usec / 100),
82     levelchar [line.flags & 15]
83 root 1.17 );
84    
85     pfx [PREFIX_LEN - 1] = line.flags & logSync ? '=' : ' ';
86    
87     struct iovec iov [2];
88    
89     iov [0].iov_base = pfx;
90     iov [0].iov_len = PREFIX_LEN;
91    
92     char *buf = line.buf;
93    
94 root 1.24 if (logsync != 2)
95     SMUTEX_LOCK (fdlock);
96    
97 root 1.17 while (char *end = strchr (buf, '\n'))
98     {
99     iov [1].iov_base = buf;
100     iov [1].iov_len = end - buf + 1;
101    
102     writev (STDERR_FILENO, iov, 2);
103     if (logfd != STDERR_FILENO)
104     writev (logfd, iov, 2);
105    
106     buf = end + 1;
107    
108     if (buf == line.buf + line.len)
109     break;
110    
111     pfx [PREFIX_LEN - 1] = '+';
112     }
113    
114 root 1.24 if (logsync != 2)
115     SMUTEX_UNLOCK (fdlock);
116    
117 root 1.17 sfree (line.buf, line.len);
118     }
119    
120     static void *
121     logthread_proc (void *arg)
122     {
123     int idx = 0;
124    
125     for (;;)
126     {
127     logline line;
128    
129     SMUTEX_LOCK (mutex);
130    
131     while (queue.empty ())
132     SCOND_WAIT (cond, mutex);
133    
134     line = queue [idx++];
135    
136     // this algorithm could result in an ever-increasing vector
137     // size if we log faster than we can write, but if that happens
138     // we have bigger problems.
139     if (idx == queue.size ())
140     {
141 root 1.25 if (idx < 32)
142     queue.clear ();
143     else
144     logvector ().swap (queue); // free memory, hopefully
145    
146 root 1.17 idx = 0;
147     }
148    
149     SMUTEX_UNLOCK (mutex);
150    
151     log_sync (line);
152     }
153     }
154    
155     void
156     log_cleanup ()
157     {
158     logsync = 1;
159 root 1.25 SMUTEX_UNLOCK (fdlock);
160 root 1.17
161     for (;;)
162     {
163     int done;
164    
165     SMUTEX_LOCK (mutex);
166     done = queue.empty ();
167     SMUTEX_UNLOCK (mutex);
168    
169     if (done)
170     break;
171    
172     usleep (10000);
173 root 1.24 SMUTEX_LOCK (fdlock);
174     SMUTEX_UNLOCK (fdlock);
175 root 1.17 }
176     }
177    
178     static void
179     af_child ()
180     {
181 root 1.24 logsync = 2;
182 root 1.17 }
183    
184     static struct logthread : thread
185     {
186     logthread ()
187     {
188     pthread_atfork (0, 0, af_child);
189     start (logthread_proc);
190     logsync = 0;
191     }
192     } logthread;
193 elmex 1.1
194 root 1.4 void
195 root 1.22 LOG (int flags, const_utf8_string format, ...)
196 elmex 1.1 {
197 root 1.12 int level = flags & 15;
198    
199     if (level > settings.debug)
200     return;
201    
202 root 1.17 logline line;
203     gettimeofday (&line.tv, 0);
204 root 1.12
205 root 1.17 static dynbuf_text buf;
206 root 1.12
207     va_list ap;
208     va_start (ap, format);
209 root 1.17 buf.vprintf (format, ap);
210 root 1.12 va_end (ap);
211    
212 root 1.17 buf << '\n';
213    
214     line.buf = buf.linearise ();
215     line.len = buf.size ();
216 root 1.12
217 root 1.17 if (line.buf [line.len - 2] == '\n')
218     --line.len;
219 root 1.14
220 root 1.17 line.buf = salloc<char> (line.len, line.buf);
221    
222     buf.clear ();
223    
224     if (logsync)
225     flags |= logSync;
226 root 1.12
227     if (flags & logBacktrace)
228 root 1.17 {
229     line.buf [line.len - 1] = 0;
230     log_backtrace (line.buf);
231     line.buf [line.len - 1] = '\n';
232     }
233    
234     line.flags = flags;
235    
236     if (line.flags & logSync)
237     log_sync (line);
238     else
239     {
240     SMUTEX_LOCK (mutex);
241     queue.push_back (line);
242     SMUTEX_UNLOCK (mutex);
243     SCOND_SIGNAL (cond);
244     }
245 elmex 1.1 }
246 root 1.17
247 root 1.24 static int suspended;
248    
249     void
250     log_suspend ()
251     {
252     if (!suspended++)
253     {
254     LOG (llevDebug, "logging suspended.");
255     SMUTEX_LOCK (fdlock);
256     }
257     }
258    
259     void
260     log_resume ()
261     {
262     if (!--suspended)
263     {
264     SMUTEX_UNLOCK (fdlock);
265     LOG (llevDebug, "logging resumed.");
266     }
267     }
268