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