fluent-plugin-google-cloud 0.6.25.1 → 0.7.0.pre.1
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 +15 -24
- data/Rakefile +2 -1
- data/fluent-plugin-google-cloud.gemspec +8 -8
- data/lib/fluent/plugin/out_google_cloud.rb +33 -36
- data/test/plugin/base_test.rb +115 -223
- data/test/plugin/constants.rb +33 -33
- metadata +14 -16
- data/lib/fluent/plugin/filter_add_insert_ids.rb +0 -99
- data/test/plugin/test_filter_add_insert_ids.rb +0 -135
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fa418888cc895f5387d6d03b60154d4940a07d7
|
4
|
+
data.tar.gz: 06ecdf13e10c1262dabf80aedff34af14d132656
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8c1e36fdb141fc017340abcc27c39dd9989e1bda2a9dd16fdd77d8596f3b68203a022b4c221de35240546117f9ad78e67c8ed3dcf570804c62bb9459cd6676d
|
7
|
+
data.tar.gz: 1b4cc062cfbfe10db494c3ecb52499df5f5a52f204e863723f1212b72e61ac89d1a137039af4f7ea3d293a50c9f9eb14d043b22dfe1ef293fb888c7b4fc0da48
|
data/README.rdoc
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
= Google Cloud Logging plugin for {fluentd}[http://github.com/fluent/fluentd]
|
2
2
|
|
3
|
-
fluent-plugin-google-cloud
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
which sends logs to the {Stackdriver Logging API}[https://cloud.google.com/logging/docs/api/].
|
3
|
+
fluent-plugin-google-cloud is an
|
4
|
+
{output plugin for fluentd}[http://docs.fluentd.org/articles/output-plugin-overview]
|
5
|
+
which sends logs to the
|
6
|
+
{Stackdriver Logging API}[https://cloud.google.com/logging/docs/api/].
|
8
7
|
|
9
8
|
This is an official Google Ruby gem.
|
10
9
|
|
@@ -13,7 +12,8 @@ This is an official Google Ruby gem.
|
|
13
12
|
|
14
13
|
== Installation
|
15
14
|
|
16
|
-
This gem is hosted at
|
15
|
+
This gem is hosted at
|
16
|
+
{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,31 +23,22 @@ will also install and configure the gem.
|
|
23
23
|
|
24
24
|
== Configuration
|
25
25
|
|
26
|
-
To
|
27
|
-
in a
|
28
|
-
|
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:
|
26
|
+
To send logs to Google Cloud Logging, specify <code>type google_cloud</code>
|
27
|
+
in a
|
28
|
+
{match clause}[http://docs.fluentd.org/articles/config-file#2-ldquomatchrdquo-tell-fluentd-what-to-do]
|
29
|
+
of your fluentd configuration file, for example:
|
40
30
|
|
41
31
|
<match **>
|
42
|
-
|
32
|
+
type google_cloud
|
43
33
|
</match>
|
44
34
|
|
45
|
-
|
46
|
-
The plugin uses
|
35
|
+
No further configuration is required. The plugin uses
|
47
36
|
{Google Application Default Credentials}[https://developers.google.com/identity/protocols/application-default-credentials]
|
48
|
-
for authorization - for additional information see
|
37
|
+
for authorization - for additional information see
|
49
38
|
{here}[https://cloud.google.com/logging/docs/agent/authorization].
|
50
39
|
|
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>
|
51
42
|
|
52
43
|
== Copyright
|
53
44
|
|
data/Rakefile
CHANGED
@@ -21,7 +21,8 @@ end
|
|
21
21
|
desc 'Fix file permissions'
|
22
22
|
task :fix_perms do
|
23
23
|
files = [
|
24
|
-
'lib/fluent/plugin
|
24
|
+
'lib/fluent/plugin/out_google_cloud.rb',
|
25
|
+
'lib/fluent/plugin/monitoring.rb',
|
25
26
|
'lib/google/**/*.rb'
|
26
27
|
].flat_map do |file|
|
27
28
|
file.include?('*') ? Dir.glob(file) : [file]
|
@@ -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
|
-
viewable in the
|
6
|
-
in Google Cloud Storage and/or BigQuery.
|
4
|
+
Fluentd output plugin for the Stackdriver Logging API, which will make
|
5
|
+
logs viewable in the Developer Console's log viewer and can optionally
|
6
|
+
store them 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 output plugin 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.
|
14
|
-
gem.authors = ['
|
13
|
+
gem.version = '0.7.0.pre.1'
|
14
|
+
gem.authors = ['Ling Huang', 'Igor Peshansky']
|
15
15
|
gem.email = ['stackdriver-agents@google.com']
|
16
16
|
gem.required_ruby_version = Gem::Requirement.new('>= 2.2')
|
17
17
|
|
@@ -19,13 +19,13 @@ eos
|
|
19
19
|
gem.test_files = gem.files.grep(/^(test)/)
|
20
20
|
gem.require_paths = ['lib']
|
21
21
|
|
22
|
-
gem.add_runtime_dependency 'fluentd', '~>
|
22
|
+
gem.add_runtime_dependency 'fluentd', '~> 1.2.5'
|
23
23
|
gem.add_runtime_dependency 'googleapis-common-protos', '~> 1.3'
|
24
24
|
gem.add_runtime_dependency 'google-api-client', '~> 0.17'
|
25
25
|
gem.add_runtime_dependency 'google-cloud-logging', '~> 1.3', '>= 1.3.2'
|
26
26
|
gem.add_runtime_dependency 'googleauth', '~> 0.6'
|
27
27
|
gem.add_runtime_dependency 'grpc', '~> 1.0'
|
28
|
-
gem.add_runtime_dependency 'json', '~>
|
28
|
+
gem.add_runtime_dependency 'json', '~> 1.8'
|
29
29
|
gem.add_runtime_dependency 'google-protobuf', '~> 3.6', '>= 3.6.1'
|
30
30
|
|
31
31
|
gem.add_development_dependency 'mocha', '~> 1.1'
|
@@ -86,7 +86,7 @@ module Fluent
|
|
86
86
|
resource_type: 'container',
|
87
87
|
extra_resource_labels: %w(namespace_id pod_id container_name),
|
88
88
|
extra_common_labels: %w(namespace_name pod_name),
|
89
|
-
metadata_attributes: %w(
|
89
|
+
metadata_attributes: %w(kube-env),
|
90
90
|
stream_severity_map: {
|
91
91
|
'stdout' => 'INFO',
|
92
92
|
'stderr' => 'ERROR'
|
@@ -228,16 +228,7 @@ module Fluent
|
|
228
228
|
Fluent::Plugin.register_output('google_cloud', self)
|
229
229
|
|
230
230
|
PLUGIN_NAME = 'Fluentd Google Cloud Logging plugin'.freeze
|
231
|
-
|
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
|
+
PLUGIN_VERSION = '0.7.0.pre.1'.freeze
|
241
232
|
|
242
233
|
# Name of the the Google cloud logging write scope.
|
243
234
|
LOGGING_SCOPE = 'https://www.googleapis.com/auth/logging.write'.freeze
|
@@ -550,12 +541,10 @@ module Fluent
|
|
550
541
|
@write_request = method(:write_request_via_rest)
|
551
542
|
end
|
552
543
|
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
"instance_id/#{@vm_id}"
|
558
|
-
end
|
544
|
+
# Log an informational message containing the Logs viewer URL
|
545
|
+
@log.info 'Logs viewer address: https://console.cloud.google.com/logs/',
|
546
|
+
"viewer?project=#{@project_id}&resource=#{@resource.type}/",
|
547
|
+
"instance_id/#{@vm_id}"
|
559
548
|
end
|
560
549
|
|
561
550
|
def start
|
@@ -586,17 +575,16 @@ module Fluent
|
|
586
575
|
|
587
576
|
is_json = false
|
588
577
|
if @detect_json
|
589
|
-
# Save the
|
578
|
+
# Save the timestamp and severity if available, then clear it out to
|
590
579
|
# allow for determining whether we should parse the log or message
|
591
580
|
# field.
|
592
|
-
|
593
|
-
|
594
|
-
]
|
581
|
+
timestamp = record.delete('time')
|
582
|
+
severity = record.delete('severity')
|
595
583
|
|
596
584
|
# If the log is json, we want to export it as a structured log
|
597
585
|
# unless there is additional metadata that would be lost.
|
598
586
|
record_json = nil
|
599
|
-
if
|
587
|
+
if record.length == 1
|
600
588
|
%w(log message msg).each do |field|
|
601
589
|
if record.key?(field)
|
602
590
|
record_json = parse_json_or_nil(record[field])
|
@@ -604,15 +592,13 @@ module Fluent
|
|
604
592
|
end
|
605
593
|
end
|
606
594
|
unless record_json.nil?
|
607
|
-
# Propagate these if necessary. Note that we don't want to
|
608
|
-
# override these keys in the JSON we've just parsed.
|
609
|
-
preserved_keys.each do |key|
|
610
|
-
record_json[key] ||= record[key] if record.key?(key)
|
611
|
-
end
|
612
|
-
|
613
595
|
record = record_json
|
614
596
|
is_json = true
|
615
597
|
end
|
598
|
+
# Restore timestamp and severity if necessary. Note that we don't
|
599
|
+
# want to override these keys in the JSON we've just parsed.
|
600
|
+
record['time'] ||= timestamp if timestamp
|
601
|
+
record['severity'] ||= severity if severity
|
616
602
|
end
|
617
603
|
|
618
604
|
ts_secs, ts_nanos = compute_timestamp(
|
@@ -626,10 +612,13 @@ module Fluent
|
|
626
612
|
ts_secs,
|
627
613
|
ts_nanos)
|
628
614
|
|
629
|
-
trace
|
630
|
-
|
615
|
+
# Get fully-qualified trace id for LogEntry "trace" field.
|
616
|
+
fq_trace_id = record.delete(@trace_key)
|
617
|
+
entry.trace = fq_trace_id if fq_trace_id
|
618
|
+
|
631
619
|
span_id = record.delete(@span_id_key)
|
632
620
|
entry.span_id = span_id if span_id
|
621
|
+
|
633
622
|
insert_id = record.delete(@insert_id_key)
|
634
623
|
entry.insert_id = insert_id if insert_id
|
635
624
|
|
@@ -1029,10 +1018,8 @@ module Fluent
|
|
1029
1018
|
# All metadata parameters must now be set.
|
1030
1019
|
missing = []
|
1031
1020
|
missing << 'project_id' unless @project_id
|
1032
|
-
|
1033
|
-
|
1034
|
-
missing << 'vm_id' unless @vm_id
|
1035
|
-
end
|
1021
|
+
missing << 'zone' unless @zone
|
1022
|
+
missing << 'vm_id' unless @vm_id
|
1036
1023
|
return if missing.empty?
|
1037
1024
|
raise Fluent::ConfigError,
|
1038
1025
|
"Unable to obtain metadata parameters: #{missing.join(' ')}"
|
@@ -1148,11 +1135,12 @@ module Fluent
|
|
1148
1135
|
|
1149
1136
|
# GKE container.
|
1150
1137
|
when GKE_CONSTANTS[:resource_type]
|
1138
|
+
raw_kube_env = fetch_gce_metadata('instance/attributes/kube-env')
|
1139
|
+
kube_env = YAML.load(raw_kube_env)
|
1151
1140
|
return {
|
1152
1141
|
'instance_id' => @vm_id,
|
1153
1142
|
'zone' => @zone,
|
1154
|
-
'cluster_name' =>
|
1155
|
-
fetch_gce_metadata('instance/attributes/cluster-name')
|
1143
|
+
'cluster_name' => cluster_name_from_kube_env(kube_env)
|
1156
1144
|
}
|
1157
1145
|
|
1158
1146
|
# Cloud Dataproc.
|
@@ -1534,6 +1522,15 @@ module Fluent
|
|
1534
1522
|
end
|
1535
1523
|
end
|
1536
1524
|
|
1525
|
+
def cluster_name_from_kube_env(kube_env)
|
1526
|
+
return kube_env['CLUSTER_NAME'] if kube_env.key?('CLUSTER_NAME')
|
1527
|
+
instance_prefix = kube_env['INSTANCE_PREFIX']
|
1528
|
+
gke_name_match = /^gke-(.+)-[0-9a-f]{8}$/.match(instance_prefix)
|
1529
|
+
return gke_name_match.captures[0] if gke_name_match &&
|
1530
|
+
!gke_name_match.captures.empty?
|
1531
|
+
instance_prefix
|
1532
|
+
end
|
1533
|
+
|
1537
1534
|
def time_or_nil(ts_secs, ts_nanos)
|
1538
1535
|
Time.at((Integer ts_secs), (Integer ts_nanos) / 1_000.0)
|
1539
1536
|
rescue ArgumentError, TypeError
|
data/test/plugin/base_test.rb
CHANGED
@@ -81,27 +81,26 @@ module BaseTest
|
|
81
81
|
assert_equal CUSTOM_VM_ID, d.instance.vm_id
|
82
82
|
end
|
83
83
|
|
84
|
-
def
|
84
|
+
def test_configure_invalid_metadata_missing_parts
|
85
85
|
setup_no_metadata_service_stubs
|
86
86
|
Fluent::GoogleCloudOutput::CredentialsInfo.stubs(:project_id).returns(nil)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
87
|
+
{ CONFIG_MISSING_METADATA_PROJECT_ID => ['project_id'],
|
88
|
+
CONFIG_MISSING_METADATA_ZONE => ['zone'],
|
89
|
+
CONFIG_MISSING_METADATA_VM_ID => ['vm_id'],
|
90
|
+
CONFIG_MISSING_METADATA_ALL => %w(project_id zone vm_id)
|
91
|
+
}.each_with_index do |(config, parts), index|
|
92
|
+
exception_count = 0
|
92
93
|
begin
|
93
94
|
create_driver(config)
|
94
|
-
assert_true is_valid_config, "Invalid config at index #{index} should "\
|
95
|
-
'have raised an error.'
|
96
95
|
rescue Fluent::ConfigError => error
|
97
|
-
assert_false is_valid_config, "Valid config at index #{index} should "\
|
98
|
-
"not have raised an error #{error}."
|
99
96
|
assert error.message.include?('Unable to obtain metadata parameters:'),
|
100
97
|
"Index #{index} failed."
|
101
|
-
|
98
|
+
parts.each do |part|
|
102
99
|
assert error.message.include?(part), "Index #{index} failed."
|
103
100
|
end
|
101
|
+
exception_count += 1
|
104
102
|
end
|
103
|
+
assert_equal 1, exception_count, "Index #{index} failed."
|
105
104
|
end
|
106
105
|
end
|
107
106
|
|
@@ -180,7 +179,7 @@ module BaseTest
|
|
180
179
|
setup_gce_metadata_stubs
|
181
180
|
# This would cause the resource type to be container.googleapis.com if not
|
182
181
|
# for the detect_subservice=false config.
|
183
|
-
|
182
|
+
setup_container_metadata_stubs
|
184
183
|
d = create_driver(NO_DETECT_SUBSERVICE_CONFIG)
|
185
184
|
d.run
|
186
185
|
assert_equal COMPUTE_CONSTANTS[:resource_type], d.instance.resource.type
|
@@ -468,7 +467,7 @@ module BaseTest
|
|
468
467
|
|
469
468
|
def test_structured_payload_json_log_default_container_not_parsed
|
470
469
|
setup_gce_metadata_stubs
|
471
|
-
|
470
|
+
setup_container_metadata_stubs
|
472
471
|
json_string = '{"msg": "test log entry 0", "tag2": "test", ' \
|
473
472
|
'"data": 5000, "some_null_field": null}'
|
474
473
|
setup_logging_stubs do
|
@@ -485,7 +484,7 @@ module BaseTest
|
|
485
484
|
|
486
485
|
def test_structured_payload_json_log_detect_json_container_not_parsed
|
487
486
|
setup_gce_metadata_stubs
|
488
|
-
|
487
|
+
setup_container_metadata_stubs
|
489
488
|
json_string = '{"msg": "test log entry 0", "tag2": "test", ' \
|
490
489
|
'"data": 5000, "some_null_field": null}'
|
491
490
|
setup_logging_stubs do
|
@@ -500,7 +499,7 @@ module BaseTest
|
|
500
499
|
|
501
500
|
def test_structured_payload_json_log_detect_json_container_parsed
|
502
501
|
setup_gce_metadata_stubs
|
503
|
-
|
502
|
+
setup_container_metadata_stubs
|
504
503
|
json_string = '{"msg": "test log entry 0", "tag2": "test", ' \
|
505
504
|
'"data": 5000, "some_null_field": null}'
|
506
505
|
setup_logging_stubs do
|
@@ -539,7 +538,7 @@ module BaseTest
|
|
539
538
|
# match, thus the original tag is used as the log name.
|
540
539
|
def test_handle_empty_container_name
|
541
540
|
setup_gce_metadata_stubs
|
542
|
-
|
541
|
+
setup_container_metadata_stubs
|
543
542
|
container_name = ''
|
544
543
|
# This tag will not match the kubernetes regex because it requires a
|
545
544
|
# non-empty container name.
|
@@ -561,7 +560,7 @@ module BaseTest
|
|
561
560
|
# 'require_valid_tags' is true.
|
562
561
|
def test_reject_non_utf8_container_name_with_require_valid_tags_true
|
563
562
|
setup_gce_metadata_stubs
|
564
|
-
|
563
|
+
setup_container_metadata_stubs
|
565
564
|
non_utf8_tags = INVALID_TAGS.select do |tag, _|
|
566
565
|
tag.is_a?(String) && !tag.empty?
|
567
566
|
end
|
@@ -601,7 +600,7 @@ module BaseTest
|
|
601
600
|
# Verify that tags extracted from container names are properly encoded.
|
602
601
|
def test_encode_tags_from_container_name_with_require_valid_tags_true
|
603
602
|
setup_gce_metadata_stubs
|
604
|
-
|
603
|
+
setup_container_metadata_stubs
|
605
604
|
VALID_TAGS.each do |tag, encoded_tag|
|
606
605
|
setup_logging_stubs do
|
607
606
|
@logs_sent = []
|
@@ -640,7 +639,7 @@ module BaseTest
|
|
640
639
|
# sanitized.
|
641
640
|
def test_sanitize_tags_from_container_name_with_require_valid_tags_false
|
642
641
|
setup_gce_metadata_stubs
|
643
|
-
|
642
|
+
setup_container_metadata_stubs
|
644
643
|
# Log names are derived from container names for containers. And container
|
645
644
|
# names are extracted from the tag based on a regex match pattern. As a
|
646
645
|
# prerequisite, the tag should already be a string, thus we only test
|
@@ -969,7 +968,7 @@ module BaseTest
|
|
969
968
|
|
970
969
|
def test_one_container_log_from_tag_stderr
|
971
970
|
setup_gce_metadata_stubs
|
972
|
-
|
971
|
+
setup_container_metadata_stubs
|
973
972
|
setup_logging_stubs do
|
974
973
|
d = create_driver(APPLICATION_DEFAULT_CONFIG, CONTAINER_TAG)
|
975
974
|
d.emit(container_log_entry(log_entry(0), 'stderr'))
|
@@ -980,15 +979,15 @@ module BaseTest
|
|
980
979
|
) { |_, oldval, newval| oldval.merge(newval) }
|
981
980
|
verify_log_entries(1, expected_params) do |entry, i|
|
982
981
|
verify_default_log_entry_text(entry['textPayload'], i, entry)
|
983
|
-
assert_equal
|
984
|
-
assert_equal
|
982
|
+
assert_equal CONTAINER_SECONDS_EPOCH, entry['timestamp']['seconds'], entry
|
983
|
+
assert_equal CONTAINER_NANOS, entry['timestamp']['nanos'], entry
|
985
984
|
assert_equal 'ERROR', entry['severity'], entry
|
986
985
|
end
|
987
986
|
end
|
988
987
|
|
989
988
|
def test_json_container_log_metadata_from_plugin
|
990
989
|
setup_gce_metadata_stubs
|
991
|
-
|
990
|
+
setup_container_metadata_stubs
|
992
991
|
setup_logging_stubs do
|
993
992
|
d = create_driver(DETECT_JSON_CONFIG, CONTAINER_TAG)
|
994
993
|
d.emit(container_log_entry_with_metadata('{"msg": "test log entry 0", ' \
|
@@ -1003,15 +1002,15 @@ module BaseTest
|
|
1003
1002
|
assert_equal 'test log entry 0', get_string(fields['msg']), entry
|
1004
1003
|
assert_equal 'test', get_string(fields['tag2']), entry
|
1005
1004
|
assert_equal 5000, get_number(fields['data']), entry
|
1006
|
-
assert_equal
|
1007
|
-
assert_equal
|
1005
|
+
assert_equal CONTAINER_SECONDS_EPOCH, entry['timestamp']['seconds'], entry
|
1006
|
+
assert_equal CONTAINER_NANOS, entry['timestamp']['nanos'], entry
|
1008
1007
|
assert_equal 'WARNING', entry['severity'], entry
|
1009
1008
|
end
|
1010
1009
|
end
|
1011
1010
|
|
1012
1011
|
def test_json_container_log_metadata_from_tag
|
1013
1012
|
setup_gce_metadata_stubs
|
1014
|
-
|
1013
|
+
setup_container_metadata_stubs
|
1015
1014
|
setup_logging_stubs do
|
1016
1015
|
d = create_driver(DETECT_JSON_CONFIG, CONTAINER_TAG)
|
1017
1016
|
d.emit(container_log_entry('{"msg": "test log entry 0", ' \
|
@@ -1026,8 +1025,8 @@ module BaseTest
|
|
1026
1025
|
assert_equal 'test log entry 0', get_string(fields['msg']), entry
|
1027
1026
|
assert_equal 'test', get_string(fields['tag2']), entry
|
1028
1027
|
assert_equal 5000, get_number(fields['data']), entry
|
1029
|
-
assert_equal
|
1030
|
-
assert_equal
|
1028
|
+
assert_equal CONTAINER_SECONDS_EPOCH, entry['timestamp']['seconds'], entry
|
1029
|
+
assert_equal CONTAINER_NANOS, entry['timestamp']['nanos'], entry
|
1031
1030
|
assert_equal 'WARNING', entry['severity'], entry
|
1032
1031
|
end
|
1033
1032
|
end
|
@@ -1222,32 +1221,18 @@ module BaseTest
|
|
1222
1221
|
|
1223
1222
|
def test_log_entry_trace_field
|
1224
1223
|
verify_field_key('trace', DEFAULT_TRACE_KEY, 'custom_trace_key',
|
1225
|
-
CONFIG_CUSTOM_TRACE_KEY_SPECIFIED,
|
1224
|
+
CONFIG_CUSTOM_TRACE_KEY_SPECIFIED,
|
1225
|
+
'projects/proj1/traces/1234567890abcdef1234567890abcdef')
|
1226
1226
|
end
|
1227
1227
|
|
1228
1228
|
def test_log_entry_span_id_field
|
1229
1229
|
verify_field_key('spanId', DEFAULT_SPAN_ID_KEY, 'custom_span_id_key',
|
1230
|
-
CONFIG_CUSTOM_SPAN_ID_KEY_SPECIFIED,
|
1230
|
+
CONFIG_CUSTOM_SPAN_ID_KEY_SPECIFIED, '000000000000004a')
|
1231
1231
|
end
|
1232
1232
|
|
1233
1233
|
def test_log_entry_insert_id_field
|
1234
1234
|
verify_field_key('insertId', DEFAULT_INSERT_ID_KEY, 'custom_insert_id_key',
|
1235
|
-
CONFIG_CUSTOM_INSERT_ID_KEY_SPECIFIED,
|
1236
|
-
end
|
1237
|
-
|
1238
|
-
def test_cascading_json_detection_with_log_entry_trace_field
|
1239
|
-
verify_cascading_json_detection_with_log_entry_fields(
|
1240
|
-
'trace', DEFAULT_TRACE_KEY, TRACE, TRACE2)
|
1241
|
-
end
|
1242
|
-
|
1243
|
-
def test_cascading_json_detection_with_log_entry_span_id_field
|
1244
|
-
verify_cascading_json_detection_with_log_entry_fields(
|
1245
|
-
'spanId', DEFAULT_SPAN_ID_KEY, SPAN_ID, SPAN_ID2)
|
1246
|
-
end
|
1247
|
-
|
1248
|
-
def test_cascading_json_detection_with_log_entry_insert_id_field
|
1249
|
-
verify_cascading_json_detection_with_log_entry_fields(
|
1250
|
-
'insertId', DEFAULT_INSERT_ID_KEY, INSERT_ID, INSERT_ID2)
|
1235
|
+
CONFIG_CUSTOM_INSERT_ID_KEY_SPECIFIED, 'fah7yr7iw64tg857y')
|
1251
1236
|
end
|
1252
1237
|
|
1253
1238
|
# Metadata Agent related tests.
|
@@ -1340,24 +1325,25 @@ module BaseTest
|
|
1340
1325
|
end
|
1341
1326
|
end
|
1342
1327
|
|
1343
|
-
# Test
|
1344
|
-
#
|
1345
|
-
def
|
1328
|
+
# Test k8s monitored resource including the fallback when Metadata Agent
|
1329
|
+
# restarts.
|
1330
|
+
def test_k8s_monitored_resource_fallback
|
1346
1331
|
[
|
1332
|
+
# k8s_container.
|
1347
1333
|
# When enable_metadata_agent is false.
|
1348
1334
|
{
|
1349
1335
|
config: APPLICATION_DEFAULT_CONFIG,
|
1350
1336
|
setup_metadata_agent_stub: false,
|
1351
1337
|
setup_k8s_stub: false,
|
1352
1338
|
log_entry: k8s_container_log_entry(log_entry(0)),
|
1353
|
-
expected_params:
|
1339
|
+
expected_params: COMPUTE_PARAMS
|
1354
1340
|
},
|
1355
1341
|
{
|
1356
1342
|
config: APPLICATION_DEFAULT_CONFIG,
|
1357
1343
|
setup_metadata_agent_stub: true,
|
1358
1344
|
setup_k8s_stub: false,
|
1359
1345
|
log_entry: k8s_container_log_entry(log_entry(0)),
|
1360
|
-
expected_params:
|
1346
|
+
expected_params: COMPUTE_PARAMS
|
1361
1347
|
},
|
1362
1348
|
{
|
1363
1349
|
config: APPLICATION_DEFAULT_CONFIG,
|
@@ -1379,7 +1365,7 @@ module BaseTest
|
|
1379
1365
|
setup_metadata_agent_stub: false,
|
1380
1366
|
setup_k8s_stub: false,
|
1381
1367
|
log_entry: k8s_container_log_entry(log_entry(0)),
|
1382
|
-
expected_params:
|
1368
|
+
expected_params: COMPUTE_PARAMS
|
1383
1369
|
},
|
1384
1370
|
{
|
1385
1371
|
config: ENABLE_METADATA_AGENT_CONFIG,
|
@@ -1415,30 +1401,7 @@ module BaseTest
|
|
1415
1401
|
setup_k8s_stub: true,
|
1416
1402
|
log_entry: k8s_container_log_entry(log_entry(0)),
|
1417
1403
|
expected_params: K8S_CONTAINER_PARAMS
|
1418
|
-
}
|
1419
|
-
].each do |test_params|
|
1420
|
-
new_stub_context do
|
1421
|
-
setup_gce_metadata_stubs
|
1422
|
-
setup_metadata_agent_stubs(test_params[:setup_metadata_agent_stub])
|
1423
|
-
setup_k8s_metadata_stubs(test_params[:setup_k8s_stub])
|
1424
|
-
setup_logging_stubs do
|
1425
|
-
d = create_driver(test_params[:config], CONTAINER_TAG)
|
1426
|
-
d.emit(test_params[:log_entry])
|
1427
|
-
d.run
|
1428
|
-
end
|
1429
|
-
verify_log_entries(1, test_params[:expected_params],
|
1430
|
-
'jsonPayload') do |entry|
|
1431
|
-
fields = get_fields(entry['jsonPayload'])
|
1432
|
-
assert_equal 2, fields.size, entry
|
1433
|
-
assert_equal 'test log entry 0', get_string(fields['log']), entry
|
1434
|
-
assert_equal K8S_STREAM, get_string(fields['stream']), entry
|
1435
|
-
end
|
1436
|
-
end
|
1437
|
-
end
|
1438
|
-
end
|
1439
|
-
|
1440
|
-
def test_k8s_container_monitored_resource_invalid_local_resource_id
|
1441
|
-
[
|
1404
|
+
},
|
1442
1405
|
# When local_resource_id is not present or does not match k8s regexes.
|
1443
1406
|
{
|
1444
1407
|
config: ENABLE_METADATA_AGENT_CONFIG,
|
@@ -1446,7 +1409,7 @@ module BaseTest
|
|
1446
1409
|
setup_k8s_stub: true,
|
1447
1410
|
log_entry: k8s_container_log_entry(
|
1448
1411
|
log_entry(0)).reject { |k, _| k == LOCAL_RESOURCE_ID_KEY },
|
1449
|
-
expected_params:
|
1412
|
+
expected_params: COMPUTE_PARAMS
|
1450
1413
|
},
|
1451
1414
|
{
|
1452
1415
|
config: ENABLE_METADATA_AGENT_CONFIG,
|
@@ -1455,29 +1418,11 @@ module BaseTest
|
|
1455
1418
|
log_entry: k8s_container_log_entry(
|
1456
1419
|
log_entry(0),
|
1457
1420
|
local_resource_id: RANDOM_LOCAL_RESOURCE_ID),
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
setup_metadata_agent_stubs(test_params[:setup_metadata_agent_stub])
|
1464
|
-
setup_k8s_metadata_stubs(test_params[:setup_k8s_stub])
|
1465
|
-
setup_logging_stubs do
|
1466
|
-
d = create_driver(test_params[:config], CONTAINER_TAG)
|
1467
|
-
d.emit(test_params[:log_entry])
|
1468
|
-
d.run
|
1469
|
-
end
|
1470
|
-
verify_log_entries(1, test_params[:expected_params]) do |entry|
|
1471
|
-
assert_equal 'test log entry 0', entry['textPayload'], entry
|
1472
|
-
end
|
1473
|
-
end
|
1474
|
-
end
|
1475
|
-
end
|
1476
|
-
|
1477
|
-
# Test k8s_node monitored resource including the fallback when Metadata Agent
|
1478
|
-
# restarts.
|
1479
|
-
def test_k8s_node_monitored_resource_fallback
|
1480
|
-
[
|
1421
|
+
# When 'kube-env' is present, "compute.googleapis.com/resource_name" is
|
1422
|
+
# not added.
|
1423
|
+
expected_params: COMPUTE_PARAMS
|
1424
|
+
},
|
1425
|
+
# Specific cases for k8s_node.
|
1481
1426
|
{
|
1482
1427
|
config: APPLICATION_DEFAULT_CONFIG,
|
1483
1428
|
setup_metadata_agent_stub: true,
|
@@ -1516,8 +1461,16 @@ module BaseTest
|
|
1516
1461
|
].each do |test_params|
|
1517
1462
|
new_stub_context do
|
1518
1463
|
setup_gce_metadata_stubs
|
1519
|
-
|
1520
|
-
|
1464
|
+
if test_params[:setup_metadata_agent_stub]
|
1465
|
+
setup_metadata_agent_stubs
|
1466
|
+
else
|
1467
|
+
setup_no_metadata_agent_stubs
|
1468
|
+
end
|
1469
|
+
if test_params[:setup_k8s_stub]
|
1470
|
+
setup_k8s_metadata_stubs
|
1471
|
+
else
|
1472
|
+
setup_no_k8s_metadata_stubs
|
1473
|
+
end
|
1521
1474
|
setup_logging_stubs do
|
1522
1475
|
d = create_driver(test_params[:config])
|
1523
1476
|
d.emit(test_params[:log_entry])
|
@@ -1594,7 +1547,7 @@ module BaseTest
|
|
1594
1547
|
[1, 2, 3, 5, 11, 50].each do |n|
|
1595
1548
|
new_stub_context do
|
1596
1549
|
setup_gce_metadata_stubs
|
1597
|
-
|
1550
|
+
setup_container_metadata_stubs
|
1598
1551
|
setup_metadata_agent_stubs
|
1599
1552
|
setup_logging_stubs do
|
1600
1553
|
d = create_driver(ENABLE_METADATA_AGENT_CONFIG)
|
@@ -1606,7 +1559,7 @@ module BaseTest
|
|
1606
1559
|
verify_log_entries(n, CONTAINER_FROM_APPLICATION_PARAMS)
|
1607
1560
|
assert_requested_metadata_agent_stub(
|
1608
1561
|
"#{CONTAINER_LOCAL_RESOURCE_ID_PREFIX}.#{CONTAINER_NAMESPACE_ID}" \
|
1609
|
-
".#{
|
1562
|
+
".#{CONTAINER_POD_NAME}.#{CONTAINER_CONTAINER_NAME}")
|
1610
1563
|
end
|
1611
1564
|
end
|
1612
1565
|
end
|
@@ -1686,34 +1639,44 @@ module BaseTest
|
|
1686
1639
|
MANAGED_VM_BACKEND_VERSION)
|
1687
1640
|
end
|
1688
1641
|
|
1689
|
-
def
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1642
|
+
def setup_container_metadata_stubs
|
1643
|
+
stub_metadata_request(
|
1644
|
+
'instance/attributes/',
|
1645
|
+
"attribute1\nkube-env\nlast_attribute")
|
1646
|
+
stub_metadata_request('instance/attributes/kube-env',
|
1647
|
+
"ENABLE_NODE_LOGGING: \"true\"\n"\
|
1648
|
+
'INSTANCE_PREFIX: '\
|
1649
|
+
"gke-#{CONTAINER_CLUSTER_NAME}-740fdafa\n"\
|
1650
|
+
'KUBE_BEARER_TOKEN: AoQiMuwkNP2BMT0S')
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
def setup_k8s_metadata_stubs
|
1654
|
+
stub_metadata_request(
|
1655
|
+
'instance/attributes/',
|
1656
|
+
"attribute1\ncluster-name\ncluster-location\nlast_attribute")
|
1657
|
+
stub_metadata_request('instance/attributes/cluster-location', K8S_LOCATION2)
|
1658
|
+
stub_metadata_request('instance/attributes/cluster-name', K8S_CLUSTER_NAME)
|
1659
|
+
end
|
1660
|
+
|
1661
|
+
def setup_no_k8s_metadata_stubs
|
1662
|
+
['cluster-location', 'cluster-name'].each do |metadata_name|
|
1663
|
+
stub_request(:get, %r{.*instance/attributes/#{metadata_name}.*})
|
1664
|
+
.to_return(status: 404,
|
1665
|
+
body: 'The requested URL /computeMetadata/v1/instance/' \
|
1666
|
+
"attributes/#{metadata_name} was not found on this" \
|
1667
|
+
' server.')
|
1706
1668
|
end
|
1707
1669
|
end
|
1708
1670
|
|
1709
1671
|
def setup_cloudfunctions_metadata_stubs
|
1710
1672
|
stub_metadata_request(
|
1711
1673
|
'instance/attributes/',
|
1712
|
-
"attribute1\
|
1713
|
-
stub_metadata_request('instance/attributes/
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1674
|
+
"attribute1\nkube-env\ngcf_region\nlast_attribute")
|
1675
|
+
stub_metadata_request('instance/attributes/kube-env',
|
1676
|
+
"ENABLE_NODE_LOGGING: \"true\"\n"\
|
1677
|
+
'INSTANCE_PREFIX: '\
|
1678
|
+
"gke-#{CLOUDFUNCTIONS_CLUSTER_NAME}-740fdafa\n"\
|
1679
|
+
'KUBE_BEARER_TOKEN: AoQiMuwkNP2BMT0S')
|
1717
1680
|
stub_metadata_request('instance/attributes/gcf_region',
|
1718
1681
|
CLOUDFUNCTIONS_REGION)
|
1719
1682
|
end
|
@@ -1748,20 +1711,19 @@ module BaseTest
|
|
1748
1711
|
WebMock.reset!
|
1749
1712
|
end
|
1750
1713
|
|
1751
|
-
def setup_metadata_agent_stubs
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
.to_return(status: 200, body: resource)
|
1756
|
-
end
|
1757
|
-
stub_request(:get, metadata_request_url(RANDOM_LOCAL_RESOURCE_ID))
|
1758
|
-
.to_return(status: 404, body: '')
|
1759
|
-
else
|
1760
|
-
# Simulate an environment with no metadata agent endpoint present.
|
1761
|
-
stub_request(:get,
|
1762
|
-
%r{#{DEFAULT_METADATA_AGENT_URL}\/monitoredResource/.*})
|
1763
|
-
.to_raise(Errno::EHOSTUNREACH)
|
1714
|
+
def setup_metadata_agent_stubs
|
1715
|
+
MONITORED_RESOURCE_STUBS.each do |local_resource_id, resource|
|
1716
|
+
stub_request(:get, metadata_request_url(local_resource_id))
|
1717
|
+
.to_return(status: 200, body: resource)
|
1764
1718
|
end
|
1719
|
+
stub_request(:get, metadata_request_url(RANDOM_LOCAL_RESOURCE_ID))
|
1720
|
+
.to_return(status: 404, body: '')
|
1721
|
+
end
|
1722
|
+
|
1723
|
+
def setup_no_metadata_agent_stubs
|
1724
|
+
# Simulate an environment with no metadata agent endpoint present.
|
1725
|
+
stub_request(:get, %r{#{DEFAULT_METADATA_AGENT_URL}\/monitoredResource/.*})
|
1726
|
+
.to_raise(Errno::EHOSTUNREACH)
|
1765
1727
|
end
|
1766
1728
|
|
1767
1729
|
def assert_requested_metadata_agent_stub(local_resource_id)
|
@@ -1771,20 +1733,21 @@ module BaseTest
|
|
1771
1733
|
# GKE Container.
|
1772
1734
|
|
1773
1735
|
def container_tag_with_container_name(container_name)
|
1774
|
-
"kubernetes.#{
|
1736
|
+
"kubernetes.#{CONTAINER_POD_NAME}_#{CONTAINER_NAMESPACE_NAME}_" \
|
1737
|
+
"#{container_name}"
|
1775
1738
|
end
|
1776
1739
|
|
1777
1740
|
def container_log_entry_with_metadata(
|
1778
|
-
log, container_name =
|
1741
|
+
log, container_name = CONTAINER_CONTAINER_NAME)
|
1779
1742
|
{
|
1780
1743
|
log: log,
|
1781
|
-
stream:
|
1782
|
-
time:
|
1744
|
+
stream: CONTAINER_STREAM,
|
1745
|
+
time: CONTAINER_TIMESTAMP,
|
1783
1746
|
kubernetes: {
|
1784
1747
|
namespace_id: CONTAINER_NAMESPACE_ID,
|
1785
|
-
namespace_name:
|
1748
|
+
namespace_name: CONTAINER_NAMESPACE_NAME,
|
1786
1749
|
pod_id: CONTAINER_POD_ID,
|
1787
|
-
pod_name:
|
1750
|
+
pod_name: CONTAINER_POD_NAME,
|
1788
1751
|
container_name: container_name,
|
1789
1752
|
labels: {
|
1790
1753
|
CONTAINER_LABEL_KEY => CONTAINER_LABEL_VALUE
|
@@ -1793,11 +1756,11 @@ module BaseTest
|
|
1793
1756
|
}
|
1794
1757
|
end
|
1795
1758
|
|
1796
|
-
def container_log_entry(log, stream =
|
1759
|
+
def container_log_entry(log, stream = CONTAINER_STREAM)
|
1797
1760
|
{
|
1798
1761
|
log: log,
|
1799
1762
|
stream: stream,
|
1800
|
-
time:
|
1763
|
+
time: CONTAINER_TIMESTAMP
|
1801
1764
|
}
|
1802
1765
|
end
|
1803
1766
|
|
@@ -1806,7 +1769,7 @@ module BaseTest
|
|
1806
1769
|
log: log,
|
1807
1770
|
LOCAL_RESOURCE_ID_KEY =>
|
1808
1771
|
"#{CONTAINER_LOCAL_RESOURCE_ID_PREFIX}.#{CONTAINER_NAMESPACE_ID}" \
|
1809
|
-
".#{
|
1772
|
+
".#{CONTAINER_POD_NAME}.#{CONTAINER_CONTAINER_NAME}"
|
1810
1773
|
}
|
1811
1774
|
end
|
1812
1775
|
|
@@ -1897,13 +1860,6 @@ module BaseTest
|
|
1897
1860
|
}
|
1898
1861
|
end
|
1899
1862
|
|
1900
|
-
def structured_log_entry
|
1901
|
-
{
|
1902
|
-
'name' => 'test name',
|
1903
|
-
'code' => 'test code'
|
1904
|
-
}
|
1905
|
-
end
|
1906
|
-
|
1907
1863
|
def log_entry(i)
|
1908
1864
|
"test log entry #{i}"
|
1909
1865
|
end
|
@@ -1972,7 +1928,7 @@ module BaseTest
|
|
1972
1928
|
|
1973
1929
|
def verify_container_logs(log_entry_factory, expected_params)
|
1974
1930
|
setup_gce_metadata_stubs
|
1975
|
-
|
1931
|
+
setup_container_metadata_stubs
|
1976
1932
|
[1, 2, 3, 5, 11, 50].each do |n|
|
1977
1933
|
@logs_sent = []
|
1978
1934
|
setup_logging_stubs do
|
@@ -1982,8 +1938,9 @@ module BaseTest
|
|
1982
1938
|
end
|
1983
1939
|
verify_log_entries(n, expected_params) do |entry, i|
|
1984
1940
|
verify_default_log_entry_text(entry['textPayload'], i, entry)
|
1985
|
-
assert_equal
|
1986
|
-
|
1941
|
+
assert_equal CONTAINER_SECONDS_EPOCH, entry['timestamp']['seconds'],
|
1942
|
+
entry
|
1943
|
+
assert_equal CONTAINER_NANOS, entry['timestamp']['nanos'], entry
|
1987
1944
|
assert_equal CONTAINER_SEVERITY, entry['severity'], entry
|
1988
1945
|
end
|
1989
1946
|
end
|
@@ -2052,71 +2009,6 @@ module BaseTest
|
|
2052
2009
|
end
|
2053
2010
|
end
|
2054
2011
|
|
2055
|
-
# Cascading JSON detection is only triggered when the record has one field
|
2056
|
-
# left with name "log", "message" or "msg". This test verifies additional
|
2057
|
-
# LogEntry fields like spanId and traceId do not disable that by accident.
|
2058
|
-
def verify_cascading_json_detection_with_log_entry_fields(
|
2059
|
-
log_entry_field, default_key, root_level_value, nested_level_value)
|
2060
|
-
setup_gce_metadata_stubs
|
2061
|
-
|
2062
|
-
# {
|
2063
|
-
# "logging.googleapis.com/XXX' => 'sample value'
|
2064
|
-
# "msg": {
|
2065
|
-
# "name": "test name",
|
2066
|
-
# "code": "test code"
|
2067
|
-
# }
|
2068
|
-
# }
|
2069
|
-
log_entry_with_root_level_field = {
|
2070
|
-
default_key => root_level_value,
|
2071
|
-
'msg' => structured_log_entry.to_json
|
2072
|
-
}
|
2073
|
-
# {
|
2074
|
-
# "msg": {
|
2075
|
-
# "logging.googleapis.com/XXX' => 'another value',
|
2076
|
-
# "name": "test name",
|
2077
|
-
# "code": "test code"
|
2078
|
-
# }
|
2079
|
-
# }
|
2080
|
-
log_entry_with_nested_level_field = {
|
2081
|
-
'msg' => {
|
2082
|
-
default_key => nested_level_value
|
2083
|
-
}.merge(structured_log_entry).to_json
|
2084
|
-
}
|
2085
|
-
# {
|
2086
|
-
# "logging.googleapis.com/XXX' => 'sample value'
|
2087
|
-
# "msg": {
|
2088
|
-
# "logging.googleapis.com/XXX' => 'another value',
|
2089
|
-
# "name": "test name",
|
2090
|
-
# "code": "test code"
|
2091
|
-
# }
|
2092
|
-
# }
|
2093
|
-
log_entry_with_both_level_fields = log_entry_with_nested_level_field.merge(
|
2094
|
-
default_key => root_level_value)
|
2095
|
-
|
2096
|
-
{
|
2097
|
-
log_entry_with_root_level_field => root_level_value,
|
2098
|
-
log_entry_with_nested_level_field => nested_level_value,
|
2099
|
-
log_entry_with_both_level_fields => nested_level_value
|
2100
|
-
}.each_with_index do |(input_log_entry, expected_value), index|
|
2101
|
-
setup_logging_stubs do
|
2102
|
-
@logs_sent = []
|
2103
|
-
d = create_driver(DETECT_JSON_CONFIG)
|
2104
|
-
d.emit(input_log_entry)
|
2105
|
-
d.run
|
2106
|
-
end
|
2107
|
-
verify_log_entries(1, COMPUTE_PARAMS, 'jsonPayload') do |entry|
|
2108
|
-
assert_equal expected_value, entry[log_entry_field],
|
2109
|
-
"Index #{index} failed. #{expected_value} is expected" \
|
2110
|
-
" for #{log_entry_field} field."
|
2111
|
-
payload_fields = get_fields(entry['jsonPayload'])
|
2112
|
-
assert_equal structured_log_entry.size, payload_fields.size
|
2113
|
-
payload_fields.each do |key, value|
|
2114
|
-
assert_equal structured_log_entry[key], get_string(value)
|
2115
|
-
end
|
2116
|
-
end
|
2117
|
-
end
|
2118
|
-
end
|
2119
|
-
|
2120
2012
|
def verify_field_key(log_entry_field, default_key, custom_key,
|
2121
2013
|
custom_key_config, sample_value)
|
2122
2014
|
setup_gce_metadata_stubs
|