{"id":15047257,"url":"https://github.com/martinmoene/clue","last_synced_at":"2025-09-02T14:41:12.546Z","repository":{"id":14995472,"uuid":"17720818","full_name":"martinmoene/clue","owner":"martinmoene","description":"A tiny single-file header-only C++ logging framework","archived":false,"fork":false,"pushed_at":"2021-02-09T09:42:36.000Z","size":72,"stargazers_count":64,"open_issues_count":3,"forks_count":7,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-10T01:44:41.328Z","etag":null,"topics":["cpp98","header-only","logging","no-dependencies","single-file"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/martinmoene.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE_1_0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-03-13T18:08:36.000Z","updated_at":"2025-03-10T14:06:54.000Z","dependencies_parsed_at":"2022-09-22T23:12:00.236Z","dependency_job_id":null,"html_url":"https://github.com/martinmoene/clue","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinmoene%2Fclue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinmoene%2Fclue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinmoene%2Fclue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinmoene%2Fclue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/martinmoene","download_url":"https://codeload.github.com/martinmoene/clue/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinmoene%2Fclue/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259207440,"owners_count":22821664,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp98","header-only","logging","no-dependencies","single-file"],"created_at":"2024-09-24T20:55:50.006Z","updated_at":"2025-06-11T05:32:43.021Z","avatar_url":"https://github.com/martinmoene.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# clue \u0026ndash; Collect and learn from uncovering evidence\n\nA tiny header-only C++ logging framework.\n\nIf your logging requirements are modest or you like to begin _light_, then `clue` may be for you.\n\n`clue` is a C++03 header-only library to log messages with a severity and optional module identifier. Provided logging destinations are the console, a text file, the Windows debugger, the Windows event log, an in-memory log (string) and Unix syslog. You can also define your own logging destination. `clue` is based on an idea by Mark Nelson, presented in DrDobbs [1] and on ideas found in the CATCH test framework by Phil Nash [2].\n\n**Contents**  \n\n- [Example usage](#example-usage)\n- [Compile and run](#compile-and-run)\n- [Synopsis](#synopsis)\n- [Dependencies](#dependencies)\n- [Reported to work with](#reported-to-work-with)\n- [Other logging libraries](#other-logging-libraries)\n- [Notes and References](#notes-and-references)\n\n\u003c!--- [Variants of lest](#variants-of-lest) --\u003e\n\n## Example usage\n\n```Cpp\n#include \"clue.hpp\"\nint main()\n{\n    LOG_EMERGENCY( \"design by contract violation: \"           \u003c\u003c \"irrecoverable, terminating...\" );\n    LOG_ALERT    ( \"practically-unrecoverable condition: \"    \u003c\u003c \"need more memory; trying hard, likely failing...\" );\n    LOG_CRITICAL ( \"normative behaviour cannot be achieved: \" \u003c\u003c \"severe error\" );\n    LOG_ERROR    ( \"normative behaviour cannot be achieved: \" \u003c\u003c \"error\" );\n    LOG_WARNING  ( \"you should be aware of: \"                 \u003c\u003c \"e.g. disk 90% full, please free some\" );\n    LOG_NOTICE   ( \"operating normal: \"                       \u003c\u003c \"database connection achieved\" );\n    LOG_INFO     ( \"monitoring health of the system: \"        \u003c\u003c \"tracking more actions, not significantly degrading performance\" );\n    LOG_DEBUG    ( \"tracking detailed information: \"          \u003c\u003c \"speed: \" \u003c\u003c 3.14 \u003c\u003c \" m/s\" );\n}\n```\n\nThe terminology in above messages is taken from the article by Matthew Wilson [Choosing severity levels](http://blog.pantheios.org/2010/10/choosing-severity-levels.html) on the blog of the Pantheios logging framework [3]. A description of these terms can be found in [4].\n\n## Compile and run\n\n```Text\nprompt\u003e g++ -Wall -o example1.exe example1.cpp \u0026\u0026 example1\n2014-03-14T23:01:33 Emergency: design by contract violation: irrecoverable, terminating...\n2014-03-14T23:01:33     Alert: practically-unrecoverable condition: need more memory; trying hard, likely failing...\n2014-03-14T23:01:33  Critical: normative behaviour cannot be achieved: severe error\n2014-03-14T23:01:33     Error: normative behaviour cannot be achieved: error\n2014-03-14T23:01:33   Warning: you should be aware of: e.g. disk 90% full, please free some\n2014-03-14T23:01:33    Notice: operating normal: database connection achieved\n2014-03-14T23:01:33      Info: monitoring health of the system: tracking more actions, not significantly degrading performance\n2014-03-14T23:01:33     Debug: tracking detailed information: speed: 3.14 m/s\n```\n\n## Synopsis\n\n### Unique macro names\n\n-D\u003cb\u003eclue_NO_SHORT_NAMES\u003c/b\u003e  \nAll macros of `clue` start with `clue_` to hopefully make them unique. Note however, that at default also macros without the leading `clue_` are available. Define `clue_NO_SHORT_NAMES` to omit the short variants.\n\n### Logging macros\n\n`clue` defines the following logging macros (see note below).  \n**LOG_EMERGENCY(** _expr_ **)**  \n**LOG_ALERT    (** _expr_ **)**  \n**LOG_CRITICAL (** _expr_ **)**  \n**LOG_ERROR    (** _expr_ **)**  \n**LOG_WARNING  (** _expr_ **)**  \n**LOG_NOTICE   (** _expr_ **)**  \n**LOG_INFO     (** _expr_ **)**  \n**LOG_DEBUG    (** _expr_ **)**  \n\nUse these like `LOG_NOTICE( \"Gear switched to: \" \u003c\u003c gear );`\n\nNote: if `clue_LOG_TO_SYSLOG` or `LOG_TO_SYSLOG` is defined, the above short variants are not available as these would clash with the syslog severities.\n\n### Log severities\n\n`clue` defines the following severities.  \n**LOG_SEV_NONE**   \n**LOG_SEV_EMERGENCY**  \n**LOG_SEV_ALERT**  \n**LOG_SEV_CRITICAL**  \n**LOG_SEV_ERROR**  \n**LOG_SEV_WARNING**  \n**LOG_SEV_NOTICE**  \n**LOG_SEV_INFO**  \n**LOG_SEV_DEBUG**  \n**LOG_SEV_MAX**  \n\nSee also section _Logging level_ below.\n\n### Loggging level\n\n-D\u003cb\u003eLOG_LEVEL\u003c/b\u003e=LOG_SEV_NOTICE  \nNote: work in progress. Define the logging statements that are active via `LOG_LEVEL` before inclusion of `clue.hpp`. The idea is to make this runtime configurable.\n\n-D\u003cb\u003eLOG_LEVEL_BUILD\u003c/b\u003e=LOG_SEV_DEBUG  \nDefine which logging statements will be included in the code via `LOG_LEVEL_BUILD` before inclusion of `clue.hpp`. If you do not define `LOG_LEVEL_BUILD`, all logging statements are included. See also section Other macros, `clue_OMIT_UNUSED_LOG_EXPRESSIONS`\n\n### Module name\n\n-D\u003cb\u003eLOG_MODULE_NAME\u003c/b\u003e=*name*  \nLog messages may include a _module_ or _feature_ identifier right after the severity. You add such an identifer by defining `LOG_MODULE_NAME` before inclusion of `clue.hpp`. For example, a .cpp file may contain:\n\n```Cpp\n#define LOG_MODULE_NAME \"Gearbox\"\n#include \"clue.hpp\"\n\nint main()\n{\n    const int gear = 5;\n    LOG_NOTICE( \"Gear switched to: \" \u003c\u003c gear );\n}\n```\n\nCompile and run:\n\n```Text\nprompt\u003eg++ -Wall -o example2.exe example2.cpp \u0026\u0026 example2\n2014-03-15T17:29:11    Notice: Gearbox: Gear switched to: 5\n```\n\n### Logging destination control\n\n-D\u003cb\u003eLOG\\_TO\\_...\u003c/b\u003e  \nTo select a specific logging destination, define one of the following before inclusion of `clue.hpp`.\n\n-D\u003cb\u003eLOG_TO_CONSOLE\u003c/b\u003e  \nLog to `std::clog`.\n\n-D\u003cb\u003eLOG_TO_FILE=\\\\\"path/to/logfile.txt\\\\\"\u003c/b\u003e  \nLog to a text files pecified by the given path.\n\n-D\u003cb\u003eLOG_TO_STRING\u003c/b\u003e  \n\nLog to memory. This makes function `strlog \u0026 the_log()` available in namespace clue. Type `strlog` provides:\n- void **clear()** - reset the severity to clue_LOG_SEV_NONE and clear text (as after construction),\n- int **severity()** - the latest logged severity,\n- std::string **text()** - the logged text since the latest clear().\n\nNote: it's not safe to use `the_log()` from different threads.  \nSee also: [Define your own string logging object](#own_string_log_object).\n\n-D\u003cb\u003eLOG_TO_DEBUGGER\u003c/b\u003e  \nOn Windows, log via `OutputDebugString()`. On Unix: TBD.\n\n-D\u003cb\u003eLOG_TO_EVENTLOG\u003c/b\u003e  \nWindows only. Log via `ReportEvent()`. See source code in `to_eventlog_severity()` for mapping from clue (syslog) severity to event log severity. Note: you must link to `Advapi32.lib`.\n\n-D\u003cb\u003eLOG_TO_SYSLOG\u003c/b\u003e  \nNTS:To be verified (Unix/Windows). \nSee also syslog(3) [5].\n\nIf none of these is defined and you didn't define your own back-end (see below), `clue` will select `console` for non-GUI builds, `debugger` for Windows GUI build (`_WINDOWS` is defined), and `syslog` on Unix (TBD).\n\n### Define your own back-end or logging destination\n\n**clue_LOG_EXPRESSION(** *severity*, *expr* **)**  \n`clue` allows to specify a back-end to log to a destination  of your choice. You do this by defining `clue_LOG_EXPRESSION` before inclusion of `clue.hpp`. For example:\n\n```Cpp\n#define clue_LOG_EXPRESSION( severity, expr ) \\\n    std::cout \u003c\u003c clue::to_severity_text(severity) \u003c\u003c \": \" \u003c\u003c expr\n\n#include \"clue.hpp\"\n\nint main()\n{\n    clue_LOG_NOTICE( \"Hello\" \u003c\u003c \" world\" );\n}\n```\n\nCompile and run:\n\n```Text\nprompt\u003e g++ -Wall -o example3.exe example3.cpp \u0026\u0026 example3\nNotice: Hello world\n```\n\n\u003ca name=\"own_string_log_object\"\u003e\u003c/a\u003e\n### Define your own string logging object\n\n**clue_LOG_STRING_EXPRESSION(** *log*, *severity*, *expr* **)**  \n`clue` allows to specify your own string logging object. You do this by defining `clue_LOG_EXPRESSION`  in terms of `clue_LOG_STRING_EXPRESSION` before inclusion of `clue.hpp`. For example:\n\n```Cpp\n#define clue_LOG_TO_STRING\n#define clue_LOG_EXPRESSION( severity, expr ) \\\n    clue_LOG_STRING_EXPRESSION( my_log, severity, expr )\n\n#include \"clue.hpp\"\n#include \u003ciostream\u003e\n\nint main()\n{\n    clue::strlog my_log;\n\n    clue_LOG_NOTICE( \"Hello\" \u003c\u003c \" world\" );\n\n    std::cout \u003c\u003c \"my_log.text(): \" \u003c\u003c my_log.text() \u003c\u003c \"\\n\";\n}\n```\n\nCompile and run:\n\n```Text\nprompt\u003e g++ -Wall -Wextra -Weffc++ -I.. -o example3a.exe example3a.cpp \u0026\u0026 example3a\nmy_log.text(): Hello world\n```\n\n### Other Macros\n\n-D\u003cb\u003eclue_NO_TIMESTAMP\u003c/b\u003e  \nDefine this to omit the timestamp from the logged messages.\n\n-D\u003cb\u003eclue_OMIT_UNUSED_LOG_EXPRESSIONS\u003c/b\u003e  \nDefine this to omit the logging expressions with a severity lower than `LOG_LEVEL_BUILD`. The idea is that code that is inactivated by the chosen build log level normally is included in the code and compiled, but removed in the optimisation phase. For compilers that are not capable to do this, you may define `LOG_OMIT_UNUSED_LOG_EXPRESSIONS` to simplify these expressions to the equivalent of `do {} while(false)`.\n\n**LOG_LOGGED_SEVERITIES()**  \nCall this macro to issue a log message with severity LOG_SEV_NONE that enumerates the severities that are included in the build. For example as \"[clue]: Emergency, Alert, Critical, Error, Warning, Notice, Info, Debug.\" (time omitted).\n\n**LOG_EXPRESSION(** *severity*, *expr* **)**  \nYou can use it like `LOG_EXPRESSION( LOG_NOTICE, \"Hello\" \u003c\u003c \" world\" )`.\n\n### Namespace\n\nnamespace **clue** { }  \nTypes and functions are located in namespace clue.\n\n### Utility functions\n\n`clue` uses several functions that may also be useful to you.\n\n#### General\n\nstd::string **text_or(** std::string const \u0026 *text*, std::string const \u0026 *or_text* **)**;  \nReturn *text* if non-empty, otherwise return *or_text*.\n\nstd::string **text_with_or(** std::string const \u0026 *prefix*, std::string const \u0026 *text*, std::string const \u0026 *postfix*, std::string const \u0026 *or_text* **)**;  \nReturn *text* enclosed in *prefix* and *postfix* if *text* is non-empty, otherwise return *or_text*.\n\nstd::string **now_text()**;  \nReturn date and time as \"*yyyy-mm-dd*\u0026zwj;T\u0026zwj;*hh:mm:ss*\" [6], or empty string if `clue_NO_TIMESTAMP` is defined. \n\nstd::string **to_module_text(** std::string const \u0026 *module* **)**;  \nReturn \": _module_\" or empty string if module itself is empty.\n\nstd::string **to_severity_text(** int const *severity* **)**;    \nReturn a string such as \"Emergency\", \"Notice\", etc. for the given severity.\n\nstd::string **to_severities_text(** int const *level*, std::string const \u0026 *postfix* = \".\" **)**;  \nReturn a string enumerating the logged severities. For example `to_severities_text( LOG_SEV_CRITICAL );` yields \"Emergency, Alert, Critical.\".\n\n#### When LOG_EVENTLOG is defined\n\nint **to_eventlog_severity(** int *severity* **)**  \nReturn the eventlog severity for the given `clue` severity. Note that this isn't a one-to-one mapping.\n\n#### When LOG_SYSLOG is defined\n\nint **to_syslog_severity(** int *severity* **)**;  \nReturn the syslog severity for the given `clue` severity.\n\n## Dependencies\n\n`clue` depends on the C++ standard library. For logging to the Windows eventlog it requires access to OutputDebugString(). For logging to the Unix system logger it requires openlog(), syslog() and closelog() declared in \\\u003csyslog.h\\\u003e.\n\n## Reported to work with\n\n`clue` should work with any C++03 compiler. It has been reported to work with the following compilers:\n- g++ 4.6.2, g++ 4.6.3, g++ 4.7.2, g++ 4.8.1\n- Visual C++ 6 (Visual Studio 6), VC10 (VS2010), VC11 (VS2012)\n\n\u003c!--\nVariants of clue\n----------------\n--\u003e\n## Other logging libraries\n\n- [spdlog](https://github.com/gabime/spdlog) - Super fast C++ logging library.\n- [mlog](https://github.com/zschoche/mlog) - Comfortable lightweight C++ logging library -- cross-platform, C++11.\n- [Boost.Log](http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/) - C++ logging library -- simplicity, extensibility, performance.\n- [Pantheios](http://www.pantheios.org/) - Pantheios is a C/C++ Diagnostic Logging API library, offering type-safety, efficiency, genericity and extensibility.\n\n## Notes and References\n\n[1] Mark Nelson [Blundering into the One Definition Rule](http://www.drdobbs.com/cpp/blundering-into-the-one-definition-rule/240166489). DrDobbs. 11 March 2014.\n\n[2] Phil Nash. [CATCH, an automated test framework for C, C++ and Objective-C](http://builds.catch-lib.net/).\n\n[3] Matthew Wilson. Pantheios Tips 'n' Tricks. [Choosing severity levels](http://blog.pantheios.org/2010/10/choosing-severity-levels.html). 30 October 2010.\n\n[4] Matthew Wilson. [Quality Matters: The Worst Form of 'Error' Handling Except For All The Others](http://accu.org/index.php/journals/1681), section *A new vocabulary*. Overload, 18(98):28-32, August 2010.\n\n[5] [syslog(3)](http://man7.org/linux/man-pages/man3/syslog.3.html)\n\n[6] [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601) format, but without timezone.\n\n[![Build Status](https://travis-ci.org/martinmoene/clue.png?branch=master)](https://travis-ci.org/martinmoene/clue)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinmoene%2Fclue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartinmoene%2Fclue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinmoene%2Fclue/lists"}