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
31 changes: 31 additions & 0 deletions README-RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,37 @@ void short_names_demo() {

Самодостаточный пример, который объединяет настройки и намеренно завершает работу после fatal-сообщения, расположен в `examples/example_logit_minimal_crash.cpp`.

### Макросы системных ошибок

`LOGIT_SYSERR_<LEVEL>` захватывает текущее значение `errno` (или `GetLastError()` в Windows) и добавляет расшифровку ошибки к исходному сообщению, чтобы в логе оставался как контекст, так и код сбоя. При необходимости можно явно выбрать платформу через `LOGIT_PERROR_<LEVEL>` или `LOGIT_WINERR_<LEVEL>`.

```cpp
#include <LogIt.hpp>
#include <fcntl.h>

int main() {
LOGIT_ADD_CONSOLE_DEFAULT();

if (::open("missing.cfg", O_RDONLY) == -1) {
LOGIT_SYSERR_ERROR("Не удалось открыть конфигурацию");
}

LOGIT_WAIT();
}
```

Формат суффикса можно изменить на этапе компиляции с помощью макросов из `config.hpp`:

```cpp
#define LOGIT_OS_ERROR_JOIN " <- "
#define LOGIT_POSIX_ERROR_PATTERN "[%s] errno=%d (%s)"
#define LOGIT_WINDOWS_ERROR_PATTERN "[%s] GetLastError=%lu (%s)"
#include <LogIt.hpp>

// ... далее в коде ...
LOGIT_SYSERR_ERROR("Ошибка удаления временного каталога");
```

## Возможности

- **Гибкое форматирование логов**:
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,37 @@ void short_names_demo() {

For a standalone program that brings everything together and intentionally aborts after logging a fatal message, check `examples/example_logit_minimal_crash.cpp`.

### System error helpers

`LOGIT_SYSERR_<LEVEL>` captures the current `errno` (or `GetLastError()` on Windows) and appends the decoded information to the message, so failure details stay attached to the original context. The lower-level `LOGIT_PERROR_<LEVEL>` and `LOGIT_WINERR_<LEVEL>` families are also available if you want to explicitly choose the platform macro.

```cpp
#include <LogIt.hpp>
#include <fcntl.h>

int main() {
LOGIT_ADD_CONSOLE_DEFAULT();

if (::open("missing.cfg", O_RDONLY) == -1) {
LOGIT_SYSERR_ERROR("Failed to open configuration");
}

LOGIT_WAIT();
}
```

The error suffix can be customised at compile time via `config.hpp` macros:

```cpp
#define LOGIT_OS_ERROR_JOIN " <- "
#define LOGIT_POSIX_ERROR_PATTERN "[%s] errno=%d (%s)"
#define LOGIT_WINDOWS_ERROR_PATTERN "[%s] GetLastError=%lu (%s)"
#include <LogIt.hpp>

// ... later in the code ...
LOGIT_SYSERR_ERROR("Deleting temp directory failed");
```

---

## Features
Expand Down
14 changes: 14 additions & 0 deletions docs/groups.dox
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ int x = 42;
LOGIT_DEBUG_IF(x > 0, "x is positive: ", x);
\endcode

\par System Error Logging
Use `LOGIT_PERROR_<LEVEL>` on POSIX and `LOGIT_WINERR_<LEVEL>` on Windows to automatically
attach decoded system error codes to your message. The cross-platform
`LOGIT_SYSERR_<LEVEL>` macro aliases to the appropriate variant.
\code
int fd = ::open("missing.cfg", O_RDONLY);
if (fd == -1) {
LOGIT_SYSERR_ERROR("Failed to open configuration");
}
\endcode
Customise the suffix by overriding configuration macros such as
`LOGIT_OS_ERROR_JOIN`, `LOGIT_POSIX_ERROR_PATTERN`, or `LOGIT_WINDOWS_ERROR_PATTERN`
before including `<LogIt.hpp>`.

\par Logger Management
\code
LOGIT_SET_LOGGER_ENABLED(1, false); // Disable logger at index 1
Expand Down
112 changes: 112 additions & 0 deletions include/logit_cpp/logit/LogMacros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#include <fmt/format.h>
#endif

#include "config.hpp"
#include "utils/format.hpp"

/// \file LogMacros.hpp
/// \brief Provides various logging macros for different log levels and options.

Expand Down Expand Up @@ -65,6 +68,115 @@ static_assert(LOGIT_LEVEL_FATAL == static_cast<int>(logit::LogLevel::LOG_LVL_FAT
# define LOGIT_IF_COMPILED_LEVEL(level) if constexpr (LOGIT_COMPILED_LEVEL <= static_cast<int>(level))
#endif

//------------------------------------------------------------------------------
// System error logging helpers

#include "detail/SystemErrorMacros.hpp"

//------------------------------------------------------------------------------
// Platform-specific error logging macros

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_TRACE
#define LOGIT_PERROR_TRACE(message) LOGIT_DETAIL_PERROR(TRACE, message)
#else
#define LOGIT_PERROR_TRACE(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_DEBUG
#define LOGIT_PERROR_DEBUG(message) LOGIT_DETAIL_PERROR(DEBUG, message)
#else
#define LOGIT_PERROR_DEBUG(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_INFO
#define LOGIT_PERROR_INFO(message) LOGIT_DETAIL_PERROR(INFO, message)
#else
#define LOGIT_PERROR_INFO(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_WARN
#define LOGIT_PERROR_WARN(message) LOGIT_DETAIL_PERROR(WARN, message)
#else
#define LOGIT_PERROR_WARN(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_ERROR
#define LOGIT_PERROR_ERROR(message) LOGIT_DETAIL_PERROR(ERROR, message)
#else
#define LOGIT_PERROR_ERROR(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_FATAL
#define LOGIT_PERROR_FATAL(message) LOGIT_DETAIL_PERROR(FATAL, message)
#else
#define LOGIT_PERROR_FATAL(message) do { } while (0)
#endif

#if defined(_WIN32)

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_TRACE
#define LOGIT_WINERR_TRACE(message) LOGIT_DETAIL_WINERR(TRACE, message)
#else
#define LOGIT_WINERR_TRACE(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_DEBUG
#define LOGIT_WINERR_DEBUG(message) LOGIT_DETAIL_WINERR(DEBUG, message)
#else
#define LOGIT_WINERR_DEBUG(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_INFO
#define LOGIT_WINERR_INFO(message) LOGIT_DETAIL_WINERR(INFO, message)
#else
#define LOGIT_WINERR_INFO(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_WARN
#define LOGIT_WINERR_WARN(message) LOGIT_DETAIL_WINERR(WARN, message)
#else
#define LOGIT_WINERR_WARN(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_ERROR
#define LOGIT_WINERR_ERROR(message) LOGIT_DETAIL_WINERR(ERROR, message)
#else
#define LOGIT_WINERR_ERROR(message) do { } while (0)
#endif

#if LOGIT_COMPILED_LEVEL <= LOGIT_LEVEL_FATAL
#define LOGIT_WINERR_FATAL(message) LOGIT_DETAIL_WINERR(FATAL, message)
#else
#define LOGIT_WINERR_FATAL(message) do { } while (0)
#endif

#else // defined(_WIN32)

#define LOGIT_WINERR_TRACE(message) do { } while (0)
#define LOGIT_WINERR_DEBUG(message) do { } while (0)
#define LOGIT_WINERR_INFO(message) do { } while (0)
#define LOGIT_WINERR_WARN(message) do { } while (0)
#define LOGIT_WINERR_ERROR(message) do { } while (0)
#define LOGIT_WINERR_FATAL(message) do { } while (0)

#endif // defined(_WIN32)

#if defined(_WIN32)
#define LOGIT_SYSERR_TRACE(message) LOGIT_WINERR_TRACE(message)
#define LOGIT_SYSERR_DEBUG(message) LOGIT_WINERR_DEBUG(message)
#define LOGIT_SYSERR_INFO(message) LOGIT_WINERR_INFO(message)
#define LOGIT_SYSERR_WARN(message) LOGIT_WINERR_WARN(message)
#define LOGIT_SYSERR_ERROR(message) LOGIT_WINERR_ERROR(message)
#define LOGIT_SYSERR_FATAL(message) LOGIT_WINERR_FATAL(message)
#else
#define LOGIT_SYSERR_TRACE(message) LOGIT_PERROR_TRACE(message)
#define LOGIT_SYSERR_DEBUG(message) LOGIT_PERROR_DEBUG(message)
#define LOGIT_SYSERR_INFO(message) LOGIT_PERROR_INFO(message)
#define LOGIT_SYSERR_WARN(message) LOGIT_PERROR_WARN(message)
#define LOGIT_SYSERR_ERROR(message) LOGIT_PERROR_ERROR(message)
#define LOGIT_SYSERR_FATAL(message) LOGIT_PERROR_FATAL(message)
#endif

//------------------------------------------------------------------------------
// Stream-based logging macros for various levels

Expand Down
30 changes: 30 additions & 0 deletions include/logit_cpp/logit/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,36 @@
#define LOGIT_TAGS_JOIN " | "
#endif

/// \brief Separator appended before platform error metadata when formatting system error messages.
/// Override to customize how human-readable messages and error codes are joined in log output.
#ifndef LOGIT_OS_ERROR_JOIN
#define LOGIT_OS_ERROR_JOIN " | "
#endif

/// \brief Format string used when appending POSIX `errno` information to a log message.
/// This macro can be overridden to change how the user message, errno value, and description are rendered.
#ifndef LOGIT_POSIX_ERROR_PATTERN
#define LOGIT_POSIX_ERROR_PATTERN "%s" LOGIT_OS_ERROR_JOIN "errno=%d (%s)"
#endif

/// \brief Format string used when appending Windows `GetLastError` details to a log message.
/// This macro can be overridden to adjust how the original text, numeric code, and decoded message are displayed.
#ifndef LOGIT_WINDOWS_ERROR_PATTERN
#define LOGIT_WINDOWS_ERROR_PATTERN "%s" LOGIT_OS_ERROR_JOIN "GetLastError=%lu (%s)"
#endif

/// \brief Selects the default system error formatting macro for the current platform.
/// Users may redefine this alias to integrate custom error formatting logic.
#if defined(_WIN32)
#ifndef LOGIT_SYSTEM_ERROR_PATTERN
#define LOGIT_SYSTEM_ERROR_PATTERN LOGIT_WINDOWS_ERROR_PATTERN
#endif
#else
#ifndef LOGIT_SYSTEM_ERROR_PATTERN
#define LOGIT_SYSTEM_ERROR_PATTERN LOGIT_POSIX_ERROR_PATTERN
#endif
#endif

/// \brief Separator between individual tag pairs (e.g., " " or "; ").
#ifndef LOGIT_TAG_PAIR_SEP
#define LOGIT_TAG_PAIR_SEP " "
Expand Down
85 changes: 85 additions & 0 deletions include/logit_cpp/logit/detail/SystemErrorMacros.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#pragma once
#ifndef _LOGIT_DETAIL_SYSTEM_ERROR_MACROS_HPP_INCLUDED
#define _LOGIT_DETAIL_SYSTEM_ERROR_MACROS_HPP_INCLUDED

#include <errno.h>
#include <cstring>
#include <string>

/// \file SystemErrorMacros.hpp
/// \brief Internal helpers shared by the system error logging macros.

#include "../config.hpp"
#include "../utils/format.hpp"

#if defined(_WIN32)
#include <windows.h>
#endif

namespace logit {
namespace detail {

#if defined(_WIN32)
inline std::string _logit_format_winerr(DWORD code) {
LPSTR buffer = nullptr;
const DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
const DWORD length = ::FormatMessageA(flags,
nullptr,
code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&buffer),
0,
nullptr);

if (length == 0 || buffer == nullptr) {
return std::string();
}

std::string message(buffer, length);
::LocalFree(buffer);

while (!message.empty() && (message.back() == '\r' || message.back() == '\n')) {
message.pop_back();
}

return message;
}
#endif // defined(_WIN32)

} // namespace detail
} // namespace logit

#define LOGIT_DETAIL_PERROR(level_macro, message) \
do { \
const int _logit_errno_code = (errno); \
const char *_logit_errno_desc = ::strerror(_logit_errno_code); \
if (_logit_errno_desc == nullptr) { \
_logit_errno_desc = ""; \
} \
const std::string _logit_errno_text(message); \
LOGIT_PRINTF_##level_macro(LOGIT_POSIX_ERROR_PATTERN, \
_logit_errno_text.c_str(), \
_logit_errno_code, \
_logit_errno_desc); \
} while (0)

#if defined(_WIN32)

#define LOGIT_DETAIL_WINERR(level_macro, message) \
do { \
const DWORD _logit_winerr_code = ::GetLastError(); \
const std::string _logit_winerr_text(message); \
const std::string _logit_winerr_desc = ::logit::detail::_logit_format_winerr(_logit_winerr_code); \
LOGIT_PRINTF_##level_macro(LOGIT_WINDOWS_ERROR_PATTERN, \
_logit_winerr_text.c_str(), \
static_cast<unsigned long>(_logit_winerr_code), \
_logit_winerr_desc.c_str()); \
} while (0)

#else // defined(_WIN32)

#define LOGIT_DETAIL_WINERR(level_macro, message) do { } while (0)

#endif // defined(_WIN32)

#endif // _LOGIT_DETAIL_SYSTEM_ERROR_MACROS_HPP_INCLUDED
Loading
Loading