diff --git a/package/c/hectic/docs/logging.md b/package/c/hectic/docs/logging.md new file mode 100755 index 0000000..205b6dd --- /dev/null +++ b/package/c/hectic/docs/logging.md @@ -0,0 +1,153 @@ +# Hectic Library Logging System + +This document provides guidelines on how to use the logging system in the Hectic library effectively. + +## Log Levels + +The Hectic library implements a graduated severity-based logging system with the following levels (from lowest to highest severity): + +### TRACE +**Purpose**: Most detailed information for in-depth debugging +- Use for: Deep diagnostic details, function entry/exit, variable dumps +- Visibility: Development environments only +- Performance Impact: High +- Example: `raise_trace("Entering function with value=%d, ptr=%p", value, ptr);` + +### DEBUG +**Purpose**: Detailed information useful during development +- Use for: Development-time debugging, showing variable states, internal flows +- Visibility: Development and debugging environments +- Performance Impact: Medium-High +- Example: `raise_debug("Buffer allocated with size %zu bytes at %p", size, buffer);` + +### LOG +**Purpose**: General operational events +- Use for: Runtime events worth logging but not requiring attention +- Visibility: Always written to logs, useful for auditing/diagnostics +- Performance Impact: Medium +- Example: `raise_log("Processing file %s, size: %zu bytes", filename, file_size);` + +### INFO +**Purpose**: Informational messages highlighting progress +- Use for: Normal but noteworthy events, state changes, startup/shutdown events +- Visibility: Visible to client applications if configured +- Performance Impact: Low-Medium +- Example: `raise_info("Connection established to %s, session ID: %s", host, session_id);` + +### NOTICE +**Purpose**: More important events than INFO, but not warnings +- Use for: Important state changes, significant operations, configuration changes +- Visibility: Displayed to client by default +- Performance Impact: Low +- Example: `raise_notice("Switching to backup server due to high load");` + +### WARN +**Purpose**: Potential problems that don't prevent normal operation +- Use for: Unexpected behaviors, deprecated feature usage, recoverable errors +- Visibility: Alerts both client and server logs +- Performance Impact: Low +- Example: `raise_warn("API call retry limit (%d) reached for endpoint %s", retries, endpoint);` + +### EXCEPTION +**Purpose**: Serious errors requiring immediate attention +- Use for: Critical failures, data loss risks, business rule violations +- Visibility: Highest priority, often leads to operation termination +- Performance Impact: Low +- Example: `raise_exception("Failed to open database: %s", error_msg);` + +## Setting the Log Level + +You can control the verbosity of logs in three ways: + +1. **Environment Variable**: Set `LOG_LEVEL` environment variable + ```sh + export LOG_LEVEL=DEBUG + ``` + +2. **Programmatically**: Use the `logger_level()` function + ```c + logger_level(LOG_LEVEL_DEBUG); + ``` + +3. **Compile-Time**: Define `PRECOMPILED_LOG_LEVEL` before including hectic.h + ```c + #define PRECOMPILED_LOG_LEVEL LOG_LEVEL_INFO + #include + ``` + +## Logging Best Practices + +### DO + +- **Be specific and concise**: Include relevant details but avoid verbose descriptions +- **Include context**: Add identifiers (IDs, filenames, pointers) to help with troubleshooting +- **Use the appropriate level**: Understand the purpose of each level and use it accordingly +- **Log state transitions**: Important changes in application state should be logged +- **Use structured data**: When possible, include structured information rather than unformatted text + +### DON'T + +- **Log sensitive information**: Never log passwords, tokens, or personal information +- **Overuse high-severity levels**: Reserve WARN and EXCEPTION for real issues +- **Log in tight loops**: Avoid excessive logging in performance-critical paths +- **Use inconsistent formats**: Follow a consistent message format throughout your code +- **Ignore log levels**: Don't use DEBUG for important operational events or EXCEPTION for minor issues + +## Examples of Good Logging + +### Error Handling Pattern + +```c +void *allocate_resource(size_t size) { + raise_debug("Allocating %zu bytes", size); + + void *ptr = malloc(size); + if (!ptr) { + raise_exception("Memory allocation failed for %zu bytes", size); + return NULL; + } + + raise_debug("Successfully allocated %zu bytes at %p", size, ptr); + return ptr; +} +``` + +### State Transition Pattern + +```c +void change_connection_state(Connection *conn, ConnState new_state) { + raise_debug("Connection %p state change requested: %s -> %s", + conn, conn_state_to_string(conn->state), conn_state_to_string(new_state)); + + if (!is_valid_transition(conn->state, new_state)) { + raise_warn("Invalid state transition from %s to %s", + conn_state_to_string(conn->state), conn_state_to_string(new_state)); + return; + } + + ConnState old_state = conn->state; + conn->state = new_state; + + raise_info("Connection %s state changed: %s -> %s", + conn->id, conn_state_to_string(old_state), conn_state_to_string(new_state)); +} +``` + +### Operation Pattern + +```c +int process_file(const char *filename) { + raise_log("Processing file: %s", filename); + + FILE *f = fopen(filename, "r"); + if (!f) { + raise_warn("Could not open file %s: %s", filename, strerror(errno)); + return -1; + } + + // Process file... + + raise_info("Successfully processed file %s: %d records", filename, record_count); + return 0; +} +``` \ No newline at end of file diff --git a/package/c/hectic/docs/logging_audit.md b/package/c/hectic/docs/logging_audit.md new file mode 100755 index 0000000..16e4078 --- /dev/null +++ b/package/c/hectic/docs/logging_audit.md @@ -0,0 +1,205 @@ +# Logging Audit Guide for Hectic + +This guide provides a systematic approach to auditing and improving logging in existing functions of the Hectic library. + +## Checklist for Function Audit + +### 1. Basic Logging Check + +- [ ] Function has entry logging (DEBUG or TRACE level) +- [ ] Function has result/exit logging +- [ ] Failures and errors are logged with appropriate levels (WARN or EXCEPTION) +- [ ] Intermediate steps are logged at TRACE or DEBUG level +- [ ] Successful operation completions are logged at LOG level + +### 2. Level Consistency Check + +- [ ] `LOG_LEVEL_TRACE` is used for detailed execution tracking +- [ ] `LOG_LEVEL_DEBUG` is used for significant internal steps +- [ ] `LOG_LEVEL_LOG` is used for important operational events and successful operation completions +- [ ] `LOG_LEVEL_INFO` is used *rarely*, only for user-critical events +- [ ] `LOG_LEVEL_NOTICE` is used *very rarely*, almost not needed for a low-level library +- [ ] `LOG_LEVEL_WARN` is used for recoverable problems +- [ ] `LOG_LEVEL_EXCEPTION` is used only for critical errors + +### 3. Message Formatting Check + +- [ ] Messages start with a domain prefix (PARSE:, ALLOC:, FORMAT:, etc.) +- [ ] Message structure follows the pattern "Action: object (details)" +- [ ] Messages contain sufficient context for understanding (pointers, sizes, values) +- [ ] Messages don't contain redundant information +- [ ] Pointers use %p format +- [ ] Sizes use %zu format +- [ ] Strings use length limitation when necessary (%.20s) +- [ ] NULL pointer checks are added before using pointers in logs + +## Logging Level Usage Rules + +### TRACE +- Function calls and exits +- Detailed loop iteration information +- Variable values during execution +- Any detailed debugging information + +### DEBUG +- Entries to public API functions +- Resource allocation and deallocation +- Key algorithm steps +- Data processing details + +### LOG +- Successful component initialization +- Completion of significant operations +- State changes important for operation +- Key business logic points + +### INFO (use rarely!) +- Library startup and shutdown +- Version and configuration information +- Extremely important events visible to users +- Large operations requested by users + +### NOTICE (almost never use) +- Events that users should pay attention to +- Significant planned actions + +### WARN +- Unexpected but handled errors +- Edge cases +- Warnings about potential problems +- Use of deprecated APIs + +### EXCEPTION +- Serious errors affecting system operation +- Data integrity violations +- Resource exhaustion +- Critical security failures + +## Audit and Update Process + +### Step 1: Function Analysis + +1. Determine the function type: + - Initialization/resource management function + - Data processing function + - Utility function + - Other + +2. Identify key logging points: + - Function entry + - Parameter checks + - Main processing steps + - Error conditions + - Function exit + +### Step 2: Update Planning + +1. Create a list of necessary logs at each level +2. Determine the right prefixes for each message type +3. Prepare detailed messages with necessary context +4. Ensure parameter checks are logged before use + +### Step 3: Implementing Changes + +1. Add/update function entry logging +2. Add/update input parameter checks +3. Update intermediate step logging +4. Add/update error condition logging +5. Add/update result logging + +### Step 4: Testing + +1. Check message output correctness +2. Verify proper level usage +3. Check that all necessary information is included in logs +4. Compare with other already updated functions for consistency + +## Examples for Typical Functions + +### Memory Allocation Function: + +```c +void* memory_function__(const char *file, const char *func, int line, size_t size) { + // Function entry + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "ALLOC: Requesting memory allocation (size: %zu bytes)", size); + + // Parameter check + if (size == 0) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "ALLOC: Zero-sized memory allocation requested"); + return NULL; + } + + // Memory allocation + void *ptr = malloc(size); + if (!ptr) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "ALLOC: Memory allocation failed (requested: %zu bytes)", size); + return NULL; + } + + // Result + raise_message(LOG_LEVEL_LOG, file, func, line, + "ALLOC: Memory allocated successfully (address: %p, size: %zu bytes)", + ptr, size); + return ptr; +} +``` + +### Data Conversion Function: + +```c +char* convert_function__(const char *file, const char *func, int line, + const void *input, size_t input_size) { + // Function entry + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "CONVERT: Starting data conversion (input: %p, size: %zu)", + input, input_size); + + // Parameter check + if (!input) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "CONVERT: NULL input provided"); + return NULL; + } + + if (input_size == 0) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "CONVERT: Zero input size provided"); + return NULL; + } + + // Processing start + raise_message(LOG_LEVEL_TRACE, file, func, line, + "CONVERT: Processing input data..."); + + // Processing... + + // Result + char *result = NULL; // conversion result + if (!result) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "CONVERT: Conversion failed"); + return NULL; + } + + raise_message(LOG_LEVEL_LOG, file, func, line, + "CONVERT: Data conversion completed successfully (result: %p)", result); + return result; +} +``` + +## Update Priorities + +Recommended order for updating functions: + +1. Memory management functions (`arena_*`) +2. Parsing functions (`json_parse_*`) +3. Formatting functions (`json_to_string_*`) +4. Data processing functions (`slice_*`) +5. Utility functions + +## Conclusion + +Consistent application of these recommendations to functions in the library will significantly improve logging quality and facilitate debugging and code maintenance in the future. \ No newline at end of file diff --git a/package/c/hectic/docs/logging_improvement_plan.md b/package/c/hectic/docs/logging_improvement_plan.md new file mode 100755 index 0000000..896c82b --- /dev/null +++ b/package/c/hectic/docs/logging_improvement_plan.md @@ -0,0 +1,285 @@ +# Logging System Improvement Plan for Hectic Library + +## Current Issues + +After analyzing the logging in the hectic library, the following issues have been identified: + +1. **Inconsistent message formatting** - messages are formatted differently in various parts of the code +2. **Inefficient use of logging levels** - levels don't always correspond to the nature of the message +3. **Incomplete logging** - some important operations aren't logged at all +4. **Lack of context** - messages don't always contain necessary context for debugging +5. **Mixed formats** - no unified style for logging different types of data + +## Standardizing Logging by Function Types + +### 1. Initialization and Resource Management Functions + +**Logging pattern:** + +1. `LOG_LEVEL_DEBUG` when entering a function with parameters +2. `LOG_LEVEL_DEBUG` when performing significant steps within the function +3. `LOG_LEVEL_LOG` on successful resource allocation/initialization +4. `LOG_LEVEL_WARN` for warning conditions +5. `LOG_LEVEL_EXCEPTION` for critical errors + +**Example (arena_init):** + +```c +Arena arena_init__(const char *file, const char *func, int line, size_t size) { + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "Initializing arena with size %zu bytes", size); + + Arena arena; + arena.begin = malloc(size); + + if (!arena.begin) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "Failed to allocate memory for arena (requested %zu bytes)", size); + exit(1); + } + + memset(arena.begin, 0, size); + arena.current = arena.begin; + arena.capacity = size; + + raise_message(LOG_LEVEL_LOG, file, func, line, + "Arena initialized: address=%p, capacity=%zu bytes", arena.begin, size); + return arena; +} +``` + +### 2. Data Processing Functions (parsers, serializers) + +**Logging pattern:** + +1. `LOG_LEVEL_DEBUG` when entering a function with main parameters +2. `LOG_LEVEL_TRACE` for detailed tracking of parsing/serialization steps +3. `LOG_LEVEL_DEBUG` when discovering intermediate results +4. `LOG_LEVEL_WARN` for recoverable format issues +5. `LOG_LEVEL_EXCEPTION` for unrecoverable format errors +6. `LOG_LEVEL_LOG` on successful completion of a significant operation + +**Example (for json_parse):** + +```c +Json *json_parse__(const char* file, const char* func, int line, Arena *arena, const char **s) { + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "Starting JSON parsing from position %p", *s); + + // Start parsing + if (!s || !*s) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "Invalid input: NULL pointer provided for JSON parsing"); + return NULL; + } + + // Show first 20 characters for debugging + raise_message(LOG_LEVEL_TRACE, file, func, line, + "JSON input preview: '%.20s%s'", *s, strlen(*s) > 20 ? "..." : ""); + + Json *result = json_parse_value__(file, func, line, s, arena); + + if (!result) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "JSON parsing failed at position: %p", *s); + } else { + raise_message(LOG_LEVEL_LOG, file, func, line, + "JSON parsed successfully, type: %d", result->type); + } + + return result; +} +``` + +### 3. Utility Functions + +**Logging pattern:** + +1. `LOG_LEVEL_TRACE` when entering a function with full parameters +2. `LOG_LEVEL_DEBUG` for logging important intermediate steps +3. `LOG_LEVEL_LOG` for successful operation completion +4. `LOG_LEVEL_WARN` for unusual but handled situations +5. `LOG_LEVEL_TRACE` when exiting with a result + +**Example (substr_clone):** + +```c +void substr_clone__(const char *file, const char *func, int line, + const char * const src, char *dest, size_t from, size_t len) { + raise_message(LOG_LEVEL_TRACE, file, func, line, + "Entering substring clone: src=%p (\"%s\"), dest=%p, from=%zu, len=%zu", + src, src ? (strlen(src) < 20 ? src : "") : "", + dest, from, len); + + if (!src || !dest) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "Invalid NULL pointer: %s%s", + (!src ? "src " : ""), (!dest ? "dest" : "")); + if (dest) dest[0] = '\0'; + return; + } + + size_t srclen = strlen(src); + if (from >= srclen) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "Out of range: 'from' index (%zu) exceeds source length (%zu)", + from, srclen); + dest[0] = '\0'; + return; + } + + if (from + len > srclen) { + size_t old_len = len; + len = srclen - from; + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "Adjusted length from %zu to %zu to fit source bounds", + old_len, len); + } + + strncpy(dest, src + from, len); + dest[len] = '\0'; + + raise_message(LOG_LEVEL_TRACE, file, func, line, + "Substring cloned: result=\"%s\", copied_length=%zu", + dest, len); +} +``` + +## Log Naming Standards + +### 1. Function Prefixes + +To make logs easier to search, use prefixes in messages: + +- **INIT:** - for initialization logs +- **ALLOC:** - for memory allocation logs +- **PARSE:** - for parsing logs +- **PROCESS:** - for data processing logs +- **FREE:** - for resource freeing logs + +### 2. Message Structure + +All messages should have a consistent structure: + +- **Action**: What is being done (verb in present continuous) +- **Object**: What is being acted upon (noun phrase) +- **Details**: Additional information (in parentheses or after a colon) + +**Examples:** +- "Initializing arena (size: %zu bytes)" +- "Processing JSON object with %d members" +- "Allocating memory block: address=%p, size=%zu" + +## Logging Levels + +Recommendations for using logging levels in a low-level library: + +1. **TRACE (Very detailed)** - For detailed tracking of function operation: + - Function entry/exit + - Data contents + - Intermediate variable values + - Processing loop details + +2. **DEBUG (Detailed)** - For programmers working with the library: + - Main algorithm steps + - Resource allocation/freeing + - Intermediate object states + - Debugging information + +3. **LOG (Operational)** - For important library operations: + - Successful resource initialization + - Successful operation completion + - System state changes + - Main execution points of working algorithms + +4. **INFO (Informational)** - For informing the application user: + - *This level should be used rarely in the library* + - Only truly important events for the user + - Version and configuration information + - High-level public API calls + +5. **NOTICE (Notable)** - Important state changes: + - *Almost never used in a low-level library* + - Events requiring user attention + - Important business events (if applicable) + +6. **WARN (Warning)** - Potentially problematic situations: + - Recoverable errors + - Edge cases in data + - Requests with potentially bad results + - Deprecated APIs + +7. **EXCEPTION (Exceptional situation)** - Critical errors: + - Unrecoverable errors + - Data integrity violations + - Resource exhaustion + - Errors requiring termination + +## Action Plan for Logging Improvement + +1. **Automation**: Create a script to find inconsistencies in logging +2. **Prioritization**: First update the most critical components (memory management, parsers) +3. **Documentation**: Expand documentation with examples for developers +4. **Testing**: Add tests that check logging under various scenarios +5. **Review**: Conduct code reviews of all logging changes + +## Examples for Different Modules + +### Memory Management (arena) + +```c +// Initialization +raise_message(LOG_LEVEL_DEBUG, file, func, line, "INIT: Creating new arena (size: %zu bytes)", size); + +// Allocation +raise_message(LOG_LEVEL_DEBUG, file, func, line, "ALLOC: Requesting memory from arena (size: %zu bytes, available: %zu bytes)", size, available); + +// Error +raise_message(LOG_LEVEL_EXCEPTION, file, func, line, "ERROR: Arena memory exhausted (requested: %zu bytes, available: %zu bytes)", size, available); + +// Successful operation completion +raise_message(LOG_LEVEL_LOG, file, func, line, "ALLOC: Memory allocated successfully (address: %p, size: %zu bytes)", ptr, size); + +// Freeing +raise_message(LOG_LEVEL_DEBUG, file, func, line, "FREE: Releasing arena resources (total size: %zu bytes, used: %zu bytes)", arena->capacity, used); +``` + +### JSON Parser + +```c +// Start parsing +raise_message(LOG_LEVEL_DEBUG, file, func, line, "PARSE: Starting JSON parsing (input: %.20s%s)", *s, strlen(*s) > 20 ? "..." : ""); + +// Intermediate result +raise_message(LOG_LEVEL_TRACE, file, func, line, "PARSE: Found JSON %s at position %p", type_str, position); + +// Parsing error +raise_message(LOG_LEVEL_WARN, file, func, line, "PARSE: Invalid JSON syntax at position %p (context: '%.10s')", *s, *s); + +// Completion +raise_message(LOG_LEVEL_LOG, file, func, line, "PARSE: JSON parsing completed successfully (type: %s, size: %zu bytes)", type_str, size); +``` + +### Slice Operations + +```c +// Creating a slice +raise_message(LOG_LEVEL_TRACE, file, func, line, "SLICE: Creating slice from array (source size: %zu, slice: %zu elements from index %zu)", array_len, len, start); + +// Subslice +raise_message(LOG_LEVEL_TRACE, file, func, line, "SLICE: Extracting sub-slice (from: %zu, length: %zu)", start, len); + +// Error +raise_message(LOG_LEVEL_WARN, file, func, line, "SLICE: Out of bounds slice request (start: %zu, length: %zu, available: %zu)", start, len, available); + +// Successful creation +raise_message(LOG_LEVEL_LOG, file, func, line, "SLICE: Successfully created slice (length: %zu, element size: %zu)", slice.len, slice.isize); +``` + +## Conclusion + +Implementing these standards will: +1. Simplify library debugging +2. Speed up code understanding for new developers +3. Make it easier to find problems in production +4. Make the library more professional and maintainable \ No newline at end of file diff --git a/package/c/hectic/hectic.c b/package/c/hectic/hectic.c index 21709a3..440e9d4 100644 --- a/package/c/hectic/hectic.c +++ b/package/c/hectic/hectic.c @@ -1,6 +1,25 @@ #include "hectic.h" +// Global color mode variable definition +ColorMode color_mode = COLOR_MODE_AUTO; + +const char* color_mode_to_string(ColorMode mode) { + switch (mode) { + case COLOR_MODE_AUTO: return "AUTO"; + case COLOR_MODE_FORCE: return "FORCE"; + case COLOR_MODE_DISABLE: return "DISABLE"; + default: return "UNKNOWN"; + } +} + void set_output_color_mode(ColorMode mode) { + // Log the color mode change + const char* mode_name = color_mode_to_string(mode); + + // Using fprintf since this might be called before logging is initialized + raise_message(LOG_LEVEL_INFO, __FILE__, __func__, __LINE__, "CONFIG: Setting output color mode to %s", mode_name); + + // Set the mode color_mode = mode; } @@ -11,7 +30,6 @@ 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"; @@ -25,7 +43,6 @@ 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); @@ -41,8 +58,6 @@ 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) @@ -70,7 +85,18 @@ void logger_level(LogLevel level) { } void init_logger(void) { - current_log_level = log_level_from_string(getenv("LOG_LEVEL")); + // Read log level from environment + const char* env_level = getenv("LOG_LEVEL"); + current_log_level = log_level_from_string(env_level); + + // Log initialization with appropriate message + if (env_level) { + fprintf(stderr, "INIT: Logger initialized with level %s from environment\n", + log_level_to_string(current_log_level)); + } else { + fprintf(stderr, "INIT: Logger initialized with default level %s\n", + log_level_to_string(current_log_level)); + } } char* raise_message( @@ -80,7 +106,6 @@ char* raise_message( int line, const char *format, ...) { - (void)func; if (level < current_log_level) { return NULL; } @@ -91,8 +116,17 @@ char* raise_message( static char timeStr[20]; strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm_info); - fprintf(stderr, "%s %s%s%s %s:%d ", timeStr, log_level_to_color(level), log_level_to_string(level), OPTIONAL_COLOR(COLOR_RESET), file, line); + // Print timestamp, log level with color, location info + fprintf(stderr, "%s %s%s%s [%s:%s:%d] ", + timeStr, + log_level_to_color(level), + log_level_to_string(level), + OPTIONAL_COLOR(COLOR_RESET), + file, + func, + line); + // Print the actual message with variable arguments va_list args; va_start(args, format); vfprintf(stderr, format, args); @@ -108,85 +142,221 @@ char* raise_message( // ----------- Arena arena_init__(const char *file, const char *func, int line, size_t size) { + // Function entry logging + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "INIT: Creating arena (size: %zu bytes)", size); + Arena arena; arena.begin = malloc(size); + + // Check for allocation failure + if (!arena.begin) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "INIT: Failed to allocate memory for arena (requested: %zu bytes)", size); + exit(1); + } + memset(arena.begin, 0, size); arena.current = arena.begin; arena.capacity = size; - raise_message(LOG_LEVEL_DEBUG, file, func, line, - "Initialized arena at %p with capacity %zu", arena.begin, size); + + // Success logging at LOG level + raise_message(LOG_LEVEL_LOG, file, func, line, + "INIT: Arena initialized successfully (address: %p, capacity: %zu bytes)", arena.begin, size); return arena; } void* arena_alloc_or_null__(const char *file, const char *func, int line, Arena *arena, size_t size) { - raise_message(LOG_LEVEL_TRACE, file, func, line, "arena_alloc_or_null(%p, %zu)", arena, size); + // Function entry at TRACE level + raise_message(LOG_LEVEL_TRACE, file, func, line, + "ALLOC: Requesting memory from arena (arena: %p, size: %zu bytes)", arena, size); + void *mem = NULL; if (arena->begin == 0) { + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "ALLOC: Arena not initialized, creating new arena"); *arena = arena_init__(file, func, line, 1024); // ARENA_DEFAULT_SIZE assumed as 1024 } + size_t current = (size_t)arena->current - (size_t)arena->begin; if (arena->capacity <= current || arena->capacity - current < size) { - raise_message(LOG_LEVEL_DEBUG, file, func, line, - "Arena %p (capacity %zu) used %zu cannot allocate %zu bytes", + raise_message(LOG_LEVEL_WARN, file, func, line, + "ALLOC: Insufficient memory in arena (address: %p, capacity: %zu bytes, used: %zu bytes, requested: %zu bytes)", arena->begin, arena->capacity, current, size); return NULL; } else { raise_message(LOG_LEVEL_DEBUG, file, func, line, - "Arena %p (capacity %zu) used %zu will allocate %zu bytes", + "ALLOC: Allocating from arena (address: %p, capacity: %zu bytes, used: %zu bytes, requested: %zu bytes)", arena->begin, arena->capacity, current, size); mem = arena->current; arena->current = (char*)arena->current + size; } - raise_message(LOG_LEVEL_DEBUG, file, func, line, "Allocated at %p", mem); + + // Success logging + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "ALLOC: Memory allocated successfully (address: %p, size: %zu bytes)", mem, size); return mem; } void* arena_alloc__(const char *file, const char *func, int line, Arena *arena, size_t size) { + // Function entry logging + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "ALLOC: Allocating memory (arena: %p, size: %zu bytes)", arena, size); + void *mem = arena_alloc_or_null__(file, func, line, arena, size); if (!mem) { raise_message(LOG_LEVEL_DEBUG, file, func, line, - "Arena out of memory when trying to allocate %zu bytes", size); + "ALLOC: Allocation failed (arena: %p, requested: %zu bytes)", arena, size); raise_message(LOG_LEVEL_EXCEPTION, file, func, line, - "Arena out of memory"); + "ALLOC: Arena out of memory (requested: %zu bytes)", size); exit(1); } + + // Success logging + raise_message(LOG_LEVEL_LOG, file, func, line, + "ALLOC: Memory allocated successfully (address: %p, size: %zu bytes)", mem, size); return mem; } void arena_reset__(const char *file, const char *func, int line, Arena *arena) { - arena->current = arena->begin; + // Function entry logging raise_message(LOG_LEVEL_DEBUG, file, func, line, - "Arena %p reset", arena->begin); + "ALLOC: Resetting arena (address: %p)", arena); + + // Check for NULL arena + if (!arena) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "ALLOC: Attempted to reset NULL arena"); + return; + } + + // Reset the arena + arena->current = arena->begin; + + // Operation success logging + raise_message(LOG_LEVEL_LOG, file, func, line, + "ALLOC: Arena reset successfully (address: %p, capacity: %zu bytes)", + arena->begin, arena->capacity); } void arena_free__(const char *file, const char *func, int line, Arena *arena) { + // Function entry logging raise_message(LOG_LEVEL_DEBUG, file, func, line, - "Freeing arena at %p", arena->begin); + "FREE: Releasing arena memory (address: %p)", arena); + + // Check for NULL arena + if (!arena) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "FREE: Attempted to free NULL arena"); + return; + } + + // Check for NULL begin pointer + if (!arena->begin) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "FREE: Attempted to free arena with NULL memory block"); + return; + } + + // Calculate used memory for logging + size_t used = (size_t)arena->current - (size_t)arena->begin; + + // Free the memory free(arena->begin); + + // Success logging + raise_message(LOG_LEVEL_LOG, file, func, line, + "FREE: Arena released successfully (address: %p, capacity: %zu bytes, used: %zu bytes)", + arena->begin, arena->capacity, used); + + // Clear the pointers + arena->begin = NULL; + arena->current = NULL; + arena->capacity = 0; } char* arena_strdup__(const char *file, const char *func, int line, Arena *arena, const char *s) { - char *result; - if (s) { - size_t len = strlen(s) + 1; - result = (char*)arena_alloc__(file, func, line, arena, len); - memcpy(result, s, len); - } else { - result = NULL; + // Function entry logging + raise_message(LOG_LEVEL_TRACE, file, func, line, + "ALLOC: Duplicating string (arena: %p, source: %p, preview: %.20s%s)", + arena, s, s ? s : "", s && strlen(s) > 20 ? "..." : ""); + + // Check for NULL string + if (!s) { + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "ALLOC: Source string is NULL, returning NULL"); + return NULL; } + + // Calculate string length and allocate memory + size_t len = strlen(s) + 1; + + // Success case + char *result = (char*)arena_alloc__(file, func, line, arena, len); + + // Copy the string + memcpy(result, s, len); + + // Success logging + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "ALLOC: String duplicated successfully (result: %p, length: %zu bytes)", + result, len); + return result; } char* arena_repstr__(const char *file, const char *func, int line, Arena *arena, const char *src, size_t start, size_t len, const char *rep) { - raise_message(LOG_LEVEL_TRACE, file, func, line, "arena_repstr__(%p, %p, %zu, \"%s\")", src, start, len, rep); + // Function entry logging + raise_message(LOG_LEVEL_TRACE, file, func, line, + "STRING: Replacing substring (source: %p, start: %zu, length: %zu, replacement: %.20s%s)", + src, start, len, rep, strlen(rep) > 20 ? "..." : ""); + + // Check inputs + if (!src) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "STRING: Source string is NULL"); + return NULL; + } + + if (!rep) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "STRING: Replacement string is NULL"); + return NULL; + } + + // Calculate lengths int src_len = strlen(src); int rep_len = strlen(rep); + + // Validate start and length + if (start > (size_t)src_len) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "STRING: Start position %zu exceeds source length %d", start, src_len); + // Return a copy of the source string + return arena_strdup__(file, func, line, arena, src); + } + + if (start + len > (size_t)src_len) { + size_t old_len = len; + len = src_len - start; + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "STRING: Adjusted length from %zu to %zu to fit source bounds", old_len, len); + } + + // Calculate new length and allocate memory int new_len = src_len - (int)len + rep_len; char *new_str = (char*)arena_alloc__(file, func, line, arena, new_len + 1); + + // Perform the replacement operation memcpy(new_str, src, start); memcpy(new_str + start, rep, rep_len); strcpy(new_str + start + rep_len, src + start + len); + + // Success logging + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "STRING: Replacement complete (result: %p, new length: %d)", new_str, new_len); + return new_str; } @@ -210,36 +380,66 @@ void* arena_realloc_copy__(const char *file, const char *func, int line, Arena * // ---------- void substr_clone__(const char *file, const char *func, int line, const char * const src, char *dest, size_t from, size_t len) { - // Log function entry with all parameters. + // Log function entry at TRACE level raise_message(LOG_LEVEL_TRACE, file, func, line, - "substr_cloning(src=\"%s\", src_ptr=%p, dest=%p, from=%zu, len=%zu)", - src, src, dest, from, len); + "Function called with src=%p, dest=%p, from=%zu, len=%zu", + src, dest, from, len); + + if (!src || !dest) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "Invalid NULL pointer: %s%s", + (!src ? "src " : ""), + (!dest ? "dest" : "")); + if (dest) dest[0] = '\0'; + return; + } size_t srclen = strlen(src); if (from >= srclen) { - // Log warning with context when 'from' is out of range. + // Log warning with context when 'from' is out of range raise_message(LOG_LEVEL_WARN, file, func, line, - "Invalid 'from' index (%zu): exceeds source length (%zu)", + "Out of range: 'from' index (%zu) exceeds source length (%zu)", from, srclen); dest[0] = '\0'; return; } - if (from + len > srclen) - len = srclen - from; + // Adjust length if needed + if (from + len > srclen) { + size_t old_len = len; + len = srclen - from; + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "Adjusted length from %zu to %zu to fit source bounds", + old_len, len); + } + + // Copy the substring strncpy(dest, src + from, len); dest[len] = '\0'; - // Log success message with result. + // Log success at TRACE level raise_message(LOG_LEVEL_TRACE, file, func, line, - "Completed substr_cloning: result=\"%s\", copied_length=%zu", - dest, len); + "Successfully copied %zu bytes: \"%.*s\"", + len, (int)len, dest); } // ---------- // -- Json -- // ---------- +const char* json_type_to_string(JsonType type) { + switch (type) { + case JSON_NULL: return "NULL"; + case JSON_BOOL: return "BOOL"; + case JSON_NUMBER: return "NUMBER"; + case JSON_STRING: return "STRING"; + case JSON_ARRAY: return "ARRAY"; + case JSON_OBJECT: return "OBJECT"; + default: return "UNKNOWN"; + } +} + + /* Utility: Skip whitespace */ static const char *skip_whitespace(const char *s) { while (*s && isspace((unsigned char)*s)) @@ -458,9 +658,40 @@ static Json *json_parse_value__(const char *file, const char *func, int line, co } Json *json_parse__(const char* file, const char* func, int line, Arena *arena, const char **s) { + // Function entry logging with DEBUG level + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "PARSE: Starting JSON parsing (input: %p)", *s); + + // Check input parameters + if (!s || !*s) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "PARSE: Invalid input parameters (NULL pointer provided for JSON parsing)"); + return NULL; + } + + if (!arena) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "PARSE: Invalid arena (NULL) provided for JSON parsing"); + return NULL; + } + + // Show input preview for debugging with TRACE level + raise_message(LOG_LEVEL_TRACE, file, func, line, + "PARSE: Input preview: '%.20s%s'", *s, strlen(*s) > 20 ? "..." : ""); + + // Process JSON value Json *result = json_parse_value__(file, func, line, s, arena); - if (!result) - raise_message(LOG_LEVEL_DEBUG, file, func, line, "json_parse failed at position: %p", *s); + + // Log parsing result + if (!result) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "PARSE: Failed to parse JSON at position %p (context: '%.10s')", + *s, *s && strlen(*s) > 0 ? *s : ""); + } else { + raise_message(LOG_LEVEL_LOG, file, func, line, + "PARSE: JSON parsing completed successfully (type: %s)", json_type_to_string(result->type)); + } + return result; } @@ -472,68 +703,185 @@ char *json_to_string__(const char* file, const char* func, int line, Arena *aren When raw is non-zero and the item is a JSON_STRING, it is printed without quotes. */ 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_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_to_string_with_opts"); + // Function entry with DEBUG level + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "FORMAT: Starting JSON conversion to string (item: %p, raw_mode: %s)", + item, raw == JSON_RAW ? "enabled" : "disabled"); + + // Check input parameters + if (!item) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "FORMAT: Invalid JSON object (NULL) provided for string conversion"); return NULL; } + + if (!arena) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "FORMAT: Invalid arena (NULL) provided for string conversion"); + return NULL; + } + + // Allocate memory for the string + char *out = arena_alloc__(file, func, line, arena, 1024); + if (!out) { + raise_message(LOG_LEVEL_EXCEPTION, file, func, line, + "FORMAT: Memory allocation failed during JSON string conversion"); + return NULL; + } + char *ptr = out; + const char* type_name = "unknown"; + + // Formatting based on type if (item->type == JSON_OBJECT) { ptr += sprintf(ptr, "{"); + type_name = "object"; + Json *child = item->child; + int child_count = 0; + + raise_message(LOG_LEVEL_TRACE, file, func, line, + "FORMAT: Processing JSON object children"); + while (child) { ptr += sprintf(ptr, "\"%s\":", child->key ? child->key : ""); char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw); - ptr += sprintf(ptr, "%s", child_str); - if (child->next) + if (child_str) { + ptr += sprintf(ptr, "%s", child_str); + } else { + raise_message(LOG_LEVEL_WARN, file, func, line, + "FORMAT: Failed to stringify child element (key=%s)", + child->key ? child->key : ""); + } + + if (child->next) { ptr += sprintf(ptr, ","); + } child = child->next; + child_count++; } + sprintf(ptr, "}"); + raise_message(LOG_LEVEL_TRACE, file, func, line, + "FORMAT: Object conversion complete with %d child elements", child_count); } else if (item->type == JSON_ARRAY) { ptr += sprintf(ptr, "["); + type_name = "array"; + Json *child = item->child; + int child_count = 0; + + raise_message(LOG_LEVEL_TRACE, file, func, line, + "FORMAT: Processing JSON array elements"); + while (child) { char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw); - ptr += sprintf(ptr, "%s", child_str); - if (child->next) + if (child_str) { + ptr += sprintf(ptr, "%s", child_str); + } else { + raise_message(LOG_LEVEL_WARN, file, func, line, + "FORMAT: Failed to stringify array element at index %d", child_count); + } + + if (child->next) { ptr += sprintf(ptr, ","); + } child = child->next; + child_count++; } + sprintf(ptr, "]"); + raise_message(LOG_LEVEL_TRACE, file, func, line, + "FORMAT: Array conversion complete with %d elements", child_count); } else if (item->type == JSON_STRING) { - if ((int)raw) - sprintf(ptr, "%s", item->JsonValue.string); - else - sprintf(ptr, "\"%s\"", item->JsonValue.string); + type_name = "string"; + if ((int)raw) { + sprintf(ptr, "%s", item->JsonValue.string ? item->JsonValue.string : ""); + } else { + sprintf(ptr, "\"%s\"", item->JsonValue.string ? item->JsonValue.string : ""); + } } else if (item->type == JSON_NUMBER) { + type_name = "number"; sprintf(ptr, "%g", item->JsonValue.number); } else if (item->type == JSON_BOOL) { + type_name = "boolean"; sprintf(ptr, item->JsonValue.boolean ? "true" : "false"); } else if (item->type == JSON_NULL) { + type_name = "null"; sprintf(ptr, "null"); } - raise_message(LOG_LEVEL_DEBUG, file, func, line, "Converted JSON to string: %s", out); + + raise_message(LOG_LEVEL_LOG, file, func, line, + "FORMAT: JSON %s converted to string (length=%zu)", + type_name, strlen(out)); + return out; } /* Retrieve an object item by key (case-sensitive) */ 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_message(LOG_LEVEL_DEBUG, file, func, line, "Invalid object passed to json_get_object_item"); + raise_message(LOG_LEVEL_TRACE, file, func, line, + "ACCESS: Searching for key \"%s\" in JSON object %p", + key ? key : "", object); + + // Check input parameters + if (!object) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "ACCESS: Invalid object (NULL) passed to json_get_object_item"); return NULL; } - Json *child = object->child; - while (child) { - 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_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" found", key); - return child; - } - child = child->next; + + if (!key) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "ACCESS: Invalid key (NULL) passed to json_get_object_item"); + return NULL; } - raise_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" not found in object", key); + + if (object->type != JSON_OBJECT) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "ACCESS: JSON value is not an object (actual type: %d)", object->type); + return NULL; + } + + // Count the total number of keys for debugging + int total_keys = 0; + Json *debug_scan = object->child; + while (debug_scan) { + total_keys++; + debug_scan = debug_scan->next; + } + + raise_message(LOG_LEVEL_TRACE, file, func, line, + "ACCESS: Object has %d key-value pairs", total_keys); + + // Perform key search + Json *child = object->child; + int position = 0; + + while (child) { + if (child->key) { + raise_message(LOG_LEVEL_TRACE, file, func, line, + "ACCESS: Comparing key \"%s\" with \"%s\" at position %d", + child->key, key, position); + + if (strcmp(child->key, key) == 0) { + raise_message(LOG_LEVEL_LOG, file, func, line, + "ACCESS: Found value for key \"%s\" (type: %s)", + key, json_type_to_string(child->type)); + return child; + } + } else { + raise_message(LOG_LEVEL_TRACE, file, func, line, + "ACCESS: Skipping element at position %d with NULL key", position); + } + + child = child->next; + position++; + } + + raise_message(LOG_LEVEL_DEBUG, file, func, line, + "ACCESS: Key \"%s\" not found in object (checked %d items)", + key, position); return NULL; } @@ -543,18 +891,54 @@ Json *json_get_object_item__(const char* file, const char* func, int line, const // Create a slice from an array with boundary check. Slice slice_create__(const char *file, const char *func, int line, size_t isize, void *array, size_t array_len, size_t start, size_t len) { - raise_message(LOG_LEVEL_TRACE, file, func, line, "slice_create(, , , , )"); - if (start + len > array_len) + // Function entry logging + raise_message(LOG_LEVEL_TRACE, file, func, line, + "SLICE: Creating slice (source: %p, array_length: %zu, start: %zu, length: %zu, item_size: %zu)", + array, array_len, start, len, isize); + + // Boundary check + if (start + len > array_len) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "SLICE: Slice boundaries exceed array length (start: %zu, length: %zu, array_length: %zu)", + start, len, array_len); return (Slice){NULL, 0, isize}; - return (Slice){ (char *)array + start * isize, len, isize }; + } + + // Create valid slice + Slice result = (Slice){ (char *)array + start * isize, len, isize }; + + // Success logging + raise_message(LOG_LEVEL_TRACE, file, func, line, + "SLICE: Slice created successfully (data: %p, length: %zu, item_size: %zu)", + result.data, result.len, result.isize); + + return result; } // Return a subslice from an existing slice. Slice slice_subslice__(const char *file, const char *func, int line, Slice s, size_t start, size_t len) { - raise_message(LOG_LEVEL_TRACE, file, func, line, "slice_subslice(, , )"); - if (start + len > s.len) + // Function entry logging + raise_message(LOG_LEVEL_TRACE, file, func, line, + "SLICE: Creating subslice (source: %p, source_length: %zu, start: %zu, length: %zu)", + s.data, s.len, start, len); + + // Boundary check + if (start + len > s.len) { + raise_message(LOG_LEVEL_WARN, file, func, line, + "SLICE: Subslice boundaries exceed source slice length (start: %zu, length: %zu, source_length: %zu)", + start, len, s.len); return (Slice){NULL, 0, s.isize}; - return (Slice){(char*)s.data + start * s.isize, len, s.isize}; + } + + // Create valid subslice + Slice result = (Slice){(char*)s.data + start * s.isize, len, s.isize}; + + // Success logging + raise_message(LOG_LEVEL_TRACE, file, func, line, + "SLICE: Subslice created successfully (data: %p, length: %zu, item_size: %zu)", + result.data, result.len, result.isize); + + return result; } int* arena_slice_copy__(const char *file, const char *func, int line, Arena *arena, Slice s) { @@ -570,14 +954,14 @@ int* arena_slice_copy__(const char *file, const char *func, int line, Arena *are // ------------ char* slice_to_debug_str(Arena *arena, Slice slice) { - // Создадим полную информацию о структуре Slice + // Create complete information about the Slice structure 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-данных выведем простое сообщение + // For NULL data, output a simple message if (!slice.data) { char* result = arena_alloc(arena, meta_len + 6); strcpy(result, buffer_meta); @@ -589,7 +973,7 @@ char* slice_to_debug_str(Arena *arena, Slice slice) { size_t buffer_size = meta_len + slice.len * 4 + 20; // Extra space for escaping and closing brace char* buffer = arena_alloc(arena, buffer_size); - // Копируем метаданные + // Copy metadata strcpy(buffer, buffer_meta); char* pos = buffer + meta_len; @@ -625,7 +1009,7 @@ char* slice_to_debug_str(Arena *arena, Slice slice) { } *pos++ = '"'; - *pos++ = '}'; // Закрывающая скобка для структуры + *pos++ = '}'; // Closing brace for the structure *pos = '\0'; raise_trace("slice_to_debug_str: %s", buffer); @@ -634,22 +1018,11 @@ char* slice_to_debug_str(Arena *arena, Slice slice) { } char* json_to_debug_str(Arena *arena, Json json) { - // Добавляем информацию о самой структуре JSON + // Add information about the JSON structure itself 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); + (void*)&json, json_type_to_string(json.type), json.key ? json.key : "NULL", (void*)json.child, (void*)json.next); size_t meta_len = strlen(meta_buffer); char value_buffer[256] = {0}; @@ -677,7 +1050,7 @@ char* json_to_debug_str(Arena *arena, Json json) { } case JSON_ARRAY: { - // Для массивов просто отметим количество элементов + // For arrays, simply note the number of elements size_t count = 0; Json *item = json.child; while (item) { @@ -689,7 +1062,7 @@ char* json_to_debug_str(Arena *arena, Json json) { } case JSON_OBJECT: { - // Для объектов отметим количество пар ключ-значение + // For objects, note the number of key-value pairs size_t count = 0; Json *item = json.child; while (item) { @@ -704,8 +1077,8 @@ char* json_to_debug_str(Arena *arena, Json json) { strcpy(value_buffer, ""); } - // Создаем итоговую строку - size_t result_len = meta_len + strlen(value_buffer) + 2; // +2 для закрывающей скобки и нулевого символа + // Create final string + size_t result_len = meta_len + strlen(value_buffer) + 2; // +2 for closing brace and null character char* result = arena_alloc(arena, result_len); strcpy(result, meta_buffer); diff --git a/package/c/hectic/hectic.h b/package/c/hectic/hectic.h index 5cf078b..d34f9af 100644 --- a/package/c/hectic/hectic.h +++ b/package/c/hectic/hectic.h @@ -32,8 +32,10 @@ typedef enum { COLOR_MODE_DISABLE } ColorMode; -// Static color mode variable -static ColorMode color_mode __attribute__((unused)) = COLOR_MODE_AUTO; +// External color mode variable declaration +extern ColorMode color_mode; + +const char* color_mode_to_string(ColorMode mode); // Function to set color mode void set_output_color_mode(ColorMode mode); @@ -72,15 +74,59 @@ void set_output_color_mode(ColorMode mode); // -- Logger -- // ------------ +/** + * Log levels following a consistent severity-based hierarchy. + * Each level includes specific guidance on when it should be used. + */ typedef enum { + /** + * TRACE: Most detailed information for in-depth debugging + * Use for: Deep diagnostic details, function entry/exit, variable dumps + * Visibility: Development environments only, rarely used in production + */ LOG_LEVEL_TRACE, - LOG_LEVEL_ZALUPA, + + /** + * DEBUG: Detailed information useful during development + * Use for: Development-time debugging, showing variable states, internal flows + * Visibility: Development and debugging environments, rarely in production + */ LOG_LEVEL_DEBUG, + + /** + * LOG: General operational events + * Use for: Runtime events worth logging but not requiring attention + * Visibility: Always written to logs, useful for auditing/diagnostics + */ LOG_LEVEL_LOG, + + /** + * INFO: Informational messages highlighting progress + * Use for: Normal but noteworthy events, state changes, startup/shutdown events + * Visibility: Visible to client applications if configured + */ LOG_LEVEL_INFO, + + /** + * NOTICE: More important events than INFO, but not warnings + * Use for: Important state changes, significant operations, configuration changes + * Visibility: Displayed to client by default, meant to be seen + */ LOG_LEVEL_NOTICE, + + /** + * WARN: Potential problems that don't prevent normal operation + * Use for: Unexpected behaviors, deprecated feature usage, recoverable errors + * Visibility: Alerts both client and server logs, needs attention + */ LOG_LEVEL_WARN, - LOG_LEVEL_EXCEPTION, + + /** + * EXCEPTION: Serious errors requiring immediate attention + * Use for: Critical failures, data loss risks, business rule violations + * Visibility: Highest priority, often leads to operation termination + */ + LOG_LEVEL_EXCEPTION } LogLevel; void logger_level_reset(); @@ -91,6 +137,17 @@ void logger_level(LogLevel level); LogLevel log_level_from_string(const char *level_str); +/** + * Core logging function that formats and outputs log messages. + * + * @param level Severity level of the message + * @param file Source file where log was generated + * @param func Function where log was generated + * @param line Line number where log was generated + * @param format Printf-style format string + * @param ... Variable arguments for format string + * @return Timestamp string for the log message + */ char* raise_message(LogLevel level, const char *file, const char *func, int line, const char *format, ...); #ifndef PRECOMPILED_LOG_LEVEL @@ -139,11 +196,24 @@ 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 +// ---------- +// -- misc -- +// ---------- + +#define MEM_b 1 +#define MEM_KiB 1024 +#define MEM_MiB (MEM_KiB * 1024) +#define MEM_GiB (MEM_MiB * 1024) +#define MEM_TiB (MEM_TiB * 1024) +#define MEM_PiB (MEM_TiB * 1024) +#define MEM_EiB (MEM_PiB * 1024) +#define MEM_ZiB (MEM_EiB * 1024) +#define MEM_YiB (MEM_ZiB * 1024) +#define MEM_RiB (MEM_YiB * 1024) +#define MEM_QiB (MEM_RiB * 1024) + +void substr_clone__(const char *file, const char *func, int line, const char * const src, char *dest, size_t from, size_t len); +#define substr_clone(src, dest, from, len) substr_clone__(__FILE__, __func__, __LINE__, src, dest, from, len) // ----------- // -- arena -- @@ -201,24 +271,17 @@ void* arena_realloc_copy__(const char *file, const char *func, int line, Arena * #define arena_realloc_copy(arena, old_ptr, old_size, new_size) \ arena_realloc_copy__(__FILE__, __func__, __LINE__, arena, old_ptr, old_size, new_size) -// ---------- -// -- misc -- -// ---------- -#define MEM_b 1 -#define MEM_KiB 1024 -#define MEM_MiB (MEM_KiB * 1024) -#define MEM_GiB (MEM_MiB * 1024) -#define MEM_TiB (MEM_TiB * 1024) -#define MEM_PiB (MEM_TiB * 1024) -#define MEM_EiB (MEM_PiB * 1024) -#define MEM_ZiB (MEM_EiB * 1024) -#define MEM_YiB (MEM_ZiB * 1024) -#define MEM_RiB (MEM_YiB * 1024) -#define MEM_QiB (MEM_RiB * 1024) +static Arena disposable_arena __attribute__((unused)) = {0}; -void substr_clone__(const char *file, const char *func, int line, const char * const src, char *dest, size_t from, size_t len); -#define substr_clone(src, dest, from, len) substr_clone__(__FILE__, __func__, __LINE__, src, dest, from, len) +#define DISPOSABLE_ARENA __extension__ ({ \ + if (disposable_arena.begin == NULL) { \ + disposable_arena = arena_init__(__FILE__, __func__, __LINE__, MEM_MiB); \ + } else { \ + arena_reset(&disposable_arena); \ + } \ + &disposable_arena; \ +}) // ---------- // -- Json -- @@ -310,6 +373,10 @@ int* arena_slice_copy__(const char *file, const char *func, int line, Arena *are buf; \ }) +// ------------ +// -- Debug -- +// ------------ + // 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); diff --git a/package/c/hectic/test/00-logger.c b/package/c/hectic/test/00-logger.c index cff16ba..1265dfd 100644 --- a/package/c/hectic/test/00-logger.c +++ b/package/c/hectic/test/00-logger.c @@ -26,7 +26,7 @@ } while(0) int main(void) { - set_output_color_mode(COLOR_MODE_DISABLE); + logger_level(LOG_LEVEL_DEBUG); TEST_RAISE_GENERIC(raise_debug, LOG_LEVEL_DEBUG, "DEBUG"); TEST_RAISE_GENERIC(raise_log, LOG_LEVEL_LOG, "LOG"); @@ -35,6 +35,6 @@ int main(void) { TEST_RAISE_GENERIC(raise_warn, LOG_LEVEL_WARN, "WARN"); TEST_RAISE_GENERIC(raise_exception, LOG_LEVEL_EXCEPTION, "EXCEPTION"); - printf("%s all tests passed.\n", __FILE__); + printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); return 0; } diff --git a/package/c/hectic/test/01-arena.c b/package/c/hectic/test/01-arena.c index e0f774b..4b134d6 100644 --- a/package/c/hectic/test/01-arena.c +++ b/package/c/hectic/test/01-arena.c @@ -91,7 +91,6 @@ void test_arena_overwrite_detection() { } int main() { - set_output_color_mode(COLOR_MODE_DISABLE); logger_level(LOG_LEVEL_DEBUG); test_arena_init(); @@ -102,5 +101,5 @@ int main() { test_arena_strdup(); test_arena_repstr(); test_arena_overwrite_detection(); - printf("%s all tests passed.\n", __FILE__); + printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); } diff --git a/package/c/hectic/test/02-json.c b/package/c/hectic/test/02-json.c index 866bbe2..5f575e1 100644 --- a/package/c/hectic/test/02-json.c +++ b/package/c/hectic/test/02-json.c @@ -124,6 +124,8 @@ static void test_arena_reset_reuse(void) { } int main(void) { + logger_level(LOG_LEVEL_DEBUG); + test_parse_json_object(); test_parse_json_number(); test_parse_json_string(); @@ -134,6 +136,6 @@ int main(void) { test_nested_json_object(); test_arena_reset_reuse(); - printf("%s all tests passed.\n", __FILE__); + printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); return 0; } diff --git a/package/c/hectic/test/03-slice.c b/package/c/hectic/test/03-slice.c index 7b81adc..7d34c22 100644 --- a/package/c/hectic/test/03-slice.c +++ b/package/c/hectic/test/03-slice.c @@ -83,7 +83,6 @@ void test_slice_string() { } int main() { - set_output_color_mode(COLOR_MODE_DISABLE); logger_level(LOG_LEVEL_DEBUG); test_slice_create(); @@ -92,6 +91,6 @@ int main() { test_slice_edge_cases(); test_slice_string(); - printf("%s all tests passed.\n", __FILE__); + printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); return 0; }