ascii-chat 0.11.33
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
 Opaque handle for IO capture state. More...
 

Macros

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

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:
do { \
log_io_t _log_io = log_io_start(); \
block; \
log_io_stop(_log_io, (prefix)); \
} while (0)
log_io_t log_io_start(void)
Start capturing stdout and stderr.
Definition io.c:15
Opaque handle for IO capture state.
Definition io.h:29

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

Definition at line 81 of file io.h.

82 { \
83 log_io_t _log_io = log_io_start(); \
84 block; \
85 log_io_stop(_log_io, (prefix)); \
86 } while (0)

Function Documentation

◆ log_io_start()

log_io_t log_io_start ( void  )

Start capturing stdout and stderr.

Redirects both stdout and stderr to a pipe 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, .pipe_fd = -1};
17
18 // Save original stdout and stderr
19 capture.saved_stdout_fd = dup(STDOUT_FILENO);
20 capture.saved_stderr_fd = dup(STDERR_FILENO);
21
22 if (capture.saved_stdout_fd < 0 || capture.saved_stderr_fd < 0) {
23 if (capture.saved_stdout_fd >= 0)
24 close(capture.saved_stdout_fd);
25 if (capture.saved_stderr_fd >= 0)
26 close(capture.saved_stderr_fd);
27 return (log_io_t){.saved_stdout_fd = -1, .saved_stderr_fd = -1, .pipe_fd = -1};
28 }
29
30 // Create a pipe for capturing output
31 int pipefd[2];
32#ifdef _WIN32
33 if (_pipe(pipefd, LOG_IO_BUFFER_SIZE, _O_BINARY) < 0) {
34#else
35 if (pipe(pipefd) < 0) {
36#endif
37 close(capture.saved_stdout_fd);
38 close(capture.saved_stderr_fd);
39 return (log_io_t){.saved_stdout_fd = -1, .saved_stderr_fd = -1, .pipe_fd = -1};
40 }
41
42 // pipefd[0] is read end, pipefd[1] is write end
43 int write_fd = pipefd[1];
44 capture.pipe_fd = pipefd[0];
45
46#ifndef _WIN32
47 // Make the read end non-blocking so we can drain without hanging
48 int flags = fcntl(capture.pipe_fd, F_GETFL, 0);
49 if (flags >= 0) {
50 fcntl(capture.pipe_fd, F_SETFL, flags | O_NONBLOCK);
51 }
52
53 // Make the write end non-blocking. LOG_IO redirects the process-global stderr,
54 // so ALL threads write to this pipe. If the pipe buffer fills (64KB on Linux),
55 // blocking writes would stall other threads and potentially deadlock (e.g. x265
56 // internal threads block on stderr while avcodec_open2 can't return). Non-blocking
57 // writes silently drop excess output (EAGAIN) which is acceptable for diagnostics.
58 int wflags = fcntl(write_fd, F_GETFL, 0);
59 if (wflags >= 0) {
60 fcntl(write_fd, F_SETFL, wflags | O_NONBLOCK);
61 }
62#endif
63
64 // Redirect both stdout and stderr to the pipe
65 if (dup2(write_fd, STDOUT_FILENO) < 0 || dup2(write_fd, STDERR_FILENO) < 0) {
66 close(write_fd);
67 close(capture.pipe_fd);
68 close(capture.saved_stdout_fd);
69 close(capture.saved_stderr_fd);
70 return (log_io_t){.saved_stdout_fd = -1, .saved_stderr_fd = -1, .pipe_fd = -1};
71 }
72
73 // Close the write end in the parent process (we only need it in redirected streams)
74 close(write_fd);
75
76 return capture;
77}
#define LOG_IO_BUFFER_SIZE
Definition io.c:13
int saved_stdout_fd
Saved stdout file descriptor.
Definition io.h:30
int pipe_fd
Read end of pipe for captured output.
Definition io.h:32
int saved_stderr_fd
Saved stderr file descriptor.
Definition io.h:31

References LOG_IO_BUFFER_SIZE, log_io_t::pipe_fd, 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 pipe, and logs it at INFO 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 79 of file io.c.

79 {
80 if (capture.saved_stdout_fd < 0 || capture.saved_stderr_fd < 0) {
81 return; // Capture was not started successfully
82 }
83
84 // Flush stdout/stderr BEFORE restoring to ensure all output is in the pipe
85 fflush(stdout);
86 fflush(stderr);
87
88 // Restore stdout and stderr
89 dup2(capture.saved_stdout_fd, STDOUT_FILENO);
90 dup2(capture.saved_stderr_fd, STDERR_FILENO);
91 close(capture.saved_stdout_fd);
92 close(capture.saved_stderr_fd);
93
94 // Read and log the captured output
95 if (capture.pipe_fd >= 0) {
96 char buffer[LOG_IO_BUFFER_SIZE];
97 ssize_t bytes_read;
98
99 // Read all available data from the pipe
100 while ((bytes_read = read(capture.pipe_fd, buffer, sizeof(buffer) - 1)) > 0) {
101 buffer[bytes_read] = '\0';
102
103 // Split by lines and log each line
104 char *line_start = buffer;
105 char *line_end;
106
107 while ((line_end = strchr(line_start, '\n')) != NULL) {
108 // Null-terminate the line (overwriting the newline)
109 *line_end = '\0';
110
111 // Log the line if it's not empty
112 if (line_start[0] != '\0') {
113 if (prefix) {
114 log_debug("[%s] %s", prefix, line_start);
115 } else {
116 log_debug("%s", line_start);
117 }
118 }
119
120 // Move to the next line
121 line_start = line_end + 1;
122 }
123
124 // Log any remaining partial line
125 if (line_start[0] != '\0') {
126 if (prefix) {
127 log_debug("[%s] %s", prefix, line_start);
128 } else {
129 log_debug("%s", line_start);
130 }
131 }
132 }
133
134 close(capture.pipe_fd);
135 }
136}
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548

References log_debug, LOG_IO_BUFFER_SIZE, log_io_t::pipe_fd, log_io_t::saved_stderr_fd, and log_io_t::saved_stdout_fd.