File: debuglog.cc

package info (click to toggle)
xapian-core 1.4.29-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 22,840 kB
  • sloc: cpp: 92,356; ansic: 9,948; sh: 5,026; perl: 850; makefile: 509; javascript: 360; tcl: 319; python: 40
file content (160 lines) | stat: -rw-r--r-- 4,033 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/** @file
 * @brief Debug logging macros.
 */
/* Copyright (C) 2008,2011,2012,2014,2015,2019 Olly Betts
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

#include <config.h>

#ifdef XAPIAN_DEBUG_LOG

#include "debuglog.h"

#include "errno_to_string.h"
#include "str.h"

#include <sys/types.h>
#include "safefcntl.h"
#include "safesysstat.h"
#include "safeunistd.h"

#include <cerrno>
#include <cstdlib> // For getenv().
#include <string>

using namespace std;

DebugLogger xapian_debuglogger_;

DebugLogger::~DebugLogger()
{
    LOGLINE(ALWAYS, PACKAGE_STRING": debug log ended");
}

void
DebugLogger::initialise_categories_mask()
{
    fd = -2;
    const char* f = getenv("XAPIAN_DEBUG_LOG");
    int flags = 0;
    if (f && *f) {
	if (f[0] == '-' && f[1] == '\0') {
	    // Filename "-" means "log to stderr".
	    fd = 2;
	} else {
	    string fnm, pid;
	    while (*f) {
		if (*f == '%') {
		    if (f[1] == 'p') {
			// Replace %p in the filename with the process id.
			if (pid.empty()) pid = str(getpid());
			fnm += pid;
			f += 2;
			continue;
		    } else if (f[1] == '!') {
			// %! in the filename means we should attempt to ensure
			// that debug output is written to disk so that none is
			// lost if we crash.
			//
			// We use O_DSYNC in preference if available - updating
			// the log file's mtime isn't important.
#if O_DSYNC - 0 != 0
			flags = O_DSYNC;
#elif O_SYNC - 0 != 0
			flags = O_SYNC;
#endif
			f += 2;
			continue;
		    }
		}
		fnm += *f++;
	    }

	    flags |= O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC;
	    fd = open(fnm.c_str(), flags, 0644);
	    if (fd == -1) {
		// If we failed to open the log file, report to stderr, but
		// don't spew all the log output to stderr too or else the
		// user will probably miss the message about the debug log
		// failing to open!
		fd = 2;
		LOGLINE(ALWAYS, PACKAGE_STRING": Failed to open debug log '"
			<< fnm << "' (" << errno_to_string(errno) << ')');
		fd = -2;
	    }
	}

	if (fd >= 0) {
	    const char* v = getenv("XAPIAN_DEBUG_FLAGS");
	    if (v) {
		bool toggle = (*v == '-');
		if (toggle) ++v;
		categories_mask = 0;
		while (*v) {
		    int ch = *v++ - '@';
		    if (ch > 0 && ch <= 26) categories_mask |= 1ul << ch;
		}
		if (toggle) categories_mask ^= 0xffffffff;
	    }
	}
    }
    LOGLINE(ALWAYS, PACKAGE_STRING": debug log started");
}

void
DebugLogger::log_line(debuglog_categories category, const string& msg)
{
    if (fd < 0) return;

    // Preserve errno over logging calls, so they can safely be added to code
    // which expects errno not to change.
    int saved_errno = errno;

    string line;
    line.reserve(9 + indent_level + msg.size());
    line = char(category) + '@';
    line += ' ';
    line += str(getpid());
    line.append(indent_level + 1, ' ');
    line += msg;
    line += '\n';

    const char* p = line.data();
    size_t to_do = line.size();
    while (to_do) {
	ssize_t n = write(fd, p, to_do);
	if (n < 0) {
	    // Retry if interrupted by a signal.
	    if (errno == EINTR) continue;

	    // Upon other errors, close the log file, moan to stderr, and stop
	    // logging.
	    (void)close(fd);
	    fd = 2;
	    LOGLINE(ALWAYS, PACKAGE_STRING": Failed to write log output ("
		    << errno_to_string(errno) << ')');
	    fd = -2;
	    break;
	}
	p += n;
	to_do -= n;
    }

    errno = saved_errno;
}

#endif // XAPIAN_DEBUG_LOG