# HardySimpson/zlog: a pure C logging library with categories, formats and rules

> zlog is a C99 logging library that replaces printf and syslog with a configuration file of categories, formats and rules. It builds with CMake or a plain makefile, and the README claims 250,000 logs per second on the author's laptop.

**HardySimpson/zlog** — A reliable, high-performance, thread safe, flexsible, clear-model, pure C logging library.

- Repository: https://github.com/HardySimpson/zlog
- Stars: 2,551 · Forks: 774
- Language: C
- License: Apache-2.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/hardysimpson-zlog

## What zlog replaces in a C program

The README opens with a blunt claim: in the C world there was no good logging library comparable to logback in Java or log4cxx in C++. The two options it names are printf and syslog. printf works until you want to redirect output to a file, change the format at runtime, or filter by severity, at which point you are editing and recompiling. syslog is designed for system-wide logging and, in the README's words, is slow. zlog targets application logging in C: a library you link into your own binary, configured by a file rather than by recompilation.

The intended audience is C developers writing daemons, services or long-running tools who want per-category severity filtering and multiple output destinations without adopting a C++ dependency. The API is C, the build is C, and the README states there are no external dependencies beyond a POSIX system and a C99 compliant vsnprintf. That last point matters for embedded and minimal-container work, where pulling in a C++ runtime for logging is not acceptable.

## Categories, formats and rules: the configuration model

Three concepts carry the whole design. A category is a named stream of log entries, held in C as a zlog_category_t pointer. A format is a pattern string such as %m%n, where %m is the user message and %n is a newline. A rule binds a category and a minimum level to an output destination and a format. If the category string in a rule matches the name passed to zlog_get_category, they match.

The README argues this is better than the log4j model, where each logger must be given a level or inherit one from its parent. In zlog, the rule decouples that: a child category can emit at DEBUG while a parent category emits at ERROR, because each rule carries its own level rather than inheriting through a hierarchy. Whether that is an improvement depends on how much you rely on inheritance to avoid repeating yourself; with many categories, you repeat the level in every rule.

Destinations are not limited to files. The README lists static file paths, dynamic file paths, stdout, stderr, syslog and user-defined output. A rule can also cap a file's size, which the README shows as a quoted path followed by a size such as 1M. File rotation is described as thread-safe and process-safe, meaning several processes writing the same log file coordinate the rotation rather than corrupting it.

## Installing zlog and logging a first message

The README points to the releases page for downloads and shows a tarball extraction as the starting point. Two build paths are documented and both produce the same shared and static libraries, the zlog-chk-conf tool and a pkg-config file.

The CMake path needs CMake 3.12 or newer and installs to /usr/local by default. The GNU install directories are honoured, so a distribution that keeps libraries in lib64 can pass CMAKE_INSTALL_LIBDIR accordingly.

```bash
cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build -j8
sudo cmake --install build
```

The plain makefile path is shorter. PREFIX sets the destination and LIBRARY_PATH sets the library directory under it, lib by default.

```bash
make
sudo make install
```

After installing, the README says to refresh the dynamic linker so your program can find libzlog.so. On Linux that means adding the install directory to /etc/ld.so.conf and running ldconfig. Other systems need an equivalent step.

Configuration lives in a file whose path you pass to zlog_init. This minimal example sends category my_cat at DEBUG and above to stdout using the simple format.

```config
[formats]
simple = "%m%n"
[rules]
my_cat.DEBUG    >stdout; simple
```

To write to a file with a maximum size instead, the README gives this rule, where the quoted path is followed by the size limit.

```config
my_cat.DEBUG            "/var/log/aa.log", 1M; simple
```

The C side is four calls: zlog_init with the config path, zlog_get_category with the same category string, zlog_info to emit, and zlog_fini at shutdown. Compiling against a manual install uses the include and lib directories directly, or pkg-config, which both builds install.

```bash
cc -o test_hello test_hello.c $(pkg-config --cflags --libs zlog)
```

Add --static when linking libzlog.a so the private dependencies come along. A CMake project can instead use find_package(zlog REQUIRED) and link zlog::zlog, with zlog::zlog_s for the static library. Running the compiled sample prints hello, zlog.

## Where zlog is the wrong choice

The configuration file is parsed at zlog_init time and the README describes runtime refresh of the configuration, manually or automatically, as a feature. What it does not document is what happens when a refresh encounters a syntax error, or how to roll back to the previous configuration. If a bad edit can leave a running daemon without logging, that is a risk you carry into production, and the README is silent on it.

The output is line-oriented text shaped by format strings. There is no documented JSON formatter, no structured field emission beyond the MDC key-value map, and no built-in shipping to a remote collector. If your pipeline expects structured events, zlog writes the file and something else has to parse it.

The performance claim in the README, 250,000 logs per second on the author's laptop and roughly 1000 times faster than syslog(3) with rsyslogd, is a single unreproduced figure from the author. Treat it as an indication that the fast path is real, not as a number you can plan capacity around. The repository also carries a BUILD.bazel file and a zlog.spec alongside CMakeLists.txt and the makefile, but the README documents only the CMake and make paths; a Bazel or RPM user is working from build files rather than from documented instructions.

## Compared with log4c and syslog

log4c is the closest C relative and the README names it directly, claiming zlog is faster, safer and more powerful. The architectural difference is the inheritance model. log4c follows log4j: loggers form a hierarchy and inherit levels and appenders from their parents unless overridden. zlog drops inheritance in favour of rules, where each rule independently states category, level, destination and format. A rule-based configuration is easier to read when you want one category at DEBUG and its neighbours at ERROR, and harder to keep consistent when you have hundreds of categories that should share a policy.

syslog is the other comparison, and the difference is scope. syslog routes messages to a system daemon, which means your application's log format and destination are partly the system administrator's decision, and the README calls it slow. zlog keeps the destination inside your application's configuration, which is what you want for a service that writes its own rotating files. If your operational model already centralizes logging through syslog or journald and you have no reason to change it, zlog adds a second mechanism rather than replacing the first.

## Maintenance, licence and upgrade cost

The repository is not archived and the last push was on 2026-09-28, the same day as release 1.2.19. The release history shows a gap: 1.2.18 was published on 2024-07-03 and 1.2.17 on 2023-12-04, so the cadence between releases has been roughly annual or slower, with 1.2.19 arriving about two months after 1.2.18. Plan for a library that receives occasional releases rather than continuous churn.

The licence is Apache-2.0, changed from an earlier licence in release 1.2.17, whose release note reads Change Lience to Apache 2.0. Apache-2.0 is permissive and includes an explicit patent grant, which matters if you redistribute a binary that links libzlog. That is the extent of what the repository states; it is not legal advice and you should read LICENSE in the repository.

Upgrade cost is bounded by two surfaces. The C API shown in the README is small: init, get_category, the log calls, fini. The configuration file is where breakage would show up, since categories, formats and rules are matched by string. The repository ships zlog-chk-conf, a tool whose name indicates it checks a configuration file; the README does not document its flags or output, so confirm its behaviour on your own zlog.conf before relying on it in a deployment script.

## Conclusion

Adopt zlog if you are writing C or C++ applications that need redirectable, reformattable log output without pulling in a C++ logging framework, and you are willing to maintain a zlog.conf file alongside your code. Do not adopt it if you need structured or JSON output, a log shipping pipeline, or a library that documents a rollback procedure, because the README does not cover any of those. Before committing, verify that libzlog.so is visible to your dynamic linker after install, that your category names in C match the rule strings in zlog.conf exactly, and that the level threshold in each rule is the one you intend, since a rule that names DEBUG also admits every higher level.

## FAQ

### What is zlog?

zlog is a pure C logging library for applications, positioned by its README as the C counterpart to logback in Java or log4cxx in C++. It uses a configuration file of categories, formats and rules to control what gets logged, where it goes and in what format.

### How do I install zlog on Linux?

Extract the release tarball, then either run the CMake commands (cmake -DCMAKE_BUILD_TYPE=Release -B build, cmake --build build -j8, sudo cmake --install build) or run make followed by sudo make install. After installing, add the library directory to /etc/ld.so.conf and run sudo ldconfig so the dynamic linker can find libzlog.so.

### What goes in a zlog.conf file?

A zlog.conf file has sections for formats and rules. A format is a pattern such as "%m%n"; a rule names a category, a level, a destination and a format, for example my_cat.DEBUG >stdout; simple. The path to the file is passed to zlog_init.

### Does zlog need any external libraries?

The README states there are no external dependencies, only a POSIX system and a C99 compliant vsnprintf. Both the CMake and make builds produce shared and static libraries, a zlog-chk-conf tool and a pkg-config file.

## Sources

- [HardySimpson/zlog on GitHub](https://github.com/HardySimpson/zlog)
- [Issues](https://github.com/HardySimpson/zlog/issues)
- [License: Apache-2.0](https://github.com/HardySimpson/zlog/blob/master/LICENSE)
- [README](https://github.com/HardySimpson/zlog/blob/master/README.md)
- [Releases](https://github.com/HardySimpson/zlog/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/hardysimpson-zlog
