fluent-plugin-timestream 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 548e43a61eddcb48d440e888d4708422698da78b5584a7c52d4185aee5aa5c07
4
+ data.tar.gz: eddbfba2ca3458b060431ef6bc38ecdbeda4bebf4800f84577d824108c7107eb
5
+ SHA512:
6
+ metadata.gz: d8da4123c8e56eaa782a98246002708582d2120acb5a2f9d012d0d4a8624b275d13671868765e4276dfea3e323c90d1b3a6f65ed57e2f23b17845b269770449d
7
+ data.tar.gz: ce70a33c5502a452b3b20db2543e8f1dd02e684f68353173405dfc50f7dbed65c108ca5d96c352df770167b1304d7cdc2ea7c028ba7825031045eb0ce078539c
@@ -0,0 +1,42 @@
1
+ version: 2.1
2
+ orbs:
3
+ ruby: circleci/ruby@0.1.2
4
+
5
+ jobs:
6
+ build_and_test:
7
+ parameters:
8
+ ruby-version:
9
+ type: string
10
+ docker:
11
+ - image: circleci/ruby:<< parameters.ruby-version >>-buster
12
+ executor: ruby/default
13
+ steps:
14
+ - checkout
15
+ - run:
16
+ name: Execution environments
17
+ command: echo "Running tests with ruby $(ruby -v), bundler $(bundle -v)"
18
+ - restore_cache:
19
+ name: Restore installed gems
20
+ keys:
21
+ - v1-fluent-timestream-<< parameters.ruby-version >>
22
+ - run:
23
+ name: Bundle install
24
+ command: bundle install
25
+ - save_cache:
26
+ name: Save installed gems
27
+ paths:
28
+ - /usr/local/bundle/bin
29
+ - /usr/local/bundle/extensions
30
+ - /usr/local/bundle/gems
31
+ - /usr/local/bundle/specifications
32
+ key: v1-fluent-timestream-<< parameters.ruby-version >>-{{ checksum "Gemfile.lock" }}
33
+ - run:
34
+ name: Test
35
+ command: rake test
36
+ workflows:
37
+ build_and_test:
38
+ jobs:
39
+ - build_and_test:
40
+ matrix:
41
+ parameters:
42
+ ruby-version: ["2.6.3", "2.6.5", "2.6", "2.7", "3"]
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ td-agent.conf
14
+ fluent.conf
data/.rubocop.yml ADDED
@@ -0,0 +1,41 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/FrozenStringLiteralComment:
5
+ Enabled: true
6
+
7
+ Style/RedundantReturn:
8
+ Enabled: false
9
+
10
+ Style/NegatedIf:
11
+ Enabled: false
12
+
13
+ Style/AsciiComments:
14
+ Enabled: false
15
+
16
+ Layout/EmptyLinesAroundClassBody:
17
+ Enabled: false
18
+
19
+ Layout/EmptyLinesAroundModuleBody:
20
+ Enabled: false
21
+
22
+ Layout/IndentationConsistency:
23
+ EnforcedStyle: indented_internal_methods
24
+
25
+ Style/ClassAndModuleChildren:
26
+ Enabled: false
27
+
28
+ Metrics/BlockLength:
29
+ Exclude:
30
+ - 'test/*.rb'
31
+ - 'test/**/*.rb'
32
+ - 'test/**/**/*.rb'
33
+
34
+ Layout/LineLength:
35
+ Max: 90
36
+
37
+ Style/AndOr:
38
+ EnforcedStyle: conditionals
39
+
40
+ Layout/EmptyLineAfterGuardClause:
41
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in fluent-plugin-timestream.gemspec
6
+ gemspec
7
+
8
+ gem 'rake', '~> 13.0'
9
+
10
+ gem 'rubocop', '~> 1.7'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Studist Corporation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # fluent-plugin-timestream
2
+ [![Status](https://circleci.com/gh/StudistCorporation/fluent-plugin-timestream.svg?style=shield)](https://circleci.com/gh/StudistCorporation/fluent-plugin-timestream)
3
+
4
+ Fluentd output plugin for Amazon Timestream.
5
+
6
+
7
+ ## Installation
8
+ You can install it as follows:
9
+
10
+ $ fluent-gem install fluent-plugin-timestream
11
+
12
+ ## Configuration
13
+ Please refer to the [sample config file](https://github.com/StudistCorporation/fluent-plugin-timestream/blob/main/fluent.conf.sample)
14
+
15
+ ## Note
16
+ The plugin converts `null` values in the log to empty string.
17
+ e.g. `{key_name: null}` => `{key_name: ""}`
18
+
19
+ When writing Timestream records, `TimeUnit` is always set to `SECONDS`
20
+
21
+ Configuring multiple `MeasureName`s is not supported.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'lib' << 'test'
8
+ test.test_files = FileList['test/test_*.rb']
9
+ test.verbose = true
10
+ end
11
+
12
+ task default: [:build]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'fluent/plugin/timestream'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require 'pry'
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/fluent/plugin/timestream/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'fluent-plugin-timestream'
7
+ spec.version = Fluent::Plugin::Timestream::VERSION
8
+ spec.authors = ['Studist Corporation']
9
+
10
+ spec.summary = 'Fluentd output plugin which writes Amazon Timestream record.'
11
+ spec.license = 'MIT'
12
+
13
+ spec.metadata['source_code_uri'] = 'https://github.com/StudistCorporation/fluent-plugin-timestream'
14
+ spec.metadata['changelog_uri'] = 'https://github.com/StudistCorporation/fluent-plugin-timestream/CHANGELOG.md'
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.required_ruby_version = ">= 2.6.3"
26
+ spec.add_dependency "aws-sdk-timestreamwrite", ">=1.2.0"
27
+ spec.add_dependency "fluentd", ">= 1.11.0"
28
+
29
+ spec.add_development_dependency "net-empty_port", ">= 0.0.2"
30
+ spec.add_development_dependency "test-unit", ">= 3.3.9"
31
+ spec.add_development_dependency "webrick", ">= 1.4.2"
32
+ end
@@ -0,0 +1,45 @@
1
+ <source>
2
+ @type tail
3
+ format ltsv
4
+ path /path/to/sample.log
5
+ pos_file /tmp/sample.pos
6
+ tag "sample.log"
7
+ </source>
8
+
9
+ <match sample.log>
10
+ @type timestream
11
+
12
+ # Amazon Timestream database name.
13
+ # If not specified, the AWS_TIMESTREAM_DATABASE environment variable will be used instead
14
+ database "sampleDB"
15
+
16
+ # Amazon Timestream table name.
17
+ # If not specified, the AWS_TIMESTREAM_TABLE environment variable will be used instead
18
+ table "sampleTable"
19
+
20
+ # AWS access key id, secret key, region.
21
+ # If not specified, search it from environment variable or aws config file.
22
+ # For more details see https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/TimestreamWrite/Client.html
23
+ aws_key_id "XXXXXXXX"
24
+ aws_sec_key "XXXXXXXX"
25
+ region "us-east-1"
26
+
27
+ # Specify which key should be 'measure'.
28
+ # If not, plugin sends dummy measure and writes all keys and values as dimensions.
29
+ # Dummy measure is as follows:
30
+ # MeasureName: '-'
31
+ # MeasureValue: '-'
32
+ # MeasureValueType: 'VARCHAR'
33
+ #<measure>
34
+ # name "measureNameXXX"
35
+ # type "VARCHAR"
36
+ #</measure>
37
+
38
+ # 'chunk_limit_records' must be configured less or equal to 100.
39
+ # If not, plugin may fails to write record.
40
+ # For now, plugin sends records in buffer all at once.
41
+ # However Amazon Timestream currently accepts less or equal to 100 records.
42
+ <buffer>
43
+ chunk_limit_records 100
44
+ </buffer>
45
+ </match>
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-core'
4
+ require 'aws-sdk-timestreamwrite'
5
+ require_relative 'timestream/version'
6
+
7
+ module Fluent
8
+ module Plugin
9
+ # Fluent plugin for Amazon Timestream
10
+ class TimestreamOutput < Fluent::Plugin::Output
11
+ Fluent::Plugin.register_output('timestream', self)
12
+
13
+ config_param :region, :string, default: nil
14
+
15
+ config_param :aws_key_id, :string, secret: true, default: nil
16
+ config_param :aws_sec_key, :string, secret: true, default: nil
17
+
18
+ config_param :database, :string, default: nil
19
+ config_param :table, :string, default: nil
20
+ config_section :measure,
21
+ param_name: 'target_measure', required: false, multi: false do
22
+ config_param :name, :string
23
+ config_param :type, :string
24
+ end
25
+
26
+ config_param :endpoint, :string, default: nil
27
+ config_param :ssl_verify_peer, :bool, default: true
28
+
29
+ def configure(conf)
30
+ super
31
+ options = credential_options
32
+ options[:region] = @region if @region
33
+ options[:endpoint] = @endpoint if @endpoint
34
+ options[:ssl_verify_peer] = @ssl_verify_peer
35
+ @client = Aws::TimestreamWrite::Client.new(options)
36
+
37
+ @database = ENV['AWS_TIMESTREAM_DATABASE'] if @database.nil?
38
+ @table = ENV['AWS_TIMESTREAM_TABLE'] if @table.nil?
39
+ end
40
+
41
+ def credential_options
42
+ if @aws_key_id && @aws_sec_key
43
+ {
44
+ access_key_id: @aws_key_id,
45
+ secret_access_key: @aws_sec_key
46
+ }
47
+ else
48
+ {}
49
+ end
50
+ end
51
+
52
+ def formatted_to_msgpack_binary
53
+ true
54
+ end
55
+
56
+ def format(_tag, time, record)
57
+ [time, record].to_msgpack
58
+ end
59
+
60
+ def create_timestream_record(dimensions, time, measure)
61
+ measure = { name: '-', value: '-', type: 'VARCHAR' } if measure.empty?
62
+ {
63
+ dimensions: dimensions,
64
+ time: time.to_s,
65
+ time_unit: 'SECONDS',
66
+ measure_name: measure[:name],
67
+ measure_value: measure[:value].to_s,
68
+ measure_value_type: measure[:type]
69
+ }
70
+ end
71
+
72
+ def create_timestream_dimension(key, value)
73
+ {
74
+ dimension_value_type: 'VARCHAR',
75
+ name: key,
76
+ value: value.to_s
77
+ }
78
+ end
79
+
80
+ def create_timestream_dimensions_and_measure(record)
81
+ dimensions = []
82
+ measure = {}
83
+ record.each do |k, v|
84
+ if @target_measure && k == @target_measure[:name]
85
+ measure = { name: k, value: v, type: @target_measure[:type] }
86
+ next
87
+ end
88
+ dimensions.push(create_timestream_dimension(k, v))
89
+ end
90
+ return [dimensions, measure]
91
+ end
92
+
93
+ def create_timestream_records(chunk)
94
+ timestream_records = []
95
+ chunk.each do |time, record|
96
+ dimensions, measure = create_timestream_dimensions_and_measure(record)
97
+ timestream_records.push(create_timestream_record(dimensions, time, measure))
98
+ end
99
+
100
+ log.info("read #{timestream_records.length} records from chunk")
101
+ timestream_records
102
+ end
103
+
104
+ def write(chunk)
105
+ @client.write_records(
106
+ database_name: @database,
107
+ table_name: @table,
108
+ records: create_timestream_records(chunk)
109
+ )
110
+ rescue Aws::TimestreamWrite::Errors::RejectedRecordsException => e
111
+ log.error(e.rejected_records)
112
+ rescue StandardError => e
113
+ log.error(e.message)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fluent
4
+ module Plugin
5
+ module Timestream
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-timestream
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Studist Corporation
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-timestreamwrite
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: fluentd
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.11.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.11.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: net-empty_port
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.3.9
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 3.3.9
69
+ - !ruby/object:Gem::Dependency
70
+ name: webrick
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.4.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.4.2
83
+ description:
84
+ email:
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - ".circleci/config.yml"
90
+ - ".gitignore"
91
+ - ".rubocop.yml"
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/console
97
+ - bin/setup
98
+ - fluent-plugin-timestream.gemspec
99
+ - fluent.conf.sample
100
+ - lib/fluent/plugin/out_timestream.rb
101
+ - lib/fluent/plugin/timestream/version.rb
102
+ homepage:
103
+ licenses:
104
+ - MIT
105
+ metadata:
106
+ source_code_uri: https://github.com/StudistCorporation/fluent-plugin-timestream
107
+ changelog_uri: https://github.com/StudistCorporation/fluent-plugin-timestream/CHANGELOG.md
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 2.6.3
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubygems_version: 3.0.3
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Fluentd output plugin which writes Amazon Timestream record.
127
+ test_files: []