C buffered logging library. No C runtime, no C standard library, no dynamic allocation. Get your data formatted into a stack-allocated buffer of pre-determined size, then write it all at once to a console and/or a file to minimise writes to persistent storage.
Example (available in example.c):
#include "logs.h"
void mainCRTStartup()
{
const WCHAR* logs_file_name = u"Fluß_¼½¾_Öçé_Dž.txt";
// Open outputs
logs_open_console_output();
logs_open_file_output(logs_file_name);
union { u32 bits; f32 val; } num = {.bits = 0x3C000000};
for (u32 bit_pos = 24; bit_pos > 15; bit_pos--)
{
num.bits |= (1 << bit_pos);
// Format logs
log_literal_str("0x");
log_hex_num(num.bits);
log_literal_str(u" (");
log_dec_num(num.bits);
log_literal_str(u8") as a f32 is ");
log_dec_num(num.val);
log_character('\n');
}
// Then write buffered logs to enabled outputs
logs_flush();
// Write to specific outputs
logs_disable_output(LOGS_CONSOLE_OUTPUT);
log_literal_str("========== Logging session end ==========\n\n");
logs_flush();
logs_enable_output(LOGS_CONSOLE_OUTPUT);
logs_disable_output(LOGS_FILE_OUTPUT);
log_literal_str(u8"\nLogs written to file ");
log_null_terminated_str(logs_file_name);
// Close outputs, implicitly flushing logs buffer to enabled outputs
logs_close_file_output();
logs_close_console_output();
ExitProcess(0);
}You can compile this example yourself by running build.bat, or from a x64 Native Tools Command Prompt for VS by running cl.exe /DLOGS_ENABLED /std:c11 /utf-8 to_str_utilities.c logs.c example.c /link /subsystem:console /entry:mainCRTStartup /nodefaultlib /out:example.exe kernel32.lib
(requires Visual Studio Build Tools or the Native Desktop workload).
The code in this repository is inspired from Christopher "skeeto" Wellons's excellent article "Let's implement buffered, formatted output"
- Targets Windows XP and above
- Supports x86_64
- Compiles with MSVC (GCC and Clang not supported)
- Doesn't use the C runtime, nor the C standard library
- No dynamic allocations, the logs buffer is allocated on the stack
- Offers output streams control (open, close, disable, enable, write)
- Provides 1 console output which is either created or reused from the calling process, and set to display UTF-8-encoded characters
- Provides 1 ASCII- or UTF-16-named file output, which is either created or opened, then appended to
- Logging of fundamental types
- Signed and unsigned integers up to 64-bit, in binary, decimal and hexadecimal format, with or without a pre-determined size in bits, digits or nibbles
- 32-bit floating point numbers in binary, decimal and hexadecimal format, with or without a pre-determined size in bits or nibbles, or a pre-determined decimal fractional part size, with a few particularities:
- Values outside of [-2^32 + 1, 2^32 - 1] are output as
-bigorbig, depending on their sign (see rational in log_dec_f32_number() comments in log.c) -qnan,qnan,-snan,snan,-inforinfmay be output for matching non-number values-0will be converted to0- Without a predetermined size, 6 digits past the period are written to the output. This can be increased up to 9 (see
F32_DEC_FRAC_DEFAULT_STR_SIZEandF32_DEC_FRAC_MULTin types_max_str_size.h) - When passing a fixed fractional part size in digits that exceeds 9, the fractional part will be truncated to 9 digits (see
F32_DEC_FRAC_MAX_STR_SIZEin types_max_str_size.h) - Values are written in full. Scientific notation and other notations are not used
- Values outside of [-2^32 + 1, 2^32 - 1] are output as
- ASCII, UTF-8 and UTF-16 characters, null-terminated, sized and literal compile-time strings
- Compile-time defined logs buffer size through macro definition
/DLOGS_BUFFER_SIZE, which defaults to 4 KB - Helpers to manage logs buffer memory, with types_max_str_size.h to estimate the maximum number of characters a fundamental type may be represented with, and with logs_buffer_remaining_bytes()
- Logs are turned off by default to prevent any accidental performance hit, and are enabled by defining the compile-time macro
LOGS_ENABLED(setting it to0disables logs)
Note
Because this library gives control over the logs buffer size and when it should be written to enabled outputs, all functions, once called, assume there is enough space left in the logs buffer to append the content they are passed. You are in charge of choosing a log buffer size that is appropriate to your needs, and of calling logs_flush() before the buffer becomes over-saturated.
Note
It is important to compile with /utf-8 if you are passing UTF-8 encoded characters and strings to the functions of this library.
logs.h/logs.c: logs output management, fundamental types formatting and buffering, logs buffer consumption helperto_str_utilities.h/to_str_utilities.c: helpers to categorise and efficiently convert numbers to characterstypes.h: custom typedefs, for convenienceexample.c: example usage of this librarybuild.bat: sample script to compile the example provided, and to show how to include and compile this library into another codebase
- I like experimenting and understanding what it takes to build even the most simple things
- I'm usually using only a small subset of features from the
printf's family of functions - This small library provides me with clearer, more explicit control over logs and their outputs
- I avoid using the C standard library and Windows C runtime, which often incur hidden performance hits or perform operations I don't need, and increase executable size significantly for small tools/libraries. Using the C runtime, compiling this library (
example.cincluded) withbuild.batresults in a 106.5 KB executable. Without the C runtime, the executable shrinks down to 5.5 KB. The former can't fit into the L1 cache of an Intel's Lion Cove CPU nor in that of an AMD's Zen 5 CPU, both from 2024. The latter could fit in the L1 cache of an Intel's i486 CPU from 1989 or in that of an AMD's K6 CPU from 1997. Isn't that incredible?
This is the operating system I'm most familiar with, and Wine makes it easy to run code targeted at Windows on Linux.
See types.h.
sis forsigneduis forunsignedfis for "floating-point number"- The number following is the type's width in bits
This is purely for convenience.
The code in this repository is released in the public domain. You are allowed to use the code in this repository freely.