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 <filesystem>
6#include <optional>
7#include <string_view>
8#include <type_traits>
9#include <vector>
10
11#include "as_components.hpp"
12#include "component_column.hpp"
13#include "error.hpp"
14#include "spawn_options.hpp"
15#include "time_column.hpp"
16
17namespace rerun {
18 struct ComponentBatch;
19
20 enum class StoreKind {
21 Recording,
22 Blueprint,
23 };
24
25 /// A `RecordingStream` handles everything related to logging data into Rerun.
26 ///
27 /// ## Multithreading and ordering
28 ///
29 /// A `RecordingStream` is thread-safe.
30 ///
31 /// Internally, all operations are linearized into a pipeline:
32 /// - All operations sent by a given thread will take effect in the same exact order as that
33 /// thread originally sent them in, from its point of view.
34 /// - There isn't any well defined global order across multiple threads.
35 ///
36 /// This means that e.g. flushing the pipeline (`flush_blocking`) guarantees that all
37 /// previous data sent by the calling thread has been recorded; no more, no less.
38 /// (e.g. it does not mean that all file caches are flushed)
39 ///
40 /// ## Shutdown
41 ///
42 /// The `RecordingStream` can only be shutdown by dropping all instances of it, at which point
43 /// it will automatically take care of flushing any pending data that might remain in the
44 /// pipeline.
45 ///
46 /// TODO(andreas): The only way of having two instances of a `RecordingStream` is currently to
47 /// set it as a the global.
48 ///
49 /// Shutting down cannot ever block.
50 ///
51 /// ## Logging
52 ///
53 /// Internally, the stream will automatically micro-batch multiple log calls to optimize
54 /// transport.
55 /// See [SDK Micro Batching](https://www.rerun.io/docs/reference/sdk/micro-batching) for
56 /// more information.
57 ///
58 /// The data will be timestamped automatically based on the `RecordingStream`'s
59 /// internal clock.
61 private:
62 // TODO(grtlr): Ideally we'd expose more of the `EntityPath` struct to the C++ world so
63 // that we don't have to hardcode this here.
64 static constexpr const char PROPERTIES_ENTITY_PATH[] = "__properties/";
65 static constexpr const char RECORDING_PROPERTIES_ENTITY_PATH[] = "__properties/recording/";
66
67 public:
68 /// Creates a new recording stream to log to.
69 ///
70 /// \param app_id The user-chosen name of the application doing the logging.
71 /// \param recording_id The user-chosen name of the recording being logged to.
72 /// \param store_kind Whether to log to the recording store or the blueprint store.
74 std::string_view app_id, std::string_view recording_id = std::string_view(),
75 StoreKind store_kind = StoreKind::Recording
76 );
78
79 /// \private
81
82 // TODO(andreas): We could easily make the recording stream trivial to copy by bumping Rusts
83 // ref counter by adding a copy of the recording stream to the list of C recording streams.
84 // Doing it this way would likely yield the most consistent behavior when interacting with
85 // global streams (and especially when interacting with different languages in the same
86 // application).
87 /// \private
88 RecordingStream(const RecordingStream&) = delete;
89 /// \private
90 RecordingStream() = delete;
91
92 // -----------------------------------------------------------------------------------------
93 /// \name Properties
94 /// @{
95
96 /// Returns the store kind as passed during construction
97 StoreKind kind() const {
98 return _store_kind;
99 }
100
101 /// Returns whether the recording stream is enabled.
102 ///
103 /// All log functions early out if a recording stream is disabled.
104 /// Naturally, logging functions that take unserialized data will skip the serialization step as well.
105 bool is_enabled() const {
106 return _enabled;
107 }
108
109 /// @}
110
111 // -----------------------------------------------------------------------------------------
112 /// \name Controlling globally available instances of RecordingStream.
113 /// @{
114
115 /// Replaces the currently active recording for this stream's store kind in the global scope
116 /// with this one.
117 ///
118 /// Afterwards, destroying this recording stream will *not* change the global recording
119 /// stream, as it increases an internal ref-count.
120 void set_global() const;
121
122 /// Replaces the currently active recording for this stream's store kind in the thread-local
123 /// scope with this one
124 ///
125 /// Afterwards, destroying this recording stream will *not* change the thread local
126 /// recording stream, as it increases an internal ref-count.
127 void set_thread_local() const;
128
129 /// Retrieves the most appropriate globally available recording stream for the given kind.
130 ///
131 /// I.e. thread-local first, then global.
132 /// If neither was set, any operations on the returned stream will be no-ops.
133 static RecordingStream& current(StoreKind store_kind = StoreKind::Recording);
134
135 /// @}
136
137 // -----------------------------------------------------------------------------------------
138 /// \name Directing the recording stream.
139 /// \details Either of these needs to be called, otherwise the stream will buffer up indefinitely.
140 /// @{
141
142 /// Connect to a remote Rerun Viewer on the given URL.
143 ///
144 /// Requires that you first start a Rerun Viewer by typing 'rerun' in a terminal.
145 ///
146 /// url:
147 /// The scheme must be one of `rerun://`, `rerun+http://`, or `rerun+https://`,
148 /// and the pathname must be `/proxy`.
149 ///
150 /// The default is `rerun+http://127.0.0.1:9876/proxy`.
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 ///
157 /// This function returns immediately.
159 std::string_view url = "rerun+http://127.0.0.1:9876/proxy",
160 float flush_timeout_sec = 2.0
161 ) const;
162
163 /// Swaps the underlying sink for a gRPC server sink pre-configured to listen on `rerun+http://{bind_ip}:{port}/proxy`.
164 ///
165 /// The gRPC server will buffer all log data in memory so that late connecting viewers will get all the data.
166 /// You can limit the amount of data buffered by the gRPC server with the `server_memory_limit` argument.
167 /// Once reached, the earliest logged data will be dropped. Static data is never dropped.
168 ///
169 /// Returns the URI of the gRPC server so you can connect to it from a viewer.
170 ///
171 /// This function returns immediately.
173 std::string_view bind_ip = "0.0.0.0", uint16_t port = 9876,
174 std::string_view server_memory_limit = "75%"
175 ) const;
176
177 /// Spawns a new Rerun Viewer process from an executable available in PATH, then connects to it
178 /// over gRPC.
179 ///
180 /// flush_timeout_sec:
181 /// The minimum time the SDK will wait during a flush before potentially
182 /// dropping data if progress is not being made. Passing a negative value indicates no
183 /// timeout, and can cause a call to `flush` to block indefinitely.
184 ///
185 /// If a Rerun Viewer is already listening on this port, the stream will be redirected to
186 /// that viewer instead of starting a new one.
187 ///
188 /// ## Parameters
189 /// options:
190 /// See `rerun::SpawnOptions` for more information.
191 Error spawn(const SpawnOptions& options = {}, float flush_timeout_sec = 2.0) const;
192
193 /// @see RecordingStream::spawn
194 template <typename TRep, typename TPeriod>
196 const SpawnOptions& options = {},
197 std::chrono::duration<TRep, TPeriod> flush_timeout = std::chrono::seconds(2)
198 ) const {
199 using seconds_float = std::chrono::duration<float>; // Default ratio is 1:1 == seconds.
200 return spawn(options, std::chrono::duration_cast<seconds_float>(flush_timeout).count());
201 }
202
203 /// Stream all log-data to a given `.rrd` file.
204 ///
205 /// The Rerun Viewer is able to read continuously from the resulting rrd file while it is being written.
206 /// However, depending on your OS and configuration, changes may not be immediately visible due to file caching.
207 /// This is a common issue on Windows and (to a lesser extent) on MacOS.
208 ///
209 /// This function returns immediately.
210 Error save(std::string_view path) const;
211
212 /// Stream all log-data to standard output.
213 ///
214 /// Pipe the result into the Rerun Viewer to visualize it.
215 ///
216 /// If there isn't any listener at the other end of the pipe, the `RecordingStream` will
217 /// default back to `buffered` mode, in order not to break the user's terminal.
218 ///
219 /// This function returns immediately.
220 //
221 // NOTE: This should be called `stdout` like in other SDK, but turns out that `stdout` is a
222 // macro when compiling with msvc [1].
223 // [1]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/stdin-stdout-stderr?view=msvc-170
225
226 /// Initiates a flush the batching pipeline and waits for it to propagate.
227 ///
228 /// See `RecordingStream` docs for ordering semantics and multithreading guarantees.
229 void flush_blocking() const;
230
231 /// @}
232
233 // -----------------------------------------------------------------------------------------
234 /// \name Controlling log time (index).
235 /// \details
236 /// @{
237
238 /// Set the index value of the given timeline as a sequence number, for the current calling thread.
239 ///
240 /// Used for all subsequent logging performed from this same thread, until the next call
241 /// to one of the time setting methods.
242 ///
243 /// For example: `rec.set_time_sequence("frame_nr", frame_nr)`.
244 ///
245 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
246 /// @see set_time_sequence, set_time_duration, set_time_duration_secs, set_time_duration_nanos, set_time_timestamp, set_time_timestamp_secs_since_epoch, set_time_timestamp_nanos_since_epoch
247 void set_time_sequence(std::string_view timeline_name, int64_t sequence_nr) const;
248
249 /// Set the index value of the given timeline as a duration, for the current calling thread.
250 ///
251 /// Used for all subsequent logging performed from this same thread, until the next call
252 /// to one of the time setting methods.
253 ///
254 /// For example: `rec.set_time_duration("runtime", time_since_start)`.
255 ///
256 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
257 /// @see set_time_sequence, set_time_duration, set_time_duration_secs, set_time_duration_nanos, set_time_timestamp, set_time_timestamp_secs_since_epoch, set_time_timestamp_nanos_since_epoch
258 template <typename TRep, typename TPeriod>
260 std::string_view timeline_name, std::chrono::duration<TRep, TPeriod> duration
261 ) const {
262 auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
263 set_time_duration_nanos(timeline_name, nanos);
264 }
265
266 /// Set the index value of the given timeline as a duration in seconds, for the current calling thread.
267 ///
268 /// Used for all subsequent logging performed from this same thread, until the next call
269 /// to one of the time setting methods.
270 ///
271 /// For example: `rec.set_time_duration_secs("runtime", seconds_since_start)`.
272 ///
273 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
274 /// @see set_time_sequence, set_time_duration, set_time_duration_secs, set_time_duration_nanos, set_time_timestamp, set_time_timestamp_secs_since_epoch, set_time_timestamp_nanos_since_epoch
275 void set_time_duration_secs(std::string_view timeline_name, double secs) const {
276 set_time_duration_nanos(timeline_name, static_cast<int64_t>(1e9 * secs + 0.5));
277 }
278
279 /// Set the index value of the given timeline as a duration in nanoseconds, for the current calling thread.
280 ///
281 /// Used for all subsequent logging performed from this same thread, until the next call
282 /// to one of the time setting methods.
283 ///
284 /// For example: `rec.set_time_duration_nanos("runtime", nanos_since_start)`.
285 ///
286 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
287 /// @see set_time_sequence, set_time_duration, set_time_duration_secs, set_time_duration_nanos, set_time_timestamp, set_time_timestamp_secs_since_epoch, set_time_timestamp_nanos_since_epoch
288 void set_time_duration_nanos(std::string_view timeline_name, int64_t nanos) const;
289
290 /// Set the index value of the given timeline as a timestamp, for the current calling thread.
291 ///
292 /// Used for all subsequent logging performed from this same thread, until the next call
293 /// to one of the time setting methods.
294 ///
295 /// For example: `rec.set_time_timestamp("capture_time", now())`.
296 ///
297 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
298 /// @see set_time_sequence, set_time_duration, set_time_duration_secs, set_time_duration_nanos, set_time_timestamp, set_time_timestamp_secs_since_epoch, set_time_timestamp_nanos_since_epoch
299 template <typename TClock>
301 std::string_view timeline_name, std::chrono::time_point<TClock> timestamp
302 ) const {
304 timeline_name,
305 std::chrono::duration_cast<std::chrono::nanoseconds>(timestamp.time_since_epoch())
306 .count()
307 );
308 }
309
310 /// Set the index value of the given timeline as seconds since Unix Epoch (1970), for the current calling thread.
311 ///
312 /// Used for all subsequent logging performed from this same thread, until the next call
313 /// to one of the time setting methods.
314 ///
315 /// For example: `rec.set_time_timestamp_secs_since_epoch("capture_time", secs_since_epoch())`.
316 ///
317 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
318 /// @see set_time_sequence, set_time_duration, set_time_duration_secs, set_time_duration_nanos, set_time_timestamp, set_time_timestamp_secs_since_epoch, set_time_timestamp_nanos_since_epoch
319 void set_time_timestamp_secs_since_epoch(std::string_view timeline_name, double seconds)
320 const {
322 timeline_name,
323 static_cast<int64_t>(1e9 * seconds)
324 );
325 }
326
327 /// Set the index value of the given timeline as nanoseconds since Unix Epoch (1970), for the current calling thread.
328 ///
329 /// Used for all subsequent logging performed from this same thread, until the next call
330 /// to one of the time setting methods.
331 ///
332 /// For example: `rec.set_time_timestamp_nanos_since_epoch("capture_time", nanos_since_epoch())`.
333 ///
334 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
335 /// @see set_time_sequence, set_time_duration, set_time_duration_secs, set_time_duration_nanos, set_time_timestamp, set_time_timestamp_secs_since_epoch, set_time_timestamp_nanos_since_epoch
336 void set_time_timestamp_nanos_since_epoch(std::string_view timeline_name, int64_t nanos)
337 const;
338
339 /// Set the current time of the recording, for the current calling thread.
340 ///
341 /// Used for all subsequent logging performed from this same thread, until the next call
342 /// to one of the time setting methods.
343 ///
344 /// For example: `rec.set_time("sim_time", sim_time_secs)`.
345 ///
346 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
347 /// @see set_time_sequence, set_time_seconds, set_time_nanos, reset_time, disable_timeline
348 template <typename TClock>
349 [[deprecated("Renamed to `set_time_timestamp`")]] void set_time(
350 std::string_view timeline_name, std::chrono::time_point<TClock> time
351 ) const {
352 set_time(timeline_name, time.time_since_epoch());
353 }
354
355 /// Set the current time of the recording, for the current calling thread.
356 ///
357 /// Used for all subsequent logging performed from this same thread, until the next call
358 /// to one of the time setting methods.
359 ///
360 /// For example: `rec.set_time("sim_time", sim_time_secs)`.
361 ///
362 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
363 /// @see set_time_sequence, set_time_seconds, set_time_nanos, reset_time, disable_timeline
364 template <typename TRep, typename TPeriod>
365 [[deprecated("Renamed `set_time_duration`")]] void set_time(
366 std::string_view timeline_name, std::chrono::duration<TRep, TPeriod> time
367 ) const {
368 set_time_duration(timeline_name, time);
369 }
370
371 /// Set the current time of the recording, for the current calling thread.
372 ///
373 /// Used for all subsequent logging performed from this same thread, until the next call
374 /// to one of the time setting methods.
375 ///
376 /// For example: `rec.set_time_seconds("sim_time", sim_time_secs)`.
377 ///
378 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
379 /// @see set_time_sequence, set_time_nanos, reset_time, set_time, disable_timeline
380 [[deprecated("Use either `set_time_duration_secs` or `set_time_timestamp_secs_since_epoch`"
381 )]] void
382 set_time_seconds(std::string_view timeline_name, double seconds) const {
383 set_time_duration_secs(timeline_name, seconds);
384 }
385
386 /// Set the current time of the recording, for the current calling thread.
387 ///
388 /// Used for all subsequent logging performed from this same thread, until the next call
389 /// to one of the time setting methods.
390 ///
391 /// For example: `rec.set_time_nanos("sim_time", sim_time_nanos)`.
392 ///
393 /// You can remove a timeline from subsequent log calls again using `rec.disable_timeline`.
394 /// @see set_time_sequence, set_time_seconds, reset_time, set_time, disable_timeline
395 [[deprecated(
396 "Use either `set_time_duration_nanos` or `set_time_timestamp_nanos_since_epoch`"
397 )]] void
398 set_time_nanos(std::string_view timeline_name, int64_t nanos) const {
399 set_time_duration_nanos(timeline_name, nanos);
400 }
401
402 /// Stops logging to the specified timeline for subsequent log calls.
403 ///
404 /// The timeline is still there, but will not be updated with any new data.
405 ///
406 /// No-op if the timeline doesn't exist.
407 ///
408 /// @see set_time_sequence, set_time_seconds, set_time, reset_time
409 void disable_timeline(std::string_view timeline_name) const;
410
411 /// Clears out the current time of the recording, for the current calling thread.
412 ///
413 /// Used for all subsequent logging performed from this same thread, until the next call
414 /// to one of the time setting methods.
415 ///
416 /// For example: `rec.reset_time()`.
417 /// @see set_time_sequence, set_time_seconds, set_time_nanos, disable_timeline
418 void reset_time() const;
419
420 /// @}
421
422 // -----------------------------------------------------------------------------------------
423 /// \name Sending & logging data.
424 /// @{
425
426 /// Logs one or more archetype and/or component batches.
427 ///
428 /// This is the main entry point for logging data to rerun. It can be used to log anything
429 /// that implements the `AsComponents<T>` trait.
430 ///
431 /// When logging data, you must always provide an [entity_path](https://www.rerun.io/docs/concepts/entity-path)
432 /// for identifying the data. Note that paths prefixed with "__" are considered reserved for use by the Rerun SDK
433 /// itself and should not be used for logging user data. This is where Rerun will log additional information
434 /// such as properties and warnings.
435 ///
436 /// The most common way to log is with one of the rerun archetypes, all of which implement the `AsComponents` trait.
437 ///
438 /// For example, to log two 3D points:
439 /// ```
440 /// rec.log("my/point", rerun::Points3D({{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}}));
441 /// ```
442 ///
443 /// The `log` function can flexibly accept an arbitrary number of additional objects which will
444 /// be merged into the first entity, for instance:
445 /// ```
446 /// // Log three points with arrows sticking out of them:
447 /// rec.log(
448 /// "my/points",
449 /// rerun::Points3D({{0.2f, 0.5f, 0.3f}, {0.9f, 1.2f, 0.1f}, {1.0f, 4.2f, 0.3f}})
450 /// .with_radii({0.1, 0.2, 0.3}),
451 /// rerun::Arrows3D::from_vectors({{0.3f, 2.1f, 0.2f}, {0.9f, -1.1, 2.3f}, {-0.4f, 0.5f, 2.9f}})
452 /// );
453 /// ```
454 ///
455 /// Any failures that may are handled with `Error::handle`.
456 ///
457 /// \param entity_path Path to the entity in the space hierarchy.
458 /// \param as_components Any type for which the `AsComponents<T>` trait is implemented.
459 /// This is the case for any archetype as well as individual or collection of `ComponentBatch`.
460 /// You can implement `AsComponents` for your own types as well
461 ///
462 /// @see try_log, log_static, try_log_with_static
463 template <typename... Ts>
464 void log(std::string_view entity_path, const Ts&... as_components) const {
465 if (!is_enabled()) {
466 return;
467 }
468 try_log_with_static(entity_path, false, as_components...).handle();
469 }
470
471 /// Logs one or more archetype and/or component batches as static data.
472 ///
473 /// Like `log` but logs the data as static:
474 /// Static data has no time associated with it, exists on all timelines, and unconditionally shadows
475 /// any temporal data of the same type.
476 ///
477 /// Failures are handled with `Error::handle`.
478 ///
479 /// \param entity_path Path to the entity in the space hierarchy.
480 /// \param as_components Any type for which the `AsComponents<T>` trait is implemented.
481 /// This is the case for any archetype as well as individual or collection of `ComponentBatch`.
482 /// You can implement `AsComponents` for your own types as well
483 ///
484 /// @see log, try_log_static, try_log_with_static
485 template <typename... Ts>
486 void log_static(std::string_view entity_path, const Ts&... as_components) const {
487 if (!is_enabled()) {
488 return;
489 }
490 try_log_with_static(entity_path, true, as_components...).handle();
491 }
492
493 /// Logs one or more archetype and/or component batches.
494 ///
495 /// See `log` for more information.
496 /// Unlike `log` this method returns an error if an error occurs.
497 ///
498 /// \param entity_path Path to the entity in the space hierarchy.
499 /// \param as_components Any type for which the `AsComponents<T>` trait is implemented.
500 /// This is the case for any archetype as well as individual or collection of `ComponentBatch`.
501 /// You can implement `AsComponents` for your own types as well
502 ///
503 /// @see log, try_log_static, try_log_with_static
504 template <typename... Ts>
505 Error try_log(std::string_view entity_path, const Ts&... as_components) const {
506 if (!is_enabled()) {
507 return Error::ok();
508 }
509 return try_log_with_static(entity_path, false, as_components...);
510 }
511
512 /// Logs one or more archetype and/or component batches as static data, returning an error.
513 ///
514 /// See `log`/`log_static` for more information.
515 /// Unlike `log_static` this method returns if an error occurs.
516 ///
517 /// \param entity_path Path to the entity in the space hierarchy.
518 /// \param as_components Any type for which the `AsComponents<T>` trait is implemented.
519 /// This is the case for any archetype as well as individual or collection of `ComponentBatch`.
520 /// You can implement `AsComponents` for your own types as well
521 /// \returns An error if an error occurs during evaluation of `AsComponents` or logging.
522 ///
523 /// @see log_static, try_log, try_log_with_static
524 template <typename... Ts>
525 Error try_log_static(std::string_view entity_path, const Ts&... as_components) const {
526 if (!is_enabled()) {
527 return Error::ok();
528 }
529 return try_log_with_static(entity_path, true, as_components...);
530 }
531
532 /// Logs one or more archetype and/or component batches optionally static, returning an error.
533 ///
534 /// See `log`/`log_static` for more information.
535 /// Returns an error if an error occurs during evaluation of `AsComponents` or logging.
536 ///
537 /// \param entity_path Path to the entity in the space hierarchy.
538 /// \param static_ If true, the logged components will be static.
539 /// Static data has no time associated with it, exists on all timelines, and unconditionally shadows
540 /// any temporal data of the same type.
541 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
542 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
543 /// \param as_components Any type for which the `AsComponents<T>` trait is implemented.
544 /// This is the case for any archetype as well as individual or collection of `ComponentBatch`.
545 /// You can implement `AsComponents` for your own types as well
546 ///
547 /// @see log, try_log, log_static, try_log_static
548 template <typename... Ts>
549 void log_with_static(std::string_view entity_path, bool static_, const Ts&... as_components)
550 const {
551 try_log_with_static(entity_path, static_, as_components...).handle();
552 }
553
554 /// Logs one or more archetype and/or component batches optionally static, returning an error.
555 ///
556 /// See `log`/`log_static` for more information.
557 /// Returns an error if an error occurs during evaluation of `AsComponents` or logging.
558 ///
559 /// \param entity_path Path to the entity in the space hierarchy.
560 /// \param static_ If true, the logged components will be static.
561 /// Static data has no time associated with it, exists on all timelines, and unconditionally shadows
562 /// any temporal data of the same type.
563 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
564 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
565 /// \param as_components Any type for which the `AsComponents<T>` trait is implemented.
566 /// This is the case for any archetype as well as individual or collection of `ComponentBatch`.
567 /// You can implement `AsComponents` for your own types as well
568 /// \returns An error if an error occurs during evaluation of `AsComponents` or logging.
569 ///
570 /// @see log, try_log, log_static, try_log_static
571 template <typename... Ts>
573 std::string_view entity_path, bool static_, const Ts&... as_components
574 ) const {
575 if (!is_enabled()) {
576 return Error::ok();
577 }
578 std::vector<ComponentBatch> serialized_columns;
579 Error err;
580 (
581 [&] {
582 if (err.is_err()) {
583 return;
584 }
585
586 const Result<Collection<ComponentBatch>> serialization_result =
587 AsComponents<Ts>().as_batches(as_components);
588 if (serialization_result.is_err()) {
589 err = serialization_result.error;
590 return;
591 }
592
593 if (serialized_columns.empty()) {
594 // Fast path for the first batch (which is usually the only one!)
595 serialized_columns = std::move(serialization_result.value).to_vector();
596 } else {
597 serialized_columns.insert(
598 serialized_columns.end(),
599 std::make_move_iterator(serialization_result.value.begin()),
600 std::make_move_iterator(serialization_result.value.end())
601 );
602 }
603 }(),
604 ...
605 );
606 RR_RETURN_NOT_OK(err);
607
608 return try_log_serialized_batches(entity_path, static_, std::move(serialized_columns));
609 }
610
611 /// Logs several serialized batches batches, returning an error on failure.
612 ///
613 /// This is a more low-level API than `log`/`log_static\ and requires you to already serialize the data
614 /// ahead of time.
615 ///
616 /// \param entity_path Path to the entity in the space hierarchy.
617 /// \param static_ If true, the logged components will be static.
618 /// Static data has no time associated with it, exists on all timelines, and unconditionally shadows
619 /// any temporal data of the same type.
620 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
621 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
622 /// \param batches The serialized batches to log.
623 ///
624 /// \see `log`, `try_log`, `log_static`, `try_log_static`, `try_log_with_static`
626 std::string_view entity_path, bool static_, std::vector<ComponentBatch> batches
627 ) const;
628
629 /// Bottom level API that logs raw data cells to the recording stream.
630 ///
631 /// In order to use this you need to pass serialized Arrow data cells.
632 ///
633 /// \param entity_path Path to the entity in the space hierarchy.
634 /// \param num_data_cells Number of data cells passed in.
635 /// \param data_cells The data cells to log.
636 /// \param inject_time
637 /// If set to `true`, the row's timestamp data will be overridden using the recording
638 /// streams internal clock.
639 ///
640 /// \see `try_log_serialized_batches`
642 std::string_view entity_path, size_t num_data_cells, const ComponentBatch* data_cells,
643 bool inject_time
644 ) const;
645
646 /// Logs the file at the given `path` using all `DataLoader`s available.
647 ///
648 /// A single `path` might be handled by more than one loader.
649 ///
650 /// This method blocks until either at least one `DataLoader` starts streaming data in
651 /// or all of them fail.
652 ///
653 /// See <https://www.rerun.io/docs/reference/data-loaders/overview> for more information.
654 ///
655 /// \param filepath Path to the file to be logged.
656 /// \param entity_path_prefix What should the logged entity paths be prefixed with?
657 /// \param static_ If true, the logged components will be static.
658 /// Static data has no time associated with it, exists on all timelines, and unconditionally shadows
659 /// any temporal data of the same type.
660 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
661 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
662 ///
663 /// \see `try_log_file_from_path`
665 const std::filesystem::path& filepath,
666 std::string_view entity_path_prefix = std::string_view(), bool static_ = false
667 ) const {
668 try_log_file_from_path(filepath, entity_path_prefix, static_).handle();
669 }
670
671 /// Logs the file at the given `path` using all `DataLoader`s available.
672 ///
673 /// A single `path` might be handled by more than one loader.
674 ///
675 /// This method blocks until either at least one `DataLoader` starts streaming data in
676 /// or all of them fail.
677 ///
678 /// See <https://www.rerun.io/docs/reference/data-loaders/overview> for more information.
679 ///
680 /// \param filepath Path to the file to be logged.
681 /// \param entity_path_prefix What should the logged entity paths be prefixed with?
682 /// \param static_ If true, the logged components will be static.
683 /// Static data has no time associated with it, exists on all timelines, and unconditionally shadows
684 /// any temporal data of the same type.
685 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
686 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
687 ///
688 /// \see `log_file_from_path`
690 const std::filesystem::path& filepath,
691 std::string_view entity_path_prefix = std::string_view(), bool static_ = false
692 ) const;
693
694 /// Logs the given `contents` using all `DataLoader`s available.
695 ///
696 /// A single `path` might be handled by more than one loader.
697 ///
698 /// This method blocks until either at least one `DataLoader` starts streaming data in
699 /// or all of them fail.
700 ///
701 /// See <https://www.rerun.io/docs/reference/data-loaders/overview> for more information.
702 ///
703 /// \param filepath Path to the file that the `contents` belong to.
704 /// \param contents Contents to be logged.
705 /// \param contents_size Size in bytes of the `contents`.
706 /// \param entity_path_prefix What should the logged entity paths be prefixed with?
707 /// \param static_ If true, the logged components will be static.
708 /// Static data has no time associated with it, exists on all timelines, and unconditionally shadows
709 /// any temporal data of the same type.
710 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
711 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
712 ///
713 /// \see `try_log_file_from_contents`
715 const std::filesystem::path& filepath, const std::byte* contents, size_t contents_size,
716 std::string_view entity_path_prefix = std::string_view(), bool static_ = false
717 ) const {
719 filepath,
720 contents,
721 contents_size,
722 entity_path_prefix,
723 static_
724 )
725 .handle();
726 }
727
728 /// Logs the given `contents` using all `DataLoader`s available.
729 ///
730 /// A single `path` might be handled by more than one loader.
731 ///
732 /// This method blocks until either at least one `DataLoader` starts streaming data in
733 /// or all of them fail.
734 ///
735 /// See <https://www.rerun.io/docs/reference/data-loaders/overview> for more information.
736 ///
737 /// \param filepath Path to the file that the `contents` belong to.
738 /// \param contents Contents to be logged.
739 /// \param contents_size Size in bytes of the `contents`.
740 /// \param entity_path_prefix What should the logged entity paths be prefixed with?
741 /// \param static_ If true, the logged components will be static.
742 /// Static data has no time associated with it, exists on all timelines, and unconditionally shadows
743 /// any temporal data of the same type.
744 /// Otherwise, the data will be timestamped automatically with `log_time` and `log_tick`.
745 /// Additional timelines set by `set_time_sequence` or `set_time` will also be included.
746 ///
747 /// \see `log_file_from_contents`
749 const std::filesystem::path& filepath, const std::byte* contents, size_t contents_size,
750 std::string_view entity_path_prefix = std::string_view(), bool static_ = false
751 ) const;
752
753 /// Directly log a columns of data to Rerun.
754 ///
755 /// This variant takes in arbitrary amount of `ComponentColumn`s and `ComponentColumn` collections.
756 ///
757 /// Unlike the regular `log` API, which is row-oriented, this API lets you submit the data
758 /// in a columnar form. Each `TimeColumn` and `ComponentColumn` represents a column of data that will be sent to Rerun.
759 /// The lengths of all of these columns must match, and all
760 /// data that shares the same index across the different columns will act as a single logical row,
761 /// equivalent to a single call to `RecordingStream::log`.
762 ///
763 /// Note that this API ignores any stateful time set on the log stream via the `RecordingStream::set_time_*` APIs.
764 /// Furthermore, this will _not_ inject the default timelines `log_tick` and `log_time` timeline columns.
765 ///
766 /// Any failures that may occur during serialization are handled with `Error::handle`.
767 ///
768 /// \param entity_path Path to the entity in the space hierarchy.
769 /// \param time_columns The time columns to send.
770 /// \param component_columns The columns of components to send. Both individual `ComponentColumn`s and `Collection<ComponentColumn>`s are accepted.
771 /// \see `try_send_columns`
772 template <typename... Ts>
774 std::string_view entity_path, Collection<TimeColumn> time_columns,
775 Ts... component_columns // NOLINT
776 ) const {
777 try_send_columns(entity_path, time_columns, component_columns...).handle();
778 }
779
780 /// Directly log a columns of data to Rerun.
781 ///
782 /// This variant takes in arbitrary amount of `ComponentColumn`s and `ComponentColumn` collections.
783 ///
784 /// Unlike the regular `log` API, which is row-oriented, this API lets you submit the data
785 /// in a columnar form. Each `TimeColumn` and `ComponentColumn` represents a column of data that will be sent to Rerun.
786 /// The lengths of all of these columns must match, and all
787 /// data that shares the same index across the different columns will act as a single logical row,
788 /// equivalent to a single call to `RecordingStream::log`.
789 ///
790 /// Note that this API ignores any stateful time set on the log stream via the `RecordingStream::set_time_*` APIs.
791 /// Furthermore, this will _not_ inject the default timelines `log_tick` and `log_time` timeline columns.
792 ///
793 /// \param entity_path Path to the entity in the space hierarchy.
794 /// \param time_columns The time columns to send.
795 /// \param component_columns The columns of components to send. Both individual `ComponentColumn`s and `Collection<ComponentColumn>`s are accepted.
796 /// \see `send_columns`
797 template <typename... Ts>
799 std::string_view entity_path, Collection<TimeColumn> time_columns,
800 Ts... component_columns // NOLINT
801 ) const {
802 if constexpr (sizeof...(Ts) == 1) {
803 // Directly forward if this is only a single element,
804 // skipping collection of component column vector.
805 return try_send_columns(
806 entity_path,
807 std::move(time_columns),
808 Collection(std::forward<Ts...>(component_columns...))
809 );
810 }
811
812 std::vector<ComponentColumn> flat_column_list;
813 (
814 [&] {
815 static_assert(
816 std::is_same_v<std::remove_cv_t<Ts>, ComponentColumn> ||
817 std::is_constructible_v<Collection<ComponentColumn>, Ts>,
818 "Ts must be ComponentColumn or a collection thereof"
819 );
820
821 push_back_columns(flat_column_list, std::move(component_columns));
822 }(),
823 ...
824 );
825 return try_send_columns(
826 entity_path,
827 std::move(time_columns),
828 // Need to create collection explicitly, otherwise this becomes a recursive call.
829 Collection<ComponentColumn>(std::move(flat_column_list))
830 );
831 }
832
833 /// Directly log a columns of data to Rerun.
834 ///
835 /// Unlike the regular `log` API, which is row-oriented, this API lets you submit the data
836 /// in a columnar form. Each `TimeColumn` and `ComponentColumn` represents a column of data that will be sent to Rerun.
837 /// The lengths of all of these columns must match, and all
838 /// data that shares the same index across the different columns will act as a single logical row,
839 /// equivalent to a single call to `RecordingStream::log`.
840 ///
841 /// Note that this API ignores any stateful time set on the log stream via the `RecordingStream::set_time_*` APIs.
842 /// Furthermore, this will _not_ inject the default timelines `log_tick` and `log_time` timeline columns.
843 ///
844 /// Any failures that may occur during serialization are handled with `Error::handle`.
845 ///
846 /// \param entity_path Path to the entity in the space hierarchy.
847 /// \param time_columns The time columns to send.
848 /// \param component_columns The columns of components to send.
849 /// \see `try_send_columns`
851 std::string_view entity_path, Collection<TimeColumn> time_columns,
852 Collection<ComponentColumn> component_columns
853 ) const {
854 try_send_columns(entity_path, time_columns, component_columns).handle();
855 }
856
857 /// Directly log a columns of data to Rerun.
858 ///
859 /// Unlike the regular `log` API, which is row-oriented, this API lets you submit the data
860 /// in a columnar form. Each `TimeColumn` and `ComponentColumn` represents a column of data that will be sent to Rerun.
861 /// The lengths of all of these columns must match, and all
862 /// data that shares the same index across the different columns will act as a single logical row,
863 /// equivalent to a single call to `RecordingStream::log`.
864 ///
865 /// Note that this API ignores any stateful time set on the log stream via the `RecordingStream::set_time_*` APIs.
866 /// Furthermore, this will _not_ inject the default timelines `log_tick` and `log_time` timeline columns.
867 ///
868 /// \param entity_path Path to the entity in the space hierarchy.
869 /// \param time_columns The time columns to send.
870 /// \param component_columns The columns of components to send.
871 /// \see `send_columns`
873 std::string_view entity_path, Collection<TimeColumn> time_columns,
874 Collection<ComponentColumn> component_columns
875 ) const;
876
877 /// Set a property of a recording.
878 ///
879 /// Any failures that may occur during serialization are handled with `Error::handle`.
880 ///
881 /// \param name The name of the property.
882 /// \param values The values of the property.
883 /// \see `try_send_property`
884 template <typename... Ts>
885 void send_property(std::string_view name, const Ts&... values) const {
886 try_send_property(name, values...).handle();
887 }
888
889 /// Set a property of a recording.
890 ///
891 /// Any failures that may occur during serialization are handled with `Error::handle`.
892 ///
893 /// \param name The name of the property.
894 /// \param values The values of the property.
895 /// \see `set_property`
896 template <typename... Ts>
897 Error try_send_property(std::string_view name, const Ts&... values) const {
898 return try_log_static(
899 this->PROPERTIES_ENTITY_PATH + std::string(name),
900 values... // NOLINT
901 );
902 }
903
904 /// Set the name of a recording.
905 ///
906 /// Any failures that may occur during serialization are handled with `Error::handle`.
907 ///
908 /// \param name The name of the recording.
909 /// \see `try_send_recording_name`
910 void send_recording_name(std::string_view name) const {
912 }
913
914 /// Set the name of a recording.
915 ///
916 /// \param name The name of the recording.
917 /// \see `send_recording_name`
918 Error try_send_recording_name(std::string_view name) const;
919
920 /// Set the start time of a recording.
921 ///
922 /// Any failures that may occur during serialization are handled with `Error::handle`.
923 ///
924 /// \param nanos The timestamp of the recording in nanoseconds since Unix epoch.
925 /// \see `try_send_recording_start_time`
926 void send_recording_start_time_nanos(int64_t nanos) const {
928 }
929
930 /// Set the start time of a recording.
931 ///
932 /// \param nanos The timestamp of the recording in nanoseconds since Unix epoch.
933 /// \see `set_name`
935
936 /// @}
937
938 private:
939 // Utility function to implement `try_send_columns` variadic template.
940 static void push_back_columns(
941 std::vector<ComponentColumn>& component_columns, Collection<ComponentColumn> new_columns
942 ) {
943 for (const auto& new_column : new_columns) {
944 component_columns.emplace_back(std::move(new_column));
945 }
946 }
947
948 static void push_back_columns(
949 std::vector<ComponentColumn>& component_columns, ComponentColumn new_column
950 ) {
951 component_columns.emplace_back(std::move(new_column));
952 }
953
954 RecordingStream(uint32_t id, StoreKind store_kind);
955
956 uint32_t _id;
957 StoreKind _store_kind;
958 bool _enabled;
959 };
960} // namespace rerun
Generic collection of elements that are roughly contiguous in memory.
Definition collection.hpp:49
Status outcome object (success or error) returned for fallible operations.
Definition error.hpp:99
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:135
static Error ok()
Creates a new error set to ok.
Definition error.hpp:120
A RecordingStream handles everything related to logging data into Rerun.
Definition recording_stream.hpp:60
Error try_log_with_static(std::string_view entity_path, bool static_, const Ts &... as_components) const
Logs one or more archetype and/or component batches optionally static, returning an error.
Definition recording_stream.hpp:572
Error try_send_property(std::string_view name, const Ts &... values) const
Set a property of a recording.
Definition recording_stream.hpp:897
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 gRPC...
Error try_send_columns(std::string_view entity_path, Collection< TimeColumn > time_columns, Collection< ComponentColumn > component_columns) const
Directly log a columns of data to Rerun.
Result< std::string > serve_grpc(std::string_view bind_ip="0.0.0.0", uint16_t port=9876, std::string_view server_memory_limit="75%") const
Swaps the underlying sink for a gRPC server sink pre-configured to listen on rerun+http://{bind_ip}:{...
void log_file_from_path(const std::filesystem::path &filepath, std::string_view entity_path_prefix=std::string_view(), bool static_=false) const
Logs the file at the given path using all DataLoaders available.
Definition recording_stream.hpp:664
bool is_enabled() const
Returns whether the recording stream is enabled.
Definition recording_stream.hpp:105
void set_time_duration_nanos(std::string_view timeline_name, int64_t nanos) const
Set the index value of the given timeline as a duration in nanoseconds, for the current calling threa...
void send_property(std::string_view name, const Ts &... values) const
Set a property of a recording.
Definition recording_stream.hpp:885
Error try_send_recording_start_time_nanos(int64_t nanos) const
Set the start time of a recording.
Error try_log(std::string_view entity_path, const Ts &... as_components) const
Logs one or more archetype and/or component batches.
Definition recording_stream.hpp:505
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 connect_grpc(std::string_view url="rerun+http://127.0.0.1:9876/proxy", float flush_timeout_sec=2.0) const
Connect to a remote Rerun Viewer on the given URL.
Error to_stdout() const
Stream all log-data to standard output.
void send_columns(std::string_view entity_path, Collection< TimeColumn > time_columns, Collection< ComponentColumn > component_columns) const
Directly log a columns of data to Rerun.
Definition recording_stream.hpp:850
Error try_log_file_from_path(const std::filesystem::path &filepath, std::string_view entity_path_prefix=std::string_view(), bool static_=false) const
Logs the file at the given path using all DataLoaders available.
Error save(std::string_view path) const
Stream all log-data to a given .rrd file.
Error try_log_static(std::string_view entity_path, const Ts &... as_components) const
Logs one or more archetype and/or component batches as static data, returning an error.
Definition recording_stream.hpp:525
StoreKind kind() const
Returns the store kind as passed during construction.
Definition recording_stream.hpp:97
Error spawn(const SpawnOptions &options={}, std::chrono::duration< TRep, TPeriod > flush_timeout=std::chrono::seconds(2)) const
Definition recording_stream.hpp:195
Error try_log_data_row(std::string_view entity_path, size_t num_data_cells, const ComponentBatch *data_cells, bool inject_time) const
Bottom level API that logs raw data cells to the recording stream.
void log_file_from_contents(const std::filesystem::path &filepath, const std::byte *contents, size_t contents_size, std::string_view entity_path_prefix=std::string_view(), bool static_=false) const
Logs the given contents using all DataLoaders available.
Definition recording_stream.hpp:714
void set_time_timestamp_secs_since_epoch(std::string_view timeline_name, double seconds) const
Set the index value of the given timeline as seconds since Unix Epoch (1970), for the current calling...
Definition recording_stream.hpp:319
void set_time_duration_secs(std::string_view timeline_name, double secs) const
Set the index value of the given timeline as a duration in seconds, for the current calling thread.
Definition recording_stream.hpp:275
void set_time_duration(std::string_view timeline_name, std::chrono::duration< TRep, TPeriod > duration) const
Set the index value of the given timeline as a duration, for the current calling thread.
Definition recording_stream.hpp:259
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.
Definition recording_stream.hpp:398
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:365
void send_recording_start_time_nanos(int64_t nanos) const
Set the start time of a recording.
Definition recording_stream.hpp:926
void log_with_static(std::string_view entity_path, bool static_, const Ts &... as_components) const
Logs one or more archetype and/or component batches optionally static, returning an error.
Definition recording_stream.hpp:549
static RecordingStream & current(StoreKind store_kind=StoreKind::Recording)
Retrieves the most appropriate globally available recording stream for the given kind.
RecordingStream(std::string_view app_id, std::string_view recording_id=std::string_view(), StoreKind store_kind=StoreKind::Recording)
Creates a new recording stream to log to.
void log(std::string_view entity_path, const Ts &... as_components) const
Logs one or more archetype and/or component batches.
Definition recording_stream.hpp:464
void set_thread_local() const
Replaces the currently active recording for this stream's store kind in the thread-local scope with t...
Error try_send_recording_name(std::string_view name) const
Set the name of a recording.
Error try_log_serialized_batches(std::string_view entity_path, bool static_, std::vector< ComponentBatch > batches) const
Logs several serialized batches batches, returning an error on failure.
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:349
void set_time_seconds(std::string_view timeline_name, double seconds) const
Set the current time of the recording, for the current calling thread.
Definition recording_stream.hpp:382
void set_time_timestamp_nanos_since_epoch(std::string_view timeline_name, int64_t nanos) const
Set the index value of the given timeline as nanoseconds since Unix Epoch (1970), for the current cal...
void send_columns(std::string_view entity_path, Collection< TimeColumn > time_columns, Ts... component_columns) const
Directly log a columns of data to Rerun.
Definition recording_stream.hpp:773
Error try_log_file_from_contents(const std::filesystem::path &filepath, const std::byte *contents, size_t contents_size, std::string_view entity_path_prefix=std::string_view(), bool static_=false) const
Logs the given contents using all DataLoaders available.
void send_recording_name(std::string_view name) const
Set the name of a recording.
Definition recording_stream.hpp:910
void set_global() const
Replaces the currently active recording for this stream's store kind in the global scope with this on...
void set_time_timestamp(std::string_view timeline_name, std::chrono::time_point< TClock > timestamp) const
Set the index value of the given timeline as a timestamp, for the current calling thread.
Definition recording_stream.hpp:300
void set_time_sequence(std::string_view timeline_name, int64_t sequence_nr) const
Set the index value of the given timeline as a sequence number, for the current calling thread.
Error try_send_columns(std::string_view entity_path, Collection< TimeColumn > time_columns, Ts... component_columns) const
Directly log a columns of data to Rerun.
Definition recording_stream.hpp:798
void log_static(std::string_view entity_path, const Ts &... as_components) const
Logs one or more archetype and/or component batches as static data.
Definition recording_stream.hpp:486
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:23
Arrow-encoded data of a single batch of components together with a component descriptor.
Definition component_batch.hpp:28
Arrow-encoded data of a column of components.
Definition component_column.hpp:20
Options to control the behavior of spawn.
Definition spawn_options.hpp:17