pw_log_string is a partial backend for pw_log. This backend fowards the PW_LOG_* macros to the pw_log_string:handler facade which is backed by a C API. pw_log_string:handler does not implement the full C API, leaving projects to provide their own implementation of pw_log_string_HandleMessageVaList. See pw_log_basic for a similar pw_log backend that also provides an implementation.

As this module passes the log message, file name, and module name as a string to the handler function, it’s relatively expensive and not well suited for space-constrained devices. This module is oriented towards usage on a host (e.g. a simulated device).

Note that pw_log_string:handler may be used even when it’s not used as the backend for pw_log via pw_log_string. For example it can be useful to mix tokenized and string based logging in case you have a C ABI where tokenization can not be used on the other side.

Get started (GN)#

This section outlines how to implement a pw_log_string backend in a GN-based project.


The example code was written for a host target running on Linux.

Invoke a logging macro#

Call one of the pw_log macros in your project code:

/* //src/ */

#include <unistd.h>

#include "pw_log/log.h"

int main() {
    while (true) {
        PW_LOG_INFO("Hello, world!");
    return 0;

Implement the logging function#

Implement pw_log_string_HandleMessageVaList() in C. Macros like PW_LOG() hand off the actual logging implementation to this function.

The function signature of your implementation must match the one specified by Pigweed.

The example code below just logs most of the available information to stdout:

/* //src/pw_log_string_backend.c */

#include <stdio.h>
#include <stdarg.h>

void pw_log_string_HandleMessageVaList(int level,
                                       unsigned int flags,
                                       const char* module_name,
                                       const char* file_name,
                                       int line_number,
                                       const char* message,
                                       va_list args) {
    printf("Entering custom pw_log_string backend...\n");
    printf("%d\n", level);
    printf("%u\n", flags);
    printf("%s\n", module_name);
    printf("%s\n", file_name);
    printf("%d\n", line_number);
    printf("%s\n", message);
    if (args) { /* Do something with your args here... */ }
    printf("Exiting custom pw_log_string backend...\n\n");

What exactly pw_log_string_HandleMessageVaList() should do is entirely up to the implementation. The log handler in pw_log_basic is one example, but it’s also possible to encode as protobuf and send over a TCP port, write to a file, or even blink an LED to log as morse code.

Create source sets#

Use pw_source_set to create a source set for your logging implementation. Do not use GN’s built-in source_set feature.

# //src/


pw_source_set("pw_log_string_backend") {
    sources = [ "pw_log_string_backend.c" ]

pw_source_set("pw_log_string_backend.impl") {
    sources = []


The empty pw_log_string_backend.impl source set prevents circular dependencies. See the comment for group("impl") in //pw_log/ for more context.

Configure backends#

Update your target toolchain configuration file:

# //targets/my_target/target_toolchains.gni


my_target = {
  my_toolchain = {
    name = "my_toolchain"
    defaults = {
      pw_log_BACKEND = dir_pw_log_string
      pw_log_string_HANDLER_BACKEND = "//src:pw_log_string_backend"
      pw_build_LINK_DEPS = [


(Optional) Implement message handler#

Optionally provide your own implementation of PW_LOG_STRING_HANDLE_MESSAGE which invokes pw_log_string_HANDLER_BACKEND with your selected arguments.

API reference#

void pw_log_string_HandleMessageVaList(
int level,
unsigned int flags,
const char *module_name,
const char *file_name,
int line_number,
const char *message,
va_list args,

Logs a message with the listed attributes. This must be implemented by the backend.