fix: hectic C: impruve logging guidelines
This commit is contained in:
153
package/c/hectic/docs/logging.md
Executable file
153
package/c/hectic/docs/logging.md
Executable file
@@ -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 <hectic.h>
|
||||
```
|
||||
|
||||
## 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;
|
||||
}
|
||||
```
|
||||
205
package/c/hectic/docs/logging_audit.md
Executable file
205
package/c/hectic/docs/logging_audit.md
Executable file
@@ -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.
|
||||
285
package/c/hectic/docs/logging_improvement_plan.md
Executable file
285
package/c/hectic/docs/logging_improvement_plan.md
Executable file
@@ -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 : "<long string>") : "<null>",
|
||||
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
|
||||
@@ -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 : "<empty>");
|
||||
} 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);
|
||||
if (child_str) {
|
||||
ptr += sprintf(ptr, "%s", child_str);
|
||||
if (child->next)
|
||||
ptr += sprintf(ptr, ",");
|
||||
child = child->next;
|
||||
} else {
|
||||
raise_message(LOG_LEVEL_WARN, file, func, line,
|
||||
"FORMAT: Failed to stringify child element (key=%s)",
|
||||
child->key ? child->key : "<null>");
|
||||
}
|
||||
|
||||
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);
|
||||
if (child_str) {
|
||||
ptr += sprintf(ptr, "%s", child_str);
|
||||
if (child->next)
|
||||
ptr += sprintf(ptr, ",");
|
||||
child = child->next;
|
||||
} 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 : "<null>", 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;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
raise_message(LOG_LEVEL_WARN, file, func, line,
|
||||
"ACCESS: Invalid key (NULL) passed to json_get_object_item");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
child = child->next;
|
||||
} else {
|
||||
raise_message(LOG_LEVEL_TRACE, file, func, line,
|
||||
"ACCESS: Skipping element at position %d with NULL key", position);
|
||||
}
|
||||
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" not found in object", key);
|
||||
|
||||
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(<optimized>, <optimized>, <optimized>, <optimized>, <optimized>)");
|
||||
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(<optimized>, <optimized>, <optimized>)");
|
||||
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, "<UNKNOWN JSON TYPE>");
|
||||
}
|
||||
|
||||
// Создаем итоговую строку
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user