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
stdiologger that writes tostdoutandstderr. Separate default loggers exist for FreeRTOS and Microchip Harmony. - Custom loggers: Implement the
pubnub_logger_interfacevtable 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_LEVELat 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:
| Level | Enum value | Purpose |
|---|---|---|
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: ERROR → WARNING → INFO → DEBUG → TRACE.
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:
| Function | Purpose |
|---|---|
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
| Detail | Log 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
| Detail | Log 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:
| Component | Description |
|---|---|
| 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:
|
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:
| Platform | Implementation | Output 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 type | Cast to | Fields |
|---|---|---|
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 linesRegister 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
| Function | Return value | Notes |
|---|---|---|
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 linesAvailable 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:
| Category | Functions |
|---|---|
| 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:
| Macro | Description |
|---|---|
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(¶ms, channel)
3PUBNUB_LOG_MAP_SET_NUMBER(¶ms, 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 ¶ms, "publish parameters");
Logging best practices
Choose the appropriate log level
| Environment | Recommended 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:
- Set the log level to
PUBNUB_LOG_LEVEL_DEBUGorPUBNUB_LOG_LEVEL_TRACE. - Reproduce the issue.
- Collect logs from initialization through the problem occurrence.
- Include the PubNub SDK version. Access the version using
pubnub_uname().
Build configuration reference
| Macro | Default | Description |
|---|---|---|
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. |