Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ lib/
*.dylib
*.dll
*.exe
livekit.log
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ file(MAKE_DIRECTORY ${PROTO_BINARY_DIR})

# Livekit static protobuf.
include(protobuf)
# spdlog logging library (PRIVATE dependency).
include(spdlog)
# Ensure protoc executable is found.
if(TARGET protobuf::protoc)
set(Protobuf_PROTOC_EXECUTABLE "$<TARGET_FILE:protobuf::protoc>")
Expand Down Expand Up @@ -286,6 +288,7 @@ add_library(livekit SHARED
src/ffi_client.cpp
src/ffi_client.h
src/livekit.cpp
src/logging.cpp
src/local_audio_track.cpp
src/remote_audio_track.cpp
src/room.cpp
Expand Down Expand Up @@ -330,6 +333,12 @@ target_link_libraries(livekit
PRIVATE
livekit_ffi
${LIVEKIT_PROTOBUF_TARGET}
spdlog::spdlog
)

target_compile_definitions(livekit
PRIVATE
SPDLOG_ACTIVE_LEVEL=${_SPDLOG_ACTIVE_LEVEL}
)

message(STATUS "Protobuf: version=${Protobuf_VERSION}; protoc=${Protobuf_PROTOC_EXECUTABLE}")
Expand Down
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,93 @@ On another terminal or computer, start the sender
- Registers handlers for text and file streams, logs stream events, computes one-way latency, and saves the received file locally.


## Logging

The SDK uses [spdlog](https://github.com/gabime/spdlog) internally but does
**not** expose it in public headers. All log output goes through a thin public
API in `<livekit/logging.h>`.

### Two-tier filtering

| Tier | When | How | Cost |
|------|------|-----|------|
| **Compile-time** | CMake configure | `-DLIVEKIT_LOG_LEVEL=WARN` | Zero -- calls below the level are stripped from the binary |
| **Runtime** | Any time after `initialize()` | `livekit::setLogLevel(LogLevel::Warn)` | Minimal -- a level check before formatting |

#### Compile-time level (`LIVEKIT_LOG_LEVEL`)

Set once when you configure CMake. Calls below this threshold are completely
removed by the preprocessor -- no format-string evaluation, no function call.

```bash
# Development (default): keep everything available
cmake -DLIVEKIT_LOG_LEVEL=TRACE ..

# Release: strip TRACE / DEBUG / INFO
cmake -DLIVEKIT_LOG_LEVEL=WARN ..

# Production: only ERROR and CRITICAL survive
cmake -DLIVEKIT_LOG_LEVEL=ERROR ..
```

Valid values: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `CRITICAL`, `OFF`.

#### Runtime level (`setLogLevel`)

Among the levels that survived compilation you can still filter at runtime
without rebuilding:

```cpp
#include <livekit/livekit.h>

livekit::initialize(); // default level: Info
livekit::setLogLevel(livekit::LogLevel::Debug); // show more detail
livekit::setLogLevel(livekit::LogLevel::Warn); // suppress info chatter
```

### Custom log callback

Replace the default stderr sink with your own handler. This is the integration
point for frameworks like ROS2 (`RCLCPP_*` macros), Android logcat, or any
structured-logging pipeline:

```cpp
#include <livekit/livekit.h>

livekit::initialize();
livekit::setLogLevel(livekit::LogLevel::Trace);

livekit::setLogCallback(
[](livekit::LogLevel level,
const std::string &logger_name,
const std::string &message) {
// Route to your framework, e.g.:
// RCLCPP_INFO(get_logger(), "[%s] %s", logger_name.c_str(), message.c_str());
myLogger.log(level, logger_name, message);
});

// Pass nullptr to restore the default stderr sink:
livekit::setLogCallback(nullptr);
```

See [`examples/logging_levels/custom_sinks.cpp`](examples/logging_levels/custom_sinks.cpp)
for three copy-paste-ready patterns: **file logger**, **JSON structured lines**,
and a **ROS2 bridge** that maps `LogLevel` to `RCLCPP_*` macros.

### Available log levels

| Level | Typical use |
|-------|-------------|
| `Trace` | Per-frame / per-packet detail (very noisy) |
| `Debug` | Diagnostic info useful during development |
| `Info` | Normal operational messages (connection, track events) |
| `Warn` | Unexpected but recoverable situations |
| `Error` | Failures that affect functionality |
| `Critical` | Unrecoverable errors |
| `Off` | Suppress all output |

---

### 🧪 Integration & Stress Tests

The SDK includes integration and stress tests using Google Test (gtest).
Expand Down
8 changes: 8 additions & 0 deletions bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ target_include_directories(livekit_bridge
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${LIVEKIT_ROOT_DIR}/src
)

# Link against the main livekit SDK library (which transitively provides
# include paths for livekit/*.h and links livekit_ffi).
target_link_libraries(livekit_bridge
PUBLIC
livekit
PRIVATE
spdlog::spdlog
)

target_compile_definitions(livekit_bridge
PRIVATE
SPDLOG_ACTIVE_LEVEL=${_SPDLOG_ACTIVE_LEVEL}
)

if(MSVC)
Expand Down
1 change: 0 additions & 1 deletion bridge/include/livekit_bridge/livekit_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,6 @@ class LiveKitBridge {
std::vector<std::shared_ptr<BridgeAudioTrack>> published_audio_tracks_;
/// @copydoc published_audio_tracks_
std::vector<std::shared_ptr<BridgeVideoTrack>> published_video_tracks_;

};

} // namespace livekit_bridge
11 changes: 6 additions & 5 deletions bridge/src/bridge_audio_track.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
#include "livekit/local_participant.h"
#include "livekit/local_track_publication.h"

#include <iostream>
#include <stdexcept>

#include "lk_log.h"

namespace livekit_bridge {

BridgeAudioTrack::BridgeAudioTrack(
Expand Down Expand Up @@ -56,7 +57,7 @@ bool BridgeAudioTrack::pushFrame(const std::vector<std::int16_t> &data,
try {
source_->captureFrame(frame, timeout_ms);
} catch (const std::exception &e) {
std::cerr << "[BridgeAudioTrack] captureFrame error: " << e.what() << "\n";
LK_LOG_ERROR("BridgeAudioTrack captureFrame error: {}", e.what());
return false;
}
return true;
Expand All @@ -77,7 +78,7 @@ bool BridgeAudioTrack::pushFrame(const std::int16_t *data,
try {
source_->captureFrame(frame, timeout_ms);
} catch (const std::exception &e) {
std::cerr << "[BridgeAudioTrack] captureFrame error: " << e.what() << "\n";
LK_LOG_ERROR("BridgeAudioTrack captureFrame error: {}", e.what());
return false;
}
return true;
Expand Down Expand Up @@ -115,8 +116,8 @@ void BridgeAudioTrack::release() {
participant_->unpublishTrack(publication_->sid());
} catch (...) {
// Best-effort cleanup; ignore errors during teardown
std::cerr << "[BridgeAudioTrack] unpublishTrack error, continuing with "
"cleanup\n";
LK_LOG_WARN("BridgeAudioTrack unpublishTrack error, continuing with "
"cleanup");
}
}

Expand Down
3 changes: 2 additions & 1 deletion bridge/src/bridge_room_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class BridgeRoomDelegate : public livekit::RoomDelegate {
void onTrackSubscribed(livekit::Room &room,
const livekit::TrackSubscribedEvent &ev) override;

/// Forwards a track-unsubscribed event to LiveKitBridge::onTrackUnsubscribed().
/// Forwards a track-unsubscribed event to
/// LiveKitBridge::onTrackUnsubscribed().
void onTrackUnsubscribed(livekit::Room &room,
const livekit::TrackUnsubscribedEvent &ev) override;

Expand Down
11 changes: 6 additions & 5 deletions bridge/src/bridge_video_track.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
#include "livekit/video_frame.h"
#include "livekit/video_source.h"

#include <iostream>
#include <stdexcept>

#include "lk_log.h"

namespace livekit_bridge {

BridgeVideoTrack::BridgeVideoTrack(
Expand Down Expand Up @@ -56,7 +57,7 @@ bool BridgeVideoTrack::pushFrame(const std::vector<std::uint8_t> &rgba,
try {
source_->captureFrame(frame, timestamp_us);
} catch (const std::exception &e) {
std::cerr << "[BridgeVideoTrack] captureFrame error: " << e.what() << "\n";
LK_LOG_ERROR("BridgeVideoTrack captureFrame error: {}", e.what());
return false;
}
return true;
Expand All @@ -76,7 +77,7 @@ bool BridgeVideoTrack::pushFrame(const std::uint8_t *rgba,
try {
source_->captureFrame(frame, timestamp_us);
} catch (const std::exception &e) {
std::cerr << "[BridgeVideoTrack] captureFrame error: " << e.what() << "\n";
LK_LOG_ERROR("BridgeVideoTrack captureFrame error: {}", e.what());
return false;
}
return true;
Expand Down Expand Up @@ -114,8 +115,8 @@ void BridgeVideoTrack::release() {
participant_->unpublishTrack(publication_->sid());
} catch (...) {
// Best-effort cleanup; ignore errors during teardown
std::cerr << "[BridgeVideoTrack] unpublishTrack error, continuing with "
"cleanup\n";
LK_LOG_WARN("BridgeVideoTrack unpublishTrack error, continuing with "
"cleanup");
}
}

Expand Down
32 changes: 14 additions & 18 deletions bridge/src/livekit_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
#include "livekit/video_stream.h"

#include <cassert>
#include <iostream>
#include <stdexcept>

#include "lk_log.h"

namespace livekit_bridge {

// ---------------------------------------------------------------
Expand Down Expand Up @@ -86,7 +87,7 @@ bool LiveKitBridge::connect(const std::string &url, const std::string &token,

// Initialize the LiveKit SDK (idempotent)
if (!sdk_initialized_) {
livekit::initialize(livekit::LogSink::kConsole);
livekit::initialize();
sdk_initialized_ = true;
}
}
Expand Down Expand Up @@ -132,8 +133,8 @@ void LiveKitBridge::disconnect() {
std::lock_guard<std::mutex> lock(mutex_);

if (!connected_) {
std::cerr << "[LiveKitBridge] Attempting to disconnect an already "
"disconnected bridge. Things may not disconnect properly.\n";
LK_LOG_WARN("Attempting to disconnect an already disconnected bridge. "
"Things may not disconnect properly.");
}

connected_ = false;
Expand Down Expand Up @@ -413,12 +414,11 @@ LiveKitBridge::startAudioReader(const CallbackKey &key,
livekit::AudioStream::Options opts;
auto stream = livekit::AudioStream::fromTrack(track, opts);
if (!stream) {
std::cerr << "[LiveKitBridge] Failed to create AudioStream for "
<< key.identity << "\n";
LK_LOG_ERROR("Failed to create AudioStream for {}", key.identity);
return old_thread;
}

auto stream_copy = stream; // captured by the thread
auto stream_copy = stream;

ActiveReader reader;
reader.audio_stream = std::move(stream);
Expand All @@ -429,17 +429,15 @@ LiveKitBridge::startAudioReader(const CallbackKey &key,
try {
cb(ev.frame);
} catch (const std::exception &e) {
std::cerr << "[LiveKitBridge] Audio callback exception: " << e.what()
<< "\n";
LK_LOG_ERROR("Audio callback exception: {}", e.what());
}
}
});

active_readers_[key] = std::move(reader);
if (active_readers_.size() > kMaxActiveReaders) {
std::cerr << "[LiveKitBridge] More than expected active readers. Need to "
"evaluate how much to expect/support.";
"solution";
LK_LOG_WARN("More than expected active readers. Need to evaluate how much "
"to expect/support.");
}
return old_thread;
}
Expand All @@ -457,8 +455,7 @@ LiveKitBridge::startVideoReader(const CallbackKey &key,
opts.format = livekit::VideoBufferType::RGBA;
auto stream = livekit::VideoStream::fromTrack(track, opts);
if (!stream) {
std::cerr << "[LiveKitBridge] Failed to create VideoStream for "
<< key.identity << "\n";
LK_LOG_ERROR("Failed to create VideoStream for {}", key.identity);
return old_thread;
}

Expand All @@ -473,16 +470,15 @@ LiveKitBridge::startVideoReader(const CallbackKey &key,
try {
cb(ev.frame, ev.timestamp_us);
} catch (const std::exception &e) {
std::cerr << "[LiveKitBridge] Video callback exception: " << e.what()
<< "\n";
LK_LOG_ERROR("Video callback exception: {}", e.what());
}
}
});

active_readers_[key] = std::move(reader);
if (active_readers_.size() > kMaxActiveReaders) {
std::cerr << "[LiveKitBridge] More than expected active readers. Need to "
"evaluate how much to expect/support.";
LK_LOG_WARN("More than expected active readers. Need to evaluate how much "
"to expect/support.");
}
return old_thread;
}
Expand Down
Loading
Loading