ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
io.h File Reference

Capture stdout/stderr and redirect to logging system. More...

Go to the source code of this file.

Data Structures

struct  log_io_t
 Saved descriptors and temporary file used during stdout/stderr capture. More...

Macros

#define LOG_IO_IF(condition, prefix, block)
 Capture output from a code block and log it.
#define LOG_IO(prefix, block)

Functions

log_io_t log_io_start (void)
 Start capturing stdout and stderr.
void log_io_stop (log_io_t capture, const char *prefix)
 Stop capturing and log the accumulated output.

Detailed Description

Capture stdout/stderr and redirect to logging system.

Provides functions and macros to capture stdout and stderr output and redirect it to the logging system instead of suppressing it.

Usage: // Capture specific code: log_io_t capture = log_io_start(); ffmpeg_function_that_prints_stuff(); log_io_stop(capture, "ffmpeg");

// Capture a code block (recommended): LOG_IO("ffmpeg", { ffmpeg_function_that_prints_stuff(); });

Definition in file io.h.

Macro Definition Documentation

◆ LOG_IO

#define LOG_IO ( prefix,
block )
Value:
LOG_IO_IF(true, prefix, block)
#define LOG_IO_IF(condition, prefix, block)
Capture output from a code block and log it.
Definition io.h:93

Definition at line 104 of file io.h.

Referenced by audio_start_duplex(), audio_stop_duplex(), audio_terminate_portaudio_final(), ffmpeg_decoder_create_stdin(), ffmpeg_decoder_create_with_request_options(), gpg_sign_with_key(), h265_decoder_create(), h265_encoder_destroy(), and yt_dlp_is_available().

◆ LOG_IO_IF

#define LOG_IO_IF ( condition,
prefix,
block )
Value:
do { \
bool _log_io_enabled = (condition); \
log_io_t _log_io = {0}; \
if (_log_io_enabled) \
_log_io = log_io_start(); \
block; \
if (_log_io_enabled) \
log_io_stop(_log_io, (prefix)); \
} while (0)
log_io_t log_io_start(void)
Start capturing stdout and stderr.
Definition io.c:15
Saved descriptors and temporary file used during stdout/stderr capture.
Definition io.h:37

Capture output from a code block and log it.

This macro redirects stdout and stderr for the duration of the code block, then logs any captured output with the given prefix.

Example: LOG_IO("ffmpeg", { av_log_set_callback(NULL); // Use default ffmpeg logging process_file("input.mp4"); });

Parameters
prefixPrefix for log messages (const char *)
blockCode block to capture (must be valid C statement block)
Note
Prefix and block are only evaluated once
Variables in the enclosing scope are accessible in the block

Capture a block only when condition is true; execute the block once either way.

Note
The condition is evaluated once, before executing the block.

Definition at line 93 of file io.h.

93#define LOG_IO_IF(condition, prefix, block) \
94 do { \
95 bool _log_io_enabled = (condition); \
96 log_io_t _log_io = {0}; \
97 if (_log_io_enabled) \
98 _log_io = log_io_start(); \
99 block; \
100 if (_log_io_enabled) \
101 log_io_stop(_log_io, (prefix)); \
102 } while (0)

Referenced by yt_dlp_extract_with_options().

Function Documentation

◆ log_io_start()

log_io_t log_io_start ( void )

Start capturing stdout and stderr.

Redirects both stdout and stderr to a temporary file so their output can be captured and logged. Call log_io_stop() to end capture and log the accumulated output.

Returns
Capture handle (use with log_io_stop)
Note
If capture fails, returns a handle with saved_stdout_fd = -1
Do not use stdout/stderr directly while capturing

Definition at line 15 of file io.c.

15 {
16 log_io_t capture = {.saved_stdout_fd = -1, .saved_stderr_fd = -1, .output = NULL};
17
18 // A pipe cannot be drained until the wrapped call returns. A temporary file
19 // lets backend worker threads finish even when diagnostics exceed pipe capacity.
20 capture.output = platform_tmpfile();
21 if (!capture.output)
22 return capture;
23
24 fflush(stdout);
25 fflush(stderr);
26 capture.saved_stdout_fd = platform_dup(STDOUT_FILENO);
27 capture.saved_stderr_fd = platform_dup(STDERR_FILENO);
28 if (capture.saved_stdout_fd < 0 || capture.saved_stderr_fd < 0)
29 goto fail;
30
31 int output_fd = platform_fileno(capture.output);
32 if (platform_dup2(output_fd, STDOUT_FILENO) != ASCIICHAT_OK)
33 goto fail;
34 if (platform_dup2(output_fd, STDERR_FILENO) != ASCIICHAT_OK) {
35 // Restore stdout if only the first redirection succeeded.
36 platform_dup2(capture.saved_stdout_fd, STDOUT_FILENO);
37 goto fail;
38 }
39 return capture;
40
41fail:
42 if (capture.saved_stdout_fd >= 0)
43 platform_close(capture.saved_stdout_fd);
44 if (capture.saved_stderr_fd >= 0)
45 platform_close(capture.saved_stderr_fd);
46 fclose(capture.output);
47 return (log_io_t){.saved_stdout_fd = -1, .saved_stderr_fd = -1, .output = NULL};
48}
@ ASCIICHAT_OK
Definition error_codes.h:46
asciichat_error_t platform_dup2(int source_fd, int target_fd)
int platform_fileno(FILE *stream)
int platform_dup(int fd)
FILE * output
Temporary capture file, deleted on close.
Definition io.h:40
int saved_stdout_fd
Saved stdout file descriptor.
Definition io.h:38
int saved_stderr_fd
Saved stderr file descriptor.
Definition io.h:39

References ASCIICHAT_OK, log_io_t::output, platform_dup(), platform_dup2(), platform_fileno(), log_io_t::saved_stderr_fd, and log_io_t::saved_stdout_fd.

◆ log_io_stop()

void log_io_stop ( log_io_t capture,
const char * prefix )

Stop capturing and log the accumulated output.

Restores stdout and stderr to their original state, reads all captured output from the temporary file, and logs it at DEBUG level with the given prefix.

Parameters
captureCapture handle returned by log_io_start()
prefixPrefix to use in log messages (e.g., "ffmpeg", "portaudio")
Note
Safe to call with invalid handle (saved_stdout_fd = -1)
After calling this, the capture handle is invalidated

Definition at line 50 of file io.c.

50 {
51 if (capture.saved_stdout_fd < 0 || capture.saved_stderr_fd < 0) {
52 return; // Capture was not started successfully
53 }
54
55 // Flush before restoring so buffered backend output is captured.
56 fflush(stdout);
57 fflush(stderr);
58
59 // Restore stdout and stderr
60 platform_dup2(capture.saved_stdout_fd, STDOUT_FILENO);
61 platform_dup2(capture.saved_stderr_fd, STDERR_FILENO);
62 platform_close(capture.saved_stdout_fd);
63 platform_close(capture.saved_stderr_fd);
64
65 // Read and log the captured output
66 if (capture.output) {
67 char buffer[LOG_IO_BUFFER_SIZE];
68 size_t bytes_read;
69
70 // Replay only after restoring the descriptors, so logger output cannot feed
71 // back into this capture. fseek synchronizes the stream with descriptor writes.
72 if (fseek(capture.output, 0, SEEK_SET) != 0) {
73 fclose(capture.output);
74 return;
75 }
76 while ((bytes_read = fread(buffer, 1, sizeof(buffer) - 1, capture.output)) > 0) {
77 buffer[bytes_read] = '\0';
78
79 // Split by lines and log each line
80 char *line_start = buffer;
81 char *line_end;
82
83 while ((line_end = strchr(line_start, '\n')) != NULL) {
84 // Null-terminate the line (overwriting the newline)
85 *line_end = '\0';
86
87 // Log the line if it's not empty
88 if (line_start[0] != '\0') {
89 if (prefix) {
90 log_debug("[%s] %s", prefix, line_start);
91 } else {
92 log_debug("%s", line_start);
93 }
94 }
95
96 // Move to the next line
97 line_start = line_end + 1;
98 }
99
100 // Log any remaining partial line
101 if (line_start[0] != '\0') {
102 if (prefix) {
103 log_debug("[%s] %s", prefix, line_start);
104 } else {
105 log_debug("%s", line_start);
106 }
107 }
108 }
109
110 fclose(capture.output);
111 }
112}
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
#define LOG_IO_BUFFER_SIZE
Definition io.c:13

References log_debug, LOG_IO_BUFFER_SIZE, log_io_t::output, platform_dup2(), log_io_t::saved_stderr_fd, and log_io_t::saved_stdout_fd.