| # 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.** |