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

📝 JSON structured logging output using yyjson More...

Go to the source code of this file.

Macros

#define LOG_JSON_JSON_BUFFER_SIZE   2048
#define LOG_JSON_ESCAPED_MESSAGE_BUFFER_SIZE   512
#define LOG_JSON_ESCAPED_FILE_BUFFER_SIZE   256
#define LOG_JSON_ESCAPED_FUNC_BUFFER_SIZE   128

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)
 Write a log entry as a JSON object to the json output fd.
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 log/json.c.

Macro Definition Documentation

◆ LOG_JSON_ESCAPED_FILE_BUFFER_SIZE

#define LOG_JSON_ESCAPED_FILE_BUFFER_SIZE   256

Definition at line 10 of file log/json.c.

Referenced by log_json_async_safe().

◆ LOG_JSON_ESCAPED_FUNC_BUFFER_SIZE

#define LOG_JSON_ESCAPED_FUNC_BUFFER_SIZE   128

Definition at line 11 of file log/json.c.

Referenced by log_json_async_safe().

◆ LOG_JSON_ESCAPED_MESSAGE_BUFFER_SIZE

#define LOG_JSON_ESCAPED_MESSAGE_BUFFER_SIZE   512

Definition at line 9 of file log/json.c.

Referenced by log_json_async_safe().

◆ LOG_JSON_JSON_BUFFER_SIZE

#define LOG_JSON_JSON_BUFFER_SIZE   2048

Definition at line 8 of file log/json.c.

Referenced by log_json_async_safe().

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 273 of file log/json.c.

273 {
274 if (fd < 0 || !message) {
275 return;
276 }
277
278 /* Format JSON manually on stack using only async-safe operations */
279 char json_buffer[LOG_JSON_JSON_BUFFER_SIZE];
280 char escaped_message[LOG_JSON_ESCAPED_MESSAGE_BUFFER_SIZE];
281 char escaped_file[LOG_JSON_ESCAPED_FILE_BUFFER_SIZE];
282 char escaped_func[LOG_JSON_ESCAPED_FUNC_BUFFER_SIZE];
283 char timestamp_buf[LOG_TIMESTAMP_BUFFER_SIZE];
284
285 /* Escape the strings (normalize file path to project-relative) */
286 json_escape_async_safe(message, escaped_message, sizeof(escaped_message));
287 if (file) {
288 const char *rel_file = extract_project_relative_path(file, ASCIICHAT_OWNED_SOURCE_DIRS);
289 json_escape_async_safe(rel_file, escaped_file, sizeof(escaped_file));
290 } else {
291 escaped_file[0] = '\0';
292 }
293 json_escape_async_safe(func ? func : "", escaped_func, sizeof(escaped_func));
294
295 /* Format timestamp using only async-safe operations */
296 uint64_t time_ns = time_get_realtime_ns();
297 format_timestamp_microseconds(time_ns, timestamp_buf, sizeof(timestamp_buf));
298
299 /* Get thread ID (note: thread_current_id is assumed to be async-safe) */
300 uint64_t tid = (uint64_t)thread_current_id();
301
302 /* Format complete JSON object with timestamp */
303 int written = snprintf(json_buffer, sizeof(json_buffer),
304 "{\"header\":{\"timestamp\":\"%s\",\"level\":\"%s\",\"tid\":%lu,"
305 "\"file\":\"%s\",\"line\":%d,\"func\":\"%s\"},"
306 "\"body\":{\"message\":\"%s\"}}\n",
307 timestamp_buf, log_level_to_string(level), (unsigned long)tid, file ? escaped_file : "", line,
308 func ? escaped_func : "", escaped_message);
309
310 /* Write to fd if successful */
311 if (written > 0 && written < (int)sizeof(json_buffer)) {
312 platform_write_all(fd, (const uint8_t *)json_buffer, (size_t)written);
313 }
314}
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
#define LOG_TIMESTAMP_BUFFER_SIZE
Maximum size of a timestamp string.
Definition log/log.h:101
size_t platform_write_all(int fd, const void *buf, size_t count)
Write all bytes to a file descriptor, handling partial writes.
const char * extract_project_relative_path(const char *file, const char *const *owned_dirs)
Extract a path relative to the source root embedded by CMake.
#define LOG_JSON_JSON_BUFFER_SIZE
Definition log/json.c:8
#define LOG_JSON_ESCAPED_MESSAGE_BUFFER_SIZE
Definition log/json.c:9
#define LOG_JSON_ESCAPED_FILE_BUFFER_SIZE
Definition log/json.c:10
#define LOG_JSON_ESCAPED_FUNC_BUFFER_SIZE
Definition log/json.c:11

References extract_project_relative_path(), LOG_JSON_ESCAPED_FILE_BUFFER_SIZE, LOG_JSON_ESCAPED_FUNC_BUFFER_SIZE, LOG_JSON_ESCAPED_MESSAGE_BUFFER_SIZE, LOG_JSON_JSON_BUFFER_SIZE, LOG_TIMESTAMP_BUFFER_SIZE, and platform_write_all().

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 )

Write a log entry as a JSON object to the json output fd.

Outputs a newline-delimited JSON (NDJSON) format log entry with the following structure:

{
"header": {
"timestamp": "14:30:45.123456",
"level": "INFO",
"tid": 12345,
"file": "lib/network/server.c",
"line": 42,
"func": "handle_client"
},
"body": {
"message": "Client connected"
}
}

The file, line, and func fields are omitted from the output when they are NULL/0 (which typically occurs in release builds).

Parameters
fdFile descriptor to write JSON to (-1 = disabled)
levelLog level (DEBUG, INFO, WARN, ERROR, FATAL)
time_nanosecondsCurrent time in nanoseconds (used to format timestamp)
fileSource file name (can be NULL)
lineLine number (can be 0)
funcFunction name (can be NULL)
messageFormatted log message
See also
log_set_json_output() - Set the JSON output file descriptor

Definition at line 104 of file log/json.c.

105 {
106 if (fd < 0 || !message) {
107 return;
108 }
109
110 /* Create root object */
111 yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
112 if (!doc) {
113 return;
114 }
115
116 yyjson_mut_val *root = yyjson_mut_obj(doc);
117 if (!root) {
118 yyjson_mut_doc_free(doc);
119 return;
120 }
121 yyjson_mut_doc_set_root(doc, root);
122
123 /* Create "header" object */
124 yyjson_mut_val *header = yyjson_mut_obj(doc);
125 if (header) {
126 yyjson_mut_obj_add_val(doc, root, "header", header);
127
128 /* Add timestamp (HH:MM:SS.microseconds) */
129 char timestamp_buf[LOG_TIMESTAMP_BUFFER_SIZE];
130 size_t ts_len = format_timestamp_microseconds(time_nanoseconds, timestamp_buf, sizeof(timestamp_buf));
131 if (ts_len > 0) {
132 yyjson_mut_obj_add_strncpy(doc, header, "timestamp", timestamp_buf, ts_len);
133 }
134
135 /* Add level */
136 yyjson_mut_obj_add_str(doc, header, "level", log_level_to_string(level));
137
138 /* Add thread ID */
139 uint64_t tid = (uint64_t)thread_current_id();
140 yyjson_mut_obj_add_uint(doc, header, "tid", tid);
141
142 /* Add file (if not NULL) - normalize to project-relative path */
143 if (file) {
144 const char *rel_file = extract_project_relative_path(file, ASCIICHAT_OWNED_SOURCE_DIRS);
145 yyjson_mut_obj_add_str(doc, header, "file", rel_file);
146 }
147
148 /* Add line (if > 0) */
149 if (line > 0) {
150 yyjson_mut_obj_add_int(doc, header, "line", line);
151 }
152
153 /* Add func (if not NULL) */
154 if (func) {
155 yyjson_mut_obj_add_str(doc, header, "func", func);
156 }
157 }
158
159 /* Create "body" object with message */
160 yyjson_mut_val *body = yyjson_mut_obj(doc);
161 if (body) {
162 yyjson_mut_obj_add_val(doc, root, "body", body);
163 yyjson_mut_obj_add_str(doc, body, "message", message);
164 }
165
166 /* Serialize to compact JSON (single line, no pretty-printing) */
167 size_t json_len = 0;
168 char *json_str = yyjson_mut_write(doc, 0, &json_len);
169
170 if (json_str && json_len > 0) {
171 // Include the delimiter in the same write so concurrent records stay on separate lines.
172 json_str[json_len] = '\n';
173 platform_write_all(fd, (const uint8_t *)json_str, json_len + 1);
174
175 /* Free serialized JSON string */
176 free(json_str);
177 }
178
179 /* Free document */
180 yyjson_mut_doc_free(doc);
181}

References extract_project_relative_path(), LOG_TIMESTAMP_BUFFER_SIZE, and platform_write_all().

Referenced by log_msg(), and log_plain_msg().