mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-20 21:53:12 +00:00
Improve Swift API delegate log outputter
Capture whence information in all Serval logging API entry points. Introduce the LogLevel enum type to avoid exposing internal C API details in the Swift API. Rename Swift API methods and types to use camelCase naming style, to distinguish it from the C API, which uses under_score naming style.
This commit is contained in:
parent
00804e9fc1
commit
81d4e696ca
20
log.c
20
log.c
@ -179,12 +179,19 @@ static void log_close(struct log_output_iterator *it)
|
||||
(*it->output)->close(it);
|
||||
}
|
||||
|
||||
static void log_capture_fd(struct log_output_iterator *it, int fd, bool_t *captured)
|
||||
static bool_t log_capture_fd(struct log_output_iterator *it, int fd)
|
||||
{
|
||||
assert(it->output);
|
||||
assert(*it->output);
|
||||
if ((*it->output)->capture_fd)
|
||||
(*it->output)->capture_fd(it, fd, captured);
|
||||
return (*it->output)->capture_fd ? (*it->output)->capture_fd(it, fd) : 0;
|
||||
}
|
||||
|
||||
static void log_suppress_fd(struct log_output_iterator *it, int fd)
|
||||
{
|
||||
assert(it->output);
|
||||
assert(*it->output);
|
||||
if ((*it->output)->suppress_fd)
|
||||
(*it->output)->suppress_fd(it, fd);
|
||||
}
|
||||
|
||||
/* Functions for use by log outputters. This is the "private" API of the logging system, as
|
||||
@ -333,7 +340,12 @@ bool_t serval_log_capture_fd(int fd) {
|
||||
struct log_output_iterator it;
|
||||
log_iterator_start(&it);
|
||||
bool_t captured = 0;
|
||||
while (!captured && log_iterator_advance(&it))
|
||||
captured = log_capture_fd(&it, fd);
|
||||
if (captured) {
|
||||
log_iterator_start(&it);
|
||||
while (log_iterator_advance(&it))
|
||||
log_capture_fd(&it, fd, &captured);
|
||||
log_suppress_fd(&it, fd);
|
||||
}
|
||||
return captured;
|
||||
}
|
||||
|
13
log_output.h
13
log_output.h
@ -66,10 +66,15 @@ struct log_output {
|
||||
// vlogMessage() primitive, before start_line() is called.
|
||||
void (*open)(struct log_output_iterator *it);
|
||||
|
||||
// If *capture is 0 and the output is of a persistent nature (eg, a file or
|
||||
// system log) and is able to redirect data written to the given file
|
||||
// descriptor to its output, then do so and set *capture to 1.
|
||||
void (*capture_fd)(struct log_output_iterator *it, int fd, bool_t *capture);
|
||||
// If the output is of a persistent nature (eg, a file or system log) and
|
||||
// is able to redirect data written to the given file descriptor to its
|
||||
// output, then do so and return true, otherwise return false.
|
||||
bool_t (*capture_fd)(struct log_output_iterator *it, int fd);
|
||||
|
||||
// Cease writing any output to the given file descriptor. This is called
|
||||
// on all outputters immediately after any outputter's capture_fd(fd) call
|
||||
// returns true, to prevent duplicate log messages being captured.
|
||||
void (*suppress_fd)(struct log_output_iterator *it, int fd);
|
||||
|
||||
// Test whether output is able to handle messages; if it returns false then
|
||||
// start_line() and end_line() will not be invoked.
|
||||
|
@ -82,8 +82,10 @@ static void open_log_console(struct log_output_iterator *it)
|
||||
}
|
||||
}
|
||||
|
||||
static void capture_fd_log_console(struct log_output_iterator *it, int fd, bool_t *UNUSED(capture))
|
||||
static void suppress_fd_log_console(struct log_output_iterator *it, int fd)
|
||||
{
|
||||
// If another log outputter is capturing the console's output (eg, the logfile outputer), then
|
||||
// cease logging to the console to avoid duplicate messages being sent to that output.
|
||||
struct log_output_console_state *state = _state(*it->output);
|
||||
if (state->fp && state->fp != DISABLED && fileno(state->fp) == fd) {
|
||||
fflush(state->fp);
|
||||
@ -137,7 +139,8 @@ static struct log_output static_log_output = {
|
||||
.show_time = log_console_show_time,
|
||||
.state = &static_state,
|
||||
.open = open_log_console,
|
||||
.capture_fd = capture_fd_log_console,
|
||||
.capture_fd = NULL,
|
||||
.suppress_fd = suppress_fd_log_console,
|
||||
.is_available = is_log_console_available,
|
||||
.start_line = log_console_start_line,
|
||||
.end_line = log_console_end_line,
|
||||
|
@ -84,6 +84,17 @@ static void log_delegate_open(struct log_output_iterator *it)
|
||||
}
|
||||
}
|
||||
|
||||
static bool_t log_delegate_capture_fd(struct log_output_iterator *UNUSED(it), int fd)
|
||||
{
|
||||
return serval_log_delegate.capture_fd && serval_log_delegate.capture_fd(fd);
|
||||
}
|
||||
|
||||
static void log_delegate_suppress_fd(struct log_output_iterator *UNUSED(it), int fd)
|
||||
{
|
||||
if (serval_log_delegate.suppress_fd)
|
||||
serval_log_delegate.suppress_fd(fd);
|
||||
}
|
||||
|
||||
static void log_delegate_start_line(struct log_output_iterator *it, int UNUSED(level))
|
||||
{
|
||||
struct log_output_delegate_state *state = _state(*it->output);
|
||||
@ -113,7 +124,8 @@ static struct log_output static_log_output = {
|
||||
.show_time = log_delegate_show_time,
|
||||
.state = &static_state,
|
||||
.open = log_delegate_open,
|
||||
.capture_fd = NULL,
|
||||
.capture_fd = log_delegate_capture_fd,
|
||||
.suppress_fd = log_delegate_suppress_fd,
|
||||
.is_available = is_log_delegate_available,
|
||||
.start_line = log_delegate_start_line,
|
||||
.end_line = log_delegate_end_line,
|
||||
@ -130,5 +142,7 @@ struct log_delegate serval_log_delegate = {
|
||||
.show_pid = 0,
|
||||
.show_time = 1,
|
||||
.print = NULL,
|
||||
.flush = NULL
|
||||
.flush = NULL,
|
||||
.capture_fd = NULL,
|
||||
.suppress_fd = NULL
|
||||
};
|
||||
|
@ -29,6 +29,8 @@ struct log_delegate {
|
||||
bool_t show_time;
|
||||
void (*print)(int level, const char *message, bool_t overrun);
|
||||
void (*flush)();
|
||||
bool_t (*capture_fd)(int fd);
|
||||
void (*suppress_fd)(int fd);
|
||||
};
|
||||
|
||||
extern struct log_delegate serval_log_delegate;
|
||||
|
@ -310,9 +310,9 @@ static void open_log_file(struct log_output_iterator *it)
|
||||
}
|
||||
}
|
||||
|
||||
static void capture_fd_log_file(struct log_output_iterator *it, int fd, bool_t *capture)
|
||||
static bool_t capture_fd_log_file(struct log_output_iterator *it, int fd)
|
||||
{
|
||||
if (!*capture) {
|
||||
bool_t captured = 0;
|
||||
// This outputter does not connect the file descriptor to an output file until the next log
|
||||
// message is output.
|
||||
_state(*it->output)->capture_fd = fd;
|
||||
@ -321,15 +321,16 @@ static void capture_fd_log_file(struct log_output_iterator *it, int fd, bool_t *
|
||||
int devnull;
|
||||
if ((devnull = open("/dev/null", O_RDWR, 0)) == -1)
|
||||
WHY_perror("open(\"/dev/null\")");
|
||||
else if (devnull == fd)
|
||||
captured = 1;
|
||||
else {
|
||||
if (devnull != fd && dup2(devnull, fd) == -1)
|
||||
if (dup2(devnull, fd) == -1)
|
||||
WHYF_perror("dup2(%d, %d)", devnull, fd);
|
||||
else
|
||||
*capture = 1;
|
||||
if (devnull != fd)
|
||||
captured = 1;
|
||||
close(devnull);
|
||||
}
|
||||
}
|
||||
return captured;
|
||||
}
|
||||
|
||||
static bool_t is_log_file_available(const struct log_output_iterator *it)
|
||||
@ -377,6 +378,7 @@ static struct log_output static_log_output = {
|
||||
.state = &static_state,
|
||||
.open = open_log_file,
|
||||
.capture_fd = capture_fd_log_file,
|
||||
.suppress_fd = NULL,
|
||||
.is_available = is_log_file_available,
|
||||
.start_line = log_file_start_line,
|
||||
.end_line = NULL,
|
||||
|
@ -22,7 +22,7 @@ import ServalDNA
|
||||
|
||||
// Logging
|
||||
|
||||
logSetup()
|
||||
servalLogSetup(minimum_level: .debug)
|
||||
|
||||
// Output
|
||||
|
||||
|
@ -19,40 +19,81 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import serval_dna.lib
|
||||
|
||||
private func serval_log(level: CInt, format: String, va_list: CVaListPointer) {
|
||||
format.withCString { CString in
|
||||
serval_vlogf(level, __whence, CString, va_list)
|
||||
}
|
||||
}
|
||||
public enum LogLevel {
|
||||
case debug
|
||||
case info
|
||||
case hint
|
||||
case warn
|
||||
case error
|
||||
case fatal
|
||||
|
||||
public func serval_log(level: CInt, text: String) {
|
||||
text.withCString { CString in
|
||||
withVaList([CString]) { va_list in
|
||||
serval_log(level: level, format: "%s", va_list: va_list)
|
||||
var rawValue : CInt {
|
||||
get {
|
||||
switch (self) {
|
||||
case .debug: return LOG_LEVEL_DEBUG
|
||||
case .info: return LOG_LEVEL_INFO
|
||||
case .hint: return LOG_LEVEL_HINT
|
||||
case .warn: return LOG_LEVEL_WARN
|
||||
case .error: return LOG_LEVEL_ERROR
|
||||
case .fatal: return LOG_LEVEL_FATAL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func serval_log_fatal(_ text: String) {
|
||||
serval_log(level: LOG_LEVEL_FATAL, text: text)
|
||||
internal var baseFilePath : String = #file
|
||||
|
||||
private func trimpath(_ path: String) -> String {
|
||||
var i = path.startIndex
|
||||
for (b, p) in zip(baseFilePath.indices, path.indices) {
|
||||
if path[p] != baseFilePath[b] {
|
||||
break;
|
||||
}
|
||||
if path[p] == "/" {
|
||||
i = path.index(after: p)
|
||||
}
|
||||
}
|
||||
return String(path[i..<path.endIndex])
|
||||
}
|
||||
|
||||
public func serval_log_error(_ text: String) {
|
||||
serval_log(level: LOG_LEVEL_ERROR, text: text)
|
||||
private func servalLog(level: LogLevel, format: String, va_list: CVaListPointer, file: String = #file, line: Int = #line, function: String = #function) {
|
||||
trimpath(file).withCString { c_file in
|
||||
function.withCString { c_function in
|
||||
format.withCString { c_format in
|
||||
serval_vlogf(level.rawValue, __sourceloc(file: c_file, line: UInt32(exactly: line) ?? 0, function: c_function), c_format, va_list)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func serval_log_warning(_ text: String) {
|
||||
serval_log(level: LOG_LEVEL_WARN, text: text)
|
||||
public func servalLog(level: LogLevel, text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||
text.withCString { c_text in
|
||||
withVaList([c_text]) { va_list in
|
||||
servalLog(level: level, format: "%s", va_list: va_list, file: file, line: line, function: function)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func serval_log_hint(_ text: String) {
|
||||
serval_log(level: LOG_LEVEL_HINT, text: text)
|
||||
public func servalLogFatal(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||
servalLog(level: .fatal, text: text, file: file, line: line, function: function)
|
||||
}
|
||||
|
||||
public func serval_log_info(_ text: String) {
|
||||
serval_log(level: LOG_LEVEL_INFO, text: text)
|
||||
public func servalLogError(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||
servalLog(level: .error, text: text, file: file, line: line, function: function)
|
||||
}
|
||||
|
||||
public func serval_log_debug(_ text: String) {
|
||||
serval_log(level: LOG_LEVEL_DEBUG, text: text)
|
||||
public func servalLogWarning(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||
servalLog(level: .warn, text: text, file: file, line: line, function: function)
|
||||
}
|
||||
|
||||
public func servalLogHint(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||
servalLog(level: .hint, text: text, file: file, line: line, function: function)
|
||||
}
|
||||
|
||||
public func servalLogInfo(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||
servalLog(level: .info, text: text, file: file, line: line, function: function)
|
||||
}
|
||||
|
||||
public func servalLogDebug(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||
servalLog(level: .debug, text: text, file: file, line: line, function: function)
|
||||
}
|
||||
|
@ -67,9 +67,17 @@ private func logPrint(_ level: CInt, _ message: UnsafePointer<CChar>?, _ overrun
|
||||
|
||||
#endif
|
||||
|
||||
public func logSetup() {
|
||||
private func logSuppress(_ fd: CInt) {
|
||||
if (fd == FileHandle.standardError.fileDescriptor) {
|
||||
serval_log_delegate.print = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func servalLogSetup(minimum_level: LogLevel = .info, baseFilePath file: String = #file) {
|
||||
baseFilePath = file
|
||||
serval_log_delegate.print = logPrint
|
||||
serval_log_delegate.minimum_level = LOG_LEVEL_DEBUG
|
||||
serval_log_delegate.suppress_fd = logSuppress
|
||||
serval_log_delegate.minimum_level = minimum_level.rawValue
|
||||
serval_log_delegate.show_prolog = 1
|
||||
#if os(iOS) || os(macOS)
|
||||
// Apple's unified logging system (iOS) and syslog (macOS) both record the
|
||||
|
4
whence.c
4
whence.c
@ -64,7 +64,9 @@ void xprint_sourceloc(XPRINTF xpf, struct __sourceloc loc)
|
||||
if (loc.function && loc.function[0]) {
|
||||
if (flag)
|
||||
xputc(':', xpf);
|
||||
xprintf(xpf, "%s()", loc.function);
|
||||
xprintf(xpf, "%s", loc.function);
|
||||
if (loc.function[strlen(loc.function) - 1] != ')')
|
||||
xputs("()", xpf);
|
||||
++flag;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user