From 11150151f977b1a01305c128064e67f66536d26e Mon Sep 17 00:00:00 2001 From: yukkop Date: Sun, 6 Apr 2025 19:18:14 +0000 Subject: [PATCH] fix: `hectic C`: impruve logging --- package/c/hectic/hectic.c | 283 +++++++++++++++++++++++++++++--------- package/c/hectic/hectic.h | 30 +++- 2 files changed, 244 insertions(+), 69 deletions(-) diff --git a/package/c/hectic/hectic.c b/package/c/hectic/hectic.c index 20aa3b5..21709a3 100644 --- a/package/c/hectic/hectic.c +++ b/package/c/hectic/hectic.c @@ -11,6 +11,7 @@ void set_output_color_mode(ColorMode mode) { const char* log_level_to_string(LogLevel level) { switch (level) { case LOG_LEVEL_TRACE: return "TRACE"; + case LOG_LEVEL_ZALUPA: return "ZALUPA"; case LOG_LEVEL_DEBUG: return "DEBUG"; case LOG_LEVEL_LOG: return "LOG"; case LOG_LEVEL_INFO: return "INFO"; @@ -24,6 +25,7 @@ const char* log_level_to_string(LogLevel level) { const char* log_level_to_color(LogLevel level) { switch (level) { case LOG_LEVEL_TRACE: return OPTIONAL_COLOR(COLOR_GREEN); + case LOG_LEVEL_ZALUPA: return OPTIONAL_COLOR(COLOR_MAGENTA); case LOG_LEVEL_DEBUG: return OPTIONAL_COLOR(COLOR_BLUE); case LOG_LEVEL_LOG: return OPTIONAL_COLOR(COLOR_CYAN); case LOG_LEVEL_INFO: return OPTIONAL_COLOR(COLOR_GREEN); @@ -39,6 +41,8 @@ LogLevel log_level_from_string(const char *level_str) { if (!level_str) return LOG_LEVEL_INFO; if (strcmp(level_str, "TRACE") == 0) return LOG_LEVEL_TRACE; + else if (strcmp(level_str, "ZALUPA") == 0) + return LOG_LEVEL_ZALUPA; else if (strcmp(level_str, "DEBUG") == 0) return LOG_LEVEL_DEBUG; else if (strcmp(level_str, "LOG") == 0) @@ -244,11 +248,11 @@ static const char *skip_whitespace(const char *s) { } /* Parse a JSON string (does not handle full escaping) */ -static char *json_parse_string__(const char **s_ptr, Arena *arena) { +static char *json_parse_string__(const char *file, const char *func, int line, const char **s_ptr, Arena *arena) { const char *s = *s_ptr; - raise_debug("Entering json_parse_string__ at position: %p", s); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Entering json_parse_string__ at position: %p", s); if (*s != '"') { - raise_debug("Expected '\"' at start of string, got: %c", *s); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Expected '\"' at start of string, got: %c", *s); return NULL; } s++; // skip opening quote @@ -260,46 +264,46 @@ static char *json_parse_string__(const char **s_ptr, Arena *arena) { s++; } if (*s != '"') { - raise_debug("Unterminated string starting at: %p", start); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Unterminated string starting at: %p", start); return NULL; } size_t len = s - start; - char *str = arena_alloc(arena, len + 1); + char *str = arena_alloc__(file, func, line, arena, len + 1); if (!str) { - raise_debug("Memory allocation failed in json_parse_string__"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_string__"); return NULL; } memcpy(str, start, len); str[len] = '\0'; *s_ptr = s + 1; // skip closing quote - raise_debug("Parsed string: \"%s\" (length: %zu)", str, len); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsed string: \"%s\" (length: %zu)", str, len); return str; } /* Parse a number using strtod */ -static double json_parse_number__(const char **s_ptr) { - raise_debug("Parsing number at position: %p", *s_ptr); +static double json_parse_number__(const char *file, const char *func, int line, const char **s_ptr) { + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsing number at position: %p", *s_ptr); char *end; double num = strtod(*s_ptr, &end); if (*s_ptr == end) - raise_debug("No valid number found at: %p", *s_ptr); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "No valid number found at: %p", *s_ptr); *s_ptr = end; - raise_debug("Parsed number: %g", num); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsed number: %g", num); return num; } /* Forward declaration */ -static Json *json_parse_value__(const char **s, Arena *arena); +static Json *json_parse_value__(const char *file, const char *func, int line, const char **s, Arena *arena); /* Parse a JSON array: [ value, value, ... ] */ -static Json *json_parse_array__(const char **s, Arena *arena) { - raise_debug("Entering json_parse_array__ at position: %p", *s); +static Json *json_parse_array__(const char *file, const char *func, int line, const char **s, Arena *arena) { + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Entering json_parse_array__ at position: %p", *s); if (**s != '[') return NULL; (*s)++; // skip '[' *s = skip_whitespace(*s); - Json *array = arena_alloc(arena, sizeof(Json)); + Json *array = arena_alloc__(file, func, line, arena, sizeof(Json)); if (!array) { - raise_debug("Memory allocation failed in json_parse_array__"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_array__"); return NULL; } memset(array, 0, sizeof(Json)); @@ -307,13 +311,13 @@ static Json *json_parse_array__(const char **s, Arena *arena) { Json *last = NULL; if (**s == ']') { // empty array (*s)++; - raise_debug("Parsed empty array"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsed empty array"); return array; } while (**s) { - Json *element = json_parse_value__(s, arena); + Json *element = json_parse_value__(file, func, line, s, arena); if (!element) { - raise_debug("Failed to parse array element"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Failed to parse array element"); return NULL; } if (!array->child) @@ -327,25 +331,26 @@ static Json *json_parse_array__(const char **s, Arena *arena) { *s = skip_whitespace(*s); } else if (**s == ']') { (*s)++; + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Completed parsing array"); break; } else { - raise_debug("Unexpected character '%c' in array", **s); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Unexpected character '%c' in array", **s); return NULL; } } - raise_debug("Completed parsing array"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Completed parsing array"); return array; } /* Parse a JSON object: { "key": value, ... } */ -static Json *json_parse_object__(const char **s, Arena *arena) { - raise_debug("Entering json_parse_object__ at position: %p", *s); +static Json *json_parse_object__(const char *file, const char *func, int line, const char **s, Arena *arena) { + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Entering json_parse_object__ at position: %p", *s); if (**s != '{') return NULL; (*s)++; // skip '{' *s = skip_whitespace(*s); - Json *object = arena_alloc(arena, sizeof(Json)); + Json *object = arena_alloc__(file, func, line, arena, sizeof(Json)); if (!object) { - raise_debug("Memory allocation failed in json_parse_object__"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_object__"); return NULL; } memset(object, 0, sizeof(Json)); @@ -353,25 +358,25 @@ static Json *json_parse_object__(const char **s, Arena *arena) { Json *last = NULL; if (**s == '}') { (*s)++; - raise_debug("Parsed empty object"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsed empty object"); return object; } while (**s) { - char *key = json_parse_string__(s, arena); + char *key = json_parse_string__(file, func, line, s, arena); if (!key) { - raise_debug("Failed to parse key in object"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Failed to parse key in object"); return NULL; } *s = skip_whitespace(*s); if (**s != ':') { - raise_debug("Expected ':' after key \"%s\", got: %c", key, **s); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Expected ':' after key \"%s\", got: %c", key, **s); return NULL; } (*s)++; // skip ':' *s = skip_whitespace(*s); - Json *value = json_parse_value__(s, arena); + Json *value = json_parse_value__(file, func, line, s, arena); if (!value) { - raise_debug("Failed to parse value for key \"%s\"", key); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Failed to parse value for key \"%s\"", key); return NULL; } value->key = key; // assign key to the value @@ -388,37 +393,37 @@ static Json *json_parse_object__(const char **s, Arena *arena) { (*s)++; break; } else { - raise_debug("Unexpected character '%c' in object", **s); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Unexpected character '%c' in object", **s); return NULL; } } - raise_debug("Completed parsing object"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Completed parsing object"); return object; } /* Full JSON value parser */ -static Json *json_parse_value__(const char **s, Arena *arena) { +static Json *json_parse_value__(const char *file, const char *func, int line, const char **s, Arena *arena) { *s = skip_whitespace(*s); - raise_debug("Parsing JSON value at position: %p", *s); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Parsing JSON value at position: %p", *s); if (**s == '"') { - Json *item = arena_alloc(arena, sizeof(Json)); + Json *item = arena_alloc__(file, func, line, arena, sizeof(Json)); if (!item) { - raise_debug("Memory allocation failed in json_parse_value__ for string"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_value for string"); return NULL; } memset(item, 0, sizeof(Json)); item->type = JSON_STRING; - item->JsonValue.string = json_parse_string__(s, arena); + item->JsonValue.string = json_parse_string__(file, func, line, s, arena); return item; } else if (strncmp(*s, "null", 4) == 0) { - Json *item = arena_alloc(arena, sizeof(Json)); + Json *item = arena_alloc__(file, func, line, arena, sizeof(Json)); if (!item) return NULL; memset(item, 0, sizeof(Json)); item->type = JSON_NULL; *s += 4; return item; } else if (strncmp(*s, "true", 4) == 0) { - Json *item = arena_alloc(arena, sizeof(Json)); + Json *item = arena_alloc__(file, func, line, arena, sizeof(Json)); if (!item) return NULL; memset(item, 0, sizeof(Json)); item->type = JSON_BOOL; @@ -426,7 +431,7 @@ static Json *json_parse_value__(const char **s, Arena *arena) { *s += 4; return item; } else if (strncmp(*s, "false", 5) == 0) { - Json *item = arena_alloc(arena, sizeof(Json)); + Json *item = arena_alloc__(file, func, line, arena, sizeof(Json)); if (!item) return NULL; memset(item, 0, sizeof(Json)); item->type = JSON_BOOL; @@ -434,42 +439,42 @@ static Json *json_parse_value__(const char **s, Arena *arena) { *s += 5; return item; } else if ((**s == '-') || isdigit((unsigned char)**s)) { - Json *item = arena_alloc(arena, sizeof(Json)); + Json *item = arena_alloc__(file, func, line, arena, sizeof(Json)); if (!item) { - raise_debug("Memory allocation failed in json_parse_value__ for number"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_parse_value for number"); return NULL; } memset(item, 0, sizeof(Json)); item->type = JSON_NUMBER; - item->JsonValue.number = json_parse_number__(s); + item->JsonValue.number = json_parse_number__(file, func, line, s); return item; } else if (**s == '[') { - return json_parse_array__(s, arena); + return json_parse_array__(file, func, line, s, arena); } else if (**s == '{') { - return json_parse_object__(s, arena); + return json_parse_object__(file, func, line, s, arena); } - raise_debug("Unrecognized JSON value at position: %p", *s); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Unrecognized JSON value at position: %p", *s); return NULL; } -Json *json_parse(Arena *arena, const char **s) { - Json *result = json_parse_value__(s, arena); +Json *json_parse__(const char* file, const char* func, int line, Arena *arena, const char **s) { + Json *result = json_parse_value__(file, func, line, s, arena); if (!result) - raise_debug("json_parse failed at position: %p", *s); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "json_parse failed at position: %p", *s); return result; } -char *json_to_string(Arena *arena, const Json * const item) { - return json_to_string_with_opts(arena, item, JSON_NORAW); +char *json_to_string__(const char* file, const char* func, int line, Arena *arena, const Json * const item) { + return json_to_string_with_opts__(file, func, line, arena, item, JSON_NORAW); } /* Minimal JSON printer with raw output option. When raw is non-zero and the item is a JSON_STRING, it is printed without quotes. */ -char *json_to_string_with_opts(Arena *arena, const Json * const item, JsonRawOpt raw) { - char *out = arena_alloc(arena, 1024); +char *json_to_string_with_opts__(const char* file, const char* func, int line, Arena *arena, const Json * const item, JsonRawOpt raw) { + char *out = arena_alloc__(file, func, line, arena, 1024); if (!out) { - raise_debug("Memory allocation failed in json_to_string_with_opts"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_to_string_with_opts"); return NULL; } char *ptr = out; @@ -478,7 +483,7 @@ char *json_to_string_with_opts(Arena *arena, const Json * const item, JsonRawOpt Json *child = item->child; while (child) { ptr += sprintf(ptr, "\"%s\":", child->key ? child->key : ""); - char *child_str = json_to_string_with_opts(arena, child, raw); + char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw); ptr += sprintf(ptr, "%s", child_str); if (child->next) ptr += sprintf(ptr, ","); @@ -489,7 +494,7 @@ char *json_to_string_with_opts(Arena *arena, const Json * const item, JsonRawOpt ptr += sprintf(ptr, "["); Json *child = item->child; while (child) { - char *child_str = json_to_string_with_opts(arena, child, raw); + char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw); ptr += sprintf(ptr, "%s", child_str); if (child->next) ptr += sprintf(ptr, ","); @@ -508,27 +513,27 @@ char *json_to_string_with_opts(Arena *arena, const Json * const item, JsonRawOpt } else if (item->type == JSON_NULL) { sprintf(ptr, "null"); } - raise_debug("Converted JSON to string: %s", out); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Converted JSON to string: %s", out); return out; } /* Retrieve an object item by key (case-sensitive) */ -Json *json_get_object_item(const Json * const object, const char * const key) { - raise_debug("json_get_object_item: Searching for key \"%s\"", key); +Json *json_get_object_item__(const char* file, const char* func, int line, const Json * const object, const char * const key) { + raise_message(LOG_LEVEL_DEBUG, file, func, line, "json_get_object_item: Searching for key \"%s\"", key); if (!object || object->type != JSON_OBJECT) { - raise_debug("Invalid object passed to json_get_object_item"); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Invalid object passed to json_get_object_item"); return NULL; } Json *child = object->child; while (child) { - raise_debug("Comparing child key \"%s\" with \"%s\"", child->key, key); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Comparing child key \"%s\" with \"%s\"", child->key, key); if (child->key && strcmp(child->key, key) == 0) { - raise_debug("Key \"%s\" found", key); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" found", key); return child; } child = child->next; } - raise_debug("Key \"%s\" not found in object", key); + raise_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" not found in object", key); return NULL; } @@ -558,4 +563,154 @@ int* arena_slice_copy__(const char *file, const char *func, int line, Arena *are if (copy) memcpy(copy, s.data, s.len * s.isize); return copy; +} + +// ------------ +// -- debug -- +// ------------ + +char* slice_to_debug_str(Arena *arena, Slice slice) { + // Создадим полную информацию о структуре Slice + char buffer_meta[128]; + snprintf(buffer_meta, sizeof(buffer_meta), "Slice{addr=%p, data=%p, len=%zu, isize=%zu, content=", + (void*)&slice, slice.data, slice.len, slice.isize); + + size_t meta_len = strlen(buffer_meta); + + // Для NULL-данных выведем простое сообщение + if (!slice.data) { + char* result = arena_alloc(arena, meta_len + 6); + strcpy(result, buffer_meta); + strcat(result, "NULL}"); + return result; + } + + // Allocate buffer with space for quotes, metadata and null terminator + size_t buffer_size = meta_len + slice.len * 4 + 20; // Extra space for escaping and closing brace + char* buffer = arena_alloc(arena, buffer_size); + + // Копируем метаданные + strcpy(buffer, buffer_meta); + char* pos = buffer + meta_len; + + *pos++ = '"'; + + // Copy slice data with escaping + for (size_t i = 0; i < slice.len; i++) { + char c = ((char*)slice.data)[i]; + if (c == '\0') { + *pos++ = '\\'; + *pos++ = '0'; + } else if (c == '\n') { + *pos++ = '\\'; + *pos++ = 'n'; + } else if (c == '\r') { + *pos++ = '\\'; + *pos++ = 'r'; + } else if (c == '\t') { + *pos++ = '\\'; + *pos++ = 't'; + } else if (c == '"') { + *pos++ = '\\'; + *pos++ = '"'; + } else if (c == '\\') { + *pos++ = '\\'; + *pos++ = '\\'; + } else if (c < 32 || c > 126) { + // Non-printable characters as hex + pos += sprintf(pos, "\\x%02x", (unsigned char)c); + } else { + *pos++ = c; + } + } + + *pos++ = '"'; + *pos++ = '}'; // Закрывающая скобка для структуры + *pos = '\0'; + + raise_trace("slice_to_debug_str: %s", buffer); + + return buffer; +} + +char* json_to_debug_str(Arena *arena, Json json) { + // Добавляем информацию о самой структуре JSON + char meta_buffer[256]; + const char* type_str = ""; + + switch (json.type) { + case JSON_NULL: type_str = "NULL"; break; + case JSON_BOOL: type_str = "BOOL"; break; + case JSON_NUMBER: type_str = "NUMBER"; break; + case JSON_STRING: type_str = "STRING"; break; + case JSON_ARRAY: type_str = "ARRAY"; break; + case JSON_OBJECT: type_str = "OBJECT"; break; + default: type_str = "UNKNOWN"; + } + + snprintf(meta_buffer, sizeof(meta_buffer), "Json{addr=%p, type=%s, key=%s, child=%p, next=%p, value=", + (void*)&json, type_str, json.key ? json.key : "NULL", (void*)json.child, (void*)json.next); + + size_t meta_len = strlen(meta_buffer); + char value_buffer[256] = {0}; + + switch (json.type) { + case JSON_NULL: + strcpy(value_buffer, "null"); + break; + + case JSON_BOOL: + strcpy(value_buffer, json.JsonValue.boolean ? "true" : "false"); + break; + + case JSON_NUMBER: + snprintf(value_buffer, sizeof(value_buffer), "%g", json.JsonValue.number); + break; + + case JSON_STRING: { + if (!json.JsonValue.string) { + strcpy(value_buffer, "null"); + } else { + snprintf(value_buffer, sizeof(value_buffer), "\"%s\"", json.JsonValue.string); + } + break; + } + + case JSON_ARRAY: { + // Для массивов просто отметим количество элементов + size_t count = 0; + Json *item = json.child; + while (item) { + count++; + item = item->next; + } + snprintf(value_buffer, sizeof(value_buffer), "[array with %zu elements]", count); + break; + } + + case JSON_OBJECT: { + // Для объектов отметим количество пар ключ-значение + size_t count = 0; + Json *item = json.child; + while (item) { + count++; + item = item->next; + } + snprintf(value_buffer, sizeof(value_buffer), "{object with %zu key-value pairs}", count); + break; + } + + default: + strcpy(value_buffer, ""); + } + + // Создаем итоговую строку + size_t result_len = meta_len + strlen(value_buffer) + 2; // +2 для закрывающей скобки и нулевого символа + char* result = arena_alloc(arena, result_len); + + strcpy(result, meta_buffer); + strcat(result, value_buffer); + strcat(result, "}"); + + return result; } \ No newline at end of file diff --git a/package/c/hectic/hectic.h b/package/c/hectic/hectic.h index a33848a..5cf078b 100644 --- a/package/c/hectic/hectic.h +++ b/package/c/hectic/hectic.h @@ -74,12 +74,13 @@ void set_output_color_mode(ColorMode mode); typedef enum { LOG_LEVEL_TRACE, + LOG_LEVEL_ZALUPA, LOG_LEVEL_DEBUG, LOG_LEVEL_LOG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_WARN, - LOG_LEVEL_EXCEPTION + LOG_LEVEL_EXCEPTION, } LogLevel; void logger_level_reset(); @@ -138,6 +139,12 @@ char* raise_message(LogLevel level, const char *file, const char *func, int line #define raise_exception(...) raise_message(LOG_LEVEL_EXCEPTION, __FILE__, __func__, __LINE__, ##__VA_ARGS__) #endif +#if PRECOMPILED_LOG_LEVEL > LOG_LEVEL_ZALUPA +#define raise_zalupa(...) ((void)0) +#else +#define raise_zalupa(...) raise_message(LOG_LEVEL_ZALUPA, __FILE__, __func__, __LINE__, ##__VA_ARGS__) +#endif + // ----------- // -- arena -- // ----------- @@ -244,14 +251,18 @@ typedef struct Json { } JsonValue; } Json; -Json *json_parse(Arena *arena, const char **s); +#define json_parse(arena, s) json_parse__(__FILE__, __func__, __LINE__, arena, s) +Json *json_parse__(const char* file, const char* func, int line, Arena *arena, const char **s); -char *json_to_string(Arena *arena, const Json * const item); +#define json_to_string(arena, item) json_to_string__(__FILE__, __func__, __LINE__, arena, item) +char *json_to_string__(const char* file, const char* func, int line, Arena *arena, const Json * const item); -char *json_to_string_with_opts(Arena *arena, const Json * const item, JsonRawOpt raw); +#define json_to_string_with_opts(arena, item, raw) json_to_string_with_opts__(__FILE__, __func__, __LINE__, arena, item, raw) +char *json_to_string_with_opts__(const char* file, const char* func, int line, Arena *arena, const Json * const item, JsonRawOpt raw); /* Retrieve an object item by key (case-sensitive) */ -Json *json_get_object_item(const Json * const object, const char * const key); +#define json_get_object_item(object, key) json_get_object_item__(__FILE__, __func__, __LINE__, object, key) +Json *json_get_object_item__(const char* file, const char* func, int line, const Json * const object, const char * const key); // ----------- // -- Slice -- @@ -299,4 +310,13 @@ int* arena_slice_copy__(const char *file, const char *func, int line, Arena *are buf; \ }) +// Utility functions for debug output of Slice and Json structures +char* slice_to_debug_str(Arena *arena, Slice slice); +char* json_to_debug_str(Arena *arena, Json json); + +#define DEBUGSTR(arena, type, value) DEBUGSTR_##type(arena, value) + +#define DEBUGSTR_Slice(arena, value) slice_to_debug_str(arena, value) +#define DEBUGSTR_Json(arena, value) json_to_debug_str(arena, value) + #endif // EPRINTF_H \ No newline at end of file