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
22
log.c
22
log.c
@ -179,12 +179,19 @@ static void log_close(struct log_output_iterator *it)
|
|||||||
(*it->output)->close(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);
|
||||||
assert(*it->output);
|
assert(*it->output);
|
||||||
if ((*it->output)->capture_fd)
|
return (*it->output)->capture_fd ? (*it->output)->capture_fd(it, fd) : 0;
|
||||||
(*it->output)->capture_fd(it, fd, captured);
|
}
|
||||||
|
|
||||||
|
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
|
/* 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;
|
struct log_output_iterator it;
|
||||||
log_iterator_start(&it);
|
log_iterator_start(&it);
|
||||||
bool_t captured = 0;
|
bool_t captured = 0;
|
||||||
while (log_iterator_advance(&it))
|
while (!captured && log_iterator_advance(&it))
|
||||||
log_capture_fd(&it, fd, &captured);
|
captured = log_capture_fd(&it, fd);
|
||||||
|
if (captured) {
|
||||||
|
log_iterator_start(&it);
|
||||||
|
while (log_iterator_advance(&it))
|
||||||
|
log_suppress_fd(&it, fd);
|
||||||
|
}
|
||||||
return captured;
|
return captured;
|
||||||
}
|
}
|
||||||
|
13
log_output.h
13
log_output.h
@ -66,10 +66,15 @@ struct log_output {
|
|||||||
// vlogMessage() primitive, before start_line() is called.
|
// vlogMessage() primitive, before start_line() is called.
|
||||||
void (*open)(struct log_output_iterator *it);
|
void (*open)(struct log_output_iterator *it);
|
||||||
|
|
||||||
// If *capture is 0 and the output is of a persistent nature (eg, a file or
|
// If the output is of a persistent nature (eg, a file or system log) and
|
||||||
// system log) and is able to redirect data written to the given file
|
// is able to redirect data written to the given file descriptor to its
|
||||||
// descriptor to its output, then do so and set *capture to 1.
|
// output, then do so and return true, otherwise return false.
|
||||||
void (*capture_fd)(struct log_output_iterator *it, int fd, bool_t *capture);
|
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
|
// Test whether output is able to handle messages; if it returns false then
|
||||||
// start_line() and end_line() will not be invoked.
|
// 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);
|
struct log_output_console_state *state = _state(*it->output);
|
||||||
if (state->fp && state->fp != DISABLED && fileno(state->fp) == fd) {
|
if (state->fp && state->fp != DISABLED && fileno(state->fp) == fd) {
|
||||||
fflush(state->fp);
|
fflush(state->fp);
|
||||||
@ -137,7 +139,8 @@ static struct log_output static_log_output = {
|
|||||||
.show_time = log_console_show_time,
|
.show_time = log_console_show_time,
|
||||||
.state = &static_state,
|
.state = &static_state,
|
||||||
.open = open_log_console,
|
.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,
|
.is_available = is_log_console_available,
|
||||||
.start_line = log_console_start_line,
|
.start_line = log_console_start_line,
|
||||||
.end_line = log_console_end_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))
|
static void log_delegate_start_line(struct log_output_iterator *it, int UNUSED(level))
|
||||||
{
|
{
|
||||||
struct log_output_delegate_state *state = _state(*it->output);
|
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,
|
.show_time = log_delegate_show_time,
|
||||||
.state = &static_state,
|
.state = &static_state,
|
||||||
.open = log_delegate_open,
|
.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,
|
.is_available = is_log_delegate_available,
|
||||||
.start_line = log_delegate_start_line,
|
.start_line = log_delegate_start_line,
|
||||||
.end_line = log_delegate_end_line,
|
.end_line = log_delegate_end_line,
|
||||||
@ -130,5 +142,7 @@ struct log_delegate serval_log_delegate = {
|
|||||||
.show_pid = 0,
|
.show_pid = 0,
|
||||||
.show_time = 1,
|
.show_time = 1,
|
||||||
.print = NULL,
|
.print = NULL,
|
||||||
.flush = NULL
|
.flush = NULL,
|
||||||
|
.capture_fd = NULL,
|
||||||
|
.suppress_fd = NULL
|
||||||
};
|
};
|
||||||
|
@ -29,6 +29,8 @@ struct log_delegate {
|
|||||||
bool_t show_time;
|
bool_t show_time;
|
||||||
void (*print)(int level, const char *message, bool_t overrun);
|
void (*print)(int level, const char *message, bool_t overrun);
|
||||||
void (*flush)();
|
void (*flush)();
|
||||||
|
bool_t (*capture_fd)(int fd);
|
||||||
|
void (*suppress_fd)(int fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct log_delegate serval_log_delegate;
|
extern struct log_delegate serval_log_delegate;
|
||||||
|
@ -310,26 +310,27 @@ 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
|
// This outputter does not connect the file descriptor to an output file until the next log
|
||||||
// message is output.
|
// message is output.
|
||||||
_state(*it->output)->capture_fd = fd;
|
_state(*it->output)->capture_fd = fd;
|
||||||
// Ensure that the file descriptor is occupied, so that no other open() call can occupy it in
|
// Ensure that the file descriptor is occupied, so that no other open() call can occupy it in
|
||||||
// the meantime.
|
// the meantime.
|
||||||
int devnull;
|
int devnull;
|
||||||
if ((devnull = open("/dev/null", O_RDWR, 0)) == -1)
|
if ((devnull = open("/dev/null", O_RDWR, 0)) == -1)
|
||||||
WHY_perror("open(\"/dev/null\")");
|
WHY_perror("open(\"/dev/null\")");
|
||||||
else {
|
else if (devnull == fd)
|
||||||
if (devnull != fd && dup2(devnull, fd) == -1)
|
captured = 1;
|
||||||
WHYF_perror("dup2(%d, %d)", devnull, fd);
|
else {
|
||||||
else
|
if (dup2(devnull, fd) == -1)
|
||||||
*capture = 1;
|
WHYF_perror("dup2(%d, %d)", devnull, fd);
|
||||||
if (devnull != fd)
|
else
|
||||||
close(devnull);
|
captured = 1;
|
||||||
}
|
close(devnull);
|
||||||
}
|
}
|
||||||
|
return captured;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool_t is_log_file_available(const struct log_output_iterator *it)
|
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,
|
.state = &static_state,
|
||||||
.open = open_log_file,
|
.open = open_log_file,
|
||||||
.capture_fd = capture_fd_log_file,
|
.capture_fd = capture_fd_log_file,
|
||||||
|
.suppress_fd = NULL,
|
||||||
.is_available = is_log_file_available,
|
.is_available = is_log_file_available,
|
||||||
.start_line = log_file_start_line,
|
.start_line = log_file_start_line,
|
||||||
.end_line = NULL,
|
.end_line = NULL,
|
||||||
|
@ -22,7 +22,7 @@ import ServalDNA
|
|||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
|
|
||||||
logSetup()
|
servalLogSetup(minimum_level: .debug)
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
|
|
||||||
|
@ -19,40 +19,81 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||||||
|
|
||||||
import serval_dna.lib
|
import serval_dna.lib
|
||||||
|
|
||||||
private func serval_log(level: CInt, format: String, va_list: CVaListPointer) {
|
public enum LogLevel {
|
||||||
format.withCString { CString in
|
case debug
|
||||||
serval_vlogf(level, __whence, CString, va_list)
|
case info
|
||||||
}
|
case hint
|
||||||
}
|
case warn
|
||||||
|
case error
|
||||||
|
case fatal
|
||||||
|
|
||||||
public func serval_log(level: CInt, text: String) {
|
var rawValue : CInt {
|
||||||
text.withCString { CString in
|
get {
|
||||||
withVaList([CString]) { va_list in
|
switch (self) {
|
||||||
serval_log(level: level, format: "%s", va_list: va_list)
|
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) {
|
internal var baseFilePath : String = #file
|
||||||
serval_log(level: LOG_LEVEL_FATAL, text: text)
|
|
||||||
|
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) {
|
private func servalLog(level: LogLevel, format: String, va_list: CVaListPointer, file: String = #file, line: Int = #line, function: String = #function) {
|
||||||
serval_log(level: LOG_LEVEL_ERROR, text: text)
|
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) {
|
public func servalLog(level: LogLevel, text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||||
serval_log(level: LOG_LEVEL_WARN, text: text)
|
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) {
|
public func servalLogFatal(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||||
serval_log(level: LOG_LEVEL_HINT, text: text)
|
servalLog(level: .fatal, text: text, file: file, line: line, function: function)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func serval_log_info(_ text: String) {
|
public func servalLogError(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||||
serval_log(level: LOG_LEVEL_INFO, text: text)
|
servalLog(level: .error, text: text, file: file, line: line, function: function)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func serval_log_debug(_ text: String) {
|
public func servalLogWarning(_ text: String, file: String = #file, line: Int = #line, function: String = #function) {
|
||||||
serval_log(level: LOG_LEVEL_DEBUG, text: text)
|
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
|
#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.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
|
serval_log_delegate.show_prolog = 1
|
||||||
#if os(iOS) || os(macOS)
|
#if os(iOS) || os(macOS)
|
||||||
// Apple's unified logging system (iOS) and syslog (macOS) both record the
|
// 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 (loc.function && loc.function[0]) {
|
||||||
if (flag)
|
if (flag)
|
||||||
xputc(':', xpf);
|
xputc(':', xpf);
|
||||||
xprintf(xpf, "%s()", loc.function);
|
xprintf(xpf, "%s", loc.function);
|
||||||
|
if (loc.function[strlen(loc.function) - 1] != ')')
|
||||||
|
xputs("()", xpf);
|
||||||
++flag;
|
++flag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user