7#include <ascii-chat/common.h>
8#include <ascii-chat/log/json.h>
9#include <ascii-chat/log/logging.h>
10#include <ascii-chat/platform/abstraction.h>
11#include <ascii-chat/util/time.h>
33static size_t format_timestamp_microseconds(uint64_t time_nanoseconds,
char *buf,
size_t buf_size) {
39 time_t seconds = (time_t)(time_nanoseconds / NS_PER_SEC_INT);
40 long nanoseconds = (long)(time_nanoseconds % NS_PER_SEC_INT);
47 size_t len = strftime(buf, buf_size,
"%H:%M:%S", &tm_info);
48 if (len == 0 || len >= buf_size) {
53 long microseconds = nanoseconds / 1000;
54 if (microseconds < 0) {
57 if (microseconds > 999999) {
58 microseconds = 999999;
62 int result =
safe_snprintf(buf + len, buf_size - len,
".%06ld", microseconds);
63 if (result < 0 || result >= (
int)(buf_size - len)) {
67 return len + (size_t)result;
76static const char *log_level_to_string(log_level_t level) {
93void log_json_write(
int fd, log_level_t level, uint64_t time_nanoseconds,
const char *file,
int line,
const char *func,
94 const char *message) {
95 if (fd < 0 || !message) {
100 yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
105 yyjson_mut_val *root = yyjson_mut_obj(doc);
107 yyjson_mut_doc_free(doc);
110 yyjson_mut_doc_set_root(doc, root);
113 yyjson_mut_val *header = yyjson_mut_obj(doc);
115 yyjson_mut_obj_add_val(doc, root,
"header", header);
118 char timestamp_buf[32];
119 size_t ts_len = format_timestamp_microseconds(time_nanoseconds, timestamp_buf,
sizeof(timestamp_buf));
121 yyjson_mut_obj_add_strncpy(doc, header,
"timestamp", timestamp_buf, ts_len);
125 yyjson_mut_obj_add_str(doc, header,
"level", log_level_to_string(level));
129 yyjson_mut_obj_add_uint(doc, header,
"tid", tid);
136 yyjson_mut_obj_add_str(doc, header,
"file", rel_file);
141 yyjson_mut_obj_add_int(doc, header,
"line", line);
146 yyjson_mut_obj_add_str(doc, header,
"func", func);
151 yyjson_mut_val *body = yyjson_mut_obj(doc);
153 yyjson_mut_obj_add_val(doc, root,
"body", body);
154 yyjson_mut_obj_add_str(doc, body,
"message", message);
159 char *json_str = yyjson_mut_write(doc, 0, &json_len);
161 if (json_str && json_len > 0) {
173 yyjson_mut_doc_free(doc);
187static size_t json_escape_async_safe(
const char *src,
char *dest,
size_t dest_size) {
188 if (!src || !dest || dest_size < 2) {
193 for (
const char *p = src; *p && pos < dest_size - 1; p++) {
195 if (c ==
'"' || c ==
'\\') {
196 if (pos + 1 < dest_size - 1) {
202 }
else if (c ==
'\n') {
203 if (pos + 1 < dest_size - 1) {
209 }
else if (c ==
'\r') {
210 if (pos + 1 < dest_size - 1) {
216 }
else if (c ==
'\t') {
217 if (pos + 1 < dest_size - 1) {
223 }
else if ((
unsigned char)c < 32) {
251void log_json_async_safe(
int fd, log_level_t level,
const char *file,
int line,
const char *func,
const char *message) {
252 if (fd < 0 || !message) {
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];
264 json_escape_async_safe(message, escaped_message,
sizeof(escaped_message));
268 json_escape_async_safe(rel_file, escaped_file,
sizeof(escaped_file));
270 escaped_file[0] =
'\0';
272 json_escape_async_safe(func ? func :
"", escaped_func,
sizeof(escaped_func));
276 format_timestamp_microseconds(time_ns, timestamp_buf,
sizeof(timestamp_buf));
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);
290 if (written > 0 && written < (
int)
sizeof(json_buffer)) {
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.
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.
const char * extract_project_relative_path(const char *file)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
uint64_t asciichat_thread_current_id(void)
uint64_t time_get_realtime_ns(void)
asciichat_error_t platform_localtime(const time_t *timer, struct tm *result)
ssize_t platform_write(int fd, const void *buf, size_t count)