Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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