Tracing - CloudChamber
Overview
tracing is a framework for instrumenting Rust programs to collect
structured, event-based diagnostic information. But sometimes you need to write a C++ ffi
and this beautiful framework is unavailable to you in your C++ code.
Not anymore! tracing-cloudchanber extends tracing with a ffi via cxx
so you can construct spans and emit events from your C++ code with the same safety as you could in Rust.
Usage
Assuming you have set up your project correctly and either added tracing-cloudchamber as a direct dependency
or your using something like corrosion to link tracing-cloudchamber to your C++ code ...
You only need to do two things:
- add the header
- prevent the linker from eliding "unused" symbols
Then you'll be able to do this!
void
Emitting Events
tracing-cloudchamber's ffi is implemented somewhat restrictively and based on
re-implementing how tracing's own macros work.
That is, there are a selection of macros that expand to statically defined call-sites (::cloudchamber::Callsite)
with associated static metadata. Just like tracing all field names must be defined up front and field values must either be primitive or
there must be an overload of the tcc_field_format function that accepts const& to it.
The macros support defining fields in two formats: Paired name and value pairs, or assumed named from the ident passed in
e.g.
int val = 10;
will emit a INFO level event with a field named val and the value 10
Levels
::cloudchamber::level::ERROR;
::cloudchamber::level::WARN;
::cloudchamber::level::INFO;
::cloudchamber::level::DEBUG;
::cloudchamber::level::TRACE;
Available event macros
Raw Event
/// emit an event named after the call-site (file:line)
;
/// emit an event with passed fields, names are assumed from `idents` passed
/// e.g, `tcc_event_f(::cloudchamber::level::TRACE, val)`
;
/// emit an event with passed fields; fields are defined as named, value
/// e.g. `tcc_event_p(::cloudchamber::level::TRACE, val, 10)`
/// note that names are not quoted. that's done by the preprocessor.
;
/// emit an event with a field "message" (tracing's default) using the passed value
;
/// emit an event with a field "message" (tracing's default) using the passed value
/// adds field using names form the `idents` passed
/// e.g. `tcc_event_msg_f(::cloudchamber::level::TRACE, "shaving a yak", val)`
;
/// emit an event with a field "message" (tracing's default) using the passed value
/// adds field using paired names and values
/// e.g. `tcc_event_msg_p(::cloudchamber::level::TRACE, "shaving a yak", val, 10)`
;
Raw Named events
;
;
;
;
;
;
ERROR level Events
;
;
;
;
;
;
WARN level Events
;
;
;
;
;
;
INFO level Events
;
;
;
;
;
;
DEBUG level Events
;
;
;
;
;
;
TRACE level Events
;
;
;
;
;
;
Using Spans
Spans work almost just like in tracing.
There are a set of macros that mirror the event macros to define a span and it's call-site.
You can then use auto _guard = span->enter(); and span->in_scope(...);
Example:
;
;
The tcc_span_<...>(ident, ...) span macros expand to a call-site and a Boxed ::cloudchamber::Span
::rust::Box<::cloudchamber::Span> ident = /* span construction dependant on call-site enable */
Span::in_scope
Due to how cxx currently restricts ffi in_scope can't accept a generic std::function<>.
Instead you must wrap the function with a ::cloudchamber::ScopeLambda
std::array an_array = ;
std::map<std::string, int> ;
;
_t_span->;
Available span macros
Raw Spans
/// construct a span held by `ident` for level `level` named `name`
ERROR Spans
WARN Spans
INFO Spans
DEBUG Spans
TRACE Spans
Formatting field values
To attach a field value we need to know how to turn it into a &dyn tracing::field::Value.
Fortunately tracing-cloudchamber knows how to construct this for all "stringable" types
and some containers there-of.
In this case "stringable" means:
- Any type for which
std::to_stringexists. - Any type for which a
to_string(T const&)function exists in scope. - Any type for which a
std::ostream& operator<<(std::ostream&, Tconst&)function exists in scope - Any type for which
std::string ::cloudchamber::field_format(T const&)exists
To handle containers the following are implemented:
std::string ::cloudchamber::field_format(std::array<T, N> const&)for any "stringable" typeTstd::string ::cloudchamber::field_format(std::vector<T> const&)for any "stringable" typeTstd::string ::cloudchamber::field_format(std::map<K, V> const&)for any "stringable" typesKandV
For custom types or containers simple implement one of those functions
e.g.
// by a std::string to_string(T const &)
;
std::string
// by std::ostring& operator<<
;