Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v7
with:
submodules: 'recursive'

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v7
with:
submodules: 'recursive'

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v7
with:
submodules: 'recursive'

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ target_include_directories(logcoe

include(cmake/utils.cmake)

enable_strict_warnings(logcoe)
logcoe_enable_strict_warnings(logcoe)

option(LOGCOE_BUILD_TESTS "Build the logcoe test suite" OFF)
if(LOGCOE_BUILD_TESTS)
Expand Down
52 changes: 26 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ target_link_libraries(your_target PRIVATE logcoe)
int main() {
// Initialize with INFO level, no default source (empty string), console enabled, file disabled
// logs will be printed as [timestamp] [log_level]: <log_message>
logcoe::initialize(logcoe::LogLevel::INFO, std::string{}, true, false);
logcoe::initialize(logcoe::log_level::info, std::string{}, true, false);

logcoe::info("Application started");
logcoe::warning("This is a warning message");
Expand All @@ -67,21 +67,21 @@ int main() {
int main() {
// Enable both console and file output with DEBUG level and default source as logcoe
// logs will be printed as [timestamp] [log_level] [logcoe]: <log_message>
logcoe::initialize(logcoe::LogLevel::DEBUG, "logcoe", true, true, "app.log");
logcoe::initialize(logcoe::log_level::debug, "logcoe", true, true, "app.log");

// Customize time format
logcoe::setTimeFormat("%H:%M:%S");
logcoe::set_time_format("%H:%M:%S");

// Log with source information
logcoe::debug("Debugging network connection", "NetworkModule");
logcoe::info("User logged in successfully", "AuthSystem");

// Redirect console to custom stream
std::ofstream customLog("custom.log");
logcoe::setConsoleOutput(customLog);
logcoe::set_console_output(customLog);

// Change log level at runtime
logcoe::setLogLevel(logcoe::LogLevel::WARNING);
logcoe::set_log_level(logcoe::log_level::warning);

logcoe::shutdown();
return 0;
Expand All @@ -108,11 +108,11 @@ logcoe::initialize();

// Full configuration
logcoe::initialize(
logcoe::LogLevel::DEBUG, // Log level
"logcoe", // Default source
true, // Enable console
true, // Enable file
"application.log" // Filename
logcoe::log_level::debug, // Log level
"logcoe", // Default source
true, // Enable console
true, // Enable file
"application.log" // Filename
);

// shutdown
Expand All @@ -122,17 +122,17 @@ logcoe::shutdown();
### Configuration
```cpp
// Runtime log level changes
logcoe::setLogLevel(logcoe::LogLevel::WARNING);
LogLevel current = logcoe::getLogLevel();
logcoe::set_log_level(logcoe::log_level::warning);
log_level current = logcoe::get_log_level();

// Output configuration
logcoe::setFileOutput("new_logfile.log");
logcoe::disableFileOutput();
logcoe::setConsoleOutput(std::cerr);
logcoe::disableConsoleOutput();
logcoe::set_file_output("new_logfile.log");
logcoe::disable_file_output();
logcoe::set_console_output(std::cerr);
logcoe::disable_console_output();

// Time formatting (strftime compatible)
logcoe::setTimeFormat("%Y-%m-%d %H:%M:%S");
logcoe::set_time_format("%Y-%m-%d %H:%M:%S");
```

### Logging
Expand All @@ -155,11 +155,11 @@ logcoe::flush(); // Flush all pending messages

| Level | Value | Description |
|-------|-------|-------------|
| `DEBUG` | 0 | Detailed diagnostic information |
| `INFO` | 1 | General application information |
| `WARNING` | 2 | Warning conditions that should be noted |
| `ERROR` | 3 | Error conditions that affect functionality |
| `NONE` | 4 | Disable all logging |
| `debug` | 0 | Detailed diagnostic information |
| `info` | 1 | General application information |
| `warning` | 2 | Warning conditions that should be noted |
| `error` | 3 | Error conditions that affect functionality |
| `none` | 4 | Disable all logging |

## Thread Safety

Expand All @@ -176,7 +176,7 @@ void worker_thread(int id) {
}

int main() {
logcoe::initialize(logcoe::LogLevel::INFO, std::string{}, false, true, "concurrent.log");
logcoe::initialize(logcoe::log_level::info, std::string{}, false, true, "concurrent.log");

std::vector<std::thread> workers;
for (int i = 0; i < 10; ++i) {
Expand Down Expand Up @@ -211,9 +211,9 @@ time. Every call site keeps working with zero code changes, but none of the logg
or file I/O gets compiled in:

- `initialize`, `debug`/`info`/`warning`/`error`, `flush`, `shutdown`, and the setters do nothing.
- `isInitialized()` returns `false`.
- `getLogLevel()` returns `LogLevel::NONE`.
- `setFileOutput(...)` returns `false` and never creates a file.
- `is_initialized()` returns `false`.
- `get_log_level()` returns `log_level::none`.
- `set_file_output(...)` returns `false` and never creates a file.

No opt-in macro or CMake option needed, it's automatic whenever `NDEBUG` is defined. That includes
`RelWithDebInfo` and `MinSizeRel`, since CMake defines `NDEBUG` for those build types too, not just
Expand Down
10 changes: 5 additions & 5 deletions cmake/utils.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Automatically copies required MinGW DLLs to target directory on Windows

function(copy_mingw_dlls_to_target target_name)
function(logcoe_copy_mingw_dlls_to_target target_name)
if(WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(STATUS "[logcoe] Setting up automatic MinGW DLL copying for ${target_name}")

Expand Down Expand Up @@ -34,13 +34,13 @@ function(copy_mingw_dlls_to_target target_name)
endfunction()

# Alternative function for multiple targets
function(copy_mingw_dlls_to_targets)
function(logcoe_copy_mingw_dlls_to_targets)
foreach(target_name ${ARGN})
copy_mingw_dlls_to_target(${target_name})
logcoe_copy_mingw_dlls_to_target(${target_name})
endforeach()
endfunction()

function(ignore_external_warnings target_name)
function(logcoe_ignore_external_warnings target_name)
if(TARGET ${target_name})
get_target_property(INCLUDE_DIRS ${target_name} INTERFACE_INCLUDE_DIRECTORIES)
if(INCLUDE_DIRS)
Expand All @@ -54,7 +54,7 @@ function(ignore_external_warnings target_name)
endif()
endfunction()

function(enable_strict_warnings target_name)
function(logcoe_enable_strict_warnings target_name)
if(MSVC)
target_compile_options(${target_name} PRIVATE /W4 /WX)
else()
Expand Down
52 changes: 26 additions & 26 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

logcoe is designed as a lightweight, thread-safe logging library that provides flexible output management with minimal performance overhead. The architecture follows a singleton pattern with internal implementation hiding for API stability.
logcoe is designed as a lightweight, thread-safe logging library that provides flexible output management with minimal performance overhead. The public API hides all implementation details through an anonymous namespace containing free functions and static state, maintaining API stability across changes.

## Component Architecture

Expand All @@ -18,8 +18,8 @@ logcoe is designed as a lightweight, thread-safe logging library that provides f
└──────────────────┬──────────────────┘
│
┌──────────────────▼──────────────────┐
│ LoggerImpl │
│ (Internal Implementation) │
│ Anonymous Namespace (Internal) │
│ (Free Functions & State) │
│ │
│ ┌─────────────┬─────────────────┐ │
│ │ Mutex │ Output Streams │ │
Expand All @@ -41,33 +41,33 @@ logcoe is designed as a lightweight, thread-safe logging library that provides f
- Simple function-based API
- No exposed implementation details
- Header-only public interface
- All functions forward to LoggerImpl
- All functions forward to anonymous-namespace implementations

### LoggerImpl (Internal Implementation)
### Internal Implementation (Anonymous Namespace)
- **File**: `src/logcoe.cpp` (anonymous namespace)
- **Purpose**: Contains all logging logic and state management
- **Design Pattern**: Singleton with static members
- **Design**: Free functions with implicit internal linkage through namespace scope

#### Thread Safety Manager
```cpp
static std::mutex s_mutex;
std::mutex g_mutex;
```
- Ensures thread-safe access to all static members and operations
- Ensures thread-safe access to all state variables and operations

#### State Management
```cpp
static LogLevel s_logLevel;
static bool s_useFile;
static bool s_useConsole;
static std::string s_timeFormat;
log_level g_log_level;
bool g_use_file;
bool g_use_console;
std::string g_time_format;
```
- Maintains current logger configuration, Can be changed at runtime
- Maintains current logger configuration, can be changed at runtime

#### Output Stream Management
```cpp
static std::string s_filename;
static std::ofstream s_fileStream;
static std::ostream* s_consoleStream;
std::string g_filename;
std::ofstream g_file_stream;
std::ostream* g_console_stream;
```
- **File Output**: Direct file stream management with automatic opening/closing
- **Console Output**: Configurable output stream (default: std::cout)
Expand Down Expand Up @@ -105,7 +105,7 @@ Generate timestamp
↓
Format message with metadata
↓
writeToOutputs()
write_to_outputs()
↓
Write to console (if enabled)
↓
Expand All @@ -118,7 +118,7 @@ Release mutex lock

### 3. Configuration Changes
```
setLogLevel/setFileOutput/etc() called
set_log_level/set_file_output/etc() called
↓
Acquire mutex lock
↓
Expand All @@ -133,7 +133,7 @@ Release mutex lock

## Thread Safety Implementation

- **Single Global Mutex**: `std::mutex s_mutex`
- **Single Global Mutex**: `std::mutex g_mutex`
- **Lock Scope**: Every public API call acquires lock for entire duration

### Thread Safety Guarantees
Expand Down Expand Up @@ -168,12 +168,12 @@ std::tm tm_now;

## Release Build Stripping

Under `NDEBUG`, the entire anonymous-namespace `LoggerImpl` implementation is compiled out via
`#ifndef NDEBUG`, so a Release build carries none of its code or static state. The public
Under `NDEBUG`, the entire anonymous-namespace implementation is compiled out via
`#ifndef NDEBUG`, so a Release build carries none of its code or state variables. The public
`logcoe::` wrapper functions in `src/logcoe.cpp` switch to a separate `#ifdef NDEBUG` branch of
no-op stubs, so every call site keeps compiling unchanged. Two stubs return a fixed value instead
of an empty body, since there's no real state left to report: `isInitialized()` always returns
`false`, and `getLogLevel()` always returns `LogLevel::NONE`. The guard is on the bare `NDEBUG`
of an empty body, since there's no real state left to report: `is_initialized()` always returns
`false`, and `get_log_level()` always returns `log_level::none`. The guard is on the bare `NDEBUG`
macro, not a check for a "Release" build type specifically, so `RelWithDebInfo` and `MinSizeRel`
trigger the same stripping since CMake defines `NDEBUG` for them too.

Expand All @@ -193,11 +193,11 @@ trigger the same stripping since CMake defines `NDEBUG` for them too.

### Level Hierarchy
```
DEBUG (0) < INFO (1) < WARNING (2) < ERROR (3) < NONE (4)
debug (0) < info (1) < warning (2) < error (3) < none (4)
```

```cpp
if (static_cast<int>(level) < static_cast<int>(s_logLevel))
if (static_cast<int>(level) < static_cast<int>(g_log_level))
return;
```

Expand All @@ -212,7 +212,7 @@ if (static_cast<int>(level) < static_cast<int>(s_logLevel))
```

- **Timestamp**: Configurable format using strftime
- **Level**: String representation of LogLevel enum
- **Level**: String representation of log_level enum
- **Source**: Optional component identifier
- **Message**: User-provided content

Expand Down
18 changes: 9 additions & 9 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,21 @@ The CI runs the following checks:

### Code Style
- Follow existing naming conventions:
- `camelCase` for functions and variables
- `PascalCase` for classes and enums
- `s_` prefix for static members
- `snake_case` for functions, variables, types, and enumerators
- `g_` prefix for anonymous-namespace variables (implicit internal linkage)
- Trailing underscore for parameters that would otherwise shadow another identifier in scope (e.g. a `flush` parameter is renamed to `flush_` to avoid shadowing the `flush()` function)
- Keep lines under 120 characters
- Add comments for complex logic
- Use `const` and `constexpr` where appropriate

### Example Code Style
```cpp
namespace logcoe
namespace
{
void setLogLevel(LogLevel level)
void set_log_level(log_level level)
{
std::lock_guard<std::mutex> lock(s_mutex);
s_logLevel = level;
std::lock_guard<std::mutex> lock(g_mutex);
g_log_level = level;
}
}
```
Expand Down Expand Up @@ -131,7 +131,7 @@ When adding features:
- Maintain backward compatibility
- Add documentation comments

2. **Implement in LoggerImpl**:
2. **Implement in the anonymous namespace**:
- Add to `src/logcoe.cpp`
- Ensure thread safety with proper locking
- Handle error cases gracefully
Expand All @@ -150,7 +150,7 @@ When adding features:

When modifying logcoe:

1. **Always Use Mutex**: Every function that accesses static state must lock `s_mutex`
1. **Always Use Mutex**: Every function that accesses static state must lock `g_mutex`
2. **Minimize Lock Duration**: Perform I/O operations efficiently under lock
3. **Avoid Nested Locks**: Current design uses single mutex to prevent deadlocks
4. **Test Concurrency**: Add thread safety tests for new features
Expand Down
Loading
Loading