Skip to content

Commit 0dfbcec

Browse files
LK_LOG_* using spdlog, supporting users specifying their own logging … (#68)
1 parent cf20a85 commit 0dfbcec

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1639
-240
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ lib/
2828
*.dylib
2929
*.dll
3030
*.exe
31+
livekit.log

CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ file(MAKE_DIRECTORY ${PROTO_BINARY_DIR})
8585

8686
# Livekit static protobuf.
8787
include(protobuf)
88+
# spdlog logging library (PRIVATE dependency).
89+
include(spdlog)
8890
# Ensure protoc executable is found.
8991
if(TARGET protobuf::protoc)
9092
set(Protobuf_PROTOC_EXECUTABLE "$<TARGET_FILE:protobuf::protoc>")
@@ -286,6 +288,7 @@ add_library(livekit SHARED
286288
src/ffi_client.cpp
287289
src/ffi_client.h
288290
src/livekit.cpp
291+
src/logging.cpp
289292
src/local_audio_track.cpp
290293
src/remote_audio_track.cpp
291294
src/room.cpp
@@ -330,6 +333,12 @@ target_link_libraries(livekit
330333
PRIVATE
331334
livekit_ffi
332335
${LIVEKIT_PROTOBUF_TARGET}
336+
spdlog::spdlog
337+
)
338+
339+
target_compile_definitions(livekit
340+
PRIVATE
341+
SPDLOG_ACTIVE_LEVEL=${_SPDLOG_ACTIVE_LEVEL}
333342
)
334343

335344
message(STATUS "Protobuf: version=${Protobuf_VERSION}; protoc=${Protobuf_PROTOC_EXECUTABLE}")

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,93 @@ On another terminal or computer, start the sender
218218
- Registers handlers for text and file streams, logs stream events, computes one-way latency, and saves the received file locally.
219219

220220

221+
## Logging
222+
223+
The SDK uses [spdlog](https://github.com/gabime/spdlog) internally but does
224+
**not** expose it in public headers. All log output goes through a thin public
225+
API in `<livekit/logging.h>`.
226+
227+
### Two-tier filtering
228+
229+
| Tier | When | How | Cost |
230+
|------|------|-----|------|
231+
| **Compile-time** | CMake configure | `-DLIVEKIT_LOG_LEVEL=WARN` | Zero -- calls below the level are stripped from the binary |
232+
| **Runtime** | Any time after `initialize()` | `livekit::setLogLevel(LogLevel::Warn)` | Minimal -- a level check before formatting |
233+
234+
#### Compile-time level (`LIVEKIT_LOG_LEVEL`)
235+
236+
Set once when you configure CMake. Calls below this threshold are completely
237+
removed by the preprocessor -- no format-string evaluation, no function call.
238+
239+
```bash
240+
# Development (default): keep everything available
241+
cmake -DLIVEKIT_LOG_LEVEL=TRACE ..
242+
243+
# Release: strip TRACE / DEBUG / INFO
244+
cmake -DLIVEKIT_LOG_LEVEL=WARN ..
245+
246+
# Production: only ERROR and CRITICAL survive
247+
cmake -DLIVEKIT_LOG_LEVEL=ERROR ..
248+
```
249+
250+
Valid values: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `CRITICAL`, `OFF`.
251+
252+
#### Runtime level (`setLogLevel`)
253+
254+
Among the levels that survived compilation you can still filter at runtime
255+
without rebuilding:
256+
257+
```cpp
258+
#include <livekit/livekit.h>
259+
260+
livekit::initialize(); // default level: Info
261+
livekit::setLogLevel(livekit::LogLevel::Debug); // show more detail
262+
livekit::setLogLevel(livekit::LogLevel::Warn); // suppress info chatter
263+
```
264+
265+
### Custom log callback
266+
267+
Replace the default stderr sink with your own handler. This is the integration
268+
point for frameworks like ROS2 (`RCLCPP_*` macros), Android logcat, or any
269+
structured-logging pipeline:
270+
271+
```cpp
272+
#include <livekit/livekit.h>
273+
274+
livekit::initialize();
275+
livekit::setLogLevel(livekit::LogLevel::Trace);
276+
277+
livekit::setLogCallback(
278+
[](livekit::LogLevel level,
279+
const std::string &logger_name,
280+
const std::string &message) {
281+
// Route to your framework, e.g.:
282+
// RCLCPP_INFO(get_logger(), "[%s] %s", logger_name.c_str(), message.c_str());
283+
myLogger.log(level, logger_name, message);
284+
});
285+
286+
// Pass nullptr to restore the default stderr sink:
287+
livekit::setLogCallback(nullptr);
288+
```
289+
290+
See [`examples/logging_levels/custom_sinks.cpp`](examples/logging_levels/custom_sinks.cpp)
291+
for three copy-paste-ready patterns: **file logger**, **JSON structured lines**,
292+
and a **ROS2 bridge** that maps `LogLevel` to `RCLCPP_*` macros.
293+
294+
### Available log levels
295+
296+
| Level | Typical use |
297+
|-------|-------------|
298+
| `Trace` | Per-frame / per-packet detail (very noisy) |
299+
| `Debug` | Diagnostic info useful during development |
300+
| `Info` | Normal operational messages (connection, track events) |
301+
| `Warn` | Unexpected but recoverable situations |
302+
| `Error` | Failures that affect functionality |
303+
| `Critical` | Unrecoverable errors |
304+
| `Off` | Suppress all output |
305+
306+
---
307+
221308
### 🧪 Integration & Stress Tests
222309

223310
The SDK includes integration and stress tests using Google Test (gtest).

bridge/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,21 @@ target_include_directories(livekit_bridge
2323
$<INSTALL_INTERFACE:include>
2424
PRIVATE
2525
${CMAKE_CURRENT_SOURCE_DIR}/src
26+
${LIVEKIT_ROOT_DIR}/src
2627
)
2728

2829
# Link against the main livekit SDK library (which transitively provides
2930
# include paths for livekit/*.h and links livekit_ffi).
3031
target_link_libraries(livekit_bridge
3132
PUBLIC
3233
livekit
34+
PRIVATE
35+
spdlog::spdlog
36+
)
37+
38+
target_compile_definitions(livekit_bridge
39+
PRIVATE
40+
SPDLOG_ACTIVE_LEVEL=${_SPDLOG_ACTIVE_LEVEL}
3341
)
3442

3543
if(MSVC)

bridge/include/livekit_bridge/livekit_bridge.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,6 @@ class LiveKitBridge {
341341
std::vector<std::shared_ptr<BridgeAudioTrack>> published_audio_tracks_;
342342
/// @copydoc published_audio_tracks_
343343
std::vector<std::shared_ptr<BridgeVideoTrack>> published_video_tracks_;
344-
345344
};
346345

347346
} // namespace livekit_bridge

bridge/src/bridge_audio_track.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525
#include "livekit/local_participant.h"
2626
#include "livekit/local_track_publication.h"
2727

28-
#include <iostream>
2928
#include <stdexcept>
3029

30+
#include "lk_log.h"
31+
3132
namespace livekit_bridge {
3233

3334
BridgeAudioTrack::BridgeAudioTrack(
@@ -56,7 +57,7 @@ bool BridgeAudioTrack::pushFrame(const std::vector<std::int16_t> &data,
5657
try {
5758
source_->captureFrame(frame, timeout_ms);
5859
} catch (const std::exception &e) {
59-
std::cerr << "[BridgeAudioTrack] captureFrame error: " << e.what() << "\n";
60+
LK_LOG_ERROR("BridgeAudioTrack captureFrame error: {}", e.what());
6061
return false;
6162
}
6263
return true;
@@ -77,7 +78,7 @@ bool BridgeAudioTrack::pushFrame(const std::int16_t *data,
7778
try {
7879
source_->captureFrame(frame, timeout_ms);
7980
} catch (const std::exception &e) {
80-
std::cerr << "[BridgeAudioTrack] captureFrame error: " << e.what() << "\n";
81+
LK_LOG_ERROR("BridgeAudioTrack captureFrame error: {}", e.what());
8182
return false;
8283
}
8384
return true;
@@ -115,8 +116,8 @@ void BridgeAudioTrack::release() {
115116
participant_->unpublishTrack(publication_->sid());
116117
} catch (...) {
117118
// Best-effort cleanup; ignore errors during teardown
118-
std::cerr << "[BridgeAudioTrack] unpublishTrack error, continuing with "
119-
"cleanup\n";
119+
LK_LOG_WARN("BridgeAudioTrack unpublishTrack error, continuing with "
120+
"cleanup");
120121
}
121122
}
122123

bridge/src/bridge_room_delegate.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class BridgeRoomDelegate : public livekit::RoomDelegate {
3939
void onTrackSubscribed(livekit::Room &room,
4040
const livekit::TrackSubscribedEvent &ev) override;
4141

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

bridge/src/bridge_video_track.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525
#include "livekit/video_frame.h"
2626
#include "livekit/video_source.h"
2727

28-
#include <iostream>
2928
#include <stdexcept>
3029

30+
#include "lk_log.h"
31+
3132
namespace livekit_bridge {
3233

3334
BridgeVideoTrack::BridgeVideoTrack(
@@ -56,7 +57,7 @@ bool BridgeVideoTrack::pushFrame(const std::vector<std::uint8_t> &rgba,
5657
try {
5758
source_->captureFrame(frame, timestamp_us);
5859
} catch (const std::exception &e) {
59-
std::cerr << "[BridgeVideoTrack] captureFrame error: " << e.what() << "\n";
60+
LK_LOG_ERROR("BridgeVideoTrack captureFrame error: {}", e.what());
6061
return false;
6162
}
6263
return true;
@@ -76,7 +77,7 @@ bool BridgeVideoTrack::pushFrame(const std::uint8_t *rgba,
7677
try {
7778
source_->captureFrame(frame, timestamp_us);
7879
} catch (const std::exception &e) {
79-
std::cerr << "[BridgeVideoTrack] captureFrame error: " << e.what() << "\n";
80+
LK_LOG_ERROR("BridgeVideoTrack captureFrame error: {}", e.what());
8081
return false;
8182
}
8283
return true;
@@ -114,8 +115,8 @@ void BridgeVideoTrack::release() {
114115
participant_->unpublishTrack(publication_->sid());
115116
} catch (...) {
116117
// Best-effort cleanup; ignore errors during teardown
117-
std::cerr << "[BridgeVideoTrack] unpublishTrack error, continuing with "
118-
"cleanup\n";
118+
LK_LOG_WARN("BridgeVideoTrack unpublishTrack error, continuing with "
119+
"cleanup");
119120
}
120121
}
121122

bridge/src/livekit_bridge.cpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@
3535
#include "livekit/video_stream.h"
3636

3737
#include <cassert>
38-
#include <iostream>
3938
#include <stdexcept>
4039

40+
#include "lk_log.h"
41+
4142
namespace livekit_bridge {
4243

4344
// ---------------------------------------------------------------
@@ -86,7 +87,7 @@ bool LiveKitBridge::connect(const std::string &url, const std::string &token,
8687

8788
// Initialize the LiveKit SDK (idempotent)
8889
if (!sdk_initialized_) {
89-
livekit::initialize(livekit::LogSink::kConsole);
90+
livekit::initialize();
9091
sdk_initialized_ = true;
9192
}
9293
}
@@ -132,8 +133,8 @@ void LiveKitBridge::disconnect() {
132133
std::lock_guard<std::mutex> lock(mutex_);
133134

134135
if (!connected_) {
135-
std::cerr << "[LiveKitBridge] Attempting to disconnect an already "
136-
"disconnected bridge. Things may not disconnect properly.\n";
136+
LK_LOG_WARN("Attempting to disconnect an already disconnected bridge. "
137+
"Things may not disconnect properly.");
137138
}
138139

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

421-
auto stream_copy = stream; // captured by the thread
421+
auto stream_copy = stream;
422422

423423
ActiveReader reader;
424424
reader.audio_stream = std::move(stream);
@@ -429,17 +429,15 @@ LiveKitBridge::startAudioReader(const CallbackKey &key,
429429
try {
430430
cb(ev.frame);
431431
} catch (const std::exception &e) {
432-
std::cerr << "[LiveKitBridge] Audio callback exception: " << e.what()
433-
<< "\n";
432+
LK_LOG_ERROR("Audio callback exception: {}", e.what());
434433
}
435434
}
436435
});
437436

438437
active_readers_[key] = std::move(reader);
439438
if (active_readers_.size() > kMaxActiveReaders) {
440-
std::cerr << "[LiveKitBridge] More than expected active readers. Need to "
441-
"evaluate how much to expect/support.";
442-
"solution";
439+
LK_LOG_WARN("More than expected active readers. Need to evaluate how much "
440+
"to expect/support.");
443441
}
444442
return old_thread;
445443
}
@@ -457,8 +455,7 @@ LiveKitBridge::startVideoReader(const CallbackKey &key,
457455
opts.format = livekit::VideoBufferType::RGBA;
458456
auto stream = livekit::VideoStream::fromTrack(track, opts);
459457
if (!stream) {
460-
std::cerr << "[LiveKitBridge] Failed to create VideoStream for "
461-
<< key.identity << "\n";
458+
LK_LOG_ERROR("Failed to create VideoStream for {}", key.identity);
462459
return old_thread;
463460
}
464461

@@ -473,16 +470,15 @@ LiveKitBridge::startVideoReader(const CallbackKey &key,
473470
try {
474471
cb(ev.frame, ev.timestamp_us);
475472
} catch (const std::exception &e) {
476-
std::cerr << "[LiveKitBridge] Video callback exception: " << e.what()
477-
<< "\n";
473+
LK_LOG_ERROR("Video callback exception: {}", e.what());
478474
}
479475
}
480476
});
481477

482478
active_readers_[key] = std::move(reader);
483479
if (active_readers_.size() > kMaxActiveReaders) {
484-
std::cerr << "[LiveKitBridge] More than expected active readers. Need to "
485-
"evaluate how much to expect/support.";
480+
LK_LOG_WARN("More than expected active readers. Need to evaluate how much "
481+
"to expect/support.");
486482
}
487483
return old_thread;
488484
}

0 commit comments

Comments
 (0)