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