On this page

Logging for C-Core SDK

This page explains how to configure logging in the PubNub C-Core Software Development Kit (SDK). Logging helps you monitor SDK activity, troubleshoot issues, and maintain audit trails during development and production.

For general logging concepts and best practices applicable to all PubNub SDKs, see Logging.

Logging in pre-7.0.0 versions

This logging documentation applies to C-Core SDK version 7.0.0 and later. Older versions used a simpler preprocessor-based system with PUBNUB_LOG_PRINTF and PUBNUB_LOG_LEVEL macros. Those macros are no longer supported.

If your application used pubnub_set_log_callback(), migrate to the logger API by creating a logger with pubnub_logger_alloc() and registering it with pubnub_logger_add().

Logging architecture

The C-Core SDK provides a flexible, two-tier logging system:

  • Default logger: On POSIX and Windows, the SDK includes a built-in stdio logger that writes to stdout and stderr. Separate default loggers exist for FreeRTOS and Microchip Harmony.
  • Custom loggers: Implement the pubnub_logger_interface vtable to route logs to files, external monitoring services, databases, or analytics platforms.
  • Multiple logger support: Register multiple loggers with a single PubNub context. All registered loggers receive the same log entries.
  • Structured log entries: Each log entry includes an instance identifier, timestamp, log level, call site location, and typed content (text, structured data, error, network request, or network response).
  • Compile-time stripping: Set PUBNUB_LOG_MIN_LEVEL at build time to remove log calls below a given level from the binary, reducing code size and overhead on constrained platforms.

Logging is enabled by default when PUBNUB_USE_LOGGER is set to 1 (the default). The default runtime minimum log level is PUBNUB_LOG_LEVEL_DEBUG.

Log levels

The SDK uses standard log levels to control the amount of detail captured:

LevelEnum valuePurpose
TRACE
PUBNUB_LOG_LEVEL_TRACE
Internal operations including function calls, full payloads, internal variables, and state-machine transitions.
DEBUG
PUBNUB_LOG_LEVEL_DEBUG
Inputs and outputs of public methods, network requests and responses, and decision branches.
INFO
PUBNUB_LOG_LEVEL_INFO
Significant events including successful initialization, connection established, and entity creation.
WARNING
PUBNUB_LOG_LEVEL_WARNING
Non-fatal events including deprecation notices and request retries.
ERROR
PUBNUB_LOG_LEVEL_ERROR
Exceptions, HTTP (Hypertext Transfer Protocol) failures, and invalid states.
NONE
PUBNUB_LOG_LEVEL_NONE
Logging disabled.

Each level automatically includes messages from all higher severity levels. The hierarchy is: ERRORWARNINGINFODEBUGTRACE.

Logging sensitive information

The PUBNUB_LOG_LEVEL_DEBUG and PUBNUB_LOG_LEVEL_TRACE settings may log sensitive information including API (Application Programming Interface) keys, user identifiers, and message content. Use these levels only in development environments. Never enable DEBUG or TRACE logging in production environments with sensitive data.

Enable logging

Logging is enabled by default. When you call pubnub_init(), the SDK automatically registers the default platform logger (unless PUBNUB_USE_DEFAULT_LOGGER is set to 0 at build time). The default runtime minimum log level is PUBNUB_LOG_LEVEL_DEBUG.

Change log level at runtime

Adjust the minimum log level after initialization using pubnub_logger_set_log_level():

1pubnub_t* pb = pubnub_alloc();
2pubnub_init(pb, "demo", "demo");
3
4/* Increase detail for troubleshooting */
5pubnub_logger_set_log_level(pb, PUBNUB_LOG_LEVEL_TRACE);
6
7/* Reduce detail after resolving issues */
8pubnub_logger_set_log_level(pb, PUBNUB_LOG_LEVEL_ERROR);
9
10/* Disable logging */
11pubnub_logger_set_log_level(pb, PUBNUB_LOG_LEVEL_NONE);

Query the current level with pubnub_logger_log_level():

1enum pubnub_log_level level = pubnub_logger_log_level(pb);

Compile-time log level stripping

For embedded or resource-constrained environments, you can strip log calls below a given level at compile time. This removes the code entirely from the binary.

Set PUBNUB_LOG_MIN_LEVEL in your build system to one of: TRACE, DEBUG, INFO, WARNING, ERROR, or NONE.

CFLAGS += -DPUBNUB_LOG_MIN_LEVEL=WARNING

The default compile-time minimum is DEBUG, which strips TRACE level calls. Setting PUBNUB_LOG_MIN_LEVEL=NONE removes all log calls from the binary.

Disable logging entirely

To remove the entire logging subsystem from the build, set PUBNUB_USE_LOGGER to 0:

CFLAGS += -DPUBNUB_USE_LOGGER=0

To keep the logging subsystem but disable the default stdio logger, set PUBNUB_USE_DEFAULT_LOGGER to 0:

CFLAGS += -DPUBNUB_USE_DEFAULT_LOGGER=0

Logged information

The SDK logs information at various stages of operation.

You can also emit log entries directly using logging API functions:

FunctionPurpose
pubnub_log_text()
Log a plain text message at a specific level.
pubnub_log_text_formatted()
Log a printf-style formatted text message at a specific level.
pubnub_log_object()
Log structured data (pubnub_log_value_t) with optional details.
pubnub_log_error()
Log an error entry with code, message, and optional details. The level is always PUBNUB_LOG_LEVEL_ERROR.

The location parameter accepts any string that helps identify the source of the log entry, such as a module name, component identifier, or file-and-line literal:

1pubnub_log_text(pb, PUBNUB_LOG_LEVEL_INFO, "chat/connection",
2 "Subscription connected");
3pubnub_log_text_formatted(pb, PUBNUB_LOG_LEVEL_DEBUG, "chat/publish",
4 "Publishing to channel '%s'", channel);

API call parameters

The SDK logs all user-provided input data for each API call at the DEBUG level. These logs help identify mismatches between expected and actual parameters.

Example log:

2026-02-26T14:30:15.123Z PubNub-a1b2c3d4 DEBUG core/pubnub_coreapi.c:42 Publish parameters:
channel: my-channel
store: true
ttl: 300

Network requests and responses

The SDK logs HTTP transaction information at two levels of detail:

Request logs

DetailLog level
HTTP method (GET, POST, PATCH, DELETE, PUT)
DEBUG
Complete URL with query parameters
DEBUG
Whether the request was canceled or failed
DEBUG
Request headers as key-value pairs
TRACE
Request body content (POST, PUT, PATCH)
TRACE

Response logs

DetailLog level
HTTP status code
DEBUG
Request URL (for correlation with the originating request)
DEBUG
Response headers as key-value pairs
TRACE
Response body content
TRACE

Errors and warnings

The SDK logs errors at the ERROR level and warnings at the WARNING level.

Example error log:

2026-02-26T14:30:15.456Z PubNub-a1b2c3d4 ERROR core/pubnub_netcore.c:128 Connection failed
error_code: -1
details: Timeout after 30s

Log entry structure

Each log entry includes:

ComponentDescription
Timestamp
Two-component field: seconds (Unix timestamp, time_t) and milliseconds (uint32_t). The default logger formats this as YYYY-MM-DDTHH:MM:SS.mmmZ.
Instance identifier
An 8-character hexadecimal string derived from an FNV-1a hash of the full context instance ID. Use this identifier to filter logs from multiple SDK instances. The default logger prefixes it with PubNub- when formatting output.
Log level
The severity level: TRACE, DEBUG, INFO, WARN, or ERROR.
Location
A string identifying the source of the log entry. SDK-internal logs use file:line format (such as core/pubnub_coreapi.c:42). When calling log functions directly, you can pass any string (module name, component identifier, etc.).
Message type
The data type, represented by pubnub_log_message_type:
  • PUBNUB_LOG_MESSAGE_TYPE_TEXT (plain text string)
  • PUBNUB_LOG_MESSAGE_TYPE_OBJECT (structured key-value data)
  • PUBNUB_LOG_MESSAGE_TYPE_ERROR (error with code and details)
  • PUBNUB_LOG_MESSAGE_TYPE_NETWORK_REQUEST (outgoing HTTP request)
  • PUBNUB_LOG_MESSAGE_TYPE_NETWORK_RESPONSE (incoming HTTP response)

This table describes the base pubnub_log_message_t struct shared by all message types. To access type-specific data (such as the text string, error code, or HTTP status), cast the message pointer to the appropriate concrete type based on the message_type field. Refer to the message type table for the full list of concrete types and their fields.

Filter logs from multiple instances

Applications that create multiple PubNub contexts can filter logs using the instance identifier. Each context receives a unique 8-character hex ID derived from its instance ID.

1pubnub_t* pb1 = pubnub_alloc();
2pubnub_init(pb1, "demo", "demo");
3/* Default logger output: PubNub-a1b2c3d4 ... */
4
5pubnub_t* pb2 = pubnub_alloc();
6pubnub_init(pb2, "demo", "demo");
7/* Default logger output: PubNub-e5f6a7b8 ... */

Filter log output by searching for the specific instance identifier (for example, a1b2c3d4 in the raw pubnub_id field, or PubNub-a1b2c3d4 in the default logger output).

Default logger

The SDK ships with a platform-specific default logger:

PlatformImplementationOutput destination
POSIX / Windows
stdio logger
stdout for TRACE through WARNING; stderr for ERROR.
FreeRTOS
FreeRTOS logger
printf(); uses FreeRTOS tick count when wall-clock time is unavailable.
Microchip Harmony
Harmony logger
Platform-specific output.

The default logger is automatically registered during pubnub_init() when PUBNUB_USE_DEFAULT_LOGGER is 1 (the default). To disable it, set PUBNUB_USE_DEFAULT_LOGGER to 0 at build time, or register only your custom loggers.

If you don't want to use the default logger, its implementation will be excluded from the build and there is no way for to instantiate it later.

Custom loggers

Create custom loggers to route log entries to files, external monitoring services, databases, or analytics platforms. Custom loggers implement the pubnub_logger_interface vtable.

Logger interface

The pubnub_logger_interface struct defines six function pointers:

1struct pubnub_logger_interface {
2 void (*trace)(const pubnub_logger_t* logger, const pubnub_log_message_t* message);
3 void (*debug)(const pubnub_logger_t* logger, const pubnub_log_message_t* message);
4 void (*info)(const pubnub_logger_t* logger, const pubnub_log_message_t* message);
5 void (*warn)(const pubnub_logger_t* logger, const pubnub_log_message_t* message);
6 void (*error)(const pubnub_logger_t* logger, const pubnub_log_message_t* message);
7 void (*destroy)(pubnub_logger_t* logger);
8};

All function pointers are optional. Set unused pointers to NULL. The destroy function is called when the logger is freed with pubnub_logger_free().

Each log-level function receives a pubnub_log_message_t pointer. Cast it to the appropriate concrete type based on the message_type field:

Message typeCast toFields
PUBNUB_LOG_MESSAGE_TYPE_TEXT
pubnub_log_message_text_t
message (string)
PUBNUB_LOG_MESSAGE_TYPE_OBJECT
pubnub_log_message_object_t
message (structured value), details (optional string)
PUBNUB_LOG_MESSAGE_TYPE_ERROR
pubnub_log_message_error_t
error_code, error_message, details (optional)
PUBNUB_LOG_MESSAGE_TYPE_NETWORK_REQUEST
pubnub_log_message_network_request_t
method, url, headers, body, canceled, failed
PUBNUB_LOG_MESSAGE_TYPE_NETWORK_RESPONSE
pubnub_log_message_network_response_t
url, status_code, headers, body

All concrete types share a base field of type pubnub_log_message_t with common properties: message_type, level, pubnub_id, timestamp, location, and minimum_level.

Custom logger example

1#include "core/pubnub_logger.h"
2
3typedef struct {
4 FILE* file;
5 int entry_count;
6} my_logger_context;
7
8static void my_error(const pubnub_logger_t* logger,
9 const pubnub_log_message_t* message) {
10 my_logger_context* ctx =
11 (my_logger_context*)pubnub_logger_user_data((pubnub_logger_t*)logger);
12
13 if (message->message_type == PUBNUB_LOG_MESSAGE_TYPE_ERROR) {
14 const pubnub_log_message_error_t* err =
15 (const pubnub_log_message_error_t*)message;
show all 51 lines

Register a custom logger

Use pubnub_logger_alloc() to create a logger, then pubnub_logger_add() to register it with a PubNub context:

1my_logger_context ctx = { .file = fopen("pubnub.log", "a"), .entry_count = 0 };
2
3pubnub_logger_t* logger = pubnub_logger_alloc(&my_vtable, &ctx);
4
5pubnub_t* pb = pubnub_alloc();
6pubnub_init(pb, "demo", "demo");
7pubnub_logger_add(pb, logger);

The custom logger receives log entries alongside the default logger.

Logger lifecycle APIs

FunctionReturn valueNotes
pubnub_logger_alloc(vtable, user_data)
pubnub_logger_t*
Returns NULL on allocation failure. Free with pubnub_logger_free().
pubnub_logger_free(&logger)
void
Calls the logger destroy function (if set) and sets the pointer to NULL.
pubnub_logger_user_data(logger)
void*
Returns the pointer passed to pubnub_logger_alloc(), or NULL.
pubnub_logger_add(pb, logger)
int
0 on success, -1 on failure (NULL pointer or logger already added).
pubnub_logger_remove(pb, logger)
int
0 on success, -1 on failure (logger not found or NULL pointer).
pubnub_logger_remove_all(pb)
void
Unregisters all custom loggers from the context. Free each logger with pubnub_logger_free().

Remove loggers

Remove a specific custom logger with pubnub_logger_remove(), or all custom loggers with pubnub_logger_remove_all():

1pubnub_logger_remove(pb, logger);
2pubnub_logger_free(&logger);

After removing a logger, free it with pubnub_logger_free() to release its resources. The logger's destroy function is called during pubnub_logger_free().

Structured log values

The SDK uses pubnub_log_value_t for scalar and structured data in PUBNUB_LOG_MESSAGE_TYPE_OBJECT entries and network request/response headers.

Scope of structured log values

pubnub_log_value_t values are stack-allocated and use borrowed pointers for strings and keys. All referenced data must remain valid for the duration of the log call. Do not store pubnub_log_value_t pointers or their string contents beyond the scope of the logger callback.

When consuming structured log entries in a custom logger, use the accessor functions to read values:

1static void my_debug(const pubnub_logger_t*      logger,
2 const pubnub_log_message_t* message) {
3 if (message->message_type == PUBNUB_LOG_MESSAGE_TYPE_OBJECT) {
4 const pubnub_log_message_object_t* obj =
5 (const pubnub_log_message_object_t*)message;
6
7 /* Iterate over the structured data */
8 for (const pubnub_log_value_t* el = pubnub_log_value_first(obj->message);
9 el != NULL;
10 el = pubnub_log_value_next(el)) {
11 const char* key = pubnub_log_value_key(el);
12
13 if (pubnub_log_value_type(el) == PUBNUB_LOG_VALUE_STRING) {
14 printf(" %s = %s\n", key, pubnub_log_value_get_string(el));
15 }
show all 18 lines

Available value types: PUBNUB_LOG_VALUE_NULL, PUBNUB_LOG_VALUE_BOOL, PUBNUB_LOG_VALUE_NUMBER, PUBNUB_LOG_VALUE_STRING, PUBNUB_LOG_VALUE_ARRAY, and PUBNUB_LOG_VALUE_MAP.

Log value API functions:

CategoryFunctions
Create values
pubnub_log_value_null(), pubnub_log_value_bool(), pubnub_log_value_number(), pubnub_log_value_string()
Create containers
pubnub_log_value_array_init(), pubnub_log_value_map_init()
Populate containers
pubnub_log_value_array_append(), pubnub_log_value_map_set()
Inspect values
pubnub_log_value_type(), pubnub_log_value_get_bool(), pubnub_log_value_get_number(), pubnub_log_value_get_string(), pubnub_log_value_map_get()
Iterate containers
pubnub_log_value_first(), pubnub_log_value_key(), pubnub_log_value_next()

Convenience macros for map and array population:

MacroDescription
PUBNUB_LOG_MAP_SET_STRING(map, ...)
Add a string to a map. Supports two-argument and three-argument forms.
PUBNUB_LOG_MAP_SET_NUMBER(map, ...)
Add a number to a map. Supports two-argument and three-argument forms.
PUBNUB_LOG_MAP_SET_BOOL(map, ...)
Add a boolean to a map. Supports two-argument and three-argument forms.
PUBNUB_LOG_ARRAY_APPEND_STRING(array, ...)
Append a string to an array. Supports two-argument and three-argument forms.
PUBNUB_LOG_ARRAY_APPEND_NUMBER(array, ...)
Append a number to an array. Supports two-argument and three-argument forms.
PUBNUB_LOG_ARRAY_APPEND_BOOL(array, ...)
Append a boolean to an array. Supports two-argument and three-argument forms.

Example: create structured values and log them:

1pubnub_log_value_t params = pubnub_log_value_map_init();
2PUBNUB_LOG_MAP_SET_STRING(&params, channel)
3PUBNUB_LOG_MAP_SET_NUMBER(&params, opts.ttl, ttl)
4
5pubnub_log_value_t channels = pubnub_log_value_array_init();
6PUBNUB_LOG_ARRAY_APPEND_STRING(&channels, "channel-1", ch1)
7PUBNUB_LOG_ARRAY_APPEND_STRING(&channels, "channel-2", ch2)
8
9pubnub_log_object(pb, PUBNUB_LOG_LEVEL_DEBUG, "chat/publish",
10 &params, "publish parameters");

Logging best practices

Choose the appropriate log level

EnvironmentRecommended Log Level
Production
PUBNUB_LOG_LEVEL_ERROR to capture critical issues without performance impact.
Staging
PUBNUB_LOG_LEVEL_INFO to monitor operational events.
Development
PUBNUB_LOG_LEVEL_DEBUG to investigate issues and verify behavior.
Deep troubleshooting
PUBNUB_LOG_LEVEL_TRACE when working with PubNub support on complex issues.

Protect sensitive data

Never enable PUBNUB_LOG_LEVEL_DEBUG or PUBNUB_LOG_LEVEL_TRACE in production environments that handle sensitive information.

These levels may expose:

  • API keys and authentication tokens
  • User identifiers and personal information
  • Message content and metadata

Use compile-time stripping for embedded targets

On constrained platforms, use PUBNUB_LOG_MIN_LEVEL to exclude verbose log levels from the binary:

# Strip TRACE and DEBUG calls from the build
CFLAGS += -DPUBNUB_LOG_MIN_LEVEL=INFO

This reduces code size and avoids runtime overhead for log levels you do not intend to use.

Filter logs by instance identifier

Use the instance identifier to filter logs when debugging applications with multiple PubNub contexts. Search the default logger output for the PubNub-{id} prefix (for example, PubNub-a1b2c3d4) to view logs from a single context.

Optimize logging performance

Disable logging or limit it to errors in production environments. This optimizes performance and reduces storage costs.

1/* Capture errors only */
2pubnub_logger_set_log_level(pb, PUBNUB_LOG_LEVEL_ERROR);
3
4/* Disable logging completely */
5pubnub_logger_set_log_level(pb, PUBNUB_LOG_LEVEL_NONE);

Provide logs to support

When reporting issues to PubNub support, include complete logs:

  1. Set the log level to PUBNUB_LOG_LEVEL_DEBUG or PUBNUB_LOG_LEVEL_TRACE.
  2. Reproduce the issue.
  3. Collect logs from initialization through the problem occurrence.
  4. Include the PubNub SDK version. Access the version using pubnub_uname().

Build configuration reference

MacroDefaultDescription
PUBNUB_USE_LOGGER
1
Set to 0 to remove the entire logging subsystem from the build.
PUBNUB_USE_DEFAULT_LOGGER
1
Set to 0 to disable the built-in default stdio logger. Custom loggers can still be registered.
PUBNUB_LOG_MIN_LEVEL
DEBUG
Compile-time minimum log level. Accepts: TRACE, DEBUG, INFO, WARNING, ERROR, NONE. Log calls below this level are stripped from the binary.
Last updated on