|
| 1 | +# Context User Guide |
| 2 | + |
| 3 | +Context is an object that holds information that is specific to an operation |
| 4 | +such as an RPC. |
| 5 | + |
| 6 | +Currently, the information that is held is: |
| 7 | + * A TagMap. |
| 8 | + * A Span. |
| 9 | + |
| 10 | +In future, this may be expanded. |
| 11 | + |
| 12 | +Each thread has a currently active Context. It is stored using thread-local |
| 13 | +storage and can be retrieved with `Context::Current()`. |
| 14 | + |
| 15 | +Example: |
| 16 | + |
| 17 | +```c++ |
| 18 | +std::cout << "The current context is: " |
| 19 | + << Context::Current()::DebugString() << "n\"; |
| 20 | +``` |
| 21 | +
|
| 22 | +## Running under a different Context |
| 23 | +
|
| 24 | +Contexts are conceptually immutable: the contents of a Context object cannot be |
| 25 | +modified in-place. |
| 26 | +
|
| 27 | +To run in the Context of a different Span or TagMap, use the `WithSpan` and |
| 28 | +`WithTagMap` classes, respectively. Both of these classes are intended to be |
| 29 | +used as stack-allocated RAII objects. At construction time, they install the |
| 30 | +given object into the current Context, and restore the previous value when they |
| 31 | +fall out of scope. |
| 32 | +
|
| 33 | +Example: |
| 34 | +
|
| 35 | +```c++ |
| 36 | +// Create a Span to track a unit of work. |
| 37 | +auto span = Span::StartSpan("MyOperation"); |
| 38 | +{ |
| 39 | + WithSpan ws(span); |
| 40 | + // Perform work in the Context of the tracking Span. |
| 41 | + Process(batch_); |
| 42 | +} |
| 43 | +span.End(); |
| 44 | +``` |
| 45 | +
|
| 46 | +## Retrieving information from a Context |
| 47 | +
|
| 48 | +Use the `:context_util` libraries to retrieve information from a Context. |
| 49 | +
|
| 50 | +Example: |
| 51 | +
|
| 52 | +```c++ |
| 53 | +const Span& span = ::opencensus::trace::GetCurrentSpan(); |
| 54 | +span.AddAnnotation("Processing batch."); |
| 55 | +Process(batch_); |
| 56 | +``` |
| 57 | +
|
| 58 | +## Callbacks |
| 59 | +
|
| 60 | +We often need to continue the current operation in an asynchronous way, via a |
| 61 | +callback function or similar. To do this, use `Context::Wrap()`, which installs |
| 62 | +and uninstalls the specified Context around a given function. |
| 63 | +
|
| 64 | +Example: |
| 65 | +
|
| 66 | +```c++ |
| 67 | +void MyOpStart() { |
| 68 | + const Span& span = ::opencensus::trace::GetCurrentSpan(); |
| 69 | + span.AddAnnotation("Starting AsyncOp."); |
| 70 | + AsyncOp op; |
| 71 | + op.Run(/* on_done = */ Context::Current()::Wrap(MyOpResume)); |
| 72 | +} |
| 73 | +
|
| 74 | +void MyOpResume() { |
| 75 | + const Span& span = ::opencensus::trace::GetCurrentSpan(); |
| 76 | + // Both annotations should end up in the same Span that's tracking MyOp. |
| 77 | + span.AddAnnotation("AsyncOp completed."); |
| 78 | +} |
| 79 | +``` |
| 80 | +
|
| 81 | +## Capturing Context |
| 82 | +
|
| 83 | +Prefer using `Wrap()`, but in cases where that is not possible, the current |
| 84 | +Context can be captured and installed later using `WithContext`. |
| 85 | +
|
| 86 | +Example: |
| 87 | +
|
| 88 | +```c++ |
| 89 | +struct MyOperation { |
| 90 | + Context ctx; |
| 91 | + std::vector<Things> things_to_be_processed; |
| 92 | +}; |
| 93 | +
|
| 94 | +void StartMyOp(MyOperation* op) { |
| 95 | + auto span = Span::StartSpan("MyOperation"); |
| 96 | + WithSpan ws(span); |
| 97 | + // Capture the current Context so the rest of the op can run under it. |
| 98 | + op->ctx = Context::Current(); |
| 99 | + span.AddAnnotation("Preparing things."); |
| 100 | + Prepare(op->things_to_be_processed); |
| 101 | +} |
| 102 | +
|
| 103 | +void RunMyOp(MyOperation* op) { |
| 104 | + WithContext wc(op->ctx); |
| 105 | + const Span& span = ::opencensus::trace::GetCurrentSpan(); |
| 106 | + span.AddAnnotation("Processing things."); |
| 107 | + Process(op->things_to_be_processed); |
| 108 | + span.End(); |
| 109 | +} |
| 110 | +``` |
| 111 | +
|
| 112 | +## Passing Context between threads |
| 113 | +
|
| 114 | +New threads start with an empty context. Treat a new thread like running a |
| 115 | +callback and just `Wrap()` the function being run. |
| 116 | +
|
| 117 | +## Best Practices |
| 118 | +
|
| 119 | +Always have the correct Context installed as the "current" Context. |
| 120 | +
|
| 121 | +Always stack-allocate `With*` objects. |
| 122 | +
|
| 123 | +Never deallocate a `With*` object on a different thread, this will corrupt the |
| 124 | +thread-local Context. |
| 125 | +
|
| 126 | +## See Also |
| 127 | +
|
| 128 | +* [Context and Wrap](../context/context.h) |
| 129 | +* [WithContext](../context/with_context.h) |
| 130 | +* [WithTagMap](../tags/with_tag_map.h) and |
| 131 | + [`tags/context_util.h`](../tags/context_util.h) |
| 132 | +* [WithSpan](../trace/with_span.h) and |
| 133 | + [`trace/context_util.h`](../trace/context_util.h) |
0 commit comments