fluent-plugin-google-cloud 0.6.23 → 0.6.24
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.
- checksums.yaml +4 -4
- data/README.rdoc +24 -15
- data/fluent-plugin-google-cloud.gemspec +6 -6
- data/lib/fluent/plugin/filter_add_insert_ids.rb +103 -0
- data/lib/fluent/plugin/out_google_cloud.rb +33 -14
- data/test/plugin/base_test.rb +94 -3
- data/test/plugin/constants.rb +12 -0
- data/test/plugin/test_filter_add_insert_ids.rb +135 -0
- data/test/plugin/test_out_google_cloud.rb +15 -0
- data/test/plugin/test_out_google_cloud_grpc.rb +20 -0
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9d4e0c7112b19f2fdf43ac7bad7c2d030932f63
|
4
|
+
data.tar.gz: ea83fcd3888666a51aae9c4a03ea0ffb0bdba527
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b179f5f31c11216c3e04b5d071cb244700f8352ed11fe1fa00fcf3282e5016e52a02e1880859d92a445d641e9bc1280a861a19896e5166ee493ad9b81569b2a
|
7
|
+
data.tar.gz: 3e9e799eb94b3e35156ce44cb8511f942082ac2ebc6bde9d21590543b421148f029f24f499c5373c7ca60a57054e066bbce3c9b19e8c312fa562f902420edf1d
|
data/README.rdoc
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
= Google Cloud Logging plugin for {fluentd}[http://github.com/fluent/fluentd]
|
2
2
|
|
3
|
-
fluent-plugin-google-cloud
|
4
|
-
{
|
5
|
-
|
6
|
-
{
|
3
|
+
fluent-plugin-google-cloud gem includes two plugins:
|
4
|
+
1. A {filter plugin for fluentd}[http://docs.fluentd.org/articles/filter-plugin-overview]
|
5
|
+
that embeds insertIds into log entries to guarantee order and uniqueness.
|
6
|
+
2. An {output plugin for fluentd}[http://docs.fluentd.org/articles/output-plugin-overview]
|
7
|
+
which sends logs to the {Stackdriver Logging API}[https://cloud.google.com/logging/docs/api/].
|
7
8
|
|
8
9
|
This is an official Google Ruby gem.
|
9
10
|
|
@@ -12,8 +13,7 @@ This is an official Google Ruby gem.
|
|
12
13
|
|
13
14
|
== Installation
|
14
15
|
|
15
|
-
This gem is hosted at
|
16
|
-
{RubyGems.org}[https://rubygems.org/gems/fluent-plugin-google-cloud]
|
16
|
+
This gem is hosted at {RubyGems.org}[https://rubygems.org/gems/fluent-plugin-google-cloud]
|
17
17
|
and can be installed using:
|
18
18
|
|
19
19
|
$ gem install fluent-plugin-google-cloud
|
@@ -23,22 +23,31 @@ will also install and configure the gem.
|
|
23
23
|
|
24
24
|
== Configuration
|
25
25
|
|
26
|
-
To
|
27
|
-
in a
|
28
|
-
|
29
|
-
|
26
|
+
To embed insertIds into log entries, specify <code>@type add_insert_ids</code>
|
27
|
+
in a {filter clause}[https://docs.fluentd.org/v1.0/articles/config-file#(3)-%E2%80%9Cfilter%E2%80%9D:-event-processing-pipeline]
|
28
|
+
of your Fluentd configuration file, for example:
|
29
|
+
|
30
|
+
<filter **>
|
31
|
+
@type add_insert_ids
|
32
|
+
insert_id_key my_insert_id_field_name # Optional.
|
33
|
+
</filter>
|
34
|
+
|
35
|
+
insert_id_key can be used to customize the insertId field name.
|
36
|
+
|
37
|
+
To send logs to Google Cloud Logging, specify <code>@type google_cloud</code>
|
38
|
+
in a {match clause}[http://docs.fluentd.org/articles/config-file#2-ldquomatchrdquo-tell-fluentd-what-to-do]
|
39
|
+
of your Fluentd configuration file, for example:
|
30
40
|
|
31
41
|
<match **>
|
32
|
-
type google_cloud
|
42
|
+
@type google_cloud
|
33
43
|
</match>
|
34
44
|
|
35
|
-
|
45
|
+
See detailed instructions on how to configure this output plugin {here}[https://cloud.google.com/logging/docs/agent/configuration#cloud-fluentd-config].
|
46
|
+
The plugin uses
|
36
47
|
{Google Application Default Credentials}[https://developers.google.com/identity/protocols/application-default-credentials]
|
37
|
-
for authorization - for additional information see
|
48
|
+
for authorization - for additional information see
|
38
49
|
{here}[https://cloud.google.com/logging/docs/agent/authorization].
|
39
50
|
|
40
|
-
<em>The previously documented parameters auth_method, private_key_email,
|
41
|
-
and private_key_path are removed, and can no longer be used.</em>
|
42
51
|
|
43
52
|
== Copyright
|
44
53
|
|
@@ -1,17 +1,17 @@
|
|
1
1
|
Gem::Specification.new do |gem|
|
2
2
|
gem.name = 'fluent-plugin-google-cloud'
|
3
3
|
gem.description = <<-eos
|
4
|
-
Fluentd
|
5
|
-
|
6
|
-
|
4
|
+
Fluentd plugins for the Stackdriver Logging API, which will make logs
|
5
|
+
viewable in the Stackdriver Logs Viewer and can optionally store them
|
6
|
+
in Google Cloud Storage and/or BigQuery.
|
7
7
|
This is an official Google Ruby gem.
|
8
8
|
eos
|
9
|
-
gem.summary = 'fluentd
|
9
|
+
gem.summary = 'fluentd plugins for the Stackdriver Logging API'
|
10
10
|
gem.homepage =
|
11
11
|
'https://github.com/GoogleCloudPlatform/fluent-plugin-google-cloud'
|
12
12
|
gem.license = 'Apache-2.0'
|
13
|
-
gem.version = '0.6.
|
14
|
-
gem.authors = ['
|
13
|
+
gem.version = '0.6.24'
|
14
|
+
gem.authors = ['Stackdriver Agents Team']
|
15
15
|
gem.email = ['stackdriver-agents@google.com']
|
16
16
|
gem.required_ruby_version = Gem::Requirement.new('>= 2.2')
|
17
17
|
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# Copyright 2018 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 'fluent/plugin/filter'
|
16
|
+
|
17
|
+
module Fluent
|
18
|
+
module Plugin
|
19
|
+
# Fluentd filter plugin for adding insertIds to guarantee log entry order
|
20
|
+
# and uniqueness.
|
21
|
+
# Sample log entries enriched by this plugin:
|
22
|
+
# {
|
23
|
+
# "timestamp": "2017-08-22 13:35:28",
|
24
|
+
# "message": "1",
|
25
|
+
# "logging.googleapis.com/insertId": "aye7eakuf23h41aef0"
|
26
|
+
# }
|
27
|
+
# {
|
28
|
+
# "timestamp": "2017-08-22 13:35:28",
|
29
|
+
# "message": "2",
|
30
|
+
# "logging.googleapis.com/insertId": "aye7eakuf23h41aef1"
|
31
|
+
# }
|
32
|
+
# {
|
33
|
+
# "timestamp": "2017-08-22 13:35:28",
|
34
|
+
# "message": "3",
|
35
|
+
# "logging.googleapis.com/insertId": "aye7eakuf23h41aef2"
|
36
|
+
# }
|
37
|
+
class AddInsertIdsFilter < Filter
|
38
|
+
Fluent::Plugin.register_filter('add_insert_ids', self)
|
39
|
+
|
40
|
+
# Constants for configuration.
|
41
|
+
module ConfigConstants
|
42
|
+
# The default field name of insertIds in the log entry.
|
43
|
+
DEFAULT_INSERT_ID_KEY = 'logging.googleapis.com/insertId'.freeze
|
44
|
+
# The character size of the insertIds. This matches the setup in the
|
45
|
+
# Stackdriver Logging backend.
|
46
|
+
INSERT_ID_SIZE = 17
|
47
|
+
# The characters that are allowed in the insertIds. This matches the
|
48
|
+
# allowed collection by the Stackdriver Logging Backend.
|
49
|
+
ALLOWED_CHARS = (Array(0..9) + Array('a'..'z')).freeze
|
50
|
+
end
|
51
|
+
|
52
|
+
include self::ConfigConstants
|
53
|
+
|
54
|
+
desc 'The field name for insertIds in the log record.'
|
55
|
+
config_param :insert_id_key, :string, default: DEFAULT_INSERT_ID_KEY
|
56
|
+
|
57
|
+
# Expose attr_readers for testing.
|
58
|
+
attr_reader :insert_id_key
|
59
|
+
|
60
|
+
def start
|
61
|
+
super
|
62
|
+
@log = $log # rubocop:disable Style/GlobalVars
|
63
|
+
|
64
|
+
# Initialize the insertID.
|
65
|
+
@log.info "Started the add_insert_ids plugin with #{@insert_id_key}" \
|
66
|
+
' as the insert ID key.'
|
67
|
+
@insert_id = generate_initial_insert_id
|
68
|
+
@log.info "Initialized the insert ID key to #{@insert_id}."
|
69
|
+
end
|
70
|
+
|
71
|
+
def configure(conf)
|
72
|
+
super
|
73
|
+
end
|
74
|
+
|
75
|
+
def shutdown
|
76
|
+
super
|
77
|
+
end
|
78
|
+
|
79
|
+
# rubocop:disable Style/UnusedMethodArgument
|
80
|
+
def filter(tag, time, record)
|
81
|
+
# Only generate and add an insertId field if the record is a hash and
|
82
|
+
# the insert ID field is not already set (or set to an empty string).
|
83
|
+
if record.is_a?(Hash) && record[@insert_id_key].to_s.empty?
|
84
|
+
record[@insert_id_key] = increment_insert_id
|
85
|
+
end
|
86
|
+
record
|
87
|
+
end
|
88
|
+
# rubocop:enable Style/UnusedMethodArgument
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Generate a random string as the initial insertId.
|
93
|
+
def generate_initial_insert_id
|
94
|
+
Array.new(INSERT_ID_SIZE) { ALLOWED_CHARS.sample }.join
|
95
|
+
end
|
96
|
+
|
97
|
+
# Increment the insertId and return the new value.
|
98
|
+
def increment_insert_id
|
99
|
+
@insert_id = @insert_id.next
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -148,6 +148,7 @@ module Fluent
|
|
148
148
|
'logging.googleapis.com/sourceLocation'.freeze
|
149
149
|
DEFAULT_TRACE_KEY = 'logging.googleapis.com/trace'.freeze
|
150
150
|
DEFAULT_SPAN_ID_KEY = 'logging.googleapis.com/spanId'.freeze
|
151
|
+
DEFAULT_INSERT_ID_KEY = 'logging.googleapis.com/insertId'.freeze
|
151
152
|
|
152
153
|
DEFAULT_METADATA_AGENT_URL =
|
153
154
|
'http://local-metadata-agent.stackdriver.com:8000'.freeze
|
@@ -227,7 +228,16 @@ module Fluent
|
|
227
228
|
Fluent::Plugin.register_output('google_cloud', self)
|
228
229
|
|
229
230
|
PLUGIN_NAME = 'Fluentd Google Cloud Logging plugin'.freeze
|
230
|
-
|
231
|
+
# Extract plugin version by finding the spec this file was loaded from.
|
232
|
+
PLUGIN_VERSION = begin
|
233
|
+
dependency = Gem::Dependency.new('fluent-plugin-google-cloud')
|
234
|
+
all_specs, = Gem::SpecFetcher.fetcher.spec_for_dependency(dependency)
|
235
|
+
matching_spec, = all_specs.grep(
|
236
|
+
proc { |spec,| __FILE__.include?(spec.full_gem_path) }) do |spec,|
|
237
|
+
spec.version.to_s
|
238
|
+
end
|
239
|
+
matching_spec
|
240
|
+
end.freeze
|
231
241
|
|
232
242
|
# Name of the the Google cloud logging write scope.
|
233
243
|
LOGGING_SCOPE = 'https://www.googleapis.com/auth/logging.write'.freeze
|
@@ -273,6 +283,7 @@ module Fluent
|
|
273
283
|
DEFAULT_SOURCE_LOCATION_KEY
|
274
284
|
config_param :trace_key, :string, :default => DEFAULT_TRACE_KEY
|
275
285
|
config_param :span_id_key, :string, :default => DEFAULT_SPAN_ID_KEY
|
286
|
+
config_param :insert_id_key, :string, :default => DEFAULT_INSERT_ID_KEY
|
276
287
|
|
277
288
|
# Whether to try to detect if the record is a text log entry with JSON
|
278
289
|
# content that needs to be parsed.
|
@@ -573,16 +584,17 @@ module Fluent
|
|
573
584
|
|
574
585
|
is_json = false
|
575
586
|
if @detect_json
|
576
|
-
# Save the
|
587
|
+
# Save the following fields if available, then clear them out to
|
577
588
|
# allow for determining whether we should parse the log or message
|
578
589
|
# field.
|
579
|
-
|
580
|
-
|
590
|
+
preserved_keys = [
|
591
|
+
'time', 'severity', @trace_key, @span_id_key, @insert_id_key
|
592
|
+
]
|
581
593
|
|
582
594
|
# If the log is json, we want to export it as a structured log
|
583
595
|
# unless there is additional metadata that would be lost.
|
584
596
|
record_json = nil
|
585
|
-
if record.length == 1
|
597
|
+
if (record.keys - preserved_keys).length == 1
|
586
598
|
%w(log message msg).each do |field|
|
587
599
|
if record.key?(field)
|
588
600
|
record_json = parse_json_or_nil(record[field])
|
@@ -590,13 +602,15 @@ module Fluent
|
|
590
602
|
end
|
591
603
|
end
|
592
604
|
unless record_json.nil?
|
605
|
+
# Propagate these if necessary. Note that we don't want to
|
606
|
+
# override these keys in the JSON we've just parsed.
|
607
|
+
preserved_keys.each do |key|
|
608
|
+
record_json[key] ||= record[key] if record.key?(key)
|
609
|
+
end
|
610
|
+
|
593
611
|
record = record_json
|
594
612
|
is_json = true
|
595
613
|
end
|
596
|
-
# Restore timestamp and severity if necessary. Note that we don't
|
597
|
-
# want to override these keys in the JSON we've just parsed.
|
598
|
-
record['time'] ||= timestamp if timestamp
|
599
|
-
record['severity'] ||= severity if severity
|
600
614
|
end
|
601
615
|
|
602
616
|
ts_secs, ts_nanos = compute_timestamp(
|
@@ -610,12 +624,12 @@ module Fluent
|
|
610
624
|
ts_secs,
|
611
625
|
ts_nanos)
|
612
626
|
|
613
|
-
|
614
|
-
|
615
|
-
entry.trace = fq_trace_id if fq_trace_id
|
616
|
-
|
627
|
+
trace = record.delete(@trace_key)
|
628
|
+
entry.trace = trace if trace
|
617
629
|
span_id = record.delete(@span_id_key)
|
618
630
|
entry.span_id = span_id if span_id
|
631
|
+
insert_id = record.delete(@insert_id_key)
|
632
|
+
entry.insert_id = insert_id if insert_id
|
619
633
|
|
620
634
|
set_log_entry_fields(record, entry)
|
621
635
|
set_payload(entry_level_resource.type, record, entry, is_json)
|
@@ -1983,8 +1997,13 @@ module Fluent
|
|
1983
1997
|
creds = :this_channel_is_insecure
|
1984
1998
|
end
|
1985
1999
|
port = ":#{uri.port}" if uri.port
|
2000
|
+
user_agent = \
|
2001
|
+
"#{PLUGIN_NAME}/#{PLUGIN_VERSION} grpc-ruby/#{GRPC::VERSION} " \
|
2002
|
+
"#{Google::Apis::OS_VERSION}"
|
1986
2003
|
@client = Google::Cloud::Logging::V2::LoggingServiceV2Client.new(
|
1987
|
-
credentials: GRPC::Core::Channel.new(
|
2004
|
+
credentials: GRPC::Core::Channel.new(
|
2005
|
+
"#{host}#{port}", { 'grpc.primary_user_agent' => user_agent },
|
2006
|
+
creds))
|
1988
2007
|
else
|
1989
2008
|
# TODO: Use a non-default ClientOptions object.
|
1990
2009
|
Google::Apis::ClientOptions.default.application_name = PLUGIN_NAME
|
data/test/plugin/base_test.rb
CHANGED
@@ -1221,13 +1221,32 @@ module BaseTest
|
|
1221
1221
|
|
1222
1222
|
def test_log_entry_trace_field
|
1223
1223
|
verify_field_key('trace', DEFAULT_TRACE_KEY, 'custom_trace_key',
|
1224
|
-
CONFIG_CUSTOM_TRACE_KEY_SPECIFIED,
|
1225
|
-
'projects/proj1/traces/1234567890abcdef1234567890abcdef')
|
1224
|
+
CONFIG_CUSTOM_TRACE_KEY_SPECIFIED, TRACE)
|
1226
1225
|
end
|
1227
1226
|
|
1228
1227
|
def test_log_entry_span_id_field
|
1229
1228
|
verify_field_key('spanId', DEFAULT_SPAN_ID_KEY, 'custom_span_id_key',
|
1230
|
-
CONFIG_CUSTOM_SPAN_ID_KEY_SPECIFIED,
|
1229
|
+
CONFIG_CUSTOM_SPAN_ID_KEY_SPECIFIED, SPAN_ID)
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
def test_log_entry_insert_id_field
|
1233
|
+
verify_field_key('insertId', DEFAULT_INSERT_ID_KEY, 'custom_insert_id_key',
|
1234
|
+
CONFIG_CUSTOM_INSERT_ID_KEY_SPECIFIED, INSERT_ID)
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
def test_cascading_json_detection_with_log_entry_trace_field
|
1238
|
+
verify_cascading_json_detection_with_log_entry_fields(
|
1239
|
+
'trace', DEFAULT_TRACE_KEY, TRACE, TRACE2)
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
def test_cascading_json_detection_with_log_entry_span_id_field
|
1243
|
+
verify_cascading_json_detection_with_log_entry_fields(
|
1244
|
+
'spanId', DEFAULT_SPAN_ID_KEY, SPAN_ID, SPAN_ID2)
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
def test_cascading_json_detection_with_log_entry_insert_id_field
|
1248
|
+
verify_cascading_json_detection_with_log_entry_fields(
|
1249
|
+
'insertId', DEFAULT_INSERT_ID_KEY, INSERT_ID, INSERT_ID2)
|
1231
1250
|
end
|
1232
1251
|
|
1233
1252
|
# Metadata Agent related tests.
|
@@ -1855,6 +1874,13 @@ module BaseTest
|
|
1855
1874
|
}
|
1856
1875
|
end
|
1857
1876
|
|
1877
|
+
def structured_log_entry
|
1878
|
+
{
|
1879
|
+
'name' => 'test name',
|
1880
|
+
'code' => 'test code'
|
1881
|
+
}
|
1882
|
+
end
|
1883
|
+
|
1858
1884
|
def log_entry(i)
|
1859
1885
|
"test log entry #{i}"
|
1860
1886
|
end
|
@@ -2004,6 +2030,71 @@ module BaseTest
|
|
2004
2030
|
end
|
2005
2031
|
end
|
2006
2032
|
|
2033
|
+
# Cascading JSON detection is only triggered when the record has one field
|
2034
|
+
# left with name "log", "message" or "msg". This test verifies additional
|
2035
|
+
# LogEntry fields like spanId and traceId do not disable that by accident.
|
2036
|
+
def verify_cascading_json_detection_with_log_entry_fields(
|
2037
|
+
log_entry_field, default_key, root_level_value, nested_level_value)
|
2038
|
+
setup_gce_metadata_stubs
|
2039
|
+
|
2040
|
+
# {
|
2041
|
+
# "logging.googleapis.com/XXX' => 'sample value'
|
2042
|
+
# "msg": {
|
2043
|
+
# "name": "test name",
|
2044
|
+
# "code": "test code"
|
2045
|
+
# }
|
2046
|
+
# }
|
2047
|
+
log_entry_with_root_level_field = {
|
2048
|
+
default_key => root_level_value,
|
2049
|
+
'msg' => structured_log_entry.to_json
|
2050
|
+
}
|
2051
|
+
# {
|
2052
|
+
# "msg": {
|
2053
|
+
# "logging.googleapis.com/XXX' => 'another value',
|
2054
|
+
# "name": "test name",
|
2055
|
+
# "code": "test code"
|
2056
|
+
# }
|
2057
|
+
# }
|
2058
|
+
log_entry_with_nested_level_field = {
|
2059
|
+
'msg' => {
|
2060
|
+
default_key => nested_level_value
|
2061
|
+
}.merge(structured_log_entry).to_json
|
2062
|
+
}
|
2063
|
+
# {
|
2064
|
+
# "logging.googleapis.com/XXX' => 'sample value'
|
2065
|
+
# "msg": {
|
2066
|
+
# "logging.googleapis.com/XXX' => 'another value',
|
2067
|
+
# "name": "test name",
|
2068
|
+
# "code": "test code"
|
2069
|
+
# }
|
2070
|
+
# }
|
2071
|
+
log_entry_with_both_level_fields = log_entry_with_nested_level_field.merge(
|
2072
|
+
default_key => root_level_value)
|
2073
|
+
|
2074
|
+
{
|
2075
|
+
log_entry_with_root_level_field => root_level_value,
|
2076
|
+
log_entry_with_nested_level_field => nested_level_value,
|
2077
|
+
log_entry_with_both_level_fields => nested_level_value
|
2078
|
+
}.each_with_index do |(input_log_entry, expected_value), index|
|
2079
|
+
setup_logging_stubs do
|
2080
|
+
@logs_sent = []
|
2081
|
+
d = create_driver(DETECT_JSON_CONFIG)
|
2082
|
+
d.emit(input_log_entry)
|
2083
|
+
d.run
|
2084
|
+
end
|
2085
|
+
verify_log_entries(1, COMPUTE_PARAMS, 'jsonPayload') do |entry|
|
2086
|
+
assert_equal expected_value, entry[log_entry_field],
|
2087
|
+
"Index #{index} failed. #{expected_value} is expected" \
|
2088
|
+
" for #{log_entry_field} field."
|
2089
|
+
payload_fields = get_fields(entry['jsonPayload'])
|
2090
|
+
assert_equal structured_log_entry.size, payload_fields.size
|
2091
|
+
payload_fields.each do |key, value|
|
2092
|
+
assert_equal structured_log_entry[key], get_string(value)
|
2093
|
+
end
|
2094
|
+
end
|
2095
|
+
end
|
2096
|
+
end
|
2097
|
+
|
2007
2098
|
def verify_field_key(log_entry_field, default_key, custom_key,
|
2008
2099
|
custom_key_config, sample_value)
|
2009
2100
|
setup_gce_metadata_stubs
|
data/test/plugin/constants.rb
CHANGED
@@ -98,6 +98,14 @@ module Constants
|
|
98
98
|
MANAGED_VM_BACKEND_NAME = 'default'.freeze
|
99
99
|
MANAGED_VM_BACKEND_VERSION = 'guestbook2.0'.freeze
|
100
100
|
|
101
|
+
# LogEntry fields for extraction.
|
102
|
+
TRACE = 'projects/proj1/traces/1234567890abcdef1234567890abcdef'.freeze
|
103
|
+
TRACE2 = 'projects/proj1/traces/1234567890abcdef1234567890fedcba'.freeze
|
104
|
+
SPAN_ID = '000000000000004a'.freeze
|
105
|
+
SPAN_ID2 = '000000000000007e'.freeze
|
106
|
+
INSERT_ID = 'fah7yr7iw64tg857y'.freeze
|
107
|
+
INSERT_ID2 = 'fah7yr7iw64tgaeuf'.freeze
|
108
|
+
|
101
109
|
# Docker Container labels.
|
102
110
|
DOCKER_CONTAINER_ID =
|
103
111
|
'0d0f03ff8d3c42688692536d1af77a28cd135c0a5c531f25a31'.freeze
|
@@ -348,6 +356,10 @@ module Constants
|
|
348
356
|
span_id_key custom_span_id_key
|
349
357
|
).freeze
|
350
358
|
|
359
|
+
CONFIG_CUSTOM_INSERT_ID_KEY_SPECIFIED = %(
|
360
|
+
insert_id_key custom_insert_id_key
|
361
|
+
).freeze
|
362
|
+
|
351
363
|
# Service configurations for various services.
|
352
364
|
|
353
365
|
# GCE.
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# Copyright 2018 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_relative '../helper'
|
16
|
+
|
17
|
+
require 'fluent/test/driver/filter'
|
18
|
+
require 'fluent/plugin/filter_add_insert_ids'
|
19
|
+
|
20
|
+
# Unit tests for filter_add_insert_ids plugin.
|
21
|
+
class FilterAddInsertIdsTest < Test::Unit::TestCase
|
22
|
+
include Fluent::Plugin::AddInsertIdsFilter::ConfigConstants
|
23
|
+
|
24
|
+
CUSTOM_INSERT_ID_KEY = 'custom_insert_id_key'.freeze
|
25
|
+
INSERT_ID = 'aeyr82r92h249gh9h'.freeze
|
26
|
+
TEST_MESSAGE = 'test message for add_insert_ids plugin.'.freeze
|
27
|
+
APPLICATION_DEFAULT_CONFIG = ''.freeze
|
28
|
+
INSERT_ID_KEY_CONFIG = %(
|
29
|
+
insert_id_key #{CUSTOM_INSERT_ID_KEY}
|
30
|
+
).freeze
|
31
|
+
|
32
|
+
def setup
|
33
|
+
Fluent::Test.setup
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_configure_insert_id_key
|
37
|
+
{
|
38
|
+
APPLICATION_DEFAULT_CONFIG => DEFAULT_INSERT_ID_KEY,
|
39
|
+
INSERT_ID_KEY_CONFIG => CUSTOM_INSERT_ID_KEY
|
40
|
+
}.each do |config, insert_id_key|
|
41
|
+
d = create_driver(config)
|
42
|
+
assert_equal insert_id_key, d.instance.insert_id_key
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_add_insert_ids
|
47
|
+
total_entry_count = 1000
|
48
|
+
d = create_driver
|
49
|
+
d.run do
|
50
|
+
total_entry_count.times do |index|
|
51
|
+
d.emit(log_entry(index))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
filtered_events = d.filtered_as_array
|
55
|
+
|
56
|
+
assert_equal total_entry_count, filtered_events.size,
|
57
|
+
"#{total_entry_count} log entries after filtering is" \
|
58
|
+
" expected. Only #{filtered_events.size} are detected."
|
59
|
+
# The expected insertId will be assigned as we scan the first log entry.
|
60
|
+
expected_insert_id = nil
|
61
|
+
unique_insert_ids = Set.new
|
62
|
+
filtered_events.each_with_index do |event, index|
|
63
|
+
assert_equal 3, event.size, "Index #{index} failed. Log event should" \
|
64
|
+
' include 3 elements: tag, time and record.'
|
65
|
+
record = event[2]
|
66
|
+
assert_true record.is_a?(Hash), "Index #{index} failed. Log record" \
|
67
|
+
" #{record} should be a hash."
|
68
|
+
assert_equal index, record['id'], "Index #{index} failed. Log entries" \
|
69
|
+
' should come in order.'
|
70
|
+
assert_equal TEST_MESSAGE, record['message'], "Index #{index} failed."
|
71
|
+
|
72
|
+
# Get the first insertID.
|
73
|
+
expected_insert_id = record[DEFAULT_INSERT_ID_KEY] if index == 0
|
74
|
+
insert_id = record[DEFAULT_INSERT_ID_KEY]
|
75
|
+
assert_equal expected_insert_id, insert_id, "Index #{index} failed."
|
76
|
+
expected_insert_id = expected_insert_id.next
|
77
|
+
assert_true insert_id < expected_insert_id,
|
78
|
+
"Index #{index} failed. #{insert_id}" \
|
79
|
+
" < #{expected_insert_id} is false."
|
80
|
+
unique_insert_ids << insert_id
|
81
|
+
end
|
82
|
+
assert_equal total_entry_count, unique_insert_ids.size,
|
83
|
+
"Expected #{total_entry_count} unique insertIds." \
|
84
|
+
" Only #{unique_insert_ids.size} found."
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_insert_ids_not_added_if_present
|
88
|
+
log_entry_with_empty_insert_id = log_entry(0).merge(
|
89
|
+
DEFAULT_INSERT_ID_KEY => '')
|
90
|
+
{
|
91
|
+
log_entry(0).merge(DEFAULT_INSERT_ID_KEY => INSERT_ID) => true,
|
92
|
+
# Still generate insertId if it's an empty string
|
93
|
+
log_entry_with_empty_insert_id => false
|
94
|
+
}.each do |test_data|
|
95
|
+
input_log_entry, retain_original_insert_id = test_data
|
96
|
+
# Make a copy because the log entry gets modified by the filter plugin.
|
97
|
+
log_entry = input_log_entry.dup
|
98
|
+
d = create_driver
|
99
|
+
d.run do
|
100
|
+
d.emit(log_entry)
|
101
|
+
end
|
102
|
+
filtered_events = d.filtered_as_array
|
103
|
+
|
104
|
+
assert_equal 1, filtered_events.size, 'Exact 1 log entry after' \
|
105
|
+
" filtering is expected. Test data: #{test_data}."
|
106
|
+
event = filtered_events[0]
|
107
|
+
assert_equal 3, event.size, 'Log event should include 3 elements: tag,' \
|
108
|
+
" time and record. Test data: #{test_data}."
|
109
|
+
record = event[2]
|
110
|
+
assert_true record.is_a?(Hash), "Log record #{record} should be a hash." \
|
111
|
+
" Test data: #{test_data}."
|
112
|
+
assert_equal 0, record['id'], "Test data: #{test_data}."
|
113
|
+
assert_equal TEST_MESSAGE, record['message'], "Test data: #{test_data}."
|
114
|
+
insert_id = record[DEFAULT_INSERT_ID_KEY]
|
115
|
+
assert_false insert_id.to_s.empty?, 'Insert ID should not be empty.' \
|
116
|
+
" Test data: #{test_data}."
|
117
|
+
assert_equal retain_original_insert_id,
|
118
|
+
input_log_entry[DEFAULT_INSERT_ID_KEY] == insert_id,
|
119
|
+
"Input value is #{input_log_entry[DEFAULT_INSERT_ID_KEY]}." \
|
120
|
+
" Output value is #{insert_id}. Test data: #{test_data}."
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def create_driver(conf = APPLICATION_DEFAULT_CONFIG)
|
125
|
+
Fluent::Test::FilterTestDriver.new(
|
126
|
+
Fluent::Plugin::AddInsertIdsFilter).configure(conf, true)
|
127
|
+
end
|
128
|
+
|
129
|
+
def log_entry(index)
|
130
|
+
{
|
131
|
+
'id' => index,
|
132
|
+
'message' => TEST_MESSAGE
|
133
|
+
}
|
134
|
+
end
|
135
|
+
end
|
@@ -25,6 +25,21 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
|
|
25
25
|
assert_false d.instance.instance_variable_get(:@use_grpc)
|
26
26
|
end
|
27
27
|
|
28
|
+
def test_user_agent
|
29
|
+
setup_gce_metadata_stubs
|
30
|
+
user_agent = nil
|
31
|
+
stub_request(:post, WRITE_LOG_ENTRIES_URI).to_return do |request|
|
32
|
+
user_agent = request.headers['User-Agent']
|
33
|
+
{ body: '' }
|
34
|
+
end
|
35
|
+
d = create_driver
|
36
|
+
d.emit('message' => log_entry(0))
|
37
|
+
d.run
|
38
|
+
assert_match Regexp.new("#{Fluent::GoogleCloudOutput::PLUGIN_NAME}/" \
|
39
|
+
"#{Fluent::GoogleCloudOutput::PLUGIN_VERSION}"), \
|
40
|
+
user_agent
|
41
|
+
end
|
42
|
+
|
28
43
|
def test_client_400
|
29
44
|
setup_gce_metadata_stubs
|
30
45
|
# The API Client should not retry this and the plugin should consume
|
@@ -27,6 +27,26 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
|
|
27
27
|
assert_true d.instance.instance_variable_get(:@use_grpc)
|
28
28
|
end
|
29
29
|
|
30
|
+
def test_user_agent
|
31
|
+
setup_gce_metadata_stubs
|
32
|
+
|
33
|
+
user_agent = nil
|
34
|
+
# Record user agent when creating a GRPC::Core::Channel.
|
35
|
+
GRPC::Core::Channel.class_eval do
|
36
|
+
old_initialize = instance_method(:initialize)
|
37
|
+
define_method(:initialize) do |url, args, creds|
|
38
|
+
user_agent = args['grpc.primary_user_agent']
|
39
|
+
old_initialize.bind(self).call(url, args, creds)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
d = create_driver
|
44
|
+
d.instance.send :init_api_client
|
45
|
+
assert_match Regexp.new("#{Fluent::GoogleCloudOutput::PLUGIN_NAME}/" \
|
46
|
+
"#{Fluent::GoogleCloudOutput::PLUGIN_VERSION}"), \
|
47
|
+
user_agent
|
48
|
+
end
|
49
|
+
|
30
50
|
def test_client_error
|
31
51
|
setup_gce_metadata_stubs
|
32
52
|
{
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-google-cloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.24
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
- Igor Peshansky
|
7
|
+
- Stackdriver Agents Team
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2018-
|
11
|
+
date: 2018-09-05 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: fluentd
|
@@ -220,9 +219,9 @@ dependencies:
|
|
220
219
|
- !ruby/object:Gem::Version
|
221
220
|
version: 0.7.1
|
222
221
|
description: |2
|
223
|
-
Fluentd
|
224
|
-
|
225
|
-
|
222
|
+
Fluentd plugins for the Stackdriver Logging API, which will make logs
|
223
|
+
viewable in the Stackdriver Logs Viewer and can optionally store them
|
224
|
+
in Google Cloud Storage and/or BigQuery.
|
226
225
|
This is an official Google Ruby gem.
|
227
226
|
email:
|
228
227
|
- stackdriver-agents@google.com
|
@@ -236,6 +235,7 @@ files:
|
|
236
235
|
- README.rdoc
|
237
236
|
- Rakefile
|
238
237
|
- fluent-plugin-google-cloud.gemspec
|
238
|
+
- lib/fluent/plugin/filter_add_insert_ids.rb
|
239
239
|
- lib/fluent/plugin/monitoring.rb
|
240
240
|
- lib/fluent/plugin/out_google_cloud.rb
|
241
241
|
- test/helper.rb
|
@@ -247,6 +247,7 @@ files:
|
|
247
247
|
- test/plugin/data/invalid_credentials.json
|
248
248
|
- test/plugin/data/new-style-credentials.json
|
249
249
|
- test/plugin/test_driver.rb
|
250
|
+
- test/plugin/test_filter_add_insert_ids.rb
|
250
251
|
- test/plugin/test_out_google_cloud.rb
|
251
252
|
- test/plugin/test_out_google_cloud_grpc.rb
|
252
253
|
homepage: https://github.com/GoogleCloudPlatform/fluent-plugin-google-cloud
|
@@ -272,7 +273,7 @@ rubyforge_project:
|
|
272
273
|
rubygems_version: 2.6.14
|
273
274
|
signing_key:
|
274
275
|
specification_version: 4
|
275
|
-
summary: fluentd
|
276
|
+
summary: fluentd plugins for the Stackdriver Logging API
|
276
277
|
test_files:
|
277
278
|
- test/helper.rb
|
278
279
|
- test/plugin/base_test.rb
|
@@ -283,5 +284,6 @@ test_files:
|
|
283
284
|
- test/plugin/data/invalid_credentials.json
|
284
285
|
- test/plugin/data/new-style-credentials.json
|
285
286
|
- test/plugin/test_driver.rb
|
287
|
+
- test/plugin/test_filter_add_insert_ids.rb
|
286
288
|
- test/plugin/test_out_google_cloud.rb
|
287
289
|
- test/plugin/test_out_google_cloud_grpc.rb
|