fluent-plugin-google-cloud 0.5.3.grpc.alpha.4 → 0.5.3.grpc.alpha.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/fluent-plugin-google-cloud.gemspec +1 -1
- data/lib/fluent/plugin/out_google_cloud.rb +26 -353
- data/test/plugin/base_test.rb +36 -31
- data/test/plugin/test_out_google_cloud.rb +33 -31
- data/test/plugin/test_out_google_cloud_grpc.rb +38 -31
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af37e8c845023885ae3cec2dcef35368f30e7ee3
|
4
|
+
data.tar.gz: 57cd2c566bb2c89ee063dbb12fbd02a75e8744c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8ca9cfcb3c564822cfd06870010e21358352612a401bdfda9c0d49f4715f9a0831e3daab338a69d9b584a2b568255aa2bae35e512771cfb837de74b64712be0
|
7
|
+
data.tar.gz: 43fa64e97398e3526a6153db587806b9a56291c330b6b066f5a7a106abf769c040cd4bb6a849a4d6bf7b8ba65427dc1a3b5cd5ff3a3ed156ee01e56f790005cf
|
@@ -10,7 +10,7 @@ eos
|
|
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.5.3.grpc.alpha.
|
13
|
+
gem.version = '0.5.3.grpc.alpha.5'
|
14
14
|
gem.authors = ['Todd Derr', 'Alex Robinson']
|
15
15
|
gem.email = ['salty@google.com']
|
16
16
|
gem.required_ruby_version = Gem::Requirement.new('>= 2.0')
|
@@ -39,7 +39,7 @@ module Fluent
|
|
39
39
|
Fluent::Plugin.register_output('google_cloud', self)
|
40
40
|
|
41
41
|
PLUGIN_NAME = 'Fluentd Google Cloud Logging plugin'
|
42
|
-
PLUGIN_VERSION = '0.5.3.grpc.alpha.
|
42
|
+
PLUGIN_VERSION = '0.5.3.grpc.alpha.5'
|
43
43
|
|
44
44
|
# Constants for service names.
|
45
45
|
APPENGINE_SERVICE = 'appengine.googleapis.com'
|
@@ -599,338 +599,6 @@ module Fluent
|
|
599
599
|
end
|
600
600
|
end
|
601
601
|
|
602
|
-
# def old_write(chunk)
|
603
|
-
# # Group the entries since we have to make one call per tag.
|
604
|
-
# grouped_entries = {}
|
605
|
-
# chunk.msgpack_each do |tag, *arr|
|
606
|
-
# grouped_entries[tag] = [] unless grouped_entries.key?(tag)
|
607
|
-
# grouped_entries[tag].push(arr)
|
608
|
-
# end
|
609
|
-
#
|
610
|
-
# grouped_entries.each do |tag, arr|
|
611
|
-
# entries = []
|
612
|
-
# labels = @common_labels.clone
|
613
|
-
#
|
614
|
-
# if @running_cloudfunctions
|
615
|
-
# # If the current group of entries is coming from a Cloud Functions
|
616
|
-
# # function, the function name can be extracted from the tag.
|
617
|
-
# match_data = @cloudfunctions_tag_regexp.match(tag)
|
618
|
-
# if match_data
|
619
|
-
# # Service name is set to Cloud Functions only for logs actually
|
620
|
-
# # coming from a function.
|
621
|
-
# @service_name = CLOUDFUNCTIONS_SERVICE
|
622
|
-
# labels["#{CLOUDFUNCTIONS_SERVICE}/region"] = @gcf_region
|
623
|
-
# labels["#{CLOUDFUNCTIONS_SERVICE}/function_name"] =
|
624
|
-
# decode_cloudfunctions_function_name(
|
625
|
-
# match_data['encoded_function_name'])
|
626
|
-
# else
|
627
|
-
# # Other logs are considered as coming from the Container Engine
|
628
|
-
# # service.
|
629
|
-
# @service_name = CONTAINER_SERVICE
|
630
|
-
# end
|
631
|
-
# end
|
632
|
-
# if @service_name == CONTAINER_SERVICE && \
|
633
|
-
# @compiled_kubernetes_tag_regexp
|
634
|
-
# # Container logs in Kubernetes are tagged based on where they came
|
635
|
-
# # from, so we can extract useful metadata from the tag.
|
636
|
-
# # Do this here to avoid having to repeat it for each record.
|
637
|
-
# match_data = @compiled_kubernetes_tag_regexp.match(tag)
|
638
|
-
# if match_data
|
639
|
-
# %w(namespace_name pod_name container_name).each do |field|
|
640
|
-
# labels["#{CONTAINER_SERVICE}/#{field}"] = match_data[field]
|
641
|
-
# end
|
642
|
-
# end
|
643
|
-
# end
|
644
|
-
#
|
645
|
-
# if @use_grpc
|
646
|
-
# arr.each do |time, record|
|
647
|
-
# next unless record.is_a?(Hash)
|
648
|
-
#
|
649
|
-
# entry = Google::Logging::V1::LogEntry.new(
|
650
|
-
# metadata: Google::Logging::V1::LogEntryMetadata.new(
|
651
|
-
# service_name: @service_name.encode('utf-8'),
|
652
|
-
# project_id: @project_id.encode('utf-8'),
|
653
|
-
# zone: @zone.encode('utf-8'),
|
654
|
-
# labels: {}
|
655
|
-
# ))
|
656
|
-
#
|
657
|
-
# if @service_name == CLOUDFUNCTIONS_SERVICE && record.key?('log')
|
658
|
-
# @cloudfunctions_log_match =
|
659
|
-
# @cloudfunctions_log_regexp.match(record['log'])
|
660
|
-
# end
|
661
|
-
# if @service_name == CONTAINER_SERVICE
|
662
|
-
# # Move the stdout/stderr annotation from the record into a
|
663
|
-
# # label.
|
664
|
-
# field_to_label(record, 'stream', entry.metadata.labels,
|
665
|
-
# "#{CONTAINER_SERVICE}/stream")
|
666
|
-
# # If the record has been annotated by the
|
667
|
-
# # kubernetes_metadata_filter
|
668
|
-
# # plugin, then use that metadata. Otherwise, rely on
|
669
|
-
# # commonLabels
|
670
|
-
# # populated at the grouped_entries level from the group's tag.
|
671
|
-
# if record.key?('kubernetes')
|
672
|
-
# handle_container_metadata(record, entry)
|
673
|
-
# end
|
674
|
-
#
|
675
|
-
# # Save the timestamp if available, then clear it out to allow
|
676
|
-
# # for determining whether we should parse the log or message
|
677
|
-
# # field.
|
678
|
-
# timestamp = record.key?('time') ? record['time'] : nil
|
679
|
-
# record.delete('time')
|
680
|
-
# # If the log is json, we want to export it as a structured log
|
681
|
-
# # unless there is additional metadata that would be lost.
|
682
|
-
# is_json = false
|
683
|
-
# if record.length == 1 && record.key?('log')
|
684
|
-
# record_json = parse_json_or_nil(record['log'])
|
685
|
-
# end
|
686
|
-
# if record.length == 1 && record.key?('message')
|
687
|
-
# record_json = parse_json_or_nil(record['message'])
|
688
|
-
# end
|
689
|
-
# unless record_json.nil?
|
690
|
-
# record = record_json
|
691
|
-
# is_json = true
|
692
|
-
# end
|
693
|
-
# # Restore timestamp if necessary.
|
694
|
-
# unless record.key?('time') || timestamp.nil?
|
695
|
-
# record['time'] = timestamp
|
696
|
-
# end
|
697
|
-
# end
|
698
|
-
#
|
699
|
-
# ts_secs, ts_nanos = compute_timestamp(record, time)
|
700
|
-
# entry.metadata.timestamp = Google::Protobuf::Timestamp.new(
|
701
|
-
# seconds: ts_secs,
|
702
|
-
# nanos: ts_nanos
|
703
|
-
# )
|
704
|
-
#
|
705
|
-
# entry.metadata.severity =
|
706
|
-
# grpc_severity(compute_severity(record, entry))
|
707
|
-
#
|
708
|
-
# set_http_request_grpc(record, entry) # FIXME
|
709
|
-
#
|
710
|
-
# # If a field is present in the label_map, send its value as a
|
711
|
-
# # label
|
712
|
-
# # (mapping the field name to label name as specified in the
|
713
|
-
# # config)
|
714
|
-
# # and do not send that field as part of the payload.
|
715
|
-
# if @label_map
|
716
|
-
# @label_map.each do |field, label|
|
717
|
-
# field_to_label(record, field, entry.metadata.labels, label)
|
718
|
-
# end
|
719
|
-
# end
|
720
|
-
#
|
721
|
-
# if @service_name == CLOUDFUNCTIONS_SERVICE &&
|
722
|
-
# @cloudfunctions_log_match &&
|
723
|
-
# @cloudfunctions_log_match['execution_id']
|
724
|
-
# entry.metadata.labels['execution_id'] =
|
725
|
-
# @cloudfunctions_log_match['execution_id']
|
726
|
-
# end
|
727
|
-
#
|
728
|
-
# set_payload_grpc(record, entry, is_json)
|
729
|
-
#
|
730
|
-
# entries.push(entry)
|
731
|
-
# end
|
732
|
-
# # Don't send an empty request if we rejected all the entries.
|
733
|
-
# next if entries.empty?
|
734
|
-
#
|
735
|
-
# log_name = log_name(tag, labels)
|
736
|
-
#
|
737
|
-
# begin
|
738
|
-
# # Does the actual write to the cloud logging api.
|
739
|
-
#
|
740
|
-
# client = api_client
|
741
|
-
#
|
742
|
-
# labels_utf8_pairs = labels.map do |k, v|
|
743
|
-
# [k.encode('utf-8'), v.encode('utf-8')]
|
744
|
-
# end
|
745
|
-
#
|
746
|
-
# write_request = Google::Logging::V1::WriteLogEntriesRequest.new(
|
747
|
-
# log_name: log_name.encode('utf-8'),
|
748
|
-
# common_labels: Hash[labels_utf8_pairs],
|
749
|
-
# entries: entries
|
750
|
-
# )
|
751
|
-
#
|
752
|
-
# client.write_log_entries(write_request)
|
753
|
-
#
|
754
|
-
# # Let the user explicitly know when the first call succeeded,
|
755
|
-
# # to aid with verification and troubleshooting.
|
756
|
-
# unless @successful_call
|
757
|
-
# @successful_call = true
|
758
|
-
# @log.info 'Successfully sent gRPC to Stackdriver Logging API.'
|
759
|
-
# end
|
760
|
-
#
|
761
|
-
# rescue GRPC::Cancelled => error
|
762
|
-
# # RPC cancelled, so retry via re-raising the error.
|
763
|
-
# raise error
|
764
|
-
#
|
765
|
-
# rescue GRPC::BadStatus => error
|
766
|
-
# case error.code
|
767
|
-
# when GRPC::Core::StatusCodes::CANCELLED,
|
768
|
-
# GRPC::Core::StatusCodes::UNAVAILABLE,
|
769
|
-
# GRPC::Core::StatusCodes::DEADLINE_EXCEEDED,
|
770
|
-
# GRPC::Core::StatusCodes::INTERNAL,
|
771
|
-
# GRPC::Core::StatusCodes::UNKNOWN
|
772
|
-
# # TODO
|
773
|
-
# # Server error, so retry via re-raising the error.
|
774
|
-
# raise error
|
775
|
-
# when GRPC::Core::StatusCodes::UNIMPLEMENTED,
|
776
|
-
# GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED
|
777
|
-
# # Most client errors indicate a problem with the request itself
|
778
|
-
# # and should not be retried.
|
779
|
-
# dropped = entries.length
|
780
|
-
# @log.warn "Dropping #{dropped} log message(s)",
|
781
|
-
# error: error.to_s, error_code: error.code.to_s
|
782
|
-
# when GRPC::Core::StatusCodes::UNAUTHENTICATED
|
783
|
-
# # Authorization error.
|
784
|
-
# # These are usually solved via a `gcloud auth` call, or by
|
785
|
-
# # modifying the permissions on the Google Cloud project.
|
786
|
-
# dropped = entries.length
|
787
|
-
# @log.warn "Dropping #{dropped} log message(s)",
|
788
|
-
# error: error.to_s, error_code: error.code.to_s
|
789
|
-
# else
|
790
|
-
# @log.error "Unknown response code #{error.code} from the " \
|
791
|
-
# "server",
|
792
|
-
# error: error.to_s, error_code: error.code.to_s
|
793
|
-
# end
|
794
|
-
# end
|
795
|
-
# else
|
796
|
-
# arr.each do |time, record|
|
797
|
-
# next unless record.is_a?(Hash)
|
798
|
-
#
|
799
|
-
# entry = Google::Apis::LoggingV1beta3::LogEntry.new(
|
800
|
-
# metadata: Google::Apis::LoggingV1beta3::LogEntryMetadata.new(
|
801
|
-
# service_name: @service_name,
|
802
|
-
# project_id: @project_id,
|
803
|
-
# zone: @zone,
|
804
|
-
# labels: {}
|
805
|
-
# ))
|
806
|
-
#
|
807
|
-
# if @service_name == CLOUDFUNCTIONS_SERVICE && record.key?('log')
|
808
|
-
# @cloudfunctions_log_match =
|
809
|
-
# @cloudfunctions_log_regexp.match(record['log'])
|
810
|
-
# end
|
811
|
-
# if @service_name == CONTAINER_SERVICE
|
812
|
-
# # Move the stdout/stderr annotation from the record into a label
|
813
|
-
# field_to_label(record, 'stream', entry.metadata.labels,
|
814
|
-
# "#{CONTAINER_SERVICE}/stream")
|
815
|
-
# # If the record has been annotated by the
|
816
|
-
# # kubernetes_metadata_filter
|
817
|
-
# # plugin, then use that metadata. Otherwise, rely on
|
818
|
-
# # commonLabels
|
819
|
-
# # populated at the grouped_entries level from the group's tag.
|
820
|
-
# if record.key?('kubernetes')
|
821
|
-
# handle_container_metadata(record, entry)
|
822
|
-
# end
|
823
|
-
#
|
824
|
-
# # Save the timestamp if available, then clear it out to allow
|
825
|
-
# # for determining whether we should parse the log or message
|
826
|
-
# # field.
|
827
|
-
# timestamp = record.key?('time') ? record['time'] : nil
|
828
|
-
# record.delete('time')
|
829
|
-
# # If the log is json, we want to export it as a structured log
|
830
|
-
# # unless there is additional metadata that would be lost.
|
831
|
-
# is_json = false
|
832
|
-
# if record.length == 1 && record.key?('log')
|
833
|
-
# record_json = parse_json_or_nil(record['log'])
|
834
|
-
# end
|
835
|
-
# if record.length == 1 && record.key?('message')
|
836
|
-
# record_json = parse_json_or_nil(record['message'])
|
837
|
-
# end
|
838
|
-
# unless record_json.nil?
|
839
|
-
# record = record_json
|
840
|
-
# is_json = true
|
841
|
-
# end
|
842
|
-
# # Restore timestamp if necessary.
|
843
|
-
# unless record.key?('time') || timestamp.nil?
|
844
|
-
# record['time'] = timestamp
|
845
|
-
# end
|
846
|
-
# end
|
847
|
-
#
|
848
|
-
# ts_secs, ts_nanos = compute_timestamp(record, time)
|
849
|
-
# entry.metadata.timestamp = {
|
850
|
-
# seconds: ts_secs,
|
851
|
-
# nanos: ts_nanos
|
852
|
-
# }
|
853
|
-
#
|
854
|
-
# entry.metadata.severity = compute_severity(record, entry)
|
855
|
-
#
|
856
|
-
# set_http_request(record, entry)
|
857
|
-
#
|
858
|
-
# # If a field is present in the label_map, send its value as a
|
859
|
-
# # label
|
860
|
-
# # (mapping the field name to label name as specified in the
|
861
|
-
# # config)
|
862
|
-
# # and do not send that field as part of the payload.
|
863
|
-
# if @label_map
|
864
|
-
# @label_map.each do |field, label|
|
865
|
-
# field_to_label(record, field, entry.metadata.labels, label)
|
866
|
-
# end
|
867
|
-
# end
|
868
|
-
#
|
869
|
-
# if @service_name == CLOUDFUNCTIONS_SERVICE &&
|
870
|
-
# @cloudfunctions_log_match &&
|
871
|
-
# @cloudfunctions_log_match['execution_id']
|
872
|
-
# entry.metadata.labels['execution_id'] =
|
873
|
-
# @cloudfunctions_log_match['execution_id']
|
874
|
-
# end
|
875
|
-
#
|
876
|
-
# set_payload(record, entry, is_json)
|
877
|
-
# entry.metadata.labels = nil if entry.metadata.labels.empty?
|
878
|
-
#
|
879
|
-
# entries.push(entry)
|
880
|
-
# end
|
881
|
-
# # Don't send an empty request if we rejected all the entries.
|
882
|
-
# next if entries.empty?
|
883
|
-
#
|
884
|
-
# log_name = log_name(tag, labels)
|
885
|
-
#
|
886
|
-
# begin
|
887
|
-
# # Does the actual write to the cloud logging api.
|
888
|
-
# # The URI of the write is constructed by the Google::Api request;
|
889
|
-
# # it is equivalent to this URL:
|
890
|
-
# # 'https://logging.googleapis.com/v1beta3/projects/' \
|
891
|
-
# # "#{@project_id}/logs/#{log_name}/entries:write"
|
892
|
-
#
|
893
|
-
# client = api_client
|
894
|
-
#
|
895
|
-
# write_request = \
|
896
|
-
# Google::Apis::LoggingV1beta3::WriteLogEntriesRequest.new(
|
897
|
-
# common_labels: labels,
|
898
|
-
# entries: entries)
|
899
|
-
#
|
900
|
-
# # TODO: RequestOptions
|
901
|
-
# client.write_log_entries(@project_id, log_name, write_request)
|
902
|
-
#
|
903
|
-
# # Let the user explicitly know when the first call succeeded,
|
904
|
-
# # to aid with verification and troubleshooting.
|
905
|
-
# unless @successful_call
|
906
|
-
# @successful_call = true
|
907
|
-
# @log.info 'Successfully sent to Stackdriver Logging API.'
|
908
|
-
# end
|
909
|
-
#
|
910
|
-
# rescue Google::Apis::ServerError => error
|
911
|
-
# # Server error, so retry via re-raising the error.
|
912
|
-
# raise error
|
913
|
-
#
|
914
|
-
# rescue Google::Apis::AuthorizationError => error
|
915
|
-
# # Authorization error.
|
916
|
-
# # These are usually solved via a `gcloud auth` call, or by
|
917
|
-
# # modifying
|
918
|
-
# # the permissions on the Google Cloud project.
|
919
|
-
# dropped = entries.length
|
920
|
-
# @log.warn "Dropping #{dropped} log message(s)",
|
921
|
-
# error_class: error.class.to_s, error: error.to_s
|
922
|
-
#
|
923
|
-
# rescue Google::Apis::ClientError => error
|
924
|
-
# # Most ClientErrors indicate a problem with the request itself and
|
925
|
-
# # should not be retried.
|
926
|
-
# dropped = entries.length
|
927
|
-
# @log.warn "Dropping #{dropped} log message(s)",
|
928
|
-
# error_class: error.class.to_s, error: error.to_s
|
929
|
-
# end
|
930
|
-
# end
|
931
|
-
# end
|
932
|
-
# end
|
933
|
-
|
934
602
|
private
|
935
603
|
|
936
604
|
def parse_json_or_nil(input)
|
@@ -1159,27 +827,32 @@ module Fluent
|
|
1159
827
|
return nil unless record['httpRequest'].is_a?(Hash)
|
1160
828
|
input = record['httpRequest']
|
1161
829
|
output = Google::Logging::Type::HttpRequest.new
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
output.
|
1167
|
-
|
1168
|
-
output.
|
1169
|
-
|
1170
|
-
output.
|
1171
|
-
|
1172
|
-
output.
|
1173
|
-
|
1174
|
-
output.
|
1175
|
-
|
1176
|
-
output.
|
1177
|
-
|
1178
|
-
output.
|
1179
|
-
|
830
|
+
# We need to delete each field from 'httpRequest' even if its value is
|
831
|
+
# nil. However we do not want to assign this nil value to proto fields
|
832
|
+
# defined as strings / integers.
|
833
|
+
request_method = input.delete('requestMethod')
|
834
|
+
output.request_method = request_method unless request_method.nil?
|
835
|
+
request_url = input.delete('requestUrl')
|
836
|
+
output.request_url = request_url unless request_url.nil?
|
837
|
+
request_size = input.delete('requestSize')
|
838
|
+
output.request_size = request_size.to_i unless request_size.nil?
|
839
|
+
status = input.delete('status')
|
840
|
+
output.status = status.to_i unless status.nil?
|
841
|
+
response_size = input.delete('responseSize')
|
842
|
+
output.response_size = response_size.to_i unless response_size.nil?
|
843
|
+
user_agent = input.delete('userAgent')
|
844
|
+
output.user_agent = user_agent unless user_agent.nil?
|
845
|
+
remote_ip = input.delete('remoteIp')
|
846
|
+
output.remote_ip = remote_ip unless remote_ip.nil?
|
847
|
+
referer = input.delete('referer')
|
848
|
+
output.referer = referer unless referer.nil?
|
849
|
+
cache_hit = input.delete('cacheHit')
|
850
|
+
output.cache_hit = cache_hit unless cache_hit.nil?
|
851
|
+
cache_validated_with_origin_server = \
|
852
|
+
input.delete('cacheValidatedWithOriginServer')
|
1180
853
|
output.cache_validated_with_origin_server = \
|
1181
|
-
|
1182
|
-
|
854
|
+
cache_validated_with_origin_server \
|
855
|
+
unless cache_validated_with_origin_server.nil?
|
1183
856
|
record.delete('httpRequest') if input.empty?
|
1184
857
|
entry.http_request = output
|
1185
858
|
end
|
data/test/plugin/base_test.rb
CHANGED
@@ -307,6 +307,19 @@ module BaseTest
|
|
307
307
|
}
|
308
308
|
}
|
309
309
|
|
310
|
+
HTTP_REQUEST_MESSAGE = {
|
311
|
+
'requestMethod' => 'POST',
|
312
|
+
'requestUrl' => 'http://example/',
|
313
|
+
'requestSize' => 210,
|
314
|
+
'status' => 200,
|
315
|
+
'responseSize' => 65,
|
316
|
+
'userAgent' => 'USER AGENT 1.0',
|
317
|
+
'remoteIp' => '55.55.55.55',
|
318
|
+
'referer' => 'http://referer/',
|
319
|
+
'cacheHit' => true,
|
320
|
+
'cacheValidatedWithOriginServer' => true
|
321
|
+
}
|
322
|
+
|
310
323
|
# Shared tests.
|
311
324
|
|
312
325
|
def test_configure_service_account_application_default
|
@@ -517,15 +530,17 @@ module BaseTest
|
|
517
530
|
setup_gce_metadata_stubs
|
518
531
|
setup_logging_stubs do
|
519
532
|
d = create_driver
|
520
|
-
d.emit('msg' => log_entry(0), 'tag2' => 'test', 'data' => 5000
|
533
|
+
d.emit('msg' => log_entry(0), 'tag2' => 'test', 'data' => 5000,
|
534
|
+
'some_null_field' => nil)
|
521
535
|
d.run
|
522
536
|
end
|
523
537
|
verify_log_entries(1, COMPUTE_PARAMS, 'structPayload') do |entry|
|
524
538
|
fields = get_fields(entry['structPayload'])
|
525
|
-
assert_equal
|
539
|
+
assert_equal 4, fields.size, entry
|
526
540
|
assert_equal 'test log entry 0', get_string(fields['msg']), entry
|
527
541
|
assert_equal 'test', get_string(fields['tag2']), entry
|
528
542
|
assert_equal 5000, get_number(fields['data']), entry
|
543
|
+
assert_equal null_value, fields['some_null_field'], entry
|
529
544
|
end
|
530
545
|
end
|
531
546
|
|
@@ -550,7 +565,8 @@ module BaseTest
|
|
550
565
|
setup_container_metadata_stubs
|
551
566
|
setup_logging_stubs do
|
552
567
|
d = create_driver(APPLICATION_DEFAULT_CONFIG, CONTAINER_TAG)
|
553
|
-
json_string = '{"msg": "test log entry 0", "tag2": "test",
|
568
|
+
json_string = '{"msg": "test log entry 0", "tag2": "test", ' \
|
569
|
+
'"data": 5000, "some_null_field": null}'
|
554
570
|
d.emit(container_log_entry_with_metadata('notJSON' + json_string))
|
555
571
|
d.emit(container_log_entry_with_metadata(json_string))
|
556
572
|
d.emit(container_log_entry_with_metadata(" \r\n \t" + json_string))
|
@@ -565,10 +581,11 @@ module BaseTest
|
|
565
581
|
else
|
566
582
|
assert entry.key?('structPayload'), 'Entry did not have structPayload'
|
567
583
|
fields = get_fields(entry['structPayload'])
|
568
|
-
assert_equal
|
584
|
+
assert_equal 4, fields.size, entry
|
569
585
|
assert_equal 'test log entry 0', get_string(fields['msg']), entry
|
570
586
|
assert_equal 'test', get_string(fields['tag2']), entry
|
571
587
|
assert_equal 5000, get_number(fields['data']), entry
|
588
|
+
assert_equal null_value, fields['some_null_field'], entry
|
572
589
|
end
|
573
590
|
end
|
574
591
|
end
|
@@ -1025,20 +1042,6 @@ module BaseTest
|
|
1025
1042
|
end
|
1026
1043
|
end
|
1027
1044
|
|
1028
|
-
def test_http_request_without_referer_from_record
|
1029
|
-
setup_gce_metadata_stubs
|
1030
|
-
setup_logging_stubs do
|
1031
|
-
d = create_driver
|
1032
|
-
d.emit('httpRequest' => http_request_message_without_referer)
|
1033
|
-
d.run
|
1034
|
-
end
|
1035
|
-
verify_log_entries(1, COMPUTE_PARAMS, 'httpRequest') do |entry|
|
1036
|
-
assert_equal http_request_message_without_referer, entry['httpRequest'],
|
1037
|
-
entry
|
1038
|
-
assert_nil get_fields(entry['structPayload'])['httpRequest'], entry
|
1039
|
-
end
|
1040
|
-
end
|
1041
|
-
|
1042
1045
|
def test_http_request_when_not_hash
|
1043
1046
|
setup_gce_metadata_stubs
|
1044
1047
|
setup_logging_stubs do
|
@@ -1241,6 +1244,16 @@ module BaseTest
|
|
1241
1244
|
assert i == n, "Number of entries #{i} does not match expected number #{n}"
|
1242
1245
|
end
|
1243
1246
|
|
1247
|
+
# The http request message to test against.
|
1248
|
+
def http_request_message
|
1249
|
+
HTTP_REQUEST_MESSAGE
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
# Replace the 'referer' field with nil.
|
1253
|
+
def http_request_message_with_nil_referer
|
1254
|
+
http_request_message.merge('referer' => nil)
|
1255
|
+
end
|
1256
|
+
|
1244
1257
|
# This module expects the methods below to be overridden.
|
1245
1258
|
|
1246
1259
|
# Create a Fluentd output test driver with the Google Cloud Output plugin.
|
@@ -1269,19 +1282,6 @@ module BaseTest
|
|
1269
1282
|
_undefined
|
1270
1283
|
end
|
1271
1284
|
|
1272
|
-
# A wrapper around the constant HTTP_REQUEST_MESSAGE, so the definition can be
|
1273
|
-
# skipped in the shared module here and defined in the test class later.
|
1274
|
-
def http_request_message
|
1275
|
-
_undefined
|
1276
|
-
end
|
1277
|
-
|
1278
|
-
# A wrapper around the constant HTTP_REQUEST_MESSAGE_WITHOUT_REFERER, so the
|
1279
|
-
# definition can be skipped in the shared module and defined in the test
|
1280
|
-
# classes later.
|
1281
|
-
def http_request_message_without_referer
|
1282
|
-
_undefined
|
1283
|
-
end
|
1284
|
-
|
1285
1285
|
# Get the fields of the struct payload.
|
1286
1286
|
def get_fields(_struct_payload)
|
1287
1287
|
_undefined
|
@@ -1302,6 +1302,11 @@ module BaseTest
|
|
1302
1302
|
_undefined
|
1303
1303
|
end
|
1304
1304
|
|
1305
|
+
# The null value.
|
1306
|
+
def null_value(_field)
|
1307
|
+
_undefined
|
1308
|
+
end
|
1309
|
+
|
1305
1310
|
def _undefined
|
1306
1311
|
fail "Method #{__callee__} is unimplemented and needs to be overridden."
|
1307
1312
|
end
|
@@ -70,6 +70,24 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
|
|
70
70
|
assert_equal 1, exception_count
|
71
71
|
end
|
72
72
|
|
73
|
+
def test_http_request_from_record_with_referer_nil
|
74
|
+
setup_gce_metadata_stubs
|
75
|
+
setup_logging_stubs do
|
76
|
+
d = create_driver
|
77
|
+
d.emit('httpRequest' => http_request_message_with_nil_referer)
|
78
|
+
d.run
|
79
|
+
end
|
80
|
+
verify_log_entries(1, COMPUTE_PARAMS, 'httpRequest') do |entry|
|
81
|
+
# The request we send to Logging API has json like:
|
82
|
+
# "httpRequest": { "referer": null }, but eventually the stored LogEntry
|
83
|
+
# would be "httpRequest": {}, since 'referer' is defined as a string in
|
84
|
+
# the proto.
|
85
|
+
assert_equal http_request_message_with_nil_referer,
|
86
|
+
entry['httpRequest'], entry
|
87
|
+
assert_nil get_fields(entry['structPayload'])['httpRequest'], entry
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
73
91
|
# This test looks similar between the grpc and non-grpc paths except that when
|
74
92
|
# parsing "105", the grpc path responds with "DEBUG", while the non-grpc path
|
75
93
|
# responds with "100".
|
@@ -175,25 +193,16 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
|
|
175
193
|
|
176
194
|
private
|
177
195
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
'
|
186
|
-
|
187
|
-
|
188
|
-
'referer' => 'http://referer/',
|
189
|
-
'cacheHit' => false,
|
190
|
-
'validatedWithOriginServer' => true
|
191
|
-
}
|
192
|
-
|
193
|
-
# In the non-grpc path 'referer' is nil, while in the grpc path 'referer' is
|
194
|
-
# absent.
|
195
|
-
HTTP_REQUEST_MESSAGE_WITHOUT_REFERER = HTTP_REQUEST_MESSAGE.merge(
|
196
|
-
'referer' => nil)
|
196
|
+
def rename_key(hash, old_key, new_key)
|
197
|
+
hash.merge(new_key => hash[old_key]).reject { |k, _| k == old_key }
|
198
|
+
end
|
199
|
+
|
200
|
+
# The REST path uses old bindings that were generated prior to the field
|
201
|
+
# rename, and has to use the old name, which is 'validatedWithOriginServer'.
|
202
|
+
def http_request_message
|
203
|
+
rename_key(super, 'cacheValidatedWithOriginServer',
|
204
|
+
'validatedWithOriginServer')
|
205
|
+
end
|
197
206
|
|
198
207
|
# Set up http stubs to mock the external calls.
|
199
208
|
def setup_logging_stubs
|
@@ -234,18 +243,6 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
|
|
234
243
|
end
|
235
244
|
end
|
236
245
|
|
237
|
-
# A wrapper around the constant HTTP_REQUEST_MESSAGE, so the definition can be
|
238
|
-
# skipped in the shared module and defined here.
|
239
|
-
def http_request_message
|
240
|
-
HTTP_REQUEST_MESSAGE
|
241
|
-
end
|
242
|
-
|
243
|
-
# A wrapper around the constant HTTP_REQUEST_MESSAGE_WITHOUT_REFERER, so the
|
244
|
-
# definition can be skipped in the shared module and defined here.
|
245
|
-
def http_request_message_without_referer
|
246
|
-
HTTP_REQUEST_MESSAGE_WITHOUT_REFERER
|
247
|
-
end
|
248
|
-
|
249
246
|
# Get the fields of the struct payload.
|
250
247
|
def get_fields(struct_payload)
|
251
248
|
struct_payload
|
@@ -265,4 +262,9 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
|
|
265
262
|
def get_number(field)
|
266
263
|
field
|
267
264
|
end
|
265
|
+
|
266
|
+
# The null value.
|
267
|
+
def null_value
|
268
|
+
nil
|
269
|
+
end
|
268
270
|
end
|
@@ -72,6 +72,34 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
def test_http_request_from_record_with_referer_nil
|
76
|
+
setup_gce_metadata_stubs
|
77
|
+
setup_logging_stubs do
|
78
|
+
d = create_driver
|
79
|
+
d.emit('httpRequest' => http_request_message_with_nil_referer)
|
80
|
+
d.run
|
81
|
+
end
|
82
|
+
verify_log_entries(1, COMPUTE_PARAMS, 'httpRequest') do |entry|
|
83
|
+
assert_equal http_request_message_with_absent_referer,
|
84
|
+
entry['httpRequest'], entry
|
85
|
+
assert_nil get_fields(entry['structPayload'])['httpRequest'], entry
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_http_request_from_record_with_referer_absent
|
90
|
+
setup_gce_metadata_stubs
|
91
|
+
setup_logging_stubs do
|
92
|
+
d = create_driver
|
93
|
+
d.emit('httpRequest' => http_request_message_with_absent_referer)
|
94
|
+
d.run
|
95
|
+
end
|
96
|
+
verify_log_entries(1, COMPUTE_PARAMS, 'httpRequest') do |entry|
|
97
|
+
assert_equal http_request_message_with_absent_referer,
|
98
|
+
entry['httpRequest'], entry
|
99
|
+
assert_nil get_fields(entry['structPayload'])['httpRequest'], entry
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
75
103
|
# This test looks similar between the grpc and non-grpc paths except that when
|
76
104
|
# parsing "105", the grpc path responds with "DEBUG", while the non-grpc path
|
77
105
|
# responds with "100".
|
@@ -114,27 +142,6 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
|
|
114
142
|
use_grpc true
|
115
143
|
)
|
116
144
|
|
117
|
-
# The non-grpc path has a unique field 'validatedWithOriginServer', while
|
118
|
-
# the grpc path has a unique field 'cacheValidatedWithOriginServer'.
|
119
|
-
HTTP_REQUEST_MESSAGE = {
|
120
|
-
'requestMethod' => 'POST',
|
121
|
-
'requestUrl' => 'http://example/',
|
122
|
-
'requestSize' => 210,
|
123
|
-
'status' => 200,
|
124
|
-
'responseSize' => 65,
|
125
|
-
'userAgent' => 'USER AGENT 1.0',
|
126
|
-
'remoteIp' => '55.55.55.55',
|
127
|
-
'referer' => 'http://referer/',
|
128
|
-
'cacheHit' => true,
|
129
|
-
'cacheValidatedWithOriginServer' => true
|
130
|
-
}
|
131
|
-
|
132
|
-
# In the non-grpc path 'referer' is nil, while in the grpc path 'referer' is
|
133
|
-
# absent.
|
134
|
-
HTTP_REQUEST_MESSAGE_WITHOUT_REFERER = HTTP_REQUEST_MESSAGE.reject do |k, _|
|
135
|
-
k == 'referer'
|
136
|
-
end
|
137
|
-
|
138
145
|
# Create a Fluentd output test driver with the Google Cloud Output plugin with
|
139
146
|
# grpc enabled. The signature of this method is different between the grpc
|
140
147
|
# path and the non-grpc path. For grpc, an additional grpc stub class can be
|
@@ -275,16 +282,11 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
|
|
275
282
|
end
|
276
283
|
end
|
277
284
|
|
278
|
-
#
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
# A wrapper around the constant HTTP_REQUEST_MESSAGE_WITHOUT_REFERER, so the
|
285
|
-
# definition can be skipped in the shared module and defined here.
|
286
|
-
def http_request_message_without_referer
|
287
|
-
HTTP_REQUEST_MESSAGE_WITHOUT_REFERER
|
285
|
+
# Unset the 'referer' field.
|
286
|
+
def http_request_message_with_absent_referer
|
287
|
+
http_request_message.reject do |k, _|
|
288
|
+
k == 'referer'
|
289
|
+
end
|
288
290
|
end
|
289
291
|
|
290
292
|
# Get the fields of the struct payload.
|
@@ -306,4 +308,9 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
|
|
306
308
|
def get_number(field)
|
307
309
|
field['numberValue']
|
308
310
|
end
|
311
|
+
|
312
|
+
# The null value.
|
313
|
+
def null_value
|
314
|
+
{ 'nullValue' => 'NULL_VALUE' }
|
315
|
+
end
|
309
316
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-google-cloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.3.grpc.alpha.
|
4
|
+
version: 0.5.3.grpc.alpha.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Todd Derr
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-12-
|
12
|
+
date: 2016-12-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fluentd
|