Rerun C++ SDK
Loading...
Searching...
No Matches
recording_stream.hpp
1#pragma once
2
3#include <chrono>
4#include <cstdint> // uint32_t etc.
5#include <optional>
6#include <string_view>
7#include <vector>
8
9#include "as_components.hpp"
10#include "collection.hpp"
11#include "error.hpp"
12#include "spawn_options.hpp"
13
14namespace rerun {
15 struct DataCell;
16
17 enum class StoreKind {
18 Recording,
19 Blueprint,
20 };
21
22 /// A `RecordingStream` handles everything related to logging data into Rerun.
23 ///
24 /// ## Multithreading and ordering
25 ///
26 /// A `RecordingStream` is thread-safe.
27 ///
28 /// Internally, all operations are linearized into a pipeline:
29 /// - All operations sent by a given thread will take effect in the same exact order as that
30 /// thread originally sent them in, from its point of view.
31 /// - There isn't any well defined global order across multiple threads.
32 ///
33 /// This means that e.g. flushing the pipeline (`flush_blocking`) guarantees that all
34 /// previous data sent by the calling thread has been recorded; no more, no less.
35 /// (e.g. it does not mean that all file caches are flushed)
36 ///
37 /// ## Shutdown
38 ///
39 /// The `RecordingStream` can only be shutdown by dropping all instances of it, at which point
40 /// it will automatically take care of flushing any pending data that might remain in the
41 /// pipeline.
42 ///
43 /// TODO(andreas): The only way of having two instances of a `RecordingStream` is currently to
44 /// set it as a the global.
45 ///
46 /// Shutting down cannot ever block.
47 ///
48 /// ## Logging
49 ///
50 /// Internally, the stream will automatically micro-batch multiple log calls to optimize
51 /// transport.
52 /// See [SDK Micro Batching](https://www.rerun.io/docs/reference/sdk-micro-batching) for
53 /// more information.
54 ///
55 /// The data will be timestamped automatically based on the `RecordingStream`'s
56 /// internal clock.
58 public:
59 /// Creates a new recording stream to log to.
60 ///
61 /// \param app_id The user-chosen name of the application doing the logging.
62 /// \param store_kind Whether to log to the recording store or the blueprint store.
63 RecordingStream(std::string_view app_id, StoreKind store_kind = StoreKind::Recording);
65
66 /// \private
68
69 // TODO(andreas): We could easily make the recording stream trivial to copy by bumping Rusts
70 // ref counter by adding a copy of the recording stream to the list of C recording streams.
71 // Doing it this way would likely yield the most consistent behavior when interacting with
72 // global streams (and especially when interacting with different languages in the same
73 // application).
74 /// \private
75 RecordingStream(const RecordingStream&) = delete;
76 /// \private
77 RecordingStream() = delete;
78
79 // -----------------------------------------------------------------------------------------
80 /// \name Properties
81 /// @{
82
83 /// Returns the store kind as passed during construction
84 StoreKind kind() const {
85 return _store_kind;
86 }
87
88 /// Returns whether the recording stream is enabled.
89 ///
90 /// All log functions early out if a recording stream is disabled.
91 /// Naturally, logging functions that take unserialized data will skip the serialization step as well.
92 bool is_enabled() const {
93 return _enabled;
94 }
95
96 /// @}
97
98 // -----------------------------------------------------------------------------------------
99 /// \name Controlling globally available instances of RecordingStream.
100 /// @{
101
102 /// Replaces the currently active recording for this stream's store kind in the global scope
103 /// with this one.
104 ///
105 /// Afterwards, destroying this recording stream will *not* change the global recording
106 /// stream, as it increases an internal ref-count.
107 void set_global() const;
108
109 /// Replaces the currently active recording for this stream's store kind in the thread-local
110 /// scope with this one
111 ///
112 /// Afterwards, destroying this recording stream will *not* change the thread local
113 /// recording stream, as it increases an internal ref-count.
114 void set_thread_local() const;
115
116 /// Retrieves the most appropriate globally available recording stream for the given kind.
117 ///
118 /// I.e. thread-local first, then global.
119 /// If neither was set, any operations on the returned stream will be no-ops.
120 static RecordingStream& current(StoreKind store_kind = StoreKind::Recording);
121
122 /// @}
123
124 // -----------------------------------------------------------------------------------------
125 /// \name Directing the recording stream.
126 /// \details Either of these needs to be called, otherwise the stream will buffer up indefinitely.
127 /// @{
128
129 /// Connect to a remote Rerun Viewer on the given ip:port.
130 ///
131 /// Requires that you first start a Rerun Viewer by typing 'rerun' in a terminal.
132 ///
133 /// flush_timeout_sec:
134 /// The minimum time the SDK will wait during a flush before potentially
135 /// dropping data if progress is not being made. Passing a negative value indicates no
136 /// timeout, and can cause a call to `flush` to block indefinitely.
137 ///
138 /// This function returns immediately.
139 Error connect(std::string_view tcp_addr = "127.0.0.1:9876", float flush_timeout_sec = 2.0)
140 const;
141
142 /// Spawns a new Rerun Viewer process from an executable available in PATH, then connects to it
143 /// over TCP.
144 ///
145 /// If a Rerun Viewer is already listening on this TCP port, the stream will be redirected to
146 /// that viewer instead of starting a new one.
147 ///
148 /// ## Parameters
149 /// options:
150 /// See `rerun::SpawnOptions` for more information.
151 ///
152 /// flush_timeout_sec:
153 /// The minimum time the SDK will wait during a flush before potentially
154 /// dropping data if progress is not being made. Passing a negative value indicates no
155 /// timeout, and can cause a call to `flush` to block indefinitely.
156 Error spawn(const SpawnOptions& options = {}, float flush_timeout_sec = 2.0) const;
157
158 /// @see RecordingStream::spawn
159 template <typename TRep, typename TPeriod>
161 const SpawnOptions& options = {},
162 std::chrono::duration<TRep, TPeriod> flush_timeout = std::chrono::seconds(2)
163 ) const {
164 using seconds_float = std::chrono::duration<float>; // Default ratio is 1:1 == seconds.
165 return spawn(options, std::chrono::duration_cast<seconds_float>(flush_timeout).count());
166 }
167
168 /// Stream all log-data to a given file.
169 ///
170 /// This function returns immediately.
171 Error save(std::string_view path) const;
172
173 /// Initiates a flush the batching pipeline and waits for it to propagate.
174 ///
175 /// See `RecordingStream` docs for ordering semantics and multithreading guarantees.
176 void flush_blocking() const;
177
178 /// @}
179
180 // -----------------------------------------------------------------------------------------
181 /// \name Controlling log time.
182 /// \details
183 /// @{
184
185 /// Set the current time of the recording, for the current calling thread.
186 ///
187 /// Used for all subsequent logging performed from this same thread, until the next call
188 /// to one of the time setting methods.
189 ///
190 /// For example: `rec.set_time_sequence("frame_nr", frame_nr)`.
191 ///
192 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
193 /// @see set_time_seconds, set_time_nanos, reset_time, set_time, disable_timeline
194 void set_time_sequence(std::string_view timeline_name, int64_t sequence_nr) const;
195
196 /// Set the current time of the recording, for the current calling thread.
197 ///
198 /// Used for all subsequent logging performed from this same thread, until the next call
199 /// to one of the time setting methods.
200 ///
201 /// For example: `rec.set_time("sim_time", sim_time_secs)`.
202 ///
203 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
204 /// @see set_time_sequence, set_time_seconds, set_time_nanos, reset_time, disable_timeline
205 template <typename TClock>
206 void set_time(std::string_view timeline_name, std::chrono::time_point<TClock> time) const {
207 set_time(timeline_name, time.time_since_epoch());
208 }
209
210 /// Set the current time of the recording, for the current calling thread.
211 ///
212 /// Used for all subsequent logging performed from this same thread, until the next call
213 /// to one of the time setting methods.
214 ///
215 /// For example: `rec.set_time("sim_time", sim_time_secs)`.
216 ///
217 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
218 /// @see set_time_sequence, set_time_seconds, set_time_nanos, reset_time, disable_timeline
219 template <typename TRep, typename TPeriod>
220 void set_time(std::string_view timeline_name, std::chrono::duration<TRep, TPeriod> time)
221 const {
222 if constexpr (std::is_floating_point<TRep>::value) {
223 using seconds_double =
224 std::chrono::duration<double>; // Default ratio is 1:1 == seconds.
226 timeline_name,
227 std::chrono::duration_cast<seconds_double>(time).count()
228 );
229 } else {
231 timeline_name,
232 std::chrono::duration_cast<std::chrono::nanoseconds>(time).count()
233 );
234 }
235 }
236
237 /// Set the current time of the recording, for the current calling thread.
238 ///
239 /// Used for all subsequent logging performed from this same thread, until the next call
240 /// to one of the time setting methods.
241 ///
242 /// For example: `rec.set_time_seconds("sim_time", sim_time_secs)`.
243 ///
244 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
245 /// @see set_time_sequence, set_time_nanos, reset_time, set_time, disable_timeline
246 void set_time_seconds(std::string_view timeline_name, double seconds) const;
247
248 /// Set the current time of the recording, for the current calling thread.
249 ///
250 /// Used for all subsequent logging performed from this same thread, until the next call
251 /// to one of the time setting methods.
252 ///
253 /// For example: `rec.set_time_nanos("sim_time", sim_time_nanos)`.
254 ///
255 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
256 /// @see set_time_sequence, set_time_seconds, reset_time, set_time, disable_timeline
257 void set_time_nanos(std::string_view timeline_name, int64_t nanos) const;
258
259 /// Stops logging to the specified timeline for subsequent log calls.
260 ///
261 /// The timeline is still there, but will not be updated with any new data.
262 ///
263 /// No-op if the timeline doesn't exist.
264 ///
265 /// @see set_time_sequence, set_time_seconds, set_time, reset_time
266 void disable_timeline(std::string_view timeline_name) const;
267
268 /// Clears out the current time of the recording, for the current calling thread.
269 ///
270 /// Used for all subsequent logging performed from this same thread, until the next call
271 /// to one of the time setting methods.
272 ///
273 /// For example: `rec.reset_time()`.
274 /// @see set_time_sequence, set_time_seconds, set_time_nanos, disable_timeline
275 void reset_time() const;
276
277 /// @}
278
279 // -----------------------------------------------------------------------------------------
280 /// \name Logging
281 /// @{
282
283 /// Logs one or more archetype and/or component batches.
284 ///
285 /// This is the main entry point for logging data to rerun. It can be used to log anything
286 /// that implements the `AsComponents<T>` trait.
287 ///
288 /// When logging data, you must always provide an [entity_path](https://www.rerun.io/docs/concepts/entity-path)
289 /// for identifying the data. Note that the path prefix "rerun/" is considered reserved for use by the Rerun SDK
290 /// itself and should not be used for logging user data. This is where Rerun will log additional information
291 /// such as warnings.
292 ///
293 /// The most common way to log is with one of the rerun archetypes, all of which implement the `AsComponents` trait.
294 ///
295 /// For example, to log two 3D points:
296 /// ```
297 /// rec.log("my/point", rerun::Points3D({{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}}));
298 /// ```
299 ///
300 /// The `log` function can flexibly accept an arbitrary number of additional objects which will
301 /// be merged into the first entity so long as they don't expose conflicting components, for instance:
302 /// ```
303 /// // Log three points with arrows sticking out of them:
304 /// rec.log(
305 /// "my/points",
306 /// rerun::Points3D({{0.2f, 0.5f, 0.3f}, {0.9f, 1.2f, 0.1f}, {1.0f, 4.2f, 0.3f}})
307 /// .with_radii({0.1, 0.2, 0.3}),
308 /// rerun::Arrows3D::from_vectors({{0.3f, 2.1f, 0.2f}, {0.9f, -1.1, 2.3f}, {-0.4f, 0.5f, 2.9f}})
309 /// );
310 /// ```
311 ///
312 /// Any failures that may occur during serialization are handled with `Error::handle`.
313 ///
314 /// \param entity_path Path to the entity in the space hierarchy.
315 /// \param archetypes_or_collectiones Any type for which the `AsComponents<T>` trait is implemented.
316 /// This is the case for any archetype or `std::vector`/`std::array`/C-array of components implements.
317 ///
318 /// @see try_log, log_timeless, try_log_with_timeless
319 template <typename... Ts>
320 void log(std::string_view entity_path, const Ts&... archetypes_or_collectiones) const {
321 if (!is_enabled()) {
322 return;
323 }
324 try_log_with_timeless(entity_path, false, archetypes_or_collectiones...).handle();
325 }
326
327 /// Logs one or more archetype and/or component batches as timeless data.
328 ///
329 /// Like `log` but logs the data as timeless:
330 /// Timeless data is present on all timelines and behaves as if it was recorded infinitely
331 /// far into the past.
332 ///
333 /// Failures are handled with `Error::handle`.
334 ///
335 /// \param entity_path Path to the entity in the space hierarchy.
336 /// \param archetypes_or_collectiones Any type for which the `AsComponents<T>` trait is implemented.
337 /// This is the case for any archetype or `std::vector`/`std::array`/C-array of components implements.
338 ///
339 /// @see log, try_log_timeless, try_log_with_timeless
340 template <typename... Ts>
341 void log_timeless(std::string_view entity_path, const Ts&... archetypes_or_collectiones)
342 const {
343 if (!is_enabled()) {
344 return;
345 }
346 try_log_with_timeless(entity_path, true, archetypes_or_collectiones...).handle();
347 }
348
349 /// Logs one or more archetype and/or component batches.
350 ///
351 /// See `log` for more information.
352 /// Unlike `log` this method returns an error if an error occurs during serialization or logging.
353 ///
354 /// \param entity_path Path to the entity in the space hierarchy.
355 /// \param archetypes_or_collectiones Any type for which the `AsComponents<T>` trait is implemented.
356 /// This is the case for any archetype or `std::vector`/`std::array`/C-array of components implements.
357 ///
358 /// @see log, try_log_timeless, try_log_with_timeless
359 template <typename... Ts>
360 Error try_log(std::string_view entity_path, const Ts&... archetypes_or_collectiones) const {
361 if (!is_enabled()) {
362 return Error::ok();
363 }
364 return try_log_with_timeless(entity_path, false, archetypes_or_collectiones...);
365 }
366
367 /// Logs one or more archetype and/or component batches as timeless data, returning an error.
368 ///
369 /// See `log`/`log_timeless` for more information.
370 /// Unlike `log_timeless` this method returns if an error occurs during serialization or logging.
371 ///
372 /// \param entity_path Path to the entity in the space hierarchy.
373 /// \param archetypes_or_collectiones Any type for which the `AsComponents<T>` trait is implemented.
374 /// This is the case for any archetype or `std::vector`/`std::array`/C-array of components implements.
375 /// \returns An error if an error occurs during serialization or logging.
376 ///
377 /// @see log_timeless, try_log, try_log_with_timeless
378 template <typename... Ts>
380 std::string_view entity_path, const Ts&... archetypes_or_collectiones
381 ) const {
382 if (!is_enabled()) {
383 return Error::ok();
384 }
385 return try_log_with_timeless(entity_path, true, archetypes_or_collectiones...);
386 }
387
388 /// Logs one or more archetype and/or component batches optionally timeless, returning an error.
389 ///
390 /// See `log`/`log_timeless` for more information.
391 /// Returns an error if an error occurs during serialization or logging.
392 ///
393 /// \param entity_path Path to the entity in the space hierarchy.
394 /// \param timeless If true, the logged components will be timeless.
395 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
396 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
397 /// \param archetypes_or_collectiones Any type for which the `AsComponents<T>` trait is implemented.
398 /// This is the case for any archetype or `std::vector`/`std::array`/C-array of components implements.
399 /// \returns An error if an error occurs during serialization or logging.
400 ///
401 /// @see log, try_log, log_timeless, try_log_timeless
402 template <typename... Ts>
404 std::string_view entity_path, bool timeless, const Ts&... archetypes_or_collectiones
405 ) const {
406 if (!is_enabled()) {
407 return Error::ok();
408 }
409 std::vector<DataCell> serialized_batches;
410 Error err;
411 (
412 [&] {
413 if (err.is_err()) {
414 return;
415 }
416
417 const Result<std::vector<DataCell>> serialization_result =
418 AsComponents<Ts>().serialize(archetypes_or_collectiones);
419 if (serialization_result.is_err()) {
420 err = serialization_result.error;
421 return;
422 }
423
424 if (serialized_batches.empty()) {
425 // Fast path for the first batch (which is usually the only one!)
426 serialized_batches = std::move(serialization_result.value);
427 } else {
428 serialized_batches.insert(
429 serialized_batches.end(),
430 std::make_move_iterator(serialization_result.value.begin()),
431 std::make_move_iterator(serialization_result.value.end())
432 );
433 }
434 }(),
435 ...
436 );
437 RR_RETURN_NOT_OK(err);
438
439 return try_log_serialized_batches(entity_path, timeless, std::move(serialized_batches));
440 }
441
442 /// Logs several serialized batches batches, returning an error on failure.
443 ///
444 /// This is a more low-level API than `log`/`log_timeless\ and requires you to already serialize the data
445 /// ahead of time.
446 ///
447 /// The number of instances in each batch must either be equal to the maximum or:
448 /// - zero instances - implies a clear
449 /// - single instance (but other instances have more) - causes a splat
450 ///
451 /// \param entity_path Path to the entity in the space hierarchy.
452 /// \param timeless If true, the logged components will be timeless.
453 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
454 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
455 /// \param batches The serialized batches to log.
456 ///
457 /// \see `log`, `try_log`, `log_timeless`, `try_log_timeless`, `try_log_with_timeless`
459 std::string_view entity_path, bool timeless, std::vector<DataCell> batches
460 ) const;
461
462 /// Bottom level API that logs raw data cells to the recording stream.
463 ///
464 /// In order to use this you need to pass serialized Arrow data cells.
465 ///
466 /// \param entity_path Path to the entity in the space hierarchy.
467 /// \param num_instances
468 /// Each cell is expected to hold exactly `num_instances` instances.
469 /// \param num_data_cells Number of data cells passed in.
470 /// \param data_cells The data cells to log.
471 /// \param inject_time
472 /// If set to `true`, the row's timestamp data will be overridden using the recording
473 /// streams internal clock.
474 ///
475 /// \see `try_log_serialized_batches`
477 std::string_view entity_path, size_t num_instances, size_t num_data_cells,
478 const DataCell* data_cells, bool inject_time
479 ) const;
480
481 /// @}
482
483 private:
484 RecordingStream(uint32_t id, StoreKind store_kind);
485
486 uint32_t _id;
487 StoreKind _store_kind;
488 bool _enabled;
489 };
490} // namespace rerun
Status outcome object (success or error) returned for fallible operations.
Definition error.hpp:87
void handle() const
Handle this error based on the set log handler.
bool is_err() const
Returns true if the code is not Ok.
Definition error.hpp:123
static Error ok()
Creates a new error set to ok.
Definition error.hpp:108
A RecordingStream handles everything related to logging data into Rerun.
Definition recording_stream.hpp:57
Error spawn(const SpawnOptions &options={}, float flush_timeout_sec=2.0) const
Spawns a new Rerun Viewer process from an executable available in PATH, then connects to it over TCP.
bool is_enabled() const
Returns whether the recording stream is enabled.
Definition recording_stream.hpp:92
void log_timeless(std::string_view entity_path, const Ts &... archetypes_or_collectiones) const
Logs one or more archetype and/or component batches as timeless data.
Definition recording_stream.hpp:341
Error try_log_with_timeless(std::string_view entity_path, bool timeless, const Ts &... archetypes_or_collectiones) const
Logs one or more archetype and/or component batches optionally timeless, returning an error.
Definition recording_stream.hpp:403
void disable_timeline(std::string_view timeline_name) const
Stops logging to the specified timeline for subsequent log calls.
void reset_time() const
Clears out the current time of the recording, for the current calling thread.
Error save(std::string_view path) const
Stream all log-data to a given file.
Error try_log_serialized_batches(std::string_view entity_path, bool timeless, std::vector< DataCell > batches) const
Logs several serialized batches batches, returning an error on failure.
StoreKind kind() const
Returns the store kind as passed during construction.
Definition recording_stream.hpp:84
Error spawn(const SpawnOptions &options={}, std::chrono::duration< TRep, TPeriod > flush_timeout=std::chrono::seconds(2)) const
Definition recording_stream.hpp:160
Error try_log_timeless(std::string_view entity_path, const Ts &... archetypes_or_collectiones) const
Logs one or more archetype and/or component batches as timeless data, returning an error.
Definition recording_stream.hpp:379
void flush_blocking() const
Initiates a flush the batching pipeline and waits for it to propagate.
void set_time_nanos(std::string_view timeline_name, int64_t nanos) const
Set the current time of the recording, for the current calling thread.
void set_time(std::string_view timeline_name, std::chrono::duration< TRep, TPeriod > time) const
Set the current time of the recording, for the current calling thread.
Definition recording_stream.hpp:220
RecordingStream(std::string_view app_id, StoreKind store_kind=StoreKind::Recording)
Creates a new recording stream to log to.
static RecordingStream & current(StoreKind store_kind=StoreKind::Recording)
Retrieves the most appropriate globally available recording stream for the given kind.
void set_thread_local() const
Replaces the currently active recording for this stream's store kind in the thread-local scope with t...
void set_time(std::string_view timeline_name, std::chrono::time_point< TClock > time) const
Set the current time of the recording, for the current calling thread.
Definition recording_stream.hpp:206
Error try_log_data_row(std::string_view entity_path, size_t num_instances, size_t num_data_cells, const DataCell *data_cells, bool inject_time) const
Bottom level API that logs raw data cells to the recording stream.
Error try_log(std::string_view entity_path, const Ts &... archetypes_or_collectiones) const
Logs one or more archetype and/or component batches.
Definition recording_stream.hpp:360
void set_time_seconds(std::string_view timeline_name, double seconds) const
Set the current time of the recording, for the current calling thread.
void set_global() const
Replaces the currently active recording for this stream's store kind in the global scope with this on...
Error connect(std::string_view tcp_addr="127.0.0.1:9876", float flush_timeout_sec=2.0) const
Connect to a remote Rerun Viewer on the given ip:port.
void log(std::string_view entity_path, const Ts &... archetypes_or_collectiones) const
Logs one or more archetype and/or component batches.
Definition recording_stream.hpp:320
void set_time_sequence(std::string_view timeline_name, int64_t sequence_nr) const
Set the current time of the recording, for the current calling thread.
A class for representing either a usable value, or an error.
Definition result.hpp:14
bool is_err() const
Returns true if error is not set to rerun::ErrorCode::Ok, implying that no value is contained,...
Definition result.hpp:44
All Rerun C++ types and functions are in the rerun namespace or one of its nested namespaces.
Definition rerun.hpp:20
Arrow-encoded data of a single batch components for a single entity.
Definition data_cell.hpp:21
Options to control the behavior of spawn.
Definition spawn_options.hpp:17