xapian-core  1.4.21
debuglog.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2008,2011,2012,2014,2015,2019 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <config.h>
22 
23 #ifdef XAPIAN_DEBUG_LOG
24 
25 #include "debuglog.h"
26 
27 #include "errno_to_string.h"
28 #include "str.h"
29 
30 #include <sys/types.h>
31 #include "safefcntl.h"
32 #include "safesysstat.h"
33 #include "safeunistd.h"
34 
35 #include <cerrno>
36 #include <cstdlib> // For getenv().
37 #include <string>
38 
39 using namespace std;
40 
41 DebugLogger xapian_debuglogger_;
42 
43 DebugLogger::~DebugLogger()
44 {
45  LOGLINE(ALWAYS, PACKAGE_STRING": debug log ended");
46 }
47 
48 void
49 DebugLogger::initialise_categories_mask()
50 {
51  fd = -2;
52  const char* f = getenv("XAPIAN_DEBUG_LOG");
53  int flags = 0;
54  if (f && *f) {
55  if (f[0] == '-' && f[1] == '\0') {
56  // Filename "-" means "log to stderr".
57  fd = 2;
58  } else {
59  string fnm, pid;
60  while (*f) {
61  if (*f == '%') {
62  if (f[1] == 'p') {
63  // Replace %p in the filename with the process id.
64  if (pid.empty()) pid = str(getpid());
65  fnm += pid;
66  f += 2;
67  continue;
68  } else if (f[1] == '!') {
69  // %! in the filename means we should attempt to ensure
70  // that debug output is written to disk so that none is
71  // lost if we crash.
72  //
73  // We use O_DSYNC in preference if available - updating
74  // the log file's mtime isn't important.
75 #if O_DSYNC - 0 != 0
76  flags = O_DSYNC;
77 #elif O_SYNC - 0 != 0
78  flags = O_SYNC;
79 #endif
80  f += 2;
81  continue;
82  }
83  }
84  fnm += *f++;
85  }
86 
87  flags |= O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC;
88  fd = open(fnm.c_str(), flags, 0644);
89  if (fd == -1) {
90  // If we failed to open the log file, report to stderr, but
91  // don't spew all the log output to stderr too or else the
92  // user will probably miss the message about the debug log
93  // failing to open!
94  fd = 2;
95  LOGLINE(ALWAYS, PACKAGE_STRING": Failed to open debug log '"
96  << fnm << "' (" << errno_to_string(errno) << ')');
97  fd = -2;
98  }
99  }
100 
101  if (fd >= 0) {
102  const char* v = getenv("XAPIAN_DEBUG_FLAGS");
103  if (v) {
104  bool toggle = (*v == '-');
105  if (toggle) ++v;
106  categories_mask = 0;
107  while (*v) {
108  int ch = *v++ - '@';
109  if (ch > 0 && ch <= 26) categories_mask |= 1ul << ch;
110  }
111  if (toggle) categories_mask ^= 0xffffffff;
112  }
113  }
114  }
115  LOGLINE(ALWAYS, PACKAGE_STRING": debug log started");
116 }
117 
118 void
119 DebugLogger::log_line(debuglog_categories category, const string& msg)
120 {
121  if (fd < 0) return;
122 
123  // Preserve errno over logging calls, so they can safely be added to code
124  // which expects errno not to change.
125  int saved_errno = errno;
126 
127  string line;
128  line.reserve(9 + indent_level + msg.size());
129  line = char(category) + '@';
130  line += ' ';
131  line += str(getpid());
132  line.append(indent_level + 1, ' ');
133  line += msg;
134  line += '\n';
135 
136  const char* p = line.data();
137  size_t to_do = line.size();
138  while (to_do) {
139  ssize_t n = write(fd, p, to_do);
140  if (n < 0) {
141  // Retry if interrupted by a signal.
142  if (errno == EINTR) continue;
143 
144  // Upon other errors, close the log file, moan to stderr, and stop
145  // logging.
146  (void)close(fd);
147  fd = 2;
148  LOGLINE(ALWAYS, PACKAGE_STRING": Failed to write log output ("
149  << errno_to_string(errno) << ')');
150  fd = -2;
151  break;
152  }
153  p += n;
154  to_do -= n;
155  }
156 
157  errno = saved_errno;
158 }
159 
160 #endif // XAPIAN_DEBUG_LOG
int close(FD &fd)
Definition: fd.h:63
category
Each Unicode character is in exactly one of these categories.
Definition: unicode.h:220
Convert errno value to std::string, thread-safe if possible.
WritableDatabase open()
Construct a WritableDatabase object for a new, empty InMemory database.
Definition: dbfactory.h:104
STL namespace.
Convert types to std::string.
#define O_CLOEXEC
Definition: safefcntl.h:90
include <sys/stat.h> with portability enhancements
void errno_to_string(int e, string &s)
string str(int value)
Convert int to std::string.
Definition: str.cc:90
#define PACKAGE_STRING
Definition: config.h:337
<unistd.h>, but with compat.
#define LOGLINE(a, b)
Definition: debuglog.h:483
include <fcntl.h>, but working around broken platforms.
Debug logging macros.