CLI tool
knurling-rs/defmt avatar
knurling-rs/defmt

defmt sends the format string to the host and formats the log there

Efficient, deferred formatting for logging on embedded systems

1,238 stars134 forksRustApache-2.0

At a glance

What is it?
defmt is a Rust logging framework for microcontrollers that defers formatting to the host, so the device transmits a format string and its arguments instead of rendering text. The repository holds twelve packages split between on-target code, host side decoders and transport specific firmware libraries, and it is developed as part of the Knurling project at Ferrous Systems.
Who is it for?
defmt fits a Rust firmware project where log output competes with application code for flash and RAM, and where a host side tool can be assumed to be running. It is the wrong choice for a Linux or desktop binary, where a standard logger is simpler and the flash saving is irrelevant.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Formatting on a microcontroller is the cost defmt removes

defmt, which the README expands as de format, short for deferred formatting, is a logging framework for resource constrained devices like microcontrollers. The documentation is a book at defmt.ferrous-systems.com, and the project is part of Knurling, Ferrous Systems' effort at improving tooling used to develop for embedded systems.

The idea is a change of where the work happens. When a typical logger on a microcontroller writes a message, the device has to turn the values into text before sending anything. That means the format machinery, and the code to handle every type you log, occupies flash, and the intermediate text occupies RAM at the moment of logging.

With deferred formatting, the device transmits the format string and the arguments, and the host renders the line. The log line you read on your terminal is produced by a decoder running on your workstation, and the target only has to serialise a format identifier and a few values.

That is the whole trade, and it is worth being clear about both sides of it. The saving is real on a device where flash is measured in tens of kilobytes. The cost is that the output is no longer self describing: a log line on its own tells you nothing, because the text is not in the log, and the format string has to be available to the decoder at the same version as the firmware.

The second point is the reason the project is a set of packages rather than a single crate, and it is the reason the host side has a defined format of its own.

Twelve packages, split between the device and the workstation

The components table in the README is the map of the project, and reading it as two columns, on-target and host, explains the architecture.

On the device there is defmt itself, described as on-target code for highly efficient logging, and defmt-macros, the proc macros that turn your log statements into the deferred form. Then there are the transports. defmt-rtt sends defmt logs over RTT, defmt-itm sends them over ITM, and defmt-semihosting sends them over semihosting. Each is a separate crate rather than a feature of the main one, which is a design decision with a practical consequence: you pick the one transport your board and probe support, and the others do not come along.

There are two more on-target packages with a different job. panic-probe is for sending panic! over defmt, so a crash message travels the same path and in the same format as everything else. defmt-test is a framework for running tests on-target, and defmt-test-macros are the proc macros for it.

On the host there are four. defmt-decoder is a host library for decoding defmt log frames, defmt-parser is a host library for parsing those frames, defmt-print is a CLI utility for decoding and printing defmt encoded logs to standard output, and defmt-json-schema describes the JSON emitted by defmt-decoder.

That last one is the piece that makes the output scriptable. A JSON schema for the decoder's output means other tools can consume the decoded logs without reimplementing the wire format, and it is the difference between a log viewer and a log pipeline.

The repository root also holds a book/ directory for the documentation, a defmt-03/ directory, and deny.toml, which is the configuration for dependency licence checking, a normal thing to find in a workspace of this kind.

The host side is where the text appears

The division of labour between decoder, parser and print is worth understanding, because it determines what you can do with a captured log.

The parser is the host library for parsing defmt log frames, and the decoder is the host library for decoding them. The distinction is between the framing and the content: the parser deals with the byte stream, working out where a frame starts and ends and what its format identifier is, and the decoder applies a format definition to the arguments to produce the values.

defmt-print is the CLI utility for decoding and printing defmt encoded logs to standard output. So the typical workflow is that a device streams its frames over RTT, ITM or semihosting, a host tool captures the bytes, and the decoder turns them into values, and the print utility renders them as lines.

The JSON schema crate closes the loop. It describes the JSON that defmt-decoder emits, which means a program that consumes the decoded output does not have to scrape formatted text, and the schema is checked rather than assumed.

This is where the deferred approach has a real operational cost. The format strings live in the firmware, and they live in the host tool at the matching version. The versions have to agree, and the crates are versioned together for exactly that reason. The release tags in the repository follow that convention, with defmt-v1.2.0 on 2026-10-15, defmt-v1.1.1 on 2026-07-03 and defmt-v1.1.0 on 2026-05-18, so the firmware and the host tools are released as a set.

The output of defmt-print goes to standard output, which means it composes with the ordinary shell tools rather than being a closed application.

cargo xtask is how the project is built and tested

The developer section is short, and the tool it uses is the interesting part. Tests are run using cargo xtask, which the README is quick to explain is simply an alias, defined in .cargo/config.toml, for cargo run --package xtask --.

The listing of options is available in xtask/src/main.rs, or you can ask for it directly.

console
cargo xtask help

And running all the tests is a single command.

console
cargo xtask test-all

There is a practical requirement behind that command. You will need qemu-system-arm installed and in your $PATH for some of the tests, for example test-snapshot. A framework that runs code on a microcontroller and checks what comes out of it has to execute somewhere, and here that somewhere is an ARM emulator, which is why the repository also carries a qemu-run/ directory.

The workspace layout in Cargo.toml explains the rest. The workspace has decoder, decoder/defmt-json-schema, defmt, macros, parser, print, qemu-run and xtask as members, and it excludes firmware/* and defmt-03. The exclusion is worth noting: the firmware directories are their own workspaces, because each one is a separate example project with its own dependencies, and the older defmt-03 line is kept in the tree but outside the main build.

The release profile is tuned for a library that ends up in firmware. codegen-units is 1, lto is fat, opt-level is 3, debug-assertions is false, overflow-checks is false, and debug is 2 so there is still line information in the output. That is a profile for small final binaries with usable debug info, which is what a firmware build wants.

MSRV, licences and the Knurling project

Two things in the README matter for anyone planning to depend on this.

The first is the minimum supported Rust version, and it differs by side of the wire. The minimum supported Rust version is 1.81, or Ferrocene 24.11, for the crates that cross-compile to your microcontroller, and 1.83 for all the host-side crates. The defmt crates are tested against the latest stable Rust version and the MSRV, which is the practice that keeps that number honest.

The Ferrocene mention is a clue about the audience. Naming 24.11 alongside the plain Rust version tells you the embedded-side crates are also expected to build with Ferrocene, and the project intends to keep working with one.

The second is the licence, and it is the permissive dual option. The project is licensed under either the Apache License, Version 2.0, or the MIT license, at your option, with LICENSE-APACHE and LICENSE-MIT both in the repository root. The contribution terms say that unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work, as defined in the Apache-2.0 license, is licensed as above without additional terms or conditions.

The wider context is Knurling, the project that defmt is part of, an effort at improving the tooling used to develop for embedded systems. The book is the documentation, the GitHub Sponsors link is the funding route, and the last push was on 2026-10-18, with the project not archived.

For a team, the practical reading is that defmt is one component of a considered toolchain rather than a standalone logging library, and adopting it is partly a decision to work in that world.

Editorial conclusion

defmt fits a Rust firmware project where log output competes with application code for flash and RAM, and where a host side tool can be assumed to be running. It is the wrong choice for a Linux or desktop binary, where a standard logger is simpler and the flash saving is irrelevant. Verify first which transport your board supports, since RTT, ITM and semihosting are separate crates rather than one configurable one, and check the MSRV of 1.81 for target crates against the toolchain your project already pins.

Frequently asked questions

What does defmt do differently from a normal logger?

It defers formatting to the host. The device transmits a format string and its arguments rather than rendering text, and the host decodes the frames and prints the result, so the format machinery does not occupy flash on the microcontroller.

How do I choose between RTT, ITM and semihosting?

They are separate crates rather than one configurable transport: defmt-rtt sends logs over RTT, defmt-itm over ITM and defmt-semihosting over semihosting. You add the one your board and debug probe support.

What is the minimum supported Rust version for defmt?

1.81, or Ferrocene 24.11, for the crates that cross-compile to your microcontroller, and 1.83 for all the host-side crates. The crates are tested against the latest stable Rust and against that MSRV.

How do I run the defmt test suite?

Tests are run using cargo xtask, which is an alias for cargo run --package xtask --. Run cargo xtask test-all to run all the tests, and cargo xtask help for the list of options. You will need qemu-system-arm installed and in your $PATH for some of the tests, such as test-snapshot.

Can I decode defmt logs on my workstation?

Yes. defmt-print is the CLI utility for decoding and printing defmt encoded logs to standard output, defmt-decoder is the host library for decoding log frames, defmt-parser handles parsing the frames, and defmt-json-schema describes the JSON that the decoder emits.

Official sources

  1. knurling-rs/defmt on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/knurling-rs-defmt.svg)](https://hysenlabs.com/projects/knurling-rs-defmt)

Was this useful?