google-cloud-error_reporting 0.24.0 → 0.25.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,224 @@
1
+ # Copyright 2017 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/errors"
17
+ require "google/cloud/env"
18
+ require "google/cloud/error_reporting/service"
19
+ require "google/cloud/error_reporting/credentials"
20
+ require "google/cloud/error_reporting/error_event"
21
+
22
+ module Google
23
+ module Cloud
24
+ module ErrorReporting
25
+ ##
26
+ # Project
27
+ #
28
+ # Projects are top-level containers in Google Cloud Platform. They store
29
+ # information about billing and authorized users, and they control access
30
+ # to Stackdriver ErrorReporting. Each project has a friendly name and a
31
+ # unique ID. Projects can be created only in the [Google Developers
32
+ # Console](https://console.developers.google.com).
33
+ #
34
+ # @example
35
+ # require "google/cloud/error_reporting"
36
+ #
37
+ # error_reporting = Google::Cloud::ErrorReporting.new
38
+ # error_event = error_reporting.error_event "Error with Backtrace",
39
+ # timestamp: Time.now
40
+ # service_name: "my_app_name"
41
+ # error_reporting.report error_event
42
+ #
43
+ # See {Google::Cloud::ErrorReporting.new}
44
+ #
45
+ class Project
46
+ ##
47
+ # Find default project_id from `ERROR_REPORTING_RPOJECT`,
48
+ # `GOOGLE_CLOUD_PROJECT`, `GCLOUD_PROJECT` environment varaibles, or
49
+ # query from GCE meta service.
50
+ #
51
+ # @return [String] default valid GCP project_id
52
+ #
53
+ def self.default_project
54
+ ENV["ERROR_REPORTING_PROJECT"] ||
55
+ ENV["GOOGLE_CLOUD_PROJECT"] ||
56
+ Google::Cloud.env.project_id
57
+ end
58
+
59
+ ##
60
+ # Find default service_name from `ERROR_REPORTING_SERVICE`,
61
+ # `GAE_MODULE_NAME` environment Variables, or just "ruby".
62
+ #
63
+ # @return [String] default GCP service_name
64
+ #
65
+ def self.default_service_name
66
+ ENV["ERROR_REPORTING_SERVICE"] ||
67
+ Google::Cloud.env.app_engine_service_id ||
68
+ "ruby"
69
+ end
70
+
71
+ ##
72
+ # Find default service_version from `ERROR_REPORTING_VERSION` or
73
+ # `GAE_MODULE_VERSION` environment varaibles.
74
+ #
75
+ # @return [String] default GCP service_version
76
+ #
77
+ def self.default_service_version
78
+ ENV["ERROR_REPORTING_VERSION"] ||
79
+ Google::Cloud.env.app_engine_service_version
80
+ end
81
+
82
+ ##
83
+ # @private The Service object
84
+ attr_accessor :service
85
+
86
+ ##
87
+ # @private Create a new Project instance.
88
+ #
89
+ # @param [Google::Cloud::ErrorReporting::Service] service The underlying
90
+ # Service object
91
+ #
92
+ # @return [Google::Cloud::ErrorReporting::Project] A new Project
93
+ # instance
94
+ #
95
+ def initialize service
96
+ @service = service
97
+ end
98
+
99
+ ##
100
+ # Get the name of current project_id from underneath gRPC Service
101
+ # object.
102
+ #
103
+ # @return [String] The current project_id
104
+ #
105
+ def project
106
+ service.project
107
+ end
108
+
109
+ ##
110
+ # Report a {Google::Cloud::ErrorReporting::ErrorEvent} to Stackdriver
111
+ # Error Reporting service.
112
+ #
113
+ # @example
114
+ # require "google/cloud/error_reporting"
115
+ #
116
+ # error_reporting = Google::Cloud::ErrorReporting.new
117
+ #
118
+ # error_event = error_reporting.error_event "Error with Backtrace"
119
+ # error_reporting.report error_event
120
+ #
121
+ def report *args, &block
122
+ service.report *args, &block
123
+ end
124
+
125
+ ##
126
+ # Create a {Google::Cloud::ErrorReporting::ErrorEvent} from the
127
+ # given exception, and report this ErrorEvent to Stackdriver Error
128
+ # Reporting service.
129
+ #
130
+ # @param [Exception] exception A Ruby exception
131
+ # @param [String] service_name The service's name.
132
+ # Default to {default_service_name}
133
+ # @param [String] service_version The service's version.
134
+ # Default to {default_service_version}
135
+ #
136
+ # @example
137
+ # require "google/cloud/error_reporting"
138
+ #
139
+ # error_reporting = Google::Cloud::ErrorReporting.new
140
+ #
141
+ # begin
142
+ # fail StandardError, "A serious problem"
143
+ # rescue StandardError => exception
144
+ # error_reporting.report_exception, service_name: "my_app_name",
145
+ # service_version: "v8"
146
+ # end
147
+ #
148
+ def report_exception exception, service_name: nil, service_version: nil
149
+ error_event = ErrorEvent.from_exception exception
150
+
151
+ error_event.service_name =
152
+ service_name || self.class.default_service_name
153
+ error_event.service_version =
154
+ service_version || self.class.default_service_version
155
+
156
+ yield error_event if block_given?
157
+
158
+ report error_event
159
+ end
160
+
161
+ ##
162
+ # Create a new {Google::Cloud::ErrorReporting::ErrorEvent} instance
163
+ # with given parameters.
164
+ #
165
+ # @param [String] message The error message along with backtrace
166
+ # @param [String] service_name The service's name.
167
+ # Default to {default_service_name}
168
+ # @param [String] service_version The service's version.
169
+ # Default to {default_service_version}
170
+ # @param [Time] event_time Time when the event occurred. If not
171
+ # provided, the time when the event was received by the Error
172
+ # Reporting system will be used.
173
+ # @param [String] user The user who caused or was affected by the crash.
174
+ # This can be a user ID, an email address, or an arbitrary token that
175
+ # uniquely identifies the user. When sending an error report, leave
176
+ # this field empty if the user was not logged in. In this case the
177
+ # Error Reporting system will use other data, such as remote IP
178
+ # address, to distinguish affected users.
179
+ # @param [String] file_path The source code filename, which can include
180
+ # a truncated relative path, or a full path from a production machine.
181
+ # @param [Number] line_number 1-based. 0 indicates that the line number
182
+ # is unknown.
183
+ # @param [String] function_name Human-readable name of a function or
184
+ # method. The value can include optional context like the class or
185
+ # package name. For example, my.package.MyClass.method in case of
186
+ # Java.
187
+ #
188
+ # @return [ErrorEvent] A new ErrorEvent instance
189
+ #
190
+ # @example
191
+ # require "google/cloud/error_reporting"
192
+ #
193
+ # error_reporting = Google::Cloud::ErrorReporting.new
194
+ #
195
+ # error_event =
196
+ # error_reporting.error_event "Error Message with Backtrace",
197
+ # event_time: Time.now,
198
+ # service_name: "my_app_name",
199
+ # service_version: "v8",
200
+ # user: "johndoh",
201
+ # file_path: "MyController.rb",
202
+ # line_number: 123,
203
+ # function_name: "index"
204
+ # error_reporting.report error_event
205
+ #
206
+ def error_event message = nil, service_name: nil, service_version: nil,
207
+ event_time: nil, user: nil, file_path: nil,
208
+ line_number: nil, function_name: nil
209
+ ErrorEvent.new.tap do |e|
210
+ e.message = message
211
+ e.event_time = event_time
212
+ e.service_name = service_name || self.class.default_service_name
213
+ e.service_version = service_version ||
214
+ self.class.default_service_version
215
+ e.user = user
216
+ e.file_path = file_path
217
+ e.line_number = line_number
218
+ e.function_name = function_name
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -1,4 +1,4 @@
1
- # Copyright 2016 Google Inc. All rights reserved.
1
+ # Copyright 2017 Google Inc. All rights reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -13,38 +13,30 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
- require "google/cloud/env"
17
- require "google/cloud/error_reporting/v1beta1"
18
- require "google/cloud/error_reporting/middleware"
19
- require "google/cloud/credentials"
16
+ require "google/cloud/error_reporting"
20
17
 
21
18
  module Google
22
19
  module Cloud
23
20
  module ErrorReporting
24
21
  ##
25
- # Railtie
22
+ # # Railtie
26
23
  #
27
24
  # Google::Cloud::ErrorReporting::Railtie automatically add the
28
- # Google::Cloud::ErrorReporting::Middleware to Rack in a Rails
25
+ # {Google::Cloud::ErrorReporting::Middleware} to Rack in a Rails
29
26
  # environment. It will automatically capture Exceptions from the Rails app
30
- # and report them to Stackdriver Error Reporting.
27
+ # and report them to the Stackdriver Error Reporting service.
31
28
  #
32
29
  # The Middleware is only added when certain conditions are met. See
33
30
  # {Railtie.use_error_reporting?} for detail.
34
31
  #
35
- # When loaded, the Google::Cloud::ErrorReporting::Middleware will be
36
- # inserted after ::ActionDispatch::DebugExceptions Middleware, which
37
- # allows it to actually rescue all Exceptions and throw it back up. The
38
- # middleware should also be initialized with correct gcp project_id,
39
- # keyfile, service_name, and service_version if they are defined in
40
- # Rails environment files as follow:
41
- # config.google_cloud.project_id = "my-gcp-project"
42
- # config.google_cloud.keyfile = "/path/to/secret.json"
43
- # or
44
- # config.google_cloud.error_reporting.project_id = "my-gcp-project"
45
- # config.google_cloud.error_reporting.keyfile = "/path/to/secret.json"
46
- # config.google_cloud.error_reporting.service_name = "my-service-name"
47
- # config.google_cloud.error_reporting.service_version = "v1"
32
+ # When loaded, the {Google::Cloud::ErrorReporting::Middleware} will be
33
+ # inserted after ActionDispatch::DebugExceptions or
34
+ # ActionDispatch::ShowExceptions Middleware, which allows it to intercept
35
+ # and handle all Exceptions without interfering with Rails's normal error
36
+ # pages.
37
+ # See the [Configuration
38
+ # Guide](https://googlecloudplatform.github.io/google-cloud-ruby/#/docs/stackdriver/guides/instrumentation_configuration)
39
+ # on how to configure the Railtie and Middleware.
48
40
  #
49
41
  class Railtie < ::Rails::Railtie
50
42
  config.google_cloud = ActiveSupport::OrderedOptions.new unless
@@ -52,134 +44,112 @@ module Google
52
44
  config.google_cloud.error_reporting = ActiveSupport::OrderedOptions.new
53
45
 
54
46
  initializer "Google.Cloud.ErrorReporting" do |app|
55
- if self.class.use_error_reporting? app.config
56
- gcp_config = app.config.google_cloud
57
- er_config = gcp_config.error_reporting
58
-
59
- project_id = er_config.project_id || gcp_config.project_id
60
- keyfile = er_config.keyfile || gcp_config.keyfile
61
-
62
- channel = self.class.grpc_channel keyfile
63
- service_name = er_config.service_name
64
- service_version = er_config.service_version
65
-
66
- error_reporting = V1beta1::ReportErrorsServiceClient.new(
67
- channel: channel
68
- )
69
-
70
- # In later versions of Rails, ActionDispatch::DebugExceptions is
71
- # responsible for catching exceptions. But it didn't exist until
72
- # Rails 3.2. So we use ShowExceptions as pivot for earlier Rails.
73
- rails_exception_middleware =
74
- if defined? ::ActionDispatch::DebugExceptions
75
- ::ActionDispatch::DebugExceptions
76
- else
77
- ::ActionDispatch::ShowExceptions
78
- end
79
-
80
- app.middleware.insert_after rails_exception_middleware,
81
- Middleware,
82
- project_id: project_id,
83
- error_reporting: error_reporting,
84
- service_name: service_name,
85
- service_version: service_version
86
- end
47
+ self.class.consolidate_rails_config app.config
48
+
49
+ self.class.init_middleware app if Cloud.configure.use_error_reporting
87
50
  end
88
51
 
89
52
  ##
90
- # Construct a gRPC Channel object from given keyfile
91
- #
92
- # @param [String, Hash] keyfile Keyfile downloaded from Google Cloud. If
93
- # file path the file must be readable.
94
- #
95
- # @return [GRPC::Core::Channel] gRPC Channel object based on given
96
- # keyfile
97
- #
98
- def self.grpc_channel keyfile = nil
99
- require "grpc"
100
-
101
- scopes = V1beta1::ReportErrorsServiceClient::ALL_SCOPES
102
- credentials = if keyfile.nil?
103
- Google::Cloud::Credentials.default(
104
- scope: scopes)
105
- else
106
- Google::Cloud::Credentials.new(
107
- keyfile, scope: scopes)
108
- end
109
-
110
- # Return nil if :this_channel_is_insecure
111
- return nil if credentials == :this_channel_is_insecure
112
-
113
- channel_cred = GRPC::Core::ChannelCredentials.new.compose \
114
- GRPC::Core::CallCredentials.new credentials.client.updater_proc
115
- host = V1beta1::ReportErrorsServiceClient::SERVICE_ADDRESS
116
-
117
- GRPC::Core::Channel.new host, nil, channel_cred
53
+ # @private Init Error Reporting integration for Rails. Setup
54
+ # configuration and insert the Middleware.
55
+ def self.init_middleware app
56
+ # In later versions of Rails, ActionDispatch::DebugExceptions is
57
+ # responsible for catching exceptions. But it didn't exist until
58
+ # Rails 3.2. So we use ShowExceptions as fallback for earlier Rails.
59
+ rails_exception_middleware =
60
+ if defined? ::ActionDispatch::DebugExceptions
61
+ ::ActionDispatch::DebugExceptions
62
+ else
63
+ ::ActionDispatch::ShowExceptions
64
+ end
65
+
66
+ app.middleware.insert_after rails_exception_middleware,
67
+ Google::Cloud::ErrorReporting::Middleware
118
68
  end
119
69
 
120
70
  ##
121
- # Determine whether to use Stackdriver Error Reporting or not.
122
- #
123
- # Returns true if valid GCP project_id and keyfile are provided and
124
- # either Rails is in "production" environment or \
125
- # config.google_cloud.use_error_reporting is explicitly true. Otherwise
126
- # false.
71
+ # @private Consolidate Rails configuration into Error Reporting
72
+ # instrumentation configuration. Also consolidate the
73
+ # `use_error_reporting` setting by verifying credentials and Rails
74
+ # environment. The `use_error_reporting` setting will be true if
75
+ # credentials are valid, and the setting is manually set to true or
76
+ # Rails is in production environment.
127
77
  #
128
78
  # @param [Rails::Railtie::Configuration] config The
129
79
  # Rails.application.config
130
80
  #
131
- # @return [Boolean] Whether to use Stackdriver Error Reporting
132
- #
133
- def self.use_error_reporting? config
134
- gcp_config = config.google_cloud
81
+ def self.consolidate_rails_config config
82
+ merge_rails_config config
83
+
84
+ init_default_config
85
+
86
+ # Done if Google::Cloud.configure.use_error_reporting is explicitly
87
+ # false
88
+ return if Google::Cloud.configure.use_error_reporting == false
89
+
90
+ # Verify credentials and set use_error_reporting to false if
91
+ # credentials are invalid
92
+ unless valid_credentials? ErrorReporting.configure.project_id,
93
+ ErrorReporting.configure.keyfile
94
+ Cloud.configure.use_error_reporting = false
95
+ return
96
+ end
97
+
98
+ # Otherwise set use_error_reporting to true if Rails is running in
99
+ # production
100
+ Google::Cloud.configure.use_error_reporting ||= Rails.env.production?
101
+ end
102
+
103
+ ##
104
+ # @private Merge Rails configuration into Error Reporting
105
+ # instrumentation configuration.
106
+ def self.merge_rails_config rails_config
107
+ gcp_config = rails_config.google_cloud
135
108
  er_config = gcp_config.error_reporting
136
109
 
137
- # Return false if config.google_cloud.use_error_reporting is
138
- # explicitly false
139
- return false if gcp_config.key?(:use_error_reporting) &&
140
- !gcp_config.use_error_reporting
110
+ Cloud.configure.use_error_reporting ||= gcp_config.use_error_reporting
111
+ ErrorReporting.configure do |config|
112
+ config.project_id ||= er_config.project_id || gcp_config.project_id
113
+ config.keyfile ||= er_config.keyfile || gcp_config.keyfile
114
+ config.service_name ||= er_config.service_name
115
+ config.service_version ||= er_config.service_version
116
+ config.ignore_classes ||= er_config.ignore_classes
117
+ end
118
+ end
141
119
 
142
- # Check credentialing. Returns false if authorization errors are
143
- # rescued.
144
- keyfile = er_config.keyfile || gcp_config.keyfile
120
+ ##
121
+ # Fallback to default config values if config parameters not provided.
122
+ def self.init_default_config
123
+ config = ErrorReporting.configure
124
+ config.project_id ||= ErrorReporting::Project.default_project
125
+ config.service_name ||= ErrorReporting::Project.default_service_name
126
+ config.service_version ||=
127
+ ErrorReporting::Project.default_service_version
128
+ end
129
+
130
+ ##
131
+ # @private Verify credentials
132
+ def self.valid_credentials? project_id, keyfile
145
133
  begin
146
- grpc_channel keyfile
147
- rescue StandardError => e
148
- Rails.logger.warn "Google::Cloud::ErrorReporting is not " \
149
- "activated due to authorization error: #{e.message}"
134
+ ErrorReporting::Credentials.credentials_with_scope keyfile
135
+ rescue => e
136
+ STDOUT.puts "Note: Google::Cloud::ErrorReporting is disabled " \
137
+ "because it failed to authorize with the service. (#{e.message})"
150
138
  return false
151
139
  end
152
140
 
153
- if project_id(config).to_s.empty?
154
- Rails.logger.warn "Google::Cloud::ErrorReporting is not " \
155
- "activated due to empty project_id"
141
+ if project_id.to_s.empty?
142
+ STDOUT.puts "Note: Google::Cloud::ErrorReporting is disabled " \
143
+ "because the project ID could not be determined."
156
144
  return false
157
145
  end
158
146
 
159
- # Otherwise return true if Rails is running in production or
160
- # config.google_cloud.use_error_reporting is explicitly true
161
- Rails.env.production? ||
162
- (gcp_config.key?(:use_error_reporting) &&
163
- gcp_config.use_error_reporting)
147
+ true
164
148
  end
165
149
 
166
- ##
167
- # Determine the GCP project_id from Rails configuration or environment
168
- # variables. Default to Google::Cloud.env.project_id
169
- #
170
- # @param [Rails::Railtie::Configuration] config The
171
- # Rails.application.config
172
- #
173
- # @return [String] GCP project_id
174
- #
175
- def self.project_id config
176
- config.google_cloud.error_reporting.project_id ||
177
- config.google_cloud.project_id ||
178
- ENV["ERROR_REPORTING_PROJECT"] ||
179
- ENV["GOOGLE_CLOUD_PROJECT"] ||
180
- Google::Cloud.env.project_id
181
- end
182
- private_class_method :project_id
150
+ private_class_method :merge_rails_config,
151
+ :init_default_config,
152
+ :valid_credentials?
183
153
  end
184
154
  end
185
155
  end