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,115 @@
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
+
16
+ module Google
17
+ module Cloud
18
+ module Trace
19
+ ##
20
+ # Utility methods for configuring ActiveSupport notifications to generate
21
+ # spans in the current trace.
22
+ #
23
+ module Notifications
24
+ ##
25
+ # The default max length for label data.
26
+ DEFAULT_MAX_DATA_LENGTH = 1024
27
+
28
+ ##
29
+ # The default prefix for label keys
30
+ DEFAULT_LABEL_NAMESPACE = "/ruby/"
31
+
32
+ ##
33
+ # Stack truncation method that removes the ActiveSupport::Notifications
34
+ # calls from the top.
35
+ REMOVE_NOTIFICATION_FRAMEWORK =
36
+ lambda do |frame|
37
+ frame.absolute_path !~ %r{/lib/active_support/notifications}
38
+ end
39
+
40
+ ##
41
+ # Subscribes to the given event type or any type matching the given
42
+ # pattern. When an event is raised, a span is generated in the current
43
+ # thread's trace. The event payload is exposed as labels on the span.
44
+ # If there is no active trace for the current thread, then no span is
45
+ # generated.
46
+ #
47
+ # @param [String, Regex] type A specific type or pattern to select
48
+ # notifications to listen for.
49
+ # @param [Integer] max_length The maximum length for label values.
50
+ # If a label value exceeds this length, it is truncated.
51
+ # If the length is nil, no truncation takes place.
52
+ # @param [String] label_namespace A string to prepend to all label
53
+ # keys.
54
+ # @param [Boolean] capture_stack Whether traces should include the
55
+ # call stack.
56
+ #
57
+ # @example
58
+ #
59
+ # require "google/cloud/trace"
60
+ # require "activerecord"
61
+ #
62
+ # Google::Cloud::Trace::Notifications.instrument "sql.activerecord"
63
+ #
64
+ # trace = Google::Cloud::Trace::Trace.new
65
+ # Google::Cloud::Trace.set trace
66
+ # ActiveRecord::Base.query "SHOW TABLES"
67
+ #
68
+ def self.instrument type,
69
+ max_length: DEFAULT_MAX_DATA_LENGTH,
70
+ label_namespace: DEFAULT_LABEL_NAMESPACE,
71
+ capture_stack: false
72
+ require "active_support/notifications"
73
+ ActiveSupport::Notifications.subscribe(type) do |*args|
74
+ event = ActiveSupport::Notifications::Event.new(*args)
75
+ handle_notification_event event, max_length, label_namespace,
76
+ capture_stack
77
+ end
78
+ end
79
+
80
+ ##
81
+ # @private
82
+ def self.handle_notification_event event, maxlen, label_namespace,
83
+ capture_stack
84
+ cur_span = Google::Cloud::Trace.get
85
+ if cur_span && event.time && event.end
86
+ labels = payload_to_labels event, maxlen, label_namespace
87
+ if capture_stack
88
+ Google::Cloud::Trace::LabelKey.set_stack_trace \
89
+ labels,
90
+ skip_frames: 2,
91
+ truncate_stack: REMOVE_NOTIFICATION_FRAMEWORK
92
+ end
93
+ cur_span.create_span event.name,
94
+ start_time: event.time,
95
+ end_time: event.end,
96
+ labels: labels
97
+ end
98
+ end
99
+
100
+ ##
101
+ # @private
102
+ def self.payload_to_labels event, maxlen, label_namespace
103
+ labels = {}
104
+ event.payload.each do |k, v|
105
+ if v.is_a? ::String
106
+ v = v[0, maxlen-3] + "..." if maxlen && v.size > maxlen
107
+ labels["#{label_namespace}#{k}"] = v
108
+ end
109
+ end
110
+ labels
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,219 @@
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 "google/cloud/core/environment"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Trace
21
+ ##
22
+ # # Project
23
+ #
24
+ # Projects are top-level containers in Google Cloud Platform. They store
25
+ # information about billing and authorized users, and they control access
26
+ # to Stackdriver Trace resources. Each project has a friendly name and a
27
+ # unique ID. Projects can be created only in the [Google Developers
28
+ # Console](https://console.developers.google.com).
29
+ #
30
+ # This class is a client to make API calls for the project's trace data.
31
+ # Create an instance using {Google::Cloud::Trace.new} or
32
+ # {Google::Cloud#trace}. You may then use the `get_trace` method to
33
+ # retrieve a trace by ID, `list_traces` to query for a set of traces,
34
+ # and `patch_traces` to update trace data. You may also use `new_trace`
35
+ # as a convenience constructor to build a
36
+ # {Google::Cloud::Trace::TraceRecord} object.
37
+ #
38
+ # @example
39
+ # require "google/cloud/trace"
40
+ #
41
+ # trace_client = Google::Cloud::Trace.new
42
+ # traces = trace_client.list_traces Time.now - 3600, Time.now
43
+ #
44
+ class Project
45
+ ##
46
+ # @private The Service object.
47
+ attr_accessor :service
48
+
49
+ ##
50
+ # @private Creates a new Project instance.
51
+ def initialize service
52
+ @service = service
53
+ end
54
+
55
+ ##
56
+ # The ID of the current project.
57
+ #
58
+ # @return [String] the Google Cloud project ID
59
+ #
60
+ # @example
61
+ # require "google/cloud/trace"
62
+ #
63
+ # trace_client = Google::Cloud::Trace.new(
64
+ # project: "my-project",
65
+ # keyfile: "/path/to/keyfile.json"
66
+ # )
67
+ #
68
+ # trace_client.project #=> "my-project"
69
+ #
70
+ def project
71
+ service.project
72
+ end
73
+
74
+ ##
75
+ # Create a new empty trace record for this project. Uses the current
76
+ # thread's TraceContext by default; otherwise you may provide a
77
+ # specific TraceContext.
78
+ #
79
+ # @param [Stackdriver::Core::TraceContext, nil] trace_context The
80
+ # context within which to locate this trace (i.e. sets the trace ID
81
+ # and the context parent span, if present.) If the context is set
82
+ # explicitly to `nil`, a new trace with a new trace ID is created.
83
+ # If no context is provided, the current thread's context is used.
84
+ # @return [Google::Cloud::Trace::TraceRecord] The new trace.
85
+ #
86
+ # @example
87
+ # require "google/cloud/trace"
88
+ #
89
+ # trace_client = Google::Cloud::Trace.new(
90
+ # project: "my-project",
91
+ # keyfile: "/path/to/keyfile.json"
92
+ # )
93
+ #
94
+ # trace = trace_client.new_trace
95
+ #
96
+ def new_trace trace_context: :DEFAULT
97
+ if trace_context == :DEFAULT
98
+ trace_context = Stackdriver::Core::TraceContext.get
99
+ end
100
+ Google::Cloud::Trace::TraceRecord.new project, trace_context
101
+ end
102
+
103
+ ##
104
+ # Sends new traces to Stackdriver Trace or updates existing traces.
105
+ # If the ID of a trace that you send matches that of an existing trace,
106
+ # any fields in the existing trace and its spans are overwritten by the
107
+ # provided values, and any new fields provided are merged with the
108
+ # existing trace data. If the ID does not match, a new trace is created.
109
+ #
110
+ # @param [Google::Cloud::Trace::TraceRecord,
111
+ # Array{Google::Cloud::Trace::TraceRecord}] traces Either a single
112
+ # trace object or an array of trace objects.
113
+ # @return [Array{Google::Cloud::Trace::TraceRecord}] The traces written.
114
+ #
115
+ # @example
116
+ # require "google/cloud/trace"
117
+ #
118
+ # trace_client = Google::Cloud::Trace.new
119
+ #
120
+ # trace = trace_client.new_trace
121
+ # trace.in_span "root_span" do
122
+ # # Do stuff...
123
+ # end
124
+ #
125
+ # trace_client.patch_traces trace
126
+ #
127
+ def patch_traces traces
128
+ ensure_service!
129
+ service.patch_traces traces
130
+ end
131
+
132
+ ##
133
+ # Gets a single trace by its ID.
134
+ #
135
+ # @param [String] trace_id The ID of the trace to fetch.
136
+ # @return [Google::Cloud::Trace::TraceRecord, nil] The trace object, or
137
+ # `nil` if there is no accessible trace with the given ID.
138
+ #
139
+ # @example
140
+ # require "google/cloud/trace"
141
+ #
142
+ # trace_client = Google::Cloud::Trace.new
143
+ #
144
+ # trace = trace_client.get_trace "1234567890abcdef1234567890abcdef"
145
+ #
146
+ def get_trace trace_id
147
+ ensure_service!
148
+ service.get_trace trace_id
149
+ end
150
+
151
+ ##
152
+ # Returns of a list of traces that match the specified conditions.
153
+ # You must provide a time interval. You may optionally provide a
154
+ # filter, an ordering, a view type.
155
+ # Results are paginated, and you may specify a page size. The result
156
+ # will come with a token you can pass back to retrieve the next page.
157
+ #
158
+ # @param [Time] start_time The start of the time interval (inclusive).
159
+ # @param [Time] end_time The end of the time interval (inclusive).
160
+ # @param [String] filter An optional filter.
161
+ # @param [String] order_by The optional sort order for returned traces.
162
+ # May be `trace_id`, `name`, `duration`, or `start`. Any sort order
163
+ # may also be reversed by appending `desc`; for example use
164
+ # `start desc` to order traces from newest to oldest.
165
+ # @param [Symbol] view The optional type of view. Valid values are
166
+ # `:MINIMAL`, `:ROOTSPAN`, and `:COMPLETE`. Default is `:MINIMAL`.
167
+ # @param [Integer] page_size The size of each page to return. Optional;
168
+ # if omitted, the service will select a reasonable page size.
169
+ # @param [String] page_token A token indicating the page to return.
170
+ # Each page of results includes proper token for specifying the
171
+ # following page.
172
+ # @return [Google::Cloud::Trace::ResultSet] A page of results.
173
+ #
174
+ # @example
175
+ # require "google/cloud/trace"
176
+ #
177
+ # trace_client = Google::Cloud::Trace.new
178
+ #
179
+ # traces = trace_client.list_traces Time.now - 3600, Time.now
180
+ # traces.each do |trace|
181
+ # puts "Retrieved trace ID: #{trace.trace_id}"
182
+ # end
183
+ #
184
+ def list_traces start_time, end_time,
185
+ filter: nil,
186
+ order_by: nil,
187
+ view: nil,
188
+ page_size: nil,
189
+ page_token: nil
190
+ ensure_service!
191
+ service.list_traces project, start_time, end_time,
192
+ filter: filter,
193
+ order_by: order_by,
194
+ view: view,
195
+ page_size: page_size,
196
+ page_token: page_token
197
+ end
198
+
199
+ ##
200
+ # @private Default project.
201
+ def self.default_project
202
+ ENV["TRACE_PROJECT"] ||
203
+ ENV["GOOGLE_CLOUD_PROJECT"] ||
204
+ ENV["GCLOUD_PROJECT"] ||
205
+ Google::Cloud::Core::Environment.project_id
206
+ end
207
+
208
+ protected
209
+
210
+ ##
211
+ # @private Raise an error unless an active connection to the service is
212
+ # available.
213
+ def ensure_service!
214
+ fail "Must have active connection to service" unless service
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,231 @@
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
+ require "google/cloud/core/environment"
16
+ require "google/cloud/trace"
17
+
18
+
19
+ module Google
20
+ module Cloud
21
+ module Trace
22
+ ##
23
+ # # Rails integration for Stackdriver Trace
24
+ #
25
+ # This Railtie is a drop-in Stackdriver Trace instrumentation plugin
26
+ # for Ruby on Rails applications. If present, it automatically
27
+ # instruments your Rails app to record performance traces and cause them
28
+ # to appear on your Stackdriver console.
29
+ #
30
+ # ## Installation
31
+ #
32
+ # To install this plugin, the gem `google-cloud-trace` must be in your
33
+ # Gemfile. You also must add the following line to your `application.rb`
34
+ # file:
35
+ #
36
+ # ```ruby
37
+ # require "google/cloud/trace/rails"
38
+ # ```
39
+ #
40
+ # If you include the `stackdriver` gem in your Gemfile, the above is done
41
+ # for you automatically, and you do not need to edit your
42
+ # `application.rb`.
43
+ #
44
+ # ## Configuration
45
+ #
46
+ # The following Rails configuration options are recognized.
47
+ #
48
+ # ```ruby
49
+ # config.google_cloud.use_trace = true | false
50
+ # ```
51
+ #
52
+ # Normally, tracing is activated when `RAILS_ENV` is set to `production`
53
+ # and credentials are available. You can override this and enable tracing
54
+ # in other environments by setting `use_trace` explicitly.
55
+ #
56
+ # ```ruby
57
+ # config.google_cloud.keyfile = "path/to/file"
58
+ # ```
59
+ #
60
+ # If your application is running on Google Cloud Platform, it will
61
+ # automatically use credentials available to your project. However, if
62
+ # you are running an application locally or on a different hosting
63
+ # provider, you may provide a path to your credentials file using this
64
+ # configuration.
65
+ #
66
+ # ```ruby
67
+ # config.google_cloud.project_id = "my-project-id"
68
+ # ```
69
+ #
70
+ # If your application is running on Google Cloud Platform, it will
71
+ # automatically select the project under which it is running. However, if
72
+ # you are running an application locally or on a different hosting
73
+ # provider, or if you want to log traces to a different project than you
74
+ # are using to host your application, you may provide the project ID.
75
+ #
76
+ # ```ruby
77
+ # config.google_cloud.trace.notifications = ["event1", "event2"]
78
+ # ```
79
+ #
80
+ # By default, this Railtie subscribes to ActiveSupport notifications
81
+ # emitted by ActiveRecord queries, rendering, and emailing functions.
82
+ # See {DEFAULT_NOTIFICATIONS}. If you want to customize the list of
83
+ # notification types, edit the notifications configuration.
84
+ #
85
+ # ```ruby
86
+ # config.google_cloud.trace.max_data_length = 1024
87
+ # ```
88
+ #
89
+ # The maximum length of span properties recorded with ActiveSupport
90
+ # notification events. Any property value larger than this length is
91
+ # truncated.
92
+ #
93
+ # ```ruby
94
+ # config.google_cloud.trace.capture_stack = true | false
95
+ # ```
96
+ #
97
+ # Whether to capture the call stack with each trace span. Default is
98
+ # false.
99
+ #
100
+ # ## Measuring custom functionality
101
+ #
102
+ # To add a custom measurement to a request trace, use the classes
103
+ # provided in this library. Below is an example to get you started.
104
+ #
105
+ # ```ruby
106
+ # class MyController < ApplicationController
107
+ # def index
108
+ # Google::Cloud::Trace.in_span "Sleeping on the job!" do
109
+ # sleep rand
110
+ # end
111
+ # render plain: "Hello World!"
112
+ # end
113
+ # end
114
+ # ```
115
+ #
116
+ class Railtie < ::Rails::Railtie
117
+ ##
118
+ # The default list of ActiveSupport notification types to include in
119
+ # traces.
120
+ #
121
+ DEFAULT_NOTIFICATIONS = [
122
+ "sql.active_record",
123
+ "render_template.action_view",
124
+ "send_file.action_controller",
125
+ "send_data.action_controller",
126
+ "deliver.action_mailer"
127
+ ].freeze
128
+
129
+ unless config.respond_to? :google_cloud
130
+ config.google_cloud = ActiveSupport::OrderedOptions.new
131
+ end
132
+ config.google_cloud.trace = ActiveSupport::OrderedOptions.new
133
+ config.google_cloud.trace.notifications = DEFAULT_NOTIFICATIONS.dup
134
+ config.google_cloud.trace.max_data_length =
135
+ Google::Cloud::Trace::Notifications::DEFAULT_MAX_DATA_LENGTH
136
+
137
+ initializer "Google.Cloud.Trace" do |app|
138
+ if Google::Cloud::Trace::Railtie.use_trace? app.config
139
+ Google::Cloud::Trace::Railtie.init_trace app
140
+ end
141
+ end
142
+
143
+ ##
144
+ # Determine whether to use Stackdriver Trace or not.
145
+ #
146
+ # Returns true if valid GCP project_id and keyfile are provided and
147
+ # either Rails is in "production" environment or
148
+ # config.google_cloud.use_trace is explicitly true. Otherwise false.
149
+ #
150
+ # @param [Rails::Railtie::Configuration] config The
151
+ # Rails.application.config
152
+ #
153
+ # @return [Boolean] Whether to use Stackdriver Trace
154
+ #
155
+ def self.use_trace? config
156
+ gcp_config = config.google_cloud
157
+
158
+ # Return false if config.google_cloud.use_trace is
159
+ # explicitly false
160
+ return false if gcp_config.key?(:use_trace) &&
161
+ !gcp_config.use_trace
162
+
163
+ # Try authenticate authorize client API. Return false if unable to
164
+ # authorize.
165
+ keyfile = gcp_config.trace.keyfile || gcp_config.keyfile
166
+ begin
167
+ Google::Cloud::Trace::Credentials.credentials_with_scope keyfile
168
+ rescue Exception => e
169
+ warn "Unable to initialize Google::Cloud::Trace due " \
170
+ "to authorization error: #{e.message}"
171
+ return false
172
+ end
173
+
174
+ if project_id(config).to_s.empty?
175
+ warn "Google::Cloud::Trace is not activated due to empty project_id"
176
+ return false
177
+ end
178
+
179
+ # Otherwise return true if Rails is running in production or
180
+ # config.google_cloud.use_trace is explicitly true
181
+ Rails.env.production? ||
182
+ (gcp_config.key?(:use_trace) && gcp_config.use_trace)
183
+ end
184
+
185
+ ##
186
+ # Return the effective project ID for this app, given a Rails config.
187
+ #
188
+ # @param [Rails::Railtie::Configuration] config The
189
+ # Rails.application.config
190
+ # @return [String] The project ID, or nil if not found.
191
+ #
192
+ def self.project_id config
193
+ config.google_cloud.trace.project_id ||
194
+ config.google_cloud.project_id ||
195
+ ENV["TRACE_PROJECT"] ||
196
+ ENV["GOOGLE_CLOUD_PROJECT"] ||
197
+ Google::Cloud::Core::Environment.project_id
198
+ end
199
+
200
+ ##
201
+ # Initialize trace integration for Rails. Sets up the configuration,
202
+ # adds and configures middleware, and installs notifications.
203
+ #
204
+ # @private
205
+ #
206
+ def self.init_trace app
207
+ gcp_config = app.config.google_cloud
208
+ trace_config = gcp_config.trace
209
+ project_id = trace_config.project_id || gcp_config.project_id
210
+ keyfile = trace_config.keyfile || gcp_config.keyfile
211
+
212
+ tracer = Google::Cloud::Trace.new project: project_id,
213
+ keyfile: keyfile
214
+
215
+ app.middleware.insert_before \
216
+ Rack::Runtime,
217
+ Google::Cloud::Trace::Middleware,
218
+ service: tracer.service,
219
+ capture_stack: trace_config.capture_stack
220
+
221
+ trace_config.notifications.each do |type|
222
+ Google::Cloud::Trace::Notifications.instrument \
223
+ type,
224
+ max_length: trace_config.max_data_length,
225
+ capture_stack: trace_config.capture_stack
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end