Class: Fluent::GoogleCloudOutput
- Inherits:
-
BufferedOutput
- Object
- BufferedOutput
- Fluent::GoogleCloudOutput
- Defined in:
- lib/fluent/plugin/out_google_cloud.rb
Defined Under Namespace
Modules: Platform
Constant Summary collapse
- APPENGINE_SERVICE =
Constants for service names.
'appengine.googleapis.com'- COMPUTE_SERVICE =
'compute.googleapis.com'- DATAFLOW_SERVICE =
'dataflow.googleapis.com'- EC2_SERVICE =
'ec2.amazonaws.com'- LOGGING_SCOPE =
Name of the the Google cloud logging write scope.
'https://www.googleapis.com/auth/logging.write'- METADATA_SERVICE_ADDR =
Address of the metadata service.
'169.254.169.254'
Instance Attribute Summary collapse
-
#common_labels ⇒ Object
readonly
Returns the value of attribute common_labels.
-
#gae_backend_name ⇒ Object
readonly
Returns the value of attribute gae_backend_name.
-
#gae_backend_version ⇒ Object
readonly
Returns the value of attribute gae_backend_version.
-
#project_id ⇒ Object
readonly
Expose attr_readers to make testing of metadata more direct than only testing it indirectly through metadata sent with logs.
-
#running_on_managed_vm ⇒ Object
readonly
Returns the value of attribute running_on_managed_vm.
-
#service_name ⇒ Object
readonly
Returns the value of attribute service_name.
-
#vm_id ⇒ Object
readonly
Returns the value of attribute vm_id.
-
#zone ⇒ Object
readonly
Returns the value of attribute zone.
Instance Method Summary collapse
- #configure(conf) ⇒ Object
- #format(tag, time, record) ⇒ Object
-
#initialize ⇒ GoogleCloudOutput
constructor
A new instance of GoogleCloudOutput.
- #shutdown ⇒ Object
- #start ⇒ Object
- #write(chunk) ⇒ Object
Constructor Details
#initialize ⇒ GoogleCloudOutput
Returns a new instance of GoogleCloudOutput.
70 71 72 73 74 75 76 77 78 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 70 def initialize super require 'cgi' require 'google/api_client' require 'google/api_client/auth/compute_service_account' require 'googleauth' require 'json' require 'open-uri' end |
Instance Attribute Details
#common_labels ⇒ Object (readonly)
Returns the value of attribute common_labels.
68 69 70 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 68 def common_labels @common_labels end |
#gae_backend_name ⇒ Object (readonly)
Returns the value of attribute gae_backend_name.
65 66 67 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 65 def gae_backend_name @gae_backend_name end |
#gae_backend_version ⇒ Object (readonly)
Returns the value of attribute gae_backend_version.
66 67 68 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 66 def gae_backend_version @gae_backend_version end |
#project_id ⇒ Object (readonly)
Expose attr_readers to make testing of metadata more direct than only testing it indirectly through metadata sent with logs.
61 62 63 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 61 def project_id @project_id end |
#running_on_managed_vm ⇒ Object (readonly)
Returns the value of attribute running_on_managed_vm.
64 65 66 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 64 def running_on_managed_vm @running_on_managed_vm end |
#service_name ⇒ Object (readonly)
Returns the value of attribute service_name.
67 68 69 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 67 def service_name @service_name end |
#vm_id ⇒ Object (readonly)
Returns the value of attribute vm_id.
63 64 65 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 63 def vm_id @vm_id end |
#zone ⇒ Object (readonly)
Returns the value of attribute zone.
62 63 64 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 62 def zone @zone end |
Instance Method Details
#configure(conf) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 80 def configure(conf) super if !@auth_method.nil? $log.warn ('auth_method is deprecated; please migrate to using ' + 'Application Default Credentials.') if @auth_method == 'private_key' if !@private_key_email raise Fluent::ConfigError, ('"private_key_email" must be ' + 'specified if auth_method is "private_key"') elsif !@private_key_path raise Fluent::ConfigError, ('"private_key_path" must be ' + 'specified if auth_method is "private_key"') elsif !@private_key_passphrase raise Fluent::ConfigError, ('"private_key_passphrase" must be ' + 'specified if auth_method is "private_key"') end end end # TODO: Send instance tags and/or hostname as labels as well? @common_labels = {} # set attributes from metadata (unless overriden by static config) @platform = detect_platform case @platform when Platform::GCE if @project_id.nil? @project_id = ('project/project-id') end if @zone.nil? # this returns "projects/<number>/zones/<zone>"; we only want # the part after the final slash. fully_qualified_zone = ('instance/zone') @zone = fully_qualified_zone.rpartition('/')[2] end if @vm_id.nil? @vm_id = ('instance/id') end when Platform::EC2 = if @zone.nil? && .has_key?('availabilityZone') @zone = 'aws:' + ['availabilityZone'] end if @vm_id.nil? && .has_key?('instanceId') @vm_id = ['instanceId'] end if .has_key?('accountId') common_labels["#{EC2_SERVICE}/account_id"] = ['accountId'] end when Platform::OTHER # do nothing else raise Fluent::ConfigError, 'Unknown platform ' + @platform end # all metadata parameters must now be set unless @project_id && @zone && @vm_id missing = [] missing << "project_id" unless @project_id missing << "zone" unless @zone missing << "vm_id" unless @vm_id raise Fluent::ConfigError, ('Unable to obtain metadata parameters: ' + missing.join(' ')) end # Default this to false; it is only overwritten if we detect Managed VM. @running_on_managed_vm = false # Set labels, etc. based on the config case @platform when Platform::GCE @service_name = COMPUTE_SERVICE # Check for specialized GCE environments (Managed VM or Dataflow). # TODO: Add config options for these to allow for running outside GCE? attributes = ('instance/attributes/').split if (attributes.include?('gae_backend_name') && attributes.include?('gae_backend_version')) # Managed VM @running_on_managed_vm = true @gae_backend_name = ('instance/attributes/gae_backend_name') @gae_backend_version = ('instance/attributes/gae_backend_version') @service_name = APPENGINE_SERVICE common_labels["#{APPENGINE_SERVICE}/module_id"] = @gae_backend_name common_labels["#{APPENGINE_SERVICE}/version_id"] = @gae_backend_version elsif (attributes.include?('job_id')) # Dataflow @service_name = DATAFLOW_SERVICE @dataflow_job_id = ('instance/attributes/job_id') common_labels["#{DATAFLOW_SERVICE}/job_id"] = @dataflow_job_id end # include GCE labels unless we're running on dataflow, which # uses their own labels exclusively. if (@service_name != DATAFLOW_SERVICE) common_labels["#{COMPUTE_SERVICE}/resource_type"] = 'instance' common_labels["#{COMPUTE_SERVICE}/resource_id"] = @vm_id end when Platform::EC2 @service_name = EC2_SERVICE common_labels["#{EC2_SERVICE}/resource_type"] = 'instance' common_labels["#{EC2_SERVICE}/resource_id"] = @vm_id when Platform::OTHER # Use COMPUTE_SERVICE as the default environment. @service_name = COMPUTE_SERVICE common_labels["#{COMPUTE_SERVICE}/resource_type"] = 'instance' common_labels["#{COMPUTE_SERVICE}/resource_id"] = @vm_id end end |
#format(tag, time, record) ⇒ Object
205 206 207 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 205 def format(tag, time, record) [tag, time, record].to_msgpack end |
#shutdown ⇒ Object
201 202 203 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 201 def shutdown super end |
#start ⇒ Object
192 193 194 195 196 197 198 199 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 192 def start super init_api_client() @successful_call = false @timenanos_warning = false end |
#write(chunk) ⇒ Object
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/fluent/plugin/out_google_cloud.rb', line 209 def write(chunk) # Group the entries since we have to make one call per tag. grouped_entries = {} chunk.msgpack_each do |tag, *arr| if !grouped_entries.has_key?(tag) grouped_entries[tag] = [] end grouped_entries[tag].push(arr) end grouped_entries.each do |tag, arr| write_log_entries_request = { 'commonLabels' => @common_labels, 'entries' => [], } arr.each do |time, record| next unless record.is_a? Hash if (record.has_key?('timestamp') && record['timestamp'].has_key?('seconds') && record['timestamp'].has_key?('nanos')) ts_secs = record['timestamp']['seconds'] ts_nanos = record['timestamp']['nanos'] record.delete('timestamp') elsif (record.has_key?('timestampSeconds') && record.has_key?('timestampNanos')) ts_secs = record['timestampSeconds'] ts_nanos = record['timestampNanos'] record.delete('timestampSeconds') record.delete('timestampNanos') elsif (record.has_key?('timeNanos')) # This is deprecated since the precision is insufficient. # Use timestampSeconds/timestampNanos instead ts_secs = (record['timeNanos'] / 1000000000).to_i ts_nanos = record['timeNanos'] % 1000000000 record.delete('timeNanos') if (!@timenanos_warning) # Warn the user this is deprecated, but only once to avoid spam. @timenanos_warning = true $log.warn ("timeNanos is deprecated - please use " + "timestampSeconds and timestampNanos instead.") end else = Time.at(time) ts_secs = .tv_sec ts_nanos = .tv_nsec end entry = { 'metadata' => { 'serviceName' => @service_name, 'projectId' => @project_id, 'zone' => @zone, 'timestamp' => { 'seconds' => ts_secs, 'nanos' => ts_nanos }, }, } if record.has_key?('severity') entry['metadata']['severity'] = parse_severity(record['severity']) record.delete('severity') else entry['metadata']['severity'] = 'DEFAULT' end # use textPayload if the only remainaing key is 'message', # otherwise use a struct. if (record.size == 1 && record.has_key?('message')) entry['textPayload'] = record['message'] else entry['structPayload'] = record end write_log_entries_request['entries'].push(entry) end # Don't send an empty request if we rejected all the entries. next if write_log_entries_request['entries'].empty? # Add a prefix to VMEngines logs to prevent namespace collisions, # and also escape the log name. log_name = CGI::escape(@running_on_managed_vm ? "#{APPENGINE_SERVICE}/#{tag}" : tag) url = ('https://logging.googleapis.com/v1beta3/projects/' + "#{@project_id}/logs/#{log_name}/entries:write") begin client = api_client() request = client.generate_request({ :uri => url, :body_object => write_log_entries_request, :http_method => 'POST', :authenticated => true }) client.execute!(request) # Let the user explicitly know when the first call succeeded, # to aid with verification and troubleshooting. if (!@successful_call) @successful_call = true $log.info "Successfully sent to Google Cloud Logging API." end # Allow most exceptions to propagate, which will cause fluentd to # retry (with backoff), but in some cases we catch the error and # drop the request (we will emit a log message in those cases). rescue Google::APIClient::ClientError => error # Most ClientErrors indicate a problem with the request itself and # should not be retried, unless it is an authentication issue, in # which case we will retry the request via re-raising the exception. if (is_retriable_client_error(error)) raise error end log_write_failure(write_log_entries_request, error) rescue JSON::GeneratorError => error # This happens if the request contains illegal characters; # do not retry it because it will fail repeatedly. log_write_failure(write_log_entries_request, error) end end end |