{"id":46858849,"url":"https://github.com/embetech-official/logger","last_synced_at":"2026-03-10T17:48:35.122Z","repository":{"id":258219377,"uuid":"701400868","full_name":"embetech-official/logger","owner":"embetech-official","description":"Lightweight C library for logging","archived":false,"fork":false,"pushed_at":"2026-01-15T14:24:54.000Z","size":91,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-01-15T18:20:22.126Z","etag":null,"topics":["c","cmake","library","logging","utility"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/embetech-official.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-10-06T14:54:51.000Z","updated_at":"2025-06-02T12:25:03.000Z","dependencies_parsed_at":"2024-10-18T03:25:59.563Z","dependency_job_id":null,"html_url":"https://github.com/embetech-official/logger","commit_stats":null,"previous_names":["embetech-official/logger"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/embetech-official/logger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/embetech-official%2Flogger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/embetech-official%2Flogger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/embetech-official%2Flogger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/embetech-official%2Flogger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/embetech-official","download_url":"https://codeload.github.com/embetech-official/logger/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/embetech-official%2Flogger/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30345781,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T15:55:29.454Z","status":"ssl_error","status_checked_at":"2026-03-10T15:54:58.440Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["c","cmake","library","logging","utility"],"created_at":"2026-03-10T17:48:34.549Z","updated_at":"2026-03-10T17:48:35.116Z","avatar_url":"https://github.com/embetech-official.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LOGGER - Lightweight, portable formatted logging component\n\n[![Unit Tests](https://github.com/embetech-official/logger/actions/workflows/unit_tests.yml/badge.svg)](https://github.com/embetech-official/logger/actions/workflows/unit_tests.yml)\n![GitHub License](https://img.shields.io/github/license/embetech-official/logger)\n![GitHub Release](https://img.shields.io/github/v/release/embetech-official/logger)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/60b12b600a6842f8bf1e81a29c2ad4fe)](https://app.codacy.com/gh/embetech-official/logger/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade)\n[![SonarQube Cloud](https://sonarcloud.io/images/project_badges/sonarcloud-highlight.svg)](https://sonarcloud.io/summary/new_code?id=embetech-official_logger)\n## Overview\n\nLogger is a lightweight and portable logging component written in C99, with some preprocessor magic (still C99-compliant).\n\nFeatures:\n\n- Supports printf-compliant format syntax\n- Built in format syntax checking on (GNU compilers)\n- Unlimited amount of compile-time channels\n- SYSLOG compatibile verbosity levels\n- Runtime output stream selection\n- Thread-safe mechanisms ready\n\nThe library consists of two parts: compile-time channel and verbosity selection, as well as some runtime switches.\n\nFor each channel you may specify maximum verbosity level that will be compiled - messages with higher verbosity will be optimized-out from build, thus reducing memory requirements.\n\n## Quick Start\n\n```cmake\ninclude(FetchContent)\nFetchContent_Declare(\n    logger\n    GIT_REPOSITORY https://github.com/embetech-official/logger.git\n    GIT_TAG main\n)\n\nFetchContent_MakeAvailable(logger)\n```\n\n## Configuration\n\n### Translation unit configuration\n\nSimply add channel definition at the beginning of your source file:\n\n```C\n#include \u003cembetech/logger.h\u003e\n\n// your code starts here ...\nvoid example_function(void) {\n    LOGGER_INFO(\"eat veggies\");\n    LOGGER_WARNING(\"... or else 3:\u003e \");\n}\n```\n\nThis will result in the following log message: \n\n```shell\nDEFAULT (I): eat veggies\nDEFAULT (W): ... or else 3:\u003e\n```\n\nYou can assing your source file to compile-time **Log Channel** to enable precise verbosity configuration. The **Log Channel** will be visible in message's header:\n\n``` C\n#define LOGGER_CHANNEL FOOBAR\n#include \u003cembetech/logger.h\u003e\n\n// your code starts here ...\nvoid example_function(void) {\n    LOGGER_INFO(\"eating veggies is fun\");\n    }\n\n```\n\nThis will result in the following log message:\n\n```shell\nFOOBAR (I): eating veggies is fun\n```\n\nLogger is capable of controling which messages are compiled into your binary file. You can control this by setting each channel verbosity. If you don't specify verbosity for a channel, it will remain disabled\n\n### Verbosity configuration\n\nGlobal configuration shall be stored in logger_config.h file The example below presents all configuration options with their default values:\n\n ```C\n#ifndef LOGGER_CONFIG_H_ // Include guard naming convention is not enforced, but endorsed :)\n#define LOGGER_CONFIG_H_\n\n#define LOGGER_ENABLED 1 ///\u003c Main Component On/Off switch. If set to 0, even if every other conditions are met, logger will write nothing\n\n/********* THIS IS THE PART WHERE YOU SPECIFY YOUR OWN CHANNELS AND SUBCHANNELS *********/\n\n#define COMPONENT1_LOG_CHANNEL_LEVEL LOGGER_LEVEL_INFO ///\u003c Verbosity setting for channel COMPONENT1\n\n#define COMPONENT2_LOG_CHANNEL_LEVEL LOGGER_LEVEL_WARNING ///\u003c Verbosity setting for channel COMPONENT2\n\n#define APP_LOG_CHANNEL_LEVEL LOGGER_LEVEL_EMERGENCY ///\u003c Verbosity setting for channel APP\n\n#define SANITY_CHECKS_LOG_CHANNEL_LEVEL LOGGER_LEVEL_DISABLED ///\u003c Verbosity setting (a.k.a disabling) of channel SANITY_CHECKS :)\n\n\n#endif // That's all folks\n ```\n\n### Features configuration\n\nIn order to stay flexible and minimalistic logger uses compile-time configuration options that will affect global behaviour. The list below reflects options available in CMakeLists.txt file:\n\n### LOGGER_TIMESTAMPS\n\nIf turned on, the Logger header format will contain timestamp, acquired from the user defined callback via LOGGER_SetTimeSource function:\n\n```C\nLOGGER_SetTimeSource([](){return std::uint32_t(1);}); // Short C++ lambda to save the world\nLOGGER_INFO(\"test message\");\n```\n\nwill be printed as (assuming DEFAULT channel):\n\n```shell\n1 DEFAULT (I): test message\n```\n\n### LOGGER_HUMAN_READABLE_TIMESTAMP\n\nThis option is available only when LOGGER_TIMESTAMPS is ON.\nChanges the format of timestamp to hh:mm::ss.ms:\n\n```shell\n02:13:20.085 DEFAULT (I): test message\n```\n\n### LOGGER_CUSTOM_AFFIXES\n\nAllows setting custom prefix/suffix for every Logger message, using LOGGER_SetPrefix/LOGGER_SetSuffix function.\nBoth will be printed as binary data, so no '\\0' termination is required.\nThis feature might be useful when working with custom terminal protocols.\n\n```C\n        LOGGER_SetPrefix(\"pre- \", 5U);\n        LOGGER_SetSuffix(\" -post\", 6U);\n        LOGGER_NOTICE(\"test message1\");\n```\n\nwill produce\n\n```shell\npre- DEFAULT (N): test message -post\n```\n\n### LOGGER_RUNTIME_VERBOSITY\n\nEnables reducing verbosity of printed messages AT RUNTIME using  LOGGER_SetRuntimeLevel function. Of course, compile time level is still stronger so the resulting set of messages will be no greater than both:\n\n ```C\n    LOGGER_NOTICE(\"test message1\");\n    LOGGER_SetRuntimeLevel(LOGGER_LEVEL_WARNING);\n    LOGGER_NOTICE(\"test message2\");\n    LOGGER_SetRuntimeLevel(LOGGER_LEVEL_DEBUG);\n    LOGGER_NOTICE(\"test message3\");\n\n```\n\nwill produce\n\n```shell\nDEFAULT (N): test message1\nDEFAULT (N): test message3\n```\n\nthe second message will not be printed, however its code is still available to be enabled (assuming that DEFAULT_LOG_CHANNEL_LEVEL is at least at NOTICE)\n\n### LOGGER_HEADER_WITH_LOCATION\n\nExpands the header with code location [file:line]:\n\n```shell\nDEFAULT (N) [x:\\long_path\\file.cpp:66]: test message\n```\n\nSince file paths might be pretty long, we added CMake util to override compiler-generated file names with shorter versions:\n\n``` cmake\n# assuming the logger library is already found\ninclude(logger_utils)\nlogger_normalize_printable_filenames()\n```\n\nthis would change the above message to\n\n```shell\nDEFAULT (N) [file.cpp:66]: test message\n```\n\n### LOGGER_THREAD_SAFETY_HOOKS\n\nBy design, logger is as thread-safe as your output callback (so probably no).\nWhen this option is enabled. Logger will use user-provided lock/unlock functions to ensure thread safety.\nTo do so, you should register lock/unlock callbacks:\n\n``` C\nstatic Mutex_t myMutex;\n\nbool myLockFunction(void* context) {\n    int const customTimeoutMS = 100; // If your mutex requires timeout. you may also wrap it in terms of context\n    int const mutexSuccess = 69; // Assuming that Mutex_Take return 69 on success\n    Mutex_t* mutex = (Mutex_t*)context;\n    return mutexSuccess == Mutex_Take(mutex, customTimeoutMS); // let's assume that if succeded, the function returns 69\n}\n\nvoid myUnlockFunction(void* context) {\n    Mutex_t* mutex = (Mutex_t*)context;\n    Mutex_Give(mutex);\n}\n// ...\nLOGGER_SetLockingMechanism(myLockFunction, myUnlockFunction, \u0026myMutex);\n```\n\nThe API of callbacks is fairly universal... You might be able to simply plug your OS's mutex functions.\n\nLOGGER will always try to lock before printing message. In scoped printing case, The lock will be acquired by LOGGER_START, and released by LOGGER_END/LOGGER_ENDL.\n\n### LOGGER_FLUSH_HOOKS\n\nEnables user to bind function, to flush output. The function may either be called explicitly, using LOGGER_Flush(), or automatically on the end of each message (So every LOGGER_INFO/etc. or after each LOGGER_END/LOGGER_ENDL):\n\n```C\nLOGGER_DisableHeader();\nLOGGER_NOTICE(\"marco?\");\n\nLOGGER_SetFlushHook([](){puts(\"polo!\");}, false); // Logger will flush when asked to\nLOGGER_NOTICE(\"marco??\");\nLOGGER_NOTICE(\"marco???\");\nLOGGER_Flush();\nLOGGER_SetFlushHook([](){puts(\"polo!!\");}, true); // now logger will flush automatically\nLOGGER_NOTICE(\"marco!\");\n```\n\nwould print\n\n```shell\nmarco?\nmarco??\nmarco???\npolo!\nmarco!\npolo!!\n```\n\n### LOGGER_VERBOSE_ERRORS\n\nWhen user messed up, it may be difficult to dig through all preprocessor magic. When this option is enabled, and your compiler is eiter clang or GCC-like, each compile-time error will be appended with message, which logger channel was the culprit\n\n## Usage\n\n### Minimal configuration\nIn order to make the logger print anything, you have to provide output function callback beforehand:\n\n``` C\nvoid out(char c, void* context) {\n    (void)context; // unused parameter\n    putchar(c);\n}\n\nLOGGER_SetOutput(out, NULL);\n```\n\nYou may provide context for your function and it will be stored until next invocation of LOGGER_SetOutput.\nThe next step is to enable logger:\n\n``` C\nassert(LOGGER_Enable()); // Assert SHOULD be skipped, as this funtion is probably not that critical... Yet the function returns whether the logger was enabled\n```\n\n## Logging messages\n\nWe prepared two intended ways to produce log messages. Both will be presented in example below:\n\n``` C\nvoid example_function(void) {\n    LOGGER_TRACE(\"Trace message. Rarely enabled\");\n    LOGGER_DEBUG(\"Entering function in \" __FILE__ \":%d\", __LINE__);\n    LOGGER_VERBOSE(\"Attempting to start to defuse the nuke\");\n    LOGGER_INFO(\"Nuke defusal kit prepared\");\n    LOGGER_NOTICE(\"Nuke defusing started\");\n    LOGGER_WARNING(\"The Apple authorized service might not be the best place to fix the bomb\");\n    LOGGER_ERROR(\"Screwdriver broken\");\n    LOGGER_CRITICAL(\"Nuke warranty voided. Reason: liquid damage\");\n    LOGGER_ALERT(\"Nuke will blow in %d seconds\", 3);\n    LOGGER_EMERGENCY(\"Goodbye cruel world\");\n    LOGGER_DISABLED(\"There is no bright light at the end of the tunnel\"); // Actually useful, when you want to disable some low level debugging messages in low latency code, and not comment it out (remember: commented-out code is a bad code)\n\n    LOGGER_START(NOTICE);\n    int trainingMsgNo = 42; // Notice that the scope of trainingMsgNo is reduced to LOGGER_START/LOGGER_ENDL\n    LOGGER_CONTINUE(\"\\n --- This was a training message no %d: \\n\", trainingMsgNo);\n    LOGGER_CONTINUE(\"%s\\n\", getPrintableMsgNo(trainingMsgNo));\n    LOGGER_CONTINUE(\"you may log as many information, as you wish.\\n\");\n    LOGGER_CONTINUE(\"log channel between LOGGER_START and LOGGER_END is locked\");\n    LOGGER_CONTINUE(\"(assuming you implemented locking mechanism)\");\n    LOGGER_ENDL();\n    \n    LOGGER_NOTICE(\"bananas.\")\n}\n```\n\nThis will produce the following output:\n\n```shell\nDEFAULT (T): Trace message. Rarely enabled\nDEFAULT (D): Entering function in ./example.c:499\nDEFAULT (V): Attempting to start to defuse the nuke\nDEFAULT (I): Nuke defusal kit prepared\nDEFAULT (N): Nuke defusing started\nDEFAULT (W): The Apple authorized service might not be the best place to fix the bomb\nDEFAULT (E): Screwdriver broken\nDEFAULT (C): Nuke warranty voided. Reason: liquid damage\nDEFAULT (A): Nuke will blow in 3 seconds\nDEFAULT (M): Goodbye cruel world\nDEFAULT (N): \n --- This was a training message no 42: \nfoo\nyou may log as many information, as you wish.\nlog channel between LOGGER_START and LOGGER_END is locked(assuming you implemented locking mechanism)\nDEFAULT    (N): bananas\n```\n\n### Header runtime control\n\nUser may disable printing header, simply using LOGGER_EnableHeader/LOGGER_DisableHeader functions\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fembetech-official%2Flogger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fembetech-official%2Flogger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fembetech-official%2Flogger/lists"}