google-cloud-trace 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,80 @@
1
+ # Copyright 2014 Google Inc. All rights reserved.
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
+
16
+ module Google
17
+ module Cloud
18
+ module Trace
19
+ ##
20
+ # SpanKind represents values for the "kind" field of span.
21
+ #
22
+ class SpanKind
23
+ @@mapping = {}
24
+
25
+ ##
26
+ # Create a new SpanKind.
27
+ #
28
+ # @private
29
+ #
30
+ def initialize name
31
+ @name = name
32
+ @@mapping[name] = self
33
+ end
34
+
35
+ ##
36
+ # The `:SPAN_KIND_UNSPECIFIED` value
37
+ #
38
+ UNSPECIFIED = new :SPAN_KIND_UNSPECIFIED
39
+
40
+ ##
41
+ # The `:RPC_SERVER` value
42
+ #
43
+ RPC_SERVER = new :RPC_SERVER
44
+
45
+ ##
46
+ # The `:RPC_CLIENT` value
47
+ #
48
+ RPC_CLIENT = new :RPC_CLIENT
49
+
50
+ ##
51
+ # Returns the symbolic representation of this SpanKind
52
+ #
53
+ # @return [Symbol] Symbol representation.
54
+ #
55
+ def to_sym
56
+ @name
57
+ end
58
+
59
+ ##
60
+ # Returns the string representation of this SpanKind
61
+ #
62
+ # @return [String] String representation.
63
+ #
64
+ def to_s
65
+ to_sym.to_s
66
+ end
67
+
68
+ ##
69
+ # Returns the SpanKind given a symbol or string representation.
70
+ #
71
+ # @param [String, Symbol] name The name of the SpanKind.
72
+ # @return [SpanKind] The SpanKind, or `nil` if not known.
73
+ #
74
+ def self.get name
75
+ @@mapping[name.to_sym]
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,104 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
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
+ module Google
16
+ module Cloud
17
+ module Trace
18
+ ##
19
+ # A sampler determines whether a given request's latency trace should
20
+ # actually be reported. It is usually not necessary to trace every
21
+ # request, especially for an application serving heavy traffic. You may
22
+ # use a sampler to decide, for a given request, whether to report its
23
+ # trace. A sampler is simply a Proc that takes the Rack environment as an
24
+ # argument and returns a boolean indicating whether or not to sample the
25
+ # current request. Alternately, it could be an object that duck-types the
26
+ # Proc interface by implementing the `call` method.
27
+ #
28
+ # TimeSampler is the default sampler. It bases its sampling decision on
29
+ # two considerations:
30
+ #
31
+ # 1. It allows you to blacklist certain URI paths that should never be
32
+ # traced. For example, the Google App Engine health check request
33
+ # path `/_ah/health` is blacklisted by default.
34
+ # 2. It spaces samples out by delaying a minimum time between each
35
+ # sample. This enforces a maximum QPS for this Ruby process.
36
+ #
37
+ class TimeSampler
38
+ ##
39
+ # Default list of paths for which to disable traces. Currently includes
40
+ # App Engine Flex health checks.
41
+ DEFAULT_PATH_BLACKLIST = ["/_ah/health"].freeze
42
+
43
+ ##
44
+ # Create a TimeSampler for the given QPS.
45
+ #
46
+ # @param [Number] qps Samples per second. Default is 0.1.
47
+ # @param [Array{String,Regex}] path_blacklist An array of paths or
48
+ # path patterns indicating URIs that should never be traced.
49
+ # Default is DEFAULT_PATH_BLACKLIST.
50
+ #
51
+ def initialize qps: 0.1, path_blacklist: DEFAULT_PATH_BLACKLIST
52
+ @delay_secs = 1.0 / qps
53
+ @last_time = ::Time.now.to_f - @delay_secs
54
+ @path_blacklist = path_blacklist
55
+ end
56
+
57
+ @default = new
58
+
59
+ ##
60
+ # Get the default global TimeSampler.
61
+ #
62
+ # @return [TimeSampler]
63
+ #
64
+ def self.default
65
+ @default
66
+ end
67
+
68
+ ##
69
+ # Implements the sampler contract. Checks to see whether a sample
70
+ # should be taken at this time.
71
+ #
72
+ # @param [Hash] env Rack environment.
73
+ # @return [Boolean] Whether to sample at this time.
74
+ #
75
+ def call env
76
+ return false if path_blacklisted? env
77
+ time = ::Time.now.to_f
78
+ delays = (time - @last_time) / @delay_secs
79
+ if delays >= 2.0
80
+ @last_time = time - @delay_secs
81
+ true
82
+ elsif delays >= 1.0
83
+ @last_time += @delay_secs
84
+ true
85
+ else
86
+ false
87
+ end
88
+ end
89
+
90
+ ##
91
+ # Determines if the URI path in the given Rack environment is
92
+ # blacklisted in this sampler.
93
+ #
94
+ # @private
95
+ #
96
+ def path_blacklisted? env
97
+ path = "#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
98
+ path = "/#{path}" unless path.start_with? "/"
99
+ @path_blacklist.find { |p| p === path }
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,332 @@
1
+ # Copyright 2014 Google Inc. All rights reserved.
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
+
16
+ require "date"
17
+ require "google/devtools/cloudtrace/v1/trace_pb"
18
+ require "stackdriver/core/trace_context"
19
+
20
+ module Google
21
+ module Cloud
22
+ module Trace
23
+ ##
24
+ # Trace represents an entire trace record.
25
+ #
26
+ # A trace has an ID and contains a forest of spans. The trace object
27
+ # methods may be used to walk or manipulate the set of spans.
28
+ #
29
+ # @example
30
+ # require "google/cloud/trace"
31
+ #
32
+ # env = my_get_rack_environment
33
+ # trace_context = Stackdriver::Core::TraceContext.parse_rack_env env
34
+ #
35
+ # trace = Google::Cloud::Trace.new "my-project", trace_context
36
+ # span = trace.create_span "root_span"
37
+ # subspan = span.create_span "subspan"
38
+ #
39
+ # trace_proto = trace.to_grpc
40
+ #
41
+ class TraceRecord
42
+ ##
43
+ # Create an empty Trace object. If a trace context is provided, it is
44
+ # used to locate this trace within that context.
45
+ #
46
+ # @param [String] project The ID of the project containing this trace.
47
+ # @param [Stackdriver::Core::TraceContext] trace_context The context
48
+ # within which to locate this trace (i.e. sets the trace ID and
49
+ # the context parent span, if present.) If no context is provided,
50
+ # a new trace with a new trace ID is created.
51
+ #
52
+ def initialize project, trace_context = nil, span_id_generator: nil
53
+ @project = project
54
+ @trace_context = trace_context || Stackdriver::Core::TraceContext.new
55
+ @root_spans = []
56
+ @spans_by_id = {}
57
+ @span_id_generator =
58
+ span_id_generator || ::Proc.new { rand(0xffffffffffffffff) + 1 }
59
+ end
60
+
61
+ ##
62
+ # Standard value equality check for this object.
63
+ #
64
+ # @param [Object] other Object to compare with
65
+ # @return [Boolean]
66
+ #
67
+ def eql? other
68
+ other.is_a?(Google::Cloud::Trace::TraceRecord) &&
69
+ trace_context == other.trace_context &&
70
+ @spans_by_id == other.instance_variable_get(:@spans_by_id)
71
+ end
72
+ alias_method :==, :eql?
73
+
74
+ ##
75
+ # Create a new Trace object from a trace protobuf.
76
+ #
77
+ # @param [Google::Devtools::Cloudtrace::V1::Trace] trace_proto The
78
+ # trace protobuf from the V1 gRPC Trace API.
79
+ # @return [Trace, nil] A corresponding Trace object, or `nil` if the
80
+ # proto does not represent an existing trace object.
81
+ #
82
+ def self.from_grpc trace_proto
83
+ trace_id = trace_proto.trace_id.to_s
84
+ return nil if trace_id.empty?
85
+
86
+ span_protos = trace_proto.spans
87
+ parent_span_ids = find_root_span_ids span_protos
88
+
89
+ span_id = parent_span_ids.size == 1 ? parent_span_ids.first : 0
90
+ span_id = nil if span_id == 0
91
+ tc = Stackdriver::Core::TraceContext.new trace_id: trace_id,
92
+ span_id: span_id
93
+ trace = new trace_proto.project_id, tc
94
+
95
+ until parent_span_ids.empty?
96
+ parent_span_ids = trace.add_span_protos span_protos, parent_span_ids
97
+ end
98
+ trace
99
+ end
100
+
101
+ ##
102
+ # Convert this Trace object to an equivalent Trace protobuf suitable
103
+ # for the V1 gRPC Trace API.
104
+ #
105
+ # @return [Google::Devtools::Cloudtrace::V1::Trace] The generated
106
+ # protobuf.
107
+ #
108
+ def to_grpc
109
+ span_protos = @spans_by_id.values.map do |span|
110
+ span.to_grpc trace_context.span_id.to_i
111
+ end
112
+ Google::Devtools::Cloudtrace::V1::Trace.new \
113
+ project_id: project,
114
+ trace_id: trace_id,
115
+ spans: span_protos
116
+ end
117
+
118
+ ##
119
+ # The project ID for this trace.
120
+ #
121
+ # @return [String]
122
+ #
123
+ attr_reader :project
124
+
125
+ ##
126
+ # The context for this trace.
127
+ #
128
+ # @return [Stackdriver::Core::TraceContext]
129
+ #
130
+ attr_reader :trace_context
131
+
132
+ ##
133
+ # The ID string for the trace.
134
+ #
135
+ # @return [String]
136
+ #
137
+ def trace_id
138
+ trace_context.trace_id
139
+ end
140
+
141
+ ##
142
+ # Returns an array of all spans in this trace, not in any particular
143
+ # order
144
+ #
145
+ # @return [Array{TraceSpan}]
146
+ #
147
+ def all_spans
148
+ @spans_by_id.values
149
+ end
150
+
151
+ ##
152
+ # Returns an array of all root spans in this trace, not in any
153
+ # particular order
154
+ #
155
+ # @return [Array{TraceSpan}]
156
+ #
157
+ def root_spans
158
+ @root_spans.dup
159
+ end
160
+
161
+ ##
162
+ # Creates a new span in this trace.
163
+ #
164
+ # @param [String] name The name of the span.
165
+ # @param [Integer] span_id The numeric ID of the span, or nil to
166
+ # generate a new random unique ID. Optional (defaults to nil).
167
+ # @param [Integer] parent_span_id The span ID of the parent span, or 0
168
+ # if this should be a new root span within the context. Note that
169
+ # a root span would not necessarily end up with a parent ID of 0 if
170
+ # the trace context specifies a different context span ID. Optional
171
+ # (defaults to 0).
172
+ # @param [SpanKind] kind The kind of span. Optional.
173
+ # @param [Time] start_time The starting timestamp, or nil if not yet
174
+ # specified. Optional (defaults to nil).
175
+ # @param [Time] end_time The ending timestamp, or nil if not yet
176
+ # specified. Optional (defaults to nil).
177
+ # @param [Hash{String=>String}] labels The span properties. Optional
178
+ # (defaults to empty).
179
+ # @return [TraceSpan] The created span.
180
+ #
181
+ # @example
182
+ # require "google/cloud/trace"
183
+ #
184
+ # trace = Google::Cloud::Trace.new trace_context
185
+ # span = trace.create_span "root_span"
186
+ #
187
+ def create_span name, span_id: nil, parent_span_id: 0,
188
+ kind: SpanKind::UNSPECIFIED,
189
+ start_time: nil, end_time: nil,
190
+ labels: {}
191
+ parent_span_id = parent_span_id.to_i
192
+ parent_span_id = trace_context.span_id.to_i if parent_span_id == 0
193
+ parent_span = @spans_by_id[parent_span_id]
194
+ if parent_span
195
+ parent_span.create_span name,
196
+ span_id: span_id,
197
+ kind: kind,
198
+ start_time: start_time,
199
+ end_time: end_time,
200
+ labels: labels
201
+ else
202
+ internal_create_span nil, span_id, parent_span_id, name, kind,
203
+ start_time, end_time, labels
204
+ end
205
+ end
206
+
207
+ ##
208
+ # Creates a root span around the given block. Automatically populates
209
+ # the start and end timestamps. The span (with start time but not end
210
+ # time populated) is yielded to the block.
211
+ #
212
+ # @param [String] name The name of the span.
213
+ # @param [SpanKind] kind The kind of span. Optional.
214
+ # @param [Hash{String=>String}] labels The span properties. Optional
215
+ # (defaults to empty).
216
+ # @return [TraceSpan] The created span.
217
+ #
218
+ # @example
219
+ # require "google/cloud/trace"
220
+ #
221
+ # trace = Google::Cloud::Trace.new trace_context
222
+ # trace.in_span "root_span" do |span|
223
+ # # Do stuff...
224
+ # end
225
+ #
226
+ def in_span name, kind: SpanKind::UNSPECIFIED, labels: {}
227
+ span = create_span name, kind: kind, labels: labels
228
+ span.start!
229
+ yield span
230
+ ensure
231
+ span.finish!
232
+ end
233
+
234
+ ##
235
+ # Internal implementation of span creation. Ensures that a span ID has
236
+ # been allocated, and that the span appears in the internal indexes.
237
+ #
238
+ # @private
239
+ #
240
+ def internal_create_span parent, span_id, parent_span_id, name, kind,
241
+ start_time, end_time, labels
242
+ span_id = span_id.to_i
243
+ parent_span_id = parent_span_id.to_i
244
+ span_id = unique_span_id if span_id == 0
245
+ span = Google::Cloud::Trace::Span.new \
246
+ self, span_id, parent_span_id, parent, name, kind,
247
+ start_time, end_time, labels
248
+ @root_spans << span if parent.nil?
249
+ @spans_by_id[span_id] = span
250
+ span
251
+ end
252
+
253
+ ##
254
+ # Generates and returns a span ID that is unique in this trace.
255
+ #
256
+ # @private
257
+ #
258
+ def unique_span_id
259
+ loop do
260
+ id = @span_id_generator.call
261
+ return id if !@spans_by_id.include?(id) &&
262
+ id != trace_context.span_id.to_i
263
+ end
264
+ end
265
+
266
+ ##
267
+ # Add the given span to the list of root spans.
268
+ #
269
+ # @private
270
+ #
271
+ def add_root span
272
+ @root_spans << span
273
+ end
274
+
275
+ ##
276
+ # Remove the given span from the list of root spans.
277
+ #
278
+ # @private
279
+ #
280
+ def remove_root span
281
+ @root_spans.delete span
282
+ end
283
+
284
+ ##
285
+ # Remove the given span from the list of spans overall.
286
+ #
287
+ # @private
288
+ #
289
+ def remove_span span
290
+ @root_spans.delete span
291
+ @spans_by_id.delete span.span_id
292
+ end
293
+
294
+ ##
295
+ # Given a list of span protobufs, find the "root" span IDs, i.e. all
296
+ # parent span IDs that don't correspond to actual spans in the set.
297
+ #
298
+ # @private
299
+ #
300
+ def self.find_root_span_ids span_protos
301
+ span_ids = ::Set.new span_protos.map(&:span_id)
302
+ root_protos = span_protos.find_all do |sp|
303
+ !span_ids.include? sp.parent_span_id
304
+ end
305
+ ::Set.new root_protos.map(&:parent_span_id)
306
+ end
307
+
308
+ ##
309
+ # Given a list of span protobufs and a set of parent span IDs, add
310
+ # for all spans whose parent is in the set, convert the span to a
311
+ # `TraceSpan` object and add it into this trace. Returns the IDs of
312
+ # the spans added, which may be used in a subsequent call to this
313
+ # method. Effectively, repeated calls to this method perform a
314
+ # breadth-first walk of the span protos and populate the TraceRecord
315
+ # accordingly.
316
+ #
317
+ # @private
318
+ #
319
+ def add_span_protos span_protos, parent_span_ids
320
+ new_span_ids = ::Set.new
321
+ span_protos.each do |span_proto|
322
+ if parent_span_ids.include? span_proto.parent_span_id
323
+ Google::Cloud::Trace::Span.from_grpc span_proto, self
324
+ new_span_ids.add span_proto.span_id
325
+ end
326
+ end
327
+ new_span_ids
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end