ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
json.c File Reference

📝 JSON structured logging output using yyjson More...

Go to the source code of this file.

Functions

void log_json_write (int fd, log_level_t level, uint64_t time_nanoseconds, const char *file, int line, const char *func, const char *message)
 
void log_json_async_safe (int fd, log_level_t level, const char *file, int line, const char *func, const char *message)
 Async-safe JSON logging for signal handlers.
 

Detailed Description

📝 JSON structured logging output using yyjson

Definition in file json.c.

Function Documentation

◆ log_json_async_safe()

void log_json_async_safe ( int  fd,
log_level_t  level,
const char *  file,
int  line,
const char *  func,
const char *  message 
)

Async-safe JSON logging for signal handlers.

This function formats and writes JSON logs using ONLY async-safe operations:

  • snprintf for string formatting
  • write() for output
  • No allocations, no locks, no library calls

Suitable for calling from signal handlers (SIGTERM, SIGINT, etc.)

Parameters
fdFile descriptor to write to (typically STDOUT_FILENO or STDERR_FILENO)
levelLog level
fileSource file name (may be NULL)
lineSource line number
funcFunction name (may be NULL)
messageLog message (must not be NULL)

Definition at line 251 of file json.c.

251 {
252 if (fd < 0 || !message) {
253 return;
254 }
255
256 /* Format JSON manually on stack using only async-safe operations */
257 char json_buffer[2048];
258 char escaped_message[512];
259 char escaped_file[256];
260 char escaped_func[128];
261 char timestamp_buf[32];
262
263 /* Escape the strings (normalize file path to project-relative) */
264 json_escape_async_safe(message, escaped_message, sizeof(escaped_message));
265 if (file) {
266 extern const char *extract_project_relative_path(const char *file);
267 const char *rel_file = extract_project_relative_path(file);
268 json_escape_async_safe(rel_file, escaped_file, sizeof(escaped_file));
269 } else {
270 escaped_file[0] = '\0';
271 }
272 json_escape_async_safe(func ? func : "", escaped_func, sizeof(escaped_func));
273
274 /* Format timestamp using only async-safe operations */
275 uint64_t time_ns = time_get_realtime_ns();
276 format_timestamp_microseconds(time_ns, timestamp_buf, sizeof(timestamp_buf));
277
278 /* Get thread ID (note: asciichat_thread_current_id is assumed to be async-safe) */
279 uint64_t tid = (uint64_t)asciichat_thread_current_id();
280
281 /* Format complete JSON object with timestamp */
282 int written = snprintf(json_buffer, sizeof(json_buffer),
283 "{\"header\":{\"timestamp\":\"%s\",\"level\":\"%s\",\"tid\":%" PRIu64
284 ",\"file\":\"%s\",\"line\":%d,\"func\":\"%s\"},"
285 "\"body\":{\"message\":\"%s\"}}\n",
286 timestamp_buf, log_level_to_string(level), tid, file ? escaped_file : "", line,
287 func ? escaped_func : "", escaped_message);
288
289 /* Write to fd if successful */
290 if (written > 0 && written < (int)sizeof(json_buffer)) {
291 platform_write_all(fd, (const uint8_t *)json_buffer, (size_t)written);
292 }
293}
size_t platform_write_all(int fd, const void *buf, size_t count)
Write all data to file descriptor with automatic retry on transient errors.
Definition abstraction.c:39
const char * extract_project_relative_path(const char *file)
Definition path.c:410
uint64_t asciichat_thread_current_id(void)
Definition threading.c:84
uint64_t time_get_realtime_ns(void)
Definition util/time.c:59

References asciichat_thread_current_id(), extract_project_relative_path(), platform_write_all(), and time_get_realtime_ns().

Referenced by log_console_impl().

◆ log_json_write()

void log_json_write ( int  fd,
log_level_t  level,
uint64_t  time_nanoseconds,
const char *  file,
int  line,
const char *  func,
const char *  message 
)

Definition at line 93 of file json.c.

94 {
95 if (fd < 0 || !message) {
96 return;
97 }
98
99 /* Create root object */
100 yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
101 if (!doc) {
102 return;
103 }
104
105 yyjson_mut_val *root = yyjson_mut_obj(doc);
106 if (!root) {
107 yyjson_mut_doc_free(doc);
108 return;
109 }
110 yyjson_mut_doc_set_root(doc, root);
111
112 /* Create "header" object */
113 yyjson_mut_val *header = yyjson_mut_obj(doc);
114 if (header) {
115 yyjson_mut_obj_add_val(doc, root, "header", header);
116
117 /* Add timestamp (HH:MM:SS.microseconds) */
118 char timestamp_buf[32];
119 size_t ts_len = format_timestamp_microseconds(time_nanoseconds, timestamp_buf, sizeof(timestamp_buf));
120 if (ts_len > 0) {
121 yyjson_mut_obj_add_strncpy(doc, header, "timestamp", timestamp_buf, ts_len);
122 }
123
124 /* Add level */
125 yyjson_mut_obj_add_str(doc, header, "level", log_level_to_string(level));
126
127 /* Add thread ID */
128 uint64_t tid = (uint64_t)asciichat_thread_current_id();
129 yyjson_mut_obj_add_uint(doc, header, "tid", tid);
130
131 /* Add file (if not NULL) - normalize to project-relative path */
132 if (file) {
133 // Import from path.h for relative path extraction
134 extern const char *extract_project_relative_path(const char *file);
135 const char *rel_file = extract_project_relative_path(file);
136 yyjson_mut_obj_add_str(doc, header, "file", rel_file);
137 }
138
139 /* Add line (if > 0) */
140 if (line > 0) {
141 yyjson_mut_obj_add_int(doc, header, "line", line);
142 }
143
144 /* Add func (if not NULL) */
145 if (func) {
146 yyjson_mut_obj_add_str(doc, header, "func", func);
147 }
148 }
149
150 /* Create "body" object with message */
151 yyjson_mut_val *body = yyjson_mut_obj(doc);
152 if (body) {
153 yyjson_mut_obj_add_val(doc, root, "body", body);
154 yyjson_mut_obj_add_str(doc, body, "message", message);
155 }
156
157 /* Serialize to compact JSON (single line, no pretty-printing) */
158 size_t json_len = 0;
159 char *json_str = yyjson_mut_write(doc, 0, &json_len);
160
161 if (json_str && json_len > 0) {
162 /* Write JSON string */
163 platform_write(fd, (const uint8_t *)json_str, json_len);
164
165 /* Write newline for NDJSON format */
166 platform_write(fd, (const uint8_t *)"\n", 1);
167
168 /* Free serialized JSON string */
169 free(json_str);
170 }
171
172 /* Free document */
173 yyjson_mut_doc_free(doc);
174}
ssize_t platform_write(int fd, const void *buf, size_t count)

References asciichat_thread_current_id(), extract_project_relative_path(), and platform_write().

Referenced by log_msg(), and log_plain_msg().