|
|
# Trace Logging Library Usage
|
|
|
|
|
|
The idea behind Trace Logging is that we can choose to trace a function and
|
|
|
then, with minimal additional code, determine how long function execution ran
|
|
|
for, whether the function was successful, and connect all of this information to
|
|
|
any existing informational logs. The below provides information about how this
|
|
|
can be achieved with OSP's TraceLogging Infrastructure.
|
|
|
|
|
|
## Compilation
|
|
|
|
|
|
By default, TraceLogging is enabled as part of the build.
|
|
|
To disable TraceLogging, include flag `--args="enable_trace_logging=false"`
|
|
|
when calling `gn gen` as part of building this library.
|
|
|
|
|
|
## Imports
|
|
|
|
|
|
To use TraceLogging, import the following header file:
|
|
|
* *util/trace_logging.h*
|
|
|
This file will import all other Trace Logging dependencies.
|
|
|
|
|
|
## Trace IDs
|
|
|
|
|
|
When a Trace Log is created, a new unsigned integer is associated with this
|
|
|
specific trace, which will going forward be referred to as a Trace ID.
|
|
|
Associating a Trace ID has the following benefits:
|
|
|
|
|
|
* The same Trace ID can be associated with all Informational Logs generated
|
|
|
during this traced method’s execution. This will allow for all informational
|
|
|
logs to easily be associated with a method execution, so that if an unexpected
|
|
|
failure occurs or edge case pops up we will have additional information useful
|
|
|
in debugging this issue and can more easily determine the state of the system
|
|
|
leading to this issue.
|
|
|
|
|
|
* If another Trace Log is created during the execution of this first traced
|
|
|
method, we will have a hierarchy of Trace IDs. This will allow us to easily
|
|
|
create an execution tree (through trivial scripting, preexisting tool, or
|
|
|
database operations) to get a better view of how execution is proceeding.
|
|
|
Together, the IDs associated with these calls form a Trace ID Hierarchy. The
|
|
|
Hierarchy has the following format:
|
|
|
* *Current Trace ID*: The ID of the function currently being traced.
|
|
|
* *Parent Trace ID*: The ID of the function which was being traced when this
|
|
|
Trace was initiated.
|
|
|
* *Root Trace ID*: The ID of the first traced function under which this the
|
|
|
current Trace was called.
|
|
|
|
|
|
As an example:
|
|
|
``` cpp
|
|
|
public void DoThings() {
|
|
|
TRACE_SCOPED(category, "outside");
|
|
|
{
|
|
|
TRACE_SCOPED(category, "middle");
|
|
|
{
|
|
|
TRACE_SCOPED(category, "inside");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
This could result in the following Trace ID Information:
|
|
|
|
|
|
| NAME | ROOT ID | PARENT ID | TRACE ID | RESULT |
|
|
|
|---------|----------|------------|-----------|---------|
|
|
|
| outside | 100 | 0 | 100 | success |
|
|
|
| middle | 100 | 100 | 101 | success |
|
|
|
| inside | 100 | 101 | 102 | success |
|
|
|
|
|
|
Note that, prior to any trace calls, the Trace ID is considered to be 0x0 by
|
|
|
convention.
|
|
|
|
|
|
## Trace Results
|
|
|
|
|
|
The "result" of a trace is meant to indicate whether the traced function
|
|
|
completed successfully or not. Results are handled differently for synchronous
|
|
|
and asynchronous traces.
|
|
|
|
|
|
For scoped traces, the trace is by default assumed to be successful. If an error
|
|
|
state is desired, this should be set using `TRACE_SET_RESULT(result)` where
|
|
|
result is some Error::Code enum value.
|
|
|
|
|
|
For asynchronous calls, the result must be set as part of the `TRACE_ASYNC_END`
|
|
|
call. As with scoped traces, the result must be some Error::Code enum value.
|
|
|
|
|
|
## Tracing Functions
|
|
|
All of the below functions rely on the Platform Layer's IsTraceLoggingEnabled()
|
|
|
function. When logging is disabled, either for the specific category of trace
|
|
|
logging which the Macro specifies or for TraceCategory::Any in all other cases,
|
|
|
the below functions will be treated as a NoOp.
|
|
|
|
|
|
### Synchronous Tracing
|
|
|
```c++
|
|
|
TRACE_SCOPED(category, name)
|
|
|
TRACE_SCOPED(category, name, traceId, parentId, rootId)
|
|
|
TRACE_SCOPED(category, name, traceIdHierarchy)
|
|
|
```
|
|
|
If logging is enabled for the provided |category|, trace the current scope. The scope
|
|
|
should encompass the operation described by |name|. The latter two uses of this macro are
|
|
|
for manually providing the trace ID hierarchy; the first auto-generates a new trace ID for
|
|
|
this scope and sets its parent trace ID to that of the encompassing scope (if any).
|
|
|
|
|
|
```c++
|
|
|
TRACE_DEFAULT_SCOPED(category)
|
|
|
TRACE_DEFAULT_SCOPED(category, traceId, parentId, rootId)
|
|
|
TRACE_DEFAULT_SCOPED(category, traceIdHierarchy)
|
|
|
```
|
|
|
Same as TRACE_SCOPED(), but use the current function/method signature as the operation
|
|
|
name.
|
|
|
|
|
|
### Asynchronous Tracing
|
|
|
`TRACE_ASYNC_START(category, name)`
|
|
|
If logging is enabled for the provided category, this function will initiate
|
|
|
a new asynchronous function trace with name as provided. It will not end
|
|
|
until TRACE_ASYNC_END is called with the same Trace ID generated for this
|
|
|
async trace. When this call is used, the Trace ID Hierarchy will be
|
|
|
determined automatically and the caller does not need to worry about it and,
|
|
|
as such, **this call should be used in the majority of asynchronous tracing
|
|
|
cases**.
|
|
|
|
|
|
`TRACE_ASYNC_START(category, name, traceId, parentId, rootId)`
|
|
|
If logging is enabled for the provided category, this function will initiate
|
|
|
a new asynchronous function trace with name and full Trac ID Hierarchy as
|
|
|
provided. It will not end until TRACE_ASYNC_END is called with the same
|
|
|
Trace ID provided for this async trace. Each of trace ID, parent ID, and
|
|
|
root ID is optional, so providing only a subset of these values is also
|
|
|
valid if the caller only desires to set specific ones.
|
|
|
|
|
|
`TRACE_ASYNC_START(category, name, traceIdHierarchy)`
|
|
|
This call is intended for use in conjunction with the TRACE_HIERARCHY macro
|
|
|
(as described below). this function will initiate a new asynchronous
|
|
|
function trace with name and full Trace ID Hierarchy as provided. It will
|
|
|
not end until TRACE_ASYNC_END is called with the same Trace ID provided for
|
|
|
this async trace.
|
|
|
|
|
|
`TRACE_ASYNC_END(category, id, result)`
|
|
|
This call will end a trace initiated by TRACE_ASYNC_START (as described
|
|
|
above) if logging is enabled for the associated category. The id is expected
|
|
|
to match that used by an TRACE_ASYNC_START call, and result is the same as
|
|
|
TRACE_SET_RESULT's argument.
|
|
|
|
|
|
### Other Tracing Macros
|
|
|
`TRACE_CURRENT_ID`
|
|
|
This macro returns the current Trace ID at this point in time.
|
|
|
|
|
|
`TRACE_ROOT_ID`
|
|
|
This macro returns the root Trace ID at this point in time.
|
|
|
|
|
|
`TRACE_HIERARCHY`
|
|
|
This macro returns an instance of struct Trace ID Hierarchy containing the
|
|
|
current Trace ID Hierarchy. This is intended to be used with
|
|
|
`TRACE_SET_HIERARCHY` (described below) so that Trace ID Hierarchy can be
|
|
|
maintained when crossing thread or machine boundaries.
|
|
|
|
|
|
`TRACE_SET_HIERARCHY(ids)`
|
|
|
This macro sets the current Trace Id Hierarchy without initiating a scoped
|
|
|
trace. The set ids will cease to apply when the current scope ends. This is
|
|
|
intended to be used with `TRACE_HIERARCHY` (described above) so that Trace
|
|
|
ID Hierarchy can be maintained when crossing thread or machine boundaries.
|
|
|
|
|
|
`TRACE_SET_RESULT(result)`
|
|
|
Sets the current scoped trace's result to be the same as the Error::Code
|
|
|
argument provided.
|
|
|
|
|
|
### Example Code
|
|
|
Synchronous Tracing:
|
|
|
``` cpp
|
|
|
public void DoThings() {
|
|
|
TRACE_SCOPED(category, "DoThings");
|
|
|
|
|
|
// Do Things.
|
|
|
// A trace log is generated when the scope containing the above call ends.
|
|
|
|
|
|
TRACE_SET_RESULT(Error::Code::kNone);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Asynchronous tracing with known Trace ID (recommended):
|
|
|
This approach allows for asynchronous tracing when the function being traced can
|
|
|
be associated with a known Trace ID. For instance, a packet ID, a request ID, or
|
|
|
another ID which will live for the duration of the trace but will not need to be
|
|
|
passed around separately.
|
|
|
``` cpp
|
|
|
public void DoThingsStart() {
|
|
|
TRACE_ASYNC_START(category, "DoThings", kKnownId);
|
|
|
}
|
|
|
|
|
|
public void DoThingsEnd() {
|
|
|
TRACE_ASYNC_END(category, kKnownId, Error::Code::kNone);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Asynchronous tracing with unknown Trace ID (not recommended):
|
|
|
This approach allows for asynchronous tracing even when no existing ID can be
|
|
|
associated with the trace.
|
|
|
``` cpp
|
|
|
public TraceId DoThingsStart() {
|
|
|
TRACE_ASYNC_START(category, "DoThings");
|
|
|
return TRACE_CURRENT_ID;
|
|
|
}
|
|
|
|
|
|
public void DoThingsEnd(TraceId trace_id) {
|
|
|
TRACE_ASYNC_END(category, trace_id, Error::Code::kNone);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## File Division
|
|
|
The code for Trace Logging is divided up as follows:
|
|
|
* *util/trace_logging.h*: the macros Open Screen library code is expected to
|
|
|
use to log trace events.
|
|
|
* *platform/base/trace_logging_types.h*: the types/enums used in the platform
|
|
|
API as well as internal library code.
|
|
|
* *util/trace_logging/*: the internal infrastructure backing the macros in
|
|
|
*trace_logging.h*, and connecting it to the platform API.
|
|
|
* *platform/api/trace_logging_platform.h*: the platform implementation that is
|
|
|
used as the trace logging destination while tracing is active.
|
|
|
This information is intended to be only eplanatory for embedders - only the one
|
|
|
file mentioned above in Imports must be imported.
|
|
|
|
|
|
## Embedder-Specific Tracing Implementations
|
|
|
|
|
|
For an embedder to create a custom TraceLogging implementation:
|
|
|
|
|
|
1. *Create a TraceLoggingPlatform*
|
|
|
In platform/api/trace_logging_platform.h, the interface TraceLoggingPlatform
|
|
|
is defined. An embedder must define a class implementing this interface. The
|
|
|
methods should be as performance-optimal as possible, since they might be
|
|
|
called frequently (especially `IsLoggingEnabled(TraceCategory)`) and are often
|
|
|
in the critical execution path of the library's code.
|
|
|
|
|
|
2. *Call `openscreen::StartTracing()` and `StopTracing()`*
|
|
|
These activate/deactivate tracing by providing the TraceLoggingPlatform
|
|
|
instance and later clearing references to it.
|
|
|
|
|
|
**The default implementation of this layer can be seen in
|
|
|
platform/impl/trace_logging_platform.cc.**
|