diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18406651..859798b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,37 @@ jobs: run: cmake -S tests/install_consumer -B build-consumer -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install -DCMAKE_CXX_STANDARD=${{ matrix.std }} - name: Build consumer project run: cmake --build build-consumer + - name: Configure and install fmt dependency + if: matrix.std == 17 + run: cmake -S external/fmt -B build-fmt -DFMT_TEST=OFF -DFMT_DOC=OFF -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install-fmt + - name: Build and install fmt dependency + if: matrix.std == 17 + run: | + cmake --build build-fmt + cmake --install build-fmt + - name: Configure fmt-enabled package + if: matrix.std == 17 + run: cmake -S . -B build-fmt-package -DLOGIT_CPP_BUILD_TESTS=OFF -DLOGIT_WITH_FMT=ON -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-fmt -DLOGIT_WITH_SYSLOG=OFF -DLOGIT_WITH_WIN_EVENT_LOG=OFF + - name: Install fmt-enabled package + if: matrix.std == 17 + run: | + cmake --build build-fmt-package + cmake --install build-fmt-package --prefix install-fmt-package + - name: Configure fmt-enabled consumer project + if: matrix.std == 17 + run: cmake -S tests/install_consumer -B build-fmt-consumer -DLOGIT_CONSUMER_REQUIRE_FMT=ON -DCMAKE_PREFIX_PATH="${{ github.workspace }}/install-fmt-package;${{ github.workspace }}/install-fmt" + - name: Build fmt-enabled consumer project + if: matrix.std == 17 + run: cmake --build build-fmt-consumer + - name: Configure Prometheus server package + if: matrix.std == 17 + run: cmake -S . -B build-prometheus-package -DLOGIT_CPP_BUILD_TESTS=OFF -DLOGIT_WITH_PROMETHEUS_SERVER=ON -DLOGIT_WITH_SYSLOG=OFF -DLOGIT_WITH_WIN_EVENT_LOG=OFF + - name: Build Prometheus server package + if: matrix.std == 17 + run: cmake --build build-prometheus-package + - name: Verify Prometheus server install guard + if: matrix.std == 17 + run: cmake -DBUILD_DIR=${{ github.workspace }}/build-prometheus-package -DINSTALL_PREFIX=${{ github.workspace }}/install-prometheus -P tests/check_prometheus_server_install_guard.cmake - name: Upload logs if: failure() uses: actions/upload-artifact@v4 diff --git a/.gitmodules b/.gitmodules index 659e30ae..023310f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "external/time-shield-cpp"] path = external/time-shield-cpp - url = https://github.com/NewYaroslav/time-shield-cpp.git + url = https://github.com/LimiNode/time-shield-cpp.git [submodule "external/fmt"] path = external/fmt url = https://github.com/fmtlib/fmt.git @@ -15,7 +15,7 @@ url = https://github.com/madler/zlib [submodule "external/mdbx-containers"] path = external/mdbx-containers - url = https://github.com/NewYaroslav/mdbx-containers.git + url = https://github.com/LimiNode/mdbx-containers.git [submodule "external/kurlyk"] path = external/kurlyk - url = https://github.com/NewYaroslav/kurlyk.git + url = https://github.com/LimiNode/kurlyk.git diff --git a/AGENTS.md b/AGENTS.md index 031378fb..a5282190 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,6 +50,37 @@ These headers prepare internal dependencies in the intended order. - If a header contains mixed declarations, helpers, macros, or multiple types, use a snake_case filename. +## Scoped Instructions + +Read the nearest module guide before editing files in that tree. These guides +keep public-header, test, example, documentation, and agent-workflow rules +close to the code they govern: + +- [`include/logit_cpp/AGENTS.md`](include/logit_cpp/AGENTS.md) - public API and + header-only implementation contracts. +- [`tests/AGENTS.md`](tests/AGENTS.md) - test design, registration, and + platform-sensitive test rules. +- [`examples/AGENTS.md`](examples/AGENTS.md) - example portability and + dependency expectations. +- [`docs/AGENTS.md`](docs/AGENTS.md) - documentation consistency and generated + output boundaries. +- [`guides/AGENTS.md`](guides/AGENTS.md) - maintaining playbooks used by + humans and coding agents. + +When a change crosses module boundaries, follow all applicable guides and +record the public contract in the narrowest relevant document. Do not copy +the same rule into every guide. + +## Review Checklist + +Before submitting a change, inspect both enabled and disabled compile-time +paths. In particular, check that `noexcept` functions cannot allocate, invoke +user callbacks, or propagate exceptions; that conditions are reachable and +not permanently true or false; that repeated logic has a single owner; and +that optional dependencies do not break a default build or package export. +Run the focused tests plus the relevant CMake configure/build flow and report +platform-only failures explicitly. + ## Header Guards For every project-owned C/C++ header, use `#pragma once` together with a diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a60aea..53761f12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,21 +2,42 @@ All notable changes to this project will be documented in this file. -## [v1.0.2] - 2026-04-25 -- Added raw and section logging macros for unformatted diagnostic snapshots that bypass level filters while still using configured backends, queues, routing, and file rotation. -- Added in-memory snapshot logging APIs, buffered entry retrieval, runtime logger snapshots, and examples for control-plane style diagnostics. -- Added persisted file access APIs for listing and reading current and rotated file logs. -- Added system logging backends for POSIX syslog and Windows Event Log, plus POSIX/Windows crash logger backends and registration macros. -- Added compile-time log-level filtering, runtime log-level controls, conditional logging helpers, frequency controls, tagging macros, stream/printf/scope macro coverage, and default no-op handling for disabled macro families. -- Added file logger size-based rotation, rotation naming policies, retention coverage, gzip/zstd/external-command compression support, and idempotent rotation tests. -- Added configurable async backpressure controls, queue policies, lock-free MPSC task execution, hot queue resizing, and TSAN-oriented regression coverage. -- Added Emscripten build support, CMake package installation metadata, pkg-config generation, vcpkg overlay updates, and install-consumer coverage. -- Added benchmark coverage and refreshed benchmark adapters, latency snapshots, and CI benchmark gating. -- Expanded CI coverage with sanitizer, Emscripten, ODR, install-consumer, compression, and platform-specific regression checks. -- Reorganized public include entry points, moved internal helpers under `detail`, hardened header-only ODR behavior, and fixed utility/header dependency issues. -- Refreshed README, README-RU, Doxygen, agent guidance, macro references, examples, and architecture/task-executor documentation. -- Updated bundled dependency pins, including TimeShield through `v1.0.5` and compression dependency pins. -- Fixed Windows crash-filter naming collisions, FileLogger rotation ordering and error handling, benchmark async flushing, MPSC/drop-policy accounting, fmt-disabled macro handling, and `LOGIT_SCOPE_*` duration logging with unnamed messages. +## [Unreleased] + +Target release: **v1.0.2** + +### Added + +- Raw and section logging macros for unformatted diagnostic snapshots, plus in-memory and persisted snapshot/file access APIs. +- POSIX syslog, Windows Event Log, POSIX/Windows crash logger, Prometheus payload/registry/HTTP server, OTLP/HTTP, and MDBX logger backends. +- Structured OTLP attributes, callback-based exporting, payload splitting, compression, export counters, and MDC/NDC context support. +- Compile-time and runtime log-level controls, conditional/frequency/tagging helpers, stream/printf/fmt/scope macro families, and configurable console output. +- File rotation by size and timestamp with retention policies and gzip/zstd/external-command compression. +- Configurable asynchronous backpressure, queue policies, lock-free MPSC execution, hot queue resizing, and dedicated executor controls. +- Emscripten support, CMake/pkg-config package metadata, vcpkg integration, install-consumer coverage, and latency benchmarks. + +### Changed + +- Reorganized public umbrella headers and moved implementation helpers under `detail` while preserving header-only ODR safety. +- Made logger configuration, shutdown, queue resizing, and single-thread executor lifecycle behavior explicit and consistent across backends. + +### Fixed + +- Corrected file rotation ordering/error handling, async shutdown and flushing, queue drop accounting, MPSC lifecycle handling, and benchmark synchronization. +- Fixed platform portability issues, crash-filter naming collisions, disabled-fmt macro behavior, scope duration logging, and missing/self-contained header dependencies. + +### Packaging / Build + +- Refreshed bundled dependency revisions and canonical repository URLs. +- Added fail-closed installation checks for unsupported bundled optional dependencies and source-tree-only Prometheus server headers. + +### CI / Testing + +- Expanded regression coverage for sanitizers, TSAN, ODR, Emscripten, optional compression/backends, package consumers, and platform-specific behavior. + +### Documentation + +- Refreshed README, README-RU, Doxygen, examples, macro references, task-executor guidance, and scoped `AGENTS.md` instructions. ## [v1.0.1] - 2025-08-05 - Added initial CMake integration for building, installing, and consuming the header-only package. diff --git a/CMakeLists.txt b/CMakeLists.txt index 2efdaba8..74446e55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,11 @@ if(LOGIT_WITH_OTLP) if(NOT TARGET kurlyk AND LOGIT_USE_SUBMODULES) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/kurlyk/CMakeLists.txt") + if(CMAKE_VERSION VERSION_LESS 3.21) + message(FATAL_ERROR + "Bundled kurlyk requires CMake 3.21 or newer; install kurlyk separately " + "or upgrade CMake.") + endif() set(KURLYK_WEBSOCKET_SUPPORT OFF CACHE BOOL "Disable kurlyk WebSocket support for LogIt++ OTLP" FORCE) set(KURLYK_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) @@ -230,6 +235,7 @@ if(LOGIT_WITH_GZIP) find_package(ZLIB QUIET) endif() if(NOT TARGET ZLIB::ZLIB AND LOGIT_USE_SUBMODULES) + set(ZLIB_BUILD_EXAMPLES OFF CACHE BOOL "Disable bundled zlib examples" FORCE) add_subdirectory(external/zlib EXCLUDE_FROM_ALL) if(TARGET zlibstatic) add_library(ZLIB::ZLIB ALIAS zlibstatic) @@ -283,10 +289,6 @@ endif() include(CMakePackageConfigHelpers) -install(DIRECTORY include/ DESTINATION include) - -install(TARGETS log-it-cpp EXPORT log-it-cppTargets) - set(_logit_install_export_supported ON) # install(EXPORT) requires all linked targets to be in an export set. @@ -318,6 +320,62 @@ if(LOGIT_WITH_MDBX AND TARGET mdbx_containers::mdbx_containers) endif() endif() +if(LOGIT_WITH_FMT AND TARGET fmt::fmt) + get_target_property(_fmt_imported fmt::fmt IMPORTED) + if(NOT _fmt_imported) + set(_logit_install_export_supported OFF) + install(CODE [[ + message(FATAL_ERROR + "log-it-cpp: Installing with bundled fmt is not supported. " + "Install fmt separately and use find_package(fmt), or disable LOGIT_WITH_FMT for install.") + ]]) + endif() +endif() + +if(LOGIT_WITH_PROMETHEUS_SERVER) + set(_logit_install_export_supported OFF) + install(CODE [[ + message(FATAL_ERROR + "log-it-cpp: Installing with LOGIT_WITH_PROMETHEUS_SERVER=ON is not currently supported. " + "Disable LOGIT_WITH_PROMETHEUS_SERVER for install.") + ]]) +endif() + +# Bundled compression targets are implementation details of the build tree and +# are not part of this package's export set. Keep development builds working, +# but fail at install time with an actionable message, just like the other +# bundled optional dependencies above. +if(LOGIT_WITH_GZIP AND TARGET zlibstatic) + get_target_property(_zlib_imported zlibstatic IMPORTED) + if(NOT _zlib_imported) + set(_logit_install_export_supported OFF) + install(CODE [[ + message(FATAL_ERROR + "log-it-cpp: Installing with bundled zlib is not supported. " + "Install zlib separately and use find_package(ZLIB), or disable LOGIT_WITH_GZIP for install.") + ]]) + endif() +endif() + +if(LOGIT_WITH_ZSTD AND TARGET libzstd_static) + get_target_property(_zstd_imported libzstd_static IMPORTED) + if(NOT _zstd_imported) + set(_logit_install_export_supported OFF) + install(CODE [[ + message(FATAL_ERROR + "log-it-cpp: Installing with bundled zstd is not supported. " + "Install zstd separately and use find_package(ZSTD), or disable LOGIT_WITH_ZSTD for install.") + ]]) + endif() +endif() + +# Register the guard rules before any file or target install rule. If the +# selected dependency targets cannot be exported, `cmake --install` fails +# before writing a partial package tree. +install(DIRECTORY include/ DESTINATION include) + +install(TARGETS log-it-cpp EXPORT log-it-cppTargets) + if(_logit_install_export_supported) install(EXPORT log-it-cppTargets FILE log-it-cppTargets.cmake diff --git a/README-RU.md b/README-RU.md index eeaf4a83..8845f287 100644 --- a/README-RU.md +++ b/README-RU.md @@ -773,7 +773,7 @@ LogIt++ — это библиотека, работающая только с з 1. Клонируйте репозиторий с его подмодулями: ```bash -git clone --recurse-submodules https://github.com/NewYaroslav/log-it-cpp.git +git clone --recurse-submodules https://github.com/LimiNode/log-it-cpp.git ``` 2. Включите заголовочные файлы LogIt++ в ваш проект: @@ -885,4 +885,4 @@ LOGIT_ERROR("Что-то пошло не так"); --- ## Лицензия -Эта библиотека распространяется под лицензией MIT. Подробности смотрите в файле [LICENSE](https://github.com/NewYaroslav/log-it-cpp/blob/main/LICENSE) в репозитории. +Эта библиотека распространяется под лицензией MIT. Подробности смотрите в файле [LICENSE](https://github.com/LimiNode/log-it-cpp/blob/main/LICENSE) в репозитории. diff --git a/README.md b/README.md index 8bd852a4..031779b8 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ [![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) ![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20%7C%20Emscripten-blue) ![C++ Standard](https://img.shields.io/badge/C++-11--17-orange) -![CI Windows](https://img.shields.io/github/actions/workflow/status/NewYaroslav/log-it-cpp/ci.yml?branch=main&label=Windows&logo=windows) -![CI Linux](https://img.shields.io/github/actions/workflow/status/NewYaroslav/log-it-cpp/ci.yml?branch=main&label=Linux&logo=linux) -![CI macOS](https://img.shields.io/github/actions/workflow/status/NewYaroslav/log-it-cpp/ci.yml?branch=main&label=macOS&logo=apple) +![CI Windows](https://img.shields.io/github/actions/workflow/status/LimiNode/log-it-cpp/ci.yml?branch=main&label=Windows&logo=windows) +![CI Linux](https://img.shields.io/github/actions/workflow/status/LimiNode/log-it-cpp/ci.yml?branch=main&label=Linux&logo=linux) +![CI macOS](https://img.shields.io/github/actions/workflow/status/LimiNode/log-it-cpp/ci.yml?branch=main&label=macOS&logo=apple) [Читать на русском](README-RU.md) @@ -956,7 +956,7 @@ LogIt++ is a header-only library. To integrate it into your project, follow thes 1. Clone the repository with its submodules: ```bash -git clone --recurse-submodules https://github.com/NewYaroslav/log-it-cpp.git +git clone --recurse-submodules https://github.com/LimiNode/log-it-cpp.git ``` 2. Include the LogIt++ headers in your project: @@ -1093,4 +1093,4 @@ Detailed documentation for LogIt++, including API reference and usage examples, --- ## License -This library is licensed under the MIT License. See the [LICENSE](https://github.com/NewYaroslav/log-it-cpp/blob/main/LICENSE) file in the repository for more details. +This library is licensed under the MIT License. See the [LICENSE](https://github.com/LimiNode/log-it-cpp/blob/main/LICENSE) file in the repository for more details. diff --git a/cmake/log-it-cppConfig.cmake.in b/cmake/log-it-cppConfig.cmake.in index f0a8663b..3f2c8e71 100644 --- a/cmake/log-it-cppConfig.cmake.in +++ b/cmake/log-it-cppConfig.cmake.in @@ -2,8 +2,25 @@ include(CMakeFindDependencyMacro) find_dependency(TimeShield) + +if(@LOGIT_WITH_FMT@) + find_dependency(fmt CONFIG) +endif() + +if(@LOGIT_WITH_OTLP@) + find_dependency(kurlyk CONFIG) +endif() + +if(@LOGIT_WITH_GZIP@) + find_dependency(ZLIB) +endif() + +if(@LOGIT_WITH_ZSTD@) + find_dependency(ZSTD) +endif() + if(@LOGIT_WITH_MDBX@) - find_dependency(mdbx_containers) + find_dependency(mdbx_containers CONFIG) endif() include("${CMAKE_CURRENT_LIST_DIR}/log-it-cppTargets.cmake") diff --git a/docs/AGENTS.md b/docs/AGENTS.md new file mode 100644 index 00000000..b65db3f1 --- /dev/null +++ b/docs/AGENTS.md @@ -0,0 +1,11 @@ +# Documentation Instructions + +Documentation is part of the public API contract. + +- Update the relevant English and Russian document together when behavior, + options, examples, or compatibility changes. +- Keep snippets buildable with the stated CMake options and include paths. + Prefer linking to one canonical explanation over duplicating long blocks. +- Distinguish implemented behavior from future plans and platform limitations; + do not promise optional backends that are not tested in the documented flow. +- Do not commit generated Doxygen output or local build artifacts. diff --git a/docs/OtlpHttpLogger.md b/docs/OtlpHttpLogger.md index 31bb499a..4b6b18df 100644 --- a/docs/OtlpHttpLogger.md +++ b/docs/OtlpHttpLogger.md @@ -2,7 +2,7 @@ `OtlpHttpLogger` is an optional LogIt++ backend that exports log records to an OpenTelemetry-compatible OTLP/HTTP endpoint. -The backend is disabled by default and requires the optional [`kurlyk`](https://github.com/NewYaroslav/kurlyk) dependency. +The backend is disabled by default and requires the optional [`kurlyk`](https://github.com/LimiNode/kurlyk) dependency. ## CMake @@ -16,6 +16,10 @@ When `kurlyk` is placed at `external/kurlyk`, LogIt++ adds it only when `LOGIT_W For Windows MinGW builds, the CMake integration enables kurlyk fallback options for curl, OpenSSL, and Asio when `LOGIT_USE_SUBMODULES=ON`. This keeps OTLP optional while still allowing a ready-made MinGW dependency path through kurlyk. +The bundled kurlyk revision requires CMake 3.21 or newer. On other platforms, +install kurlyk (and its OpenSSL/curl prerequisites) separately and let +`find_package(kurlyk)` provide the target. + ## Usage For a runnable version with environment overrides, graceful shutdown, optional diff --git a/docs/mainpage.dox b/docs/mainpage.dox index 9eaade8f..64773f40 100644 --- a/docs/mainpage.dox +++ b/docs/mainpage.dox @@ -903,7 +903,7 @@ First, clone the LogIt++ repository from GitHub along with its submodules. The r To clone the repository with submodules, use the following command: \code{bash} -git clone --recurse-submodules https://github.com/NewYaroslav/log-it-cpp.git +git clone --recurse-submodules https://github.com/LimiNode/log-it-cpp.git \endcode If you have already cloned the repository without submodules, you can initialize and update the submodules by running the following commands: @@ -992,9 +992,9 @@ After adding the necessary include paths, you can proceed to build and run your \section repo_sec Repository The LogIt++ library is open-source and hosted on GitHub: -[LogIt++ GitHub Repository](https://github.com/NewYaroslav/log-it-cpp). +[LogIt++ GitHub Repository](https://github.com/LimiNode/log-it-cpp). \section license_sec License -This library is licensed under the **MIT License**. See the [LICENSE](https://github.com/NewYaroslav/log-it-cpp/blob/main/LICENSE) file in the repository for more details. +This library is licensed under the **MIT License**. See the [LICENSE](https://github.com/LimiNode/log-it-cpp/blob/main/LICENSE) file in the repository for more details. */ diff --git a/examples/AGENTS.md b/examples/AGENTS.md new file mode 100644 index 00000000..2d065ff4 --- /dev/null +++ b/examples/AGENTS.md @@ -0,0 +1,13 @@ +# Example Instructions + +Examples are compile-time and usage documentation for consumers. + +- Include only installed/public headers and use the umbrella headers where + practical. Do not reach into `detail/` from an example. +- Keep examples portable across the default C++11 build. Guard MDBX, OTLP, + compression, and platform-specific code with the same CMake feature options + as the library. +- Examples must be safe to run repeatedly: use temporary output paths and do + not assume a writable system directory or external service. +- Keep output concise and explain prerequisites in the adjacent README when a + dependency or service is required. diff --git a/external/kurlyk b/external/kurlyk index f683f0bf..785e4bc5 160000 --- a/external/kurlyk +++ b/external/kurlyk @@ -1 +1 @@ -Subproject commit f683f0bf2249cef5236f2f591adff1061afe7080 +Subproject commit 785e4bc5c14ed9cfe073236e3819103992c8b1c0 diff --git a/external/mdbx-containers b/external/mdbx-containers index 095e46c3..2e8b914b 160000 --- a/external/mdbx-containers +++ b/external/mdbx-containers @@ -1 +1 @@ -Subproject commit 095e46c3bff0305ab14a9718a4c4054701133c69 +Subproject commit 2e8b914b8a1e3a683273ee7e471de764423b2308 diff --git a/external/time-shield-cpp b/external/time-shield-cpp index d3c251bf..21d6ca76 160000 --- a/external/time-shield-cpp +++ b/external/time-shield-cpp @@ -1 +1 @@ -Subproject commit d3c251bf173ee6222f6e79186a34c4a0592d5767 +Subproject commit 21d6ca767ca492aca14b883d16abe6066fe5267e diff --git a/guides/AGENTS.md b/guides/AGENTS.md new file mode 100644 index 00000000..b1e5a4e6 --- /dev/null +++ b/guides/AGENTS.md @@ -0,0 +1,13 @@ +# Guide Maintenance Instructions + +Guides are operational instructions for maintainers and coding agents. + +- Keep rules concrete, scoped, and testable. Put module-specific rules in the + nearest `AGENTS.md` instead of expanding the root file. +- Update command examples when CMake options, dependency versions, or CI + behavior changes. Verify commands against the repository's minimum CMake + and C++ standards. +- Avoid duplicating API documentation from `README.md`; link to the canonical + source and focus on workflow, invariants, and failure handling. +- Record known platform limitations and expected environment-only failures so + future reviews do not mistake them for regressions. diff --git a/guides/build.md b/guides/build.md index a407c390..caf1bde7 100644 --- a/guides/build.md +++ b/guides/build.md @@ -41,6 +41,11 @@ From `CMakeLists.txt` and `README.md`, the most relevant toggles are: - `LOGIT_ENABLE_DROP_OLDEST_SLOWPATH` - compile the ring slow-path for `DropOldest`. +When zlib, zstd, kurlyk, or mdbx-containers are supplied from submodules, +they are suitable for development and tests. Package installation must use +installed/imported dependency targets; the CMake export deliberately stops +with an explanatory error instead of producing a broken package. + ## Typical flows ### Configure tests diff --git a/guides/ci.md b/guides/ci.md index 0852de32..958687b2 100644 --- a/guides/ci.md +++ b/guides/ci.md @@ -9,6 +9,9 @@ could compile differently across the supported matrix. - Check `cmake_minimum_required` before adding CMake syntax. This project currently declares CMake 3.18, so avoid commands, options, or policy-dependent behavior that require newer CMake unless the minimum is intentionally raised. +- The bundled `external/kurlyk` submodule currently requires CMake 3.21. Keep + the root minimum at 3.18 for builds without OTLP, but guard the bundled OTLP + path with an explicit version check and document the requirement. - Keep the C++ standard matrix in mind. The default build supports C++11, while MDBX, OTLP, and Prometheus server paths may require C++17. Do not use C++17 language/library features in C++11 paths. diff --git a/include/logit_cpp/AGENTS.md b/include/logit_cpp/AGENTS.md new file mode 100644 index 00000000..c59da3d7 --- /dev/null +++ b/include/logit_cpp/AGENTS.md @@ -0,0 +1,34 @@ +# Public Header Instructions + +This tree is the installed, header-only API. Keep changes source-compatible +with the documented C++11 baseline; code behind MDBX, OTLP, and Prometheus +server feature macros may use C++17 only when CMake selects that standard. + +For subsystem-specific work, also read the nearest guide: + +- [`logit/detail/AGENTS.md`](logit/detail/AGENTS.md) - queue, executor, and + signal-safety invariants. +- [`logit/loggers/AGENTS.md`](logit/loggers/AGENTS.md) - backend lifecycle and + callback ownership. +- [`logit/formatter/AGENTS.md`](logit/formatter/AGENTS.md) - token and + timestamp semantics. +- [`logit/utils/AGENTS.md`](logit/utils/AGENTS.md) - validation and stable + serialization helpers. + +- Include the nearest umbrella (`logit.hpp`, `utils.hpp`, `formatter.hpp`, or + `loggers.hpp`) in examples and integration tests. +- Preserve the existing public names, overloads, macro expansion contracts, + and feature guards. Add new API only with a focused test and documentation. +- Keep headers self-contained: include every standard type used directly and + do not depend on include order or transitive headers. +- A `noexcept` declaration is a contract. Do not perform allocation, invoke a + user callback, or execute code that may throw in a `noexcept` function. + Signal/crash handlers are the explicit exception and must stay async-signal + safe. +- For queue/executor code, preserve shutdown, ordering, and callback + synchronization invariants described in `guides/concurrency.md`. +- Keep detail headers private and free of includes from public `logit/...` + paths; cross-module includes go through the nearest umbrella. + +When changing an inline implementation, inspect all call sites with +Codebase Memory and add a regression test before simplifying duplicated code. diff --git a/include/logit_cpp/logit/detail/AGENTS.md b/include/logit_cpp/logit/detail/AGENTS.md new file mode 100644 index 00000000..9664e611 --- /dev/null +++ b/include/logit_cpp/logit/detail/AGENTS.md @@ -0,0 +1,24 @@ +# Detail-layer invariants + +This directory contains implementation building blocks shared by the public +headers. It is not a second public API. + +- Keep detail headers independent from `logit/...` public headers. Public + cross-module dependencies go through the nearest umbrella header. +- `MpscRingAny` is a bounded MPSC queue with one consumer. Producers may claim + a cell only when they can publish it without throwing; a claimed cell must + always become visible or the queue can stall permanently. +- Queue resize and replacement happen only under the executor lifecycle lock. + Stop accepting producers before moving or destroying a queue, then join the + worker before releasing backing storage. +- Preserve release/acquire publication ordering and the sequence-number + protocol. Do not replace atomic operations with plain loads/stores to make a + test pass. +- `noexcept` in this layer is a real contract. Check allocation, construction, + move, destruction, and callback behavior before adding it. Prefer a + compile-time trait or an explicit precondition over silently terminating. +- Keep signal/crash-safe helpers free of allocation, locks, formatting, and + non-async-signal-safe calls. + +Use `guides/concurrency.md` for the full shutdown and callback ordering +contract. Add a focused stress or lifecycle test when changing these rules. diff --git a/include/logit_cpp/logit/detail/MpscRingAny.hpp b/include/logit_cpp/logit/detail/MpscRingAny.hpp index beceb4ee..ea9d628c 100644 --- a/include/logit_cpp/logit/detail/MpscRingAny.hpp +++ b/include/logit_cpp/logit/detail/MpscRingAny.hpp @@ -14,8 +14,15 @@ namespace logit { namespace detail { /// \brief Bounded MPSC ring buffer with arbitrary capacity (C++11). /// \tparam T Stored type. + /// \note T must be nothrow move-constructible, nothrow move-assignable, + /// and nothrow-destructible because cells are published lock-free. template class MpscRingAny { + static_assert(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value && + std::is_nothrow_destructible::value, + "MpscRingAny requires no-throw move and destruction operations"); + private: /// \brief Single cell storing sequence number and raw storage for T. struct Cell { @@ -78,6 +85,8 @@ namespace logit { namespace detail { /// \return true on success; false if queue is full. template bool try_push(U&& v) noexcept { + static_assert(std::is_nothrow_constructible::value, + "MpscRingAny::try_push requires no-throw construction; pass a movable value"); std::size_t pos = m_enqueue_pos.load(std::memory_order_relaxed); for (;;) { Cell& c = m_cells[pos % m_cap]; @@ -161,9 +170,19 @@ namespace logit { namespace detail { if (!m_cells || m_cap == 0) { return; } - T tmp; - while (try_pop(tmp)) { - // Element destroyed via move-from tmp + + // Destruction is called only after producers and the consumer have + // stopped. Inspect the outstanding sequence range and destroy + // live objects in place; this keeps the ring usable with move-only + // or non-default-constructible payload types. + const std::size_t begin = m_dequeue_pos.load(std::memory_order_relaxed); + const std::size_t end = m_enqueue_pos.load(std::memory_order_relaxed); + for (std::size_t pos = begin; pos != end; ++pos) { + Cell& c = m_cells[pos % m_cap]; + const std::size_t seq = c.m_seq.load(std::memory_order_relaxed); + if (seq == pos + 1) { + reinterpret_cast(&c.m_storage)->~T(); + } } } diff --git a/include/logit_cpp/logit/detail/TaskExecutor.hpp b/include/logit_cpp/logit/detail/TaskExecutor.hpp index 0d5b57d0..516f9b77 100644 --- a/include/logit_cpp/logit/detail/TaskExecutor.hpp +++ b/include/logit_cpp/logit/detail/TaskExecutor.hpp @@ -234,7 +234,11 @@ namespace logit { namespace detail { } // Try to push into the ring buffer. - if (m_mpsc_queue.try_push(local_task)) { + // Move into the ring: std::function's move construction is + // noexcept, while copying may allocate and throw. The ring + // deliberately accepts only no-throw construction so a + // producer can never leave a claimed cell unpublished. + if (m_mpsc_queue.try_push(std::move(local_task))) { m_cv.notify_one(); // wake the worker break; } diff --git a/include/logit_cpp/logit/formatter/AGENTS.md b/include/logit_cpp/logit/formatter/AGENTS.md new file mode 100644 index 00000000..05e0662f --- /dev/null +++ b/include/logit_cpp/logit/formatter/AGENTS.md @@ -0,0 +1,18 @@ +# Formatter instructions + +Formatters convert immutable `LogRecord` values to text or payload fragments. + +- Keep formatting side-effect free: do not mutate the record, logger state, or + global configuration while formatting. +- Preserve pattern-token semantics, escaping rules, timestamp units, timezone + behavior, and raw-mode passthrough. Add a focused test for every new token + or changed edge case. +- Use TimeShield conversion helpers for date/time calculations; do not create a + second calendar or timezone implementation in this module. +- Avoid allocations in fast paths when the existing API permits it, but do not + trade correctness for micro-optimizations or introduce hidden static state. +- Keep formatter interfaces compatible with C++11 unless the enclosing feature + explicitly requires C++17. + +Document unsupported tokens and fallback behavior in the formatter guide and +keep compiler/parser helpers private to this subtree. diff --git a/include/logit_cpp/logit/loggers/AGENTS.md b/include/logit_cpp/logit/loggers/AGENTS.md new file mode 100644 index 00000000..79c99c40 --- /dev/null +++ b/include/logit_cpp/logit/loggers/AGENTS.md @@ -0,0 +1,23 @@ +# Logger backend instructions + +Each logger backend must honor the common `ILogger` lifecycle contract while +remaining independently usable through `loggers.hpp`. + +- `log()` must become a no-op after shutdown and must not access backend state + after the logger's worker or callback resources are released. +- Serialize backend-specific writes with the backend mutex/executor only; do + not hold the global `Logger` registry lock while invoking user callbacks or + doing I/O. +- Snapshot callback/subscriber lists before invocation. Callbacks may remove + themselves, add another callback, throw, or call back into the logger. +- Keep synchronous and asynchronous modes behaviorally equivalent for ordering, + error reporting, and shutdown. Test both modes when changing dispatch. +- Optional backends must remain behind their feature macros and must include + only the dependency umbrella supplied by CMake. +- File and MDBX backends own durable resources. Close/commit them before + destruction and report failures through the configured error callback without + allowing callback exceptions to escape a `noexcept` path. + +Update the backend-specific documentation and tests with any lifecycle or +configuration change. Do not duplicate serialization or retry logic when a +shared helper already owns that behavior. diff --git a/include/logit_cpp/logit/utils/AGENTS.md b/include/logit_cpp/logit/utils/AGENTS.md new file mode 100644 index 00000000..3c548dab --- /dev/null +++ b/include/logit_cpp/logit/utils/AGENTS.md @@ -0,0 +1,19 @@ +# Utility instructions + +Utilities are shared value types and conversions used by multiple backends. + +- Keep utilities deterministic, thread-safe by default, and independent of + logger singletons. Thread-local context is the explicit exception and must + preserve push/pop nesting and thread isolation. +- Validate lengths, encodings, paths, and numeric ranges at the boundary. + Malformed input should return the documented error or throw the documented + exception; never silently truncate semantic data. +- Name constants for protocol bytes, limits, units, and sentinel values. Avoid + unexplained literals in serialization and parsing code. +- Do not add convenience overloads that depend on transitive includes. Include + every standard type directly used by a utility header. +- Keep serialization formats stable. A format change requires compatibility + tests for old data and documentation of the versioning decision. + +Prefer one canonical helper over copy-pasted conversions in individual +backends. Update `utils.hpp` when adding a public utility. diff --git a/tests/AGENTS.md b/tests/AGENTS.md new file mode 100644 index 00000000..7b2c67df --- /dev/null +++ b/tests/AGENTS.md @@ -0,0 +1,20 @@ +# Test Instructions + +Tests define the supported behavior of the header-only library. + +- Prefer deterministic assertions and condition variables over sleeps or + scheduler timing. If timing is the behavior under test, use a generous + bound and document the reason. +- Register every new test in `tests/CMakeLists.txt`; keep test names stable so + CI and downstream users can filter them. +- Exercise both feature-enabled and feature-disabled paths when a change + touches a compile-time option. Include-only tests must include only the + public header named by the test. +- Do not require Unix tools (`rm`, `gzip`, `bash`) in tests that run on + Windows. Resolve tools through CMake or skip the external-command case with + a clear reason. +- Avoid global state leakage between tests. Shut down executors and loggers + before their backing objects leave scope. + +Run the focused executable first, then `ctest --output-on-failure`; report +environment-only failures separately from code failures. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d67a7066..3c904e2d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,6 +53,7 @@ else() memory_logger_integration_test.cpp mdbx_logger_test.cpp mdc_ndc_context_test.cpp + mpsc_ring_any_test.cpp os_error_macros_test.cpp otlp_http_logger_integration_test.cpp otlp_http_logger_callback_test.cpp @@ -124,6 +125,9 @@ else() add_executable(${test_name} ${test_src}) target_link_libraries(${test_name} PRIVATE log-it-cpp) add_test(NAME ${test_name} COMMAND ${test_name}) + if(test_name STREQUAL "file_logger_external_cmd_compression_test") + set_tests_properties(${test_name} PROPERTIES SKIP_RETURN_CODE 77) + endif() if(LOGIT_WITH_OTLP AND test_name MATCHES "^otlp_http_logger_(integration|callback|gzip|zstd)_test$") target_include_directories(${test_name} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../external/kurlyk/external/Simple-Web-Server") diff --git a/tests/check_prometheus_server_install_guard.cmake b/tests/check_prometheus_server_install_guard.cmake new file mode 100644 index 00000000..2c74c619 --- /dev/null +++ b/tests/check_prometheus_server_install_guard.cmake @@ -0,0 +1,24 @@ +if(NOT DEFINED BUILD_DIR OR NOT DEFINED INSTALL_PREFIX) + message(FATAL_ERROR "BUILD_DIR and INSTALL_PREFIX must be provided") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" --install "${BUILD_DIR}" --prefix "${INSTALL_PREFIX}" + RESULT_VARIABLE _install_result + OUTPUT_VARIABLE _install_stdout + ERROR_VARIABLE _install_stderr +) + +if(_install_result EQUAL 0) + message(FATAL_ERROR + "Prometheus server installation unexpectedly succeeded; the unsupported configuration was not blocked") +endif() + +set(_install_output "${_install_stdout}\n${_install_stderr}") +if(NOT _install_output MATCHES "LOGIT_WITH_PROMETHEUS_SERVER") + message(FATAL_ERROR + "Prometheus server install failed without the expected LOGIT_WITH_PROMETHEUS_SERVER diagnostic:\n" + "${_install_output}") +endif() + +message(STATUS "Prometheus server install guard rejected the unsupported package configuration") diff --git a/tests/file_logger_external_cmd_compression_test.cpp b/tests/file_logger_external_cmd_compression_test.cpp index 259960ac..9343c7b1 100644 --- a/tests/file_logger_external_cmd_compression_test.cpp +++ b/tests/file_logger_external_cmd_compression_test.cpp @@ -5,6 +5,12 @@ #include int main() { +#ifdef _WIN32 + // The test exercises the platform's external `gzip` command. Windows + // CI does not promise a POSIX shell or gzip executable, so let CTest mark + // this environment-only case as skipped. + return 77; +#else std::system("rm -rf ext_cmd_test"); logit::FileLogger::Config cfg; cfg.directory = "ext_cmd_test"; @@ -36,6 +42,7 @@ int main() { while ((n = gzread(gzfile, buf, sizeof(buf))) > 0) out.append(buf, n); gzclose(gzfile); return out.find(msg) != std::string::npos ? 0 : 1; +#endif } #else int main() { return 0; } diff --git a/tests/install_consumer/CMakeLists.txt b/tests/install_consumer/CMakeLists.txt index a27006f5..d69b9eab 100644 --- a/tests/install_consumer/CMakeLists.txt +++ b/tests/install_consumer/CMakeLists.txt @@ -1,7 +1,12 @@ cmake_minimum_required(VERSION 3.18) project(install_consumer LANGUAGES CXX) +option(LOGIT_CONSUMER_REQUIRE_FMT "Exercise the installed fmt-enabled package" OFF) + find_package(log-it-cpp CONFIG REQUIRED) add_executable(install_consumer main.cpp) target_link_libraries(install_consumer PRIVATE log-it-cpp::log-it-cpp) +if(LOGIT_CONSUMER_REQUIRE_FMT) + target_compile_definitions(install_consumer PRIVATE LOGIT_CONSUMER_REQUIRE_FMT=1) +endif() diff --git a/tests/install_consumer/main.cpp b/tests/install_consumer/main.cpp index 74668bdd..e9488721 100644 --- a/tests/install_consumer/main.cpp +++ b/tests/install_consumer/main.cpp @@ -1,6 +1,12 @@ #include int main() { +#if defined(LOGIT_CONSUMER_REQUIRE_FMT) +# if !defined(LOGIT_WITH_FMT) +# error "The installed package did not propagate LOGIT_WITH_FMT" +# endif + LOGIT_FMT_INFO("consumer fmt {}", 42); +#endif #if LOGIT_SYSLOG_ENABLED LOGIT_ADD_SYSLOG_DEFAULT(); LOGIT_INFO("hello"); diff --git a/tests/mpsc_ring_any_test.cpp b/tests/mpsc_ring_any_test.cpp new file mode 100644 index 00000000..01752232 --- /dev/null +++ b/tests/mpsc_ring_any_test.cpp @@ -0,0 +1,55 @@ +#include + +#include + +namespace { + +struct MoveOnly { + static int alive; + + explicit MoveOnly(int value) : value(value) { ++alive; } + MoveOnly(const MoveOnly&) = delete; + MoveOnly& operator=(const MoveOnly&) = delete; + + MoveOnly(MoveOnly&& other) noexcept : value(other.value) { + ++alive; + other.value = -1; + } + + MoveOnly& operator=(MoveOnly&& other) noexcept { + value = other.value; + other.value = -1; + return *this; + } + + ~MoveOnly() { --alive; } + + int value; +}; + +int MoveOnly::alive = 0; + +} // namespace + +int main() { + { + logit::detail::MpscRingAny queue(2); + assert(queue.try_push(MoveOnly(42))); + assert(MoveOnly::alive == 1); + + MoveOnly output(0); + assert(queue.try_pop(output)); + assert(output.value == 42); + } + + assert(MoveOnly::alive == 0); + + { + logit::detail::MpscRingAny queue(2); + assert(queue.try_push(MoveOnly(7))); + assert(MoveOnly::alive == 1); + } + + assert(MoveOnly::alive == 0); + return 0; +} diff --git a/vcpkg-overlay/ports/log-it-cpp/portfile.cmake b/vcpkg-overlay/ports/log-it-cpp/portfile.cmake index e74b7cd5..ee86620e 100644 --- a/vcpkg-overlay/ports/log-it-cpp/portfile.cmake +++ b/vcpkg-overlay/ports/log-it-cpp/portfile.cmake @@ -1,6 +1,6 @@ vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH - REPO NewYaroslav/log-it-cpp + REPO LimiNode/log-it-cpp REF 9ba0ce1212de3b1f60e22801cca0dc17277e27a3 SHA512 0094708c32a77aeee4a6de9e99b29f4cd1c7f41bbf69a65c5aad4254bd238e2effb6e669c0b1a035bd242cecf5ef6d048ab5c548dde52450b0b4bc9df371a2b8 HEAD_REF main diff --git a/vcpkg-overlay/ports/log-it-cpp/vcpkg.json b/vcpkg-overlay/ports/log-it-cpp/vcpkg.json index 4fb7949d..11f5aec1 100644 --- a/vcpkg-overlay/ports/log-it-cpp/vcpkg.json +++ b/vcpkg-overlay/ports/log-it-cpp/vcpkg.json @@ -2,7 +2,7 @@ "name": "log-it-cpp", "version-string": "1.0.1", "description": "LogIt++ logging library", - "homepage": "https://github.com/NewYaroslav/log-it-cpp", + "homepage": "https://github.com/LimiNode/log-it-cpp", "dependencies": [ "vcpkg-cmake", "vcpkg-cmake-config",