1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef FCP_TRACING_TRACING_SPAN_H_
16 #define FCP_TRACING_TRACING_SPAN_H_
17
18 #include <memory>
19 #include <string>
20 #include <utility>
21
22 #include "fcp/base/error.h"
23 #include "fcp/tracing/tracing_recorder_impl.h"
24 #include "fcp/tracing/tracing_span_impl.h"
25 #include "fcp/tracing/tracing_span_ref.h"
26 #include "fcp/tracing/tracing_traits.h"
27 #include "flatbuffers/flatbuffers.h"
28
29 namespace fcp {
30
31 namespace tracing_internal {
32
33 template <class FlatBufferTable>
AssertIsNotSpan()34 void AssertIsNotSpan() {
35 static_assert(TracingTraits<FlatBufferTable>::kIsSpan == false,
36 "Trace can only be called on an event table, not a span. To "
37 "convert the table to an event remove 'span' from the table "
38 "attributes.");
39 }
40
41 template <class FlatBufferTable>
AssertIsError()42 void AssertIsError() {
43 static_assert(
44 TracingTraits<FlatBufferTable>::kSeverity == TracingSeverity::kError,
45 "TraceError can only be called on table of severity Error. To "
46 "convert the table to error severity add 'error' to the "
47 "table attributes.");
48 }
49
50 // Generic method to build FlatBuff tables from a set of args.
51 template <class FlatBufferTable, class... Arg>
BuildFlatBuffer(Arg &&...args)52 flatbuffers::DetachedBuffer BuildFlatBuffer(Arg&&... args) {
53 flatbuffers::FlatBufferBuilder fbb;
54 flatbuffers::Offset<FlatBufferTable> root =
55 TracingTraits<FlatBufferTable>::Create(std::forward<Arg>(args)..., &fbb);
56 constexpr TracingTag tag = TracingTraits<FlatBufferTable>::kTag;
57 constexpr char signature[5] = {tag.data[0], tag.data[1], tag.data[2],
58 tag.data[3], 0};
59 fbb.Finish(root, signature);
60 return fbb.Release();
61 }
62
63 /*
64 * Un-templatized version of the tracing span which stores the raw
65 * definition of the tracing span and returns a reference to it.
66 *
67 * This class should not/cannot be publicly initialized, instead it should be
68 * used as a base class for both UnscopedTracingSpan and TracingSpan.
69 *
70 * For Java based tracing spans, all native spans will be cast to this common
71 * structure to pass along the JNI boundary.
72 */
73 class TracingSpanBase {
74 public:
75 // Returns a reference to this span.
Ref()76 inline TracingSpanRef Ref() const { return impl_->Ref(); }
77 virtual ~TracingSpanBase() = default;
78
79 protected:
80 explicit TracingSpanBase() = default;
81 // Set by the child classes after an instance of TracingSpanImpl is created
82 // from the flatbuf definitions.
83 std::unique_ptr<TracingSpanImpl> impl_;
84 };
85
86 } // namespace tracing_internal
87
88 // Unscoped tracing span carries tracing context for some logical activity.
89 // The primary purpose of this class is to keep the tracing context of a
90 // long-running asynchronous activity, typically associated with the lifetime of
91 // some long-lived object.
92 //
93 // This span is not scoped to a block/function and need not necessarily be
94 // instantiated as a local variable on the stack. It is OK to initialise this
95 // span on a heap and/or as a field member of a class. For scoped variant,
96 // prefer using TracingSpan instead.
97 //
98 // To record a Trace within an UnscopedTracingSpan, the caller must explicitly
99 // pass a reference to a span e.g Trace<FlatBuff>(span, args...).
100 //
101 // The class is NOT thread-safe, but OK to use from different threads if invoked
102 // sequentially (i.e. with external synchronisation providing sequential
103 // consistency memory ordering).
104 //
105 // Recommended usage is to create new child span for every sub-activity or
106 // operation.
107 template <class FlatBufferTable>
108 class UnscopedTracingSpan : public tracing_internal::TracingSpanBase {
109 public:
110 // UnscopedTracingSpan is neither copyable nor movable.
111 UnscopedTracingSpan(const UnscopedTracingSpan&) = delete;
112 UnscopedTracingSpan& operator=(const UnscopedTracingSpan&) = delete;
113 // Public constructors allow creating new tracing span.
114 // A parent span reference can be optionally provided as a first argument.
115 // By default current span is used as a parent.
116 template <class... Arg>
117 explicit UnscopedTracingSpan(Arg&&... args);
118 template <class... Arg>
119 explicit UnscopedTracingSpan(TracingSpanRef parent, Arg&&... args);
120
121 private:
122 template <class... Arg>
123 void Create(TracingSpanRef parent, Arg&&... args);
124 };
125
126 template <class FlatBufferTable>
127 template <class... Arg>
UnscopedTracingSpan(Arg &&...args)128 UnscopedTracingSpan<FlatBufferTable>::UnscopedTracingSpan(Arg&&... args) {
129 Create(TracingSpanRef::Top(), std::forward<Arg>(args)...);
130 }
131
132 template <class FlatBufferTable>
133 template <class... Arg>
UnscopedTracingSpan(TracingSpanRef parent,Arg &&...args)134 UnscopedTracingSpan<FlatBufferTable>::UnscopedTracingSpan(TracingSpanRef parent,
135 Arg&&... args) {
136 Create(parent, std::forward<Arg>(args)...);
137 }
138
139 template <class FlatBufferTable>
140 template <class... Arg>
Create(TracingSpanRef parent,Arg &&...args)141 void UnscopedTracingSpan<FlatBufferTable>::Create(TracingSpanRef parent,
142 Arg&&... args) {
143 static_assert(
144 TracingTraits<FlatBufferTable>::kIsSpan == true,
145 "UnscopedTracingSpan can only be created from a span table, not "
146 "an event. To convert the table to an span add 'span' "
147 "to the table attributes.");
148 flatbuffers::DetachedBuffer trace_data =
149 tracing_internal::BuildFlatBuffer<FlatBufferTable>(
150 std::forward<Arg>(args)...);
151 impl_ = parent.recorder()->CreateChildSpan(parent.span_id(),
152 std::move(trace_data),
153 TracingTraits<FlatBufferTable>());
154 }
155
156 // Tracing span, carrying abstract tracing context for some logical activity
157 // performed by the application. Provides ability to log various events
158 // which are automatically associated with such a context, thus allowing logging
159 // essential information only.
160 //
161 // This class uses RAII-style mechanism of entering/existing the tracing span
162 // for the duration of a scoped block or a function, this class must be
163 // instantiated as a local variable on the stack, in a similar manner as
164 // std::lock_guard.
165 //
166 // The class is NOT thread-safe, but OK to use from different threads if invoked
167 // sequentially (i.e. with external synchronisation providing sequential
168 // consistency memory ordering).
169 //
170 // Recommended usage is to create new child span for every sub-activity or
171 // operation.
172 //
173 // For a more general variant that is not necessarily tied to a scoped block,
174 // prefer using UnscopedTracingSpan directly.
175 template <class FlatBufferTable>
176 class TracingSpan final : public UnscopedTracingSpan<FlatBufferTable> {
177 public:
178 // Since this manipulates TLS/FLS in RAII fashion, this is intended to be
179 // used as a stack local (no heap alloc allowed):
180 void* operator new(std::size_t) = delete;
181 void* operator new[](std::size_t) = delete;
182 // Public constructors allow creating new tracing span.
183 template <class... Arg>
TracingSpan(Arg &&...args)184 explicit TracingSpan(Arg&&... args)
185 : UnscopedTracingSpan<FlatBufferTable>(std::forward<Arg>(args)...) {
186 UnscopedTracingSpan<FlatBufferTable>::impl_->Push();
187 }
188 template <class... Arg>
TracingSpan(TracingSpanRef parent,Arg &&...args)189 explicit TracingSpan(TracingSpanRef parent, Arg&&... args)
190 : UnscopedTracingSpan<FlatBufferTable>(parent,
191 std::forward<Arg>(args)...) {
192 UnscopedTracingSpan<FlatBufferTable>::impl_->Push();
193 }
194
195 // Destructor closes the span
~TracingSpan()196 ~TracingSpan() override {
197 UnscopedTracingSpan<FlatBufferTable>::impl_->Pop();
198 }
199 };
200
201 // Writes a trace with the specified args under the topmost span located on
202 // TLS. If no span exists on TLS, then root span is fetched from the global
203 // recorder.
204 template <class FlatBufferTable, class... Arg>
Trace(Arg &&...args)205 void Trace(Arg&&... args) {
206 tracing_internal::AssertIsNotSpan<FlatBufferTable>();
207 flatbuffers::DetachedBuffer trace_data =
208 tracing_internal::BuildFlatBuffer<FlatBufferTable>(
209 std::forward<Arg>(args)...);
210 // Now discover what tracing span to log that with:
211 tracing_internal::TracingSpanImpl* top =
212 tracing_internal::TracingSpanImpl::Top();
213 if (top != nullptr) {
214 // Fast path: getting top tracing span from TLS/FCB:
215 top->TraceImpl(std::move(trace_data), TracingTraits<FlatBufferTable>());
216 } else {
217 // Slow path, finding root span from global recorder. This
218 // involves increasing its reference counter:
219 std::shared_ptr<tracing_internal::TracingRecorderImpl> recorder =
220 tracing_internal::TracingRecorderImpl::GetCurrent();
221 recorder->GetRootSpan()->TraceImpl(std::move(trace_data),
222 TracingTraits<FlatBufferTable>());
223 // now, since we done with using root span, it is safe to release shared_ptr
224 }
225 }
226
227 // Writes a trace under the specified span with the given args.
228 template <class FlatBufferTable, class... Arg>
Trace(TracingSpanRef span,Arg &&...args)229 void Trace(TracingSpanRef span, Arg&&... args) {
230 tracing_internal::AssertIsNotSpan<FlatBufferTable>();
231 flatbuffers::DetachedBuffer trace_data =
232 tracing_internal::BuildFlatBuffer<FlatBufferTable>(
233 std::forward<Arg>(args)...);
234 span.recorder()->TraceImpl(span.span_id(), std::move(trace_data),
235 TracingTraits<FlatBufferTable>());
236 }
237
238 // Writes an error trace with the specified args under the topmost span located
239 // on TLS. If no span exists on TLS, then root span is fetched from the global
240 // recorder.
241 template <class FlatBufferTable, class... Arg>
TraceError(Arg &&...args)242 ABSL_MUST_USE_RESULT Error TraceError(Arg&&... args) {
243 tracing_internal::AssertIsError<FlatBufferTable>();
244 Trace<FlatBufferTable>(args...);
245 return Error(Error::ConstructorAccess{});
246 }
247
248 } // namespace fcp
249
250 #endif // FCP_TRACING_TRACING_SPAN_H_
251