fluent-plugin-application-insights-freiheit 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 22e120f434eadfe8e0afcb31e81d478ee61b7bc2245188388bc9566d31d4422f
4
+ data.tar.gz: c24602a7155574a03e2513e02f4e29c871c5adedacaab5fcab37577989bfd3fc
5
+ SHA512:
6
+ metadata.gz: b2dac0fc06d4e6a99c83f593620a2b1b46ff694aba9bd3344c7c752a6055cb68de5b42465ba3122ee0ef00bbcc348ab64863242dd99f872434ae66e69c7e6723
7
+ data.tar.gz: 8aa343099a55a76ad75557b81e3a0d325823053b98d07232ab8431ae4953d180a6b19f34a7982d5d104421c211a44c3917728e7481380d91172efb156708dbd8
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ Gemfile.lock
46
+ .ruby-version
47
+ .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.2.0
5
+ - 2.3.3
6
+ - 2.4.0
7
+ - ruby-head
8
+
9
+ cache: bundler
10
+
11
+ before_install:
12
+ - gem install bundler
13
+
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: ruby-head
17
+
18
+ install:
19
+ - bundle install
@@ -0,0 +1,33 @@
1
+ # Development
2
+ ## Build Gem
3
+ Run ```gem build fluent-plugin-application-insights.gemspec```.
4
+
5
+ ## Run Test
6
+ Make sure you have bundler installed, you can install it by ```sudo gem install bundler```. And run ```bundler install``` once to install all dependencies.
7
+
8
+ Run ```rake test```.
9
+
10
+ ## Release
11
+ If you are the current maintainer of this plugin:
12
+ 1. Ensure all tests passed
13
+ 2. Bump up version in ```fluent-plugin-application-insights.gemspec```
14
+ 3. Build the gem, install it locally and test it
15
+ 4. Create a PR with whatever changes needed before releasing, e.g., version bump up, documentation
16
+ 5. Tag and push: ```git tag vx.xx.xx; git push --tags```
17
+ 6. Create a github release with the pushed tag
18
+ 7. Push to rubygems.org: ```gem push fluent-plugin-application-insights-<version>.gem```
19
+
20
+ # Contributing
21
+
22
+ This project welcomes contributions and suggestions. Most contributions require you to
23
+ agree to a Contributor License Agreement (CLA) declaring that you have the right to,
24
+ and actually do, grant us the rights to use your contribution. For details, visit
25
+ https://cla.microsoft.com.
26
+
27
+ When you submit a pull request, a CLA-bot will automatically determine whether you need
28
+ to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the
29
+ instructions provided by the bot. You will only need to do this once across all repositories using our CLA.
30
+
31
+ This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
32
+ For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
33
+ or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation. All rights reserved.
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 all
13
+ 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 THE
21
+ SOFTWARE
@@ -0,0 +1,89 @@
1
+ # [UNSUPPORTED] fluent-plugin-application-insights
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/fluent-plugin-application-insights.svg)](https://badge.fury.io/rb/fluent-plugin-application-insights)
4
+ [![Build Status](https://travis-ci.org/Microsoft/fluent-plugin-application-insights.svg?branch=master)](https://travis-ci.org/Microsoft/fluent-plugin-application-insights)
5
+
6
+ This is the [Fluentd](https://fluentd.org/) output plugin for [Azure Application Insights](https://docs.microsoft.com/azure/application-insights/)
7
+
8
+ Application Insights is an extensible Application Performance Management (APM) service for web developers on multiple platforms.
9
+ Use it to monitor your live web application. It will automatically detect performance anomalies. It includes powerful analytics
10
+ tools to help you diagnose issues and to understand what users actually do with your app.
11
+ It's designed to help you continuously improve performance and usability.
12
+
13
+ ## Installation
14
+
15
+ ```
16
+ $ gem install fluent-plugin-application-insights
17
+ ```
18
+
19
+ ## Configuration
20
+
21
+ To send data to Application Insights, add the following piece to your fluentd configuration file:
22
+
23
+ ```
24
+ <match **>
25
+ @type application_insights
26
+ instrumentation_key <your instrumentation key>
27
+ </match>
28
+ ```
29
+
30
+ Here is the configuration options for this plugin:
31
+
32
+ * `instrumentation_key` - Required, the Application Insights instrumentation key
33
+ * `service_endpoint_uri` - The endpoint of the Application Insights for Azure Sovereign Clouds
34
+ * `send_buffer_size` - The batch size to send data to Application Insights service (default `1000`). Setting this to a large size will usually result in better output throughput.
35
+ * `standard_schema` - The parameter indicating whether the record is in standard schema. i.e., the format that is recognized by Application Insights backend (default `false`).
36
+ If the record is not in standard schema, it will be tracked as Application Insights trace telemetry. Otherwise, the record is just forwarded to the backend. See [Standard Schema](#standard-schema) for more info.
37
+ * `message_property` - The property name for the trace message (default `message`).
38
+ * `time_property` - The property name for the timestamp (default `nil`).
39
+ Fluentd input plugin will assign a timestamp for each emitted record, and this timestamp is used as the telemetry creation time by default. Set the `time_property` if you want to use the value of this property instead of the one assigned by the input plugin.
40
+ * `severity_property` - The property name for severity level (default `severity`). If the severity property doesn't exist, the record will be treated as information level. See [Severity Level](https://docs.microsoft.com/azure/application-insights/application-insights-data-model-trace-telemetry#severity-level) for more info.
41
+ * `severity_level_verbose` - The value of severity property that maps to Application Insights' verbose severity level (default `verbose`).
42
+ * `severity_level_information` - The value of severity property that maps to Application Insights' information severity level (default `information`).
43
+ * `severity_level_warning` - The value of severity property that maps to Application Insights' warning severity level (default `warning`).
44
+ * `severity_level_error` - The value of severity property that maps to Application Insights' error severity level (default `error`).
45
+ * `severity_level_critical` - The value of severity property that maps to Application Insights' critical severity level (default `critical`).
46
+ * `context_tag_sources` - The dictionary that instructs the Application Insights plugin to set Application Insights context tags using record properties.
47
+ In this dictionary keys are Application Insights context tags to set, and values are the source properties that are used to set the context tags value. For the source property, you can specify the property name or jsonpath like syntax for nested property, see [record_accessor syntax](https://docs.fluentd.org/v1.0/articles/api-plugin-helper-record_accessor#syntax) for more info. For example:
48
+ ```
49
+ context_tag_sources {
50
+ "ai.cloud.role": "kubernetes_container_name",
51
+ "ai.cloud.roleInstance": "$.docker.container_id"
52
+ }
53
+ ```
54
+ Here is the list of all [context tag keys](https://github.com/Microsoft/ApplicationInsights-dotnet/blob/develop/Schema/PublicSchema/ContextTagKeys.bond)
55
+
56
+ ## Standard Schema
57
+
58
+ The standard schema for Application Insights telemetry is defined [here](https://github.com/Microsoft/ApplicationInsights-Home/tree/master/EndpointSpecs/Schemas/Bond).
59
+
60
+ Below is an example of a Request telemetry in standard schema format. `data`, `data.baseType` and `data.baseData` are required properties. Different telemetry types will have different properties associated with the `baseData` object.
61
+
62
+ ```
63
+ {
64
+ "name": "Microsoft.ApplicationInsights.Request",
65
+ "time": "2018-02-28T00:24:00.5676240Z",
66
+ "tags":{
67
+ "ai.cloud.role": "webfront",
68
+ "ai.cloud.roleInstance":"85a1e424491d07b6c1ed032f"
69
+ },
70
+ "data": {
71
+ "baseType": "RequestData",
72
+ "baseData": {
73
+ "ver":2,
74
+ "id":"|85a1e424-491d07b6c1ed032f.",
75
+ "name":"PUT Flights/StartNewFlightAsync",
76
+ "duration":"00:00:01.0782934",
77
+ "success":true,
78
+ "responseCode":"204",
79
+ "url":"http://localhost:5023/api/flights
80
+ }
81
+ }
82
+ ```
83
+
84
+ ## Contributing
85
+ Refer to [Contributing Guide](CONTRIBUTING.md).
86
+
87
+ This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
88
+ For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
89
+ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
@@ -0,0 +1,13 @@
1
+ require "bundler"
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs.push("lib", "test")
8
+ t.test_files = FileList["test/**/test_*.rb"]
9
+ t.verbose = true
10
+ t.warning = true
11
+ end
12
+
13
+ task default: [:test]
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "fluent-plugin-application-insights-freiheit"
6
+ spec.version = "0.2.5"
7
+ spec.authors = ["freiheit.com"]
8
+ spec.email = ["xyz@freiheit.com"]
9
+
10
+ spec.summary = "This is a fork of the fluentd output plugin for Azure Application Insights."
11
+ spec.description = "Fluentd output plugin for Azure Application Insights."
12
+
13
+ spec.homepage = "https://github.com/dennis-tra/fluent-plugin-application-insights"
14
+ spec.license = "MIT"
15
+
16
+ test_files, files = `git ls-files -z`.split("\x0").partition do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.files = files.push('lib/fluent/plugin/out_application_insights.rb')
20
+ spec.executables = files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = test_files
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rake", "~> 12.0"
26
+ spec.add_development_dependency "test-unit", "~> 3.0"
27
+ spec.add_runtime_dependency "fluentd", [">= 1.0", "< 2"]
28
+ spec.add_runtime_dependency "application_insights", "~> 0.5.5"
29
+ end
@@ -0,0 +1,228 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ require 'json'
5
+ require 'fluent/plugin/output'
6
+ require 'application_insights'
7
+
8
+ module Fluent::Plugin
9
+ class ApplicationInsightsOutput < Output
10
+ Fluent::Plugin.register_output("application_insights", self)
11
+
12
+ attr_accessor :tc
13
+
14
+ helpers :record_accessor
15
+
16
+ # The Application Insights instrumentation key
17
+ config_param :instrumentation_key, :string
18
+ # The service endpoint uri to push the telemetry to
19
+ config_param :service_endpoint_uri, :string, default: nil
20
+ # The batch size to send data to Application Insights service.
21
+ config_param :send_buffer_size, :integer, default: 1000
22
+ # The parameter indication whether the record is in standard schema. i.e., the format that is recognized by Application Insights backend.
23
+ config_param :standard_schema, :bool, default: false
24
+ # The property name for the message. It will be ignored if the record is in standard schema.
25
+ config_param :message_property, :string, default: 'message'
26
+ # The property name for the timestamp. It will be ignored if the record is in standard schema.
27
+ config_param :time_property, :string, default: nil
28
+ # The property name for severity level. It will be ignored if the record is in standard schema.
29
+ config_param :severity_property, :string, default: 'severity'
30
+ # The value of severity property that maps to Application Insights' verbose severity level.
31
+ config_param :severity_level_verbose, :array, value_type: :string, default: ['verbose']
32
+ # The value of severity property that maps to Application Insights' information severity level.
33
+ config_param :severity_level_information, :array, value_type: :string, default: ['information']
34
+ # The value of severity property that maps to Application Insights' warning severity level.
35
+ config_param :severity_level_warning, :array, value_type: :string, default: ['warning']
36
+ # The value of severity property that maps to Application Insights' error severity level.
37
+ config_param :severity_level_error, :array, value_type: :string, default: ['error']
38
+ # The value of severity property that maps to Application Insights' critical severity level.
39
+ config_param :severity_level_critical, :array, value_type: :string, default: ['critical']
40
+ # The dictionary that instructs the Application Insights plugin to set Application Insights context tags using record properties.
41
+ # In this dictionary keys are Application Insights context tags to set, and values are names of properties to use as source of data.
42
+ config_param :context_tag_sources, :hash, default: {}, value_type: :string
43
+
44
+ TELEMETRY_TYPES = ["RequestData", "RemoteDependencyData", "MessageData", "ExceptionData", "EventData", "MetricData", "PageViewData", "AvailabilityData"]
45
+
46
+ def configure(conf)
47
+ super
48
+
49
+ @severity_level_mapping = {}
50
+ @severity_level_verbose.each { |l| @severity_level_mapping[l.downcase] = Channel::Contracts::SeverityLevel::VERBOSE }
51
+ @severity_level_information.each { |l| @severity_level_mapping[l.downcase] = Channel::Contracts::SeverityLevel::INFORMATION }
52
+ @severity_level_warning.each { |l| @severity_level_mapping[l.downcase] = Channel::Contracts::SeverityLevel::WARNING }
53
+ @severity_level_error.each { |l| @severity_level_mapping[l.downcase] = Channel::Contracts::SeverityLevel::ERROR }
54
+ @severity_level_critical.each { |l| @severity_level_mapping[l.downcase] = Channel::Contracts::SeverityLevel::CRITICAL }
55
+
56
+ context_tag_keys = []
57
+ context_tag_keys.concat Channel::Contracts::Application.json_mappings.values
58
+ context_tag_keys.concat Channel::Contracts::Cloud.json_mappings.values
59
+ context_tag_keys.concat Channel::Contracts::Device.json_mappings.values
60
+ context_tag_keys.concat Channel::Contracts::Internal.json_mappings.values
61
+ context_tag_keys.concat Channel::Contracts::Location.json_mappings.values
62
+ context_tag_keys.concat Channel::Contracts::Operation.json_mappings.values
63
+ context_tag_keys.concat Channel::Contracts::Session.json_mappings.values
64
+ context_tag_keys.concat Channel::Contracts::User.json_mappings.values
65
+
66
+ @context_tag_accessors = {}
67
+ context_tag_sources.each do |tag, property_path|
68
+ raise ArgumentError.new("Context tag '#{tag}' is invalid!") unless context_tag_keys.include?(tag)
69
+
70
+ @context_tag_accessors[tag] = record_accessor_create(property_path)
71
+ end
72
+ end
73
+
74
+ def start
75
+ super
76
+
77
+ sender = @service_endpoint_uri.nil? ? Channel::AsynchronousSender.new : Channel::AsynchronousSender.new(@service_endpoint_uri)
78
+ queue = Channel::AsynchronousQueue.new sender
79
+ channel = Channel::TelemetryChannel.new nil, queue
80
+ @tc = TelemetryClient.new @instrumentation_key, channel
81
+ @tc.channel.queue.max_queue_length = @send_buffer_size
82
+ @tc.channel.sender.send_buffer_size = @send_buffer_size
83
+ end
84
+
85
+ def shutdown
86
+ super
87
+
88
+ # Draining the events in the queue.
89
+ # We need to make sure the work thread has finished. Otherwise, it's possible that the queue is empty, but the http request to send the data is not finished.
90
+ # However, a drawback of waiting for the work thread to finish is that even if the events have been drained, it will still poll the queue for some time (default is 3 seconds, set by sender.send_time).
91
+ # This can be improved if the SDK exposes another variable indicating whether the work thread is sending data or just polling the queue.
92
+ while !@tc.channel.queue.empty? || @tc.channel.sender.work_thread != nil
93
+ # It's possible that the work thread has already exited but there are still items in the queue.
94
+ # https://github.com/Microsoft/ApplicationInsights-Ruby/blob/master/lib/application_insights/channel/asynchronous_sender.rb#L115
95
+ # Trigger flush to make the work thread working again in this case.
96
+ if @tc.channel.sender.work_thread == nil && !@tc.channel.queue.empty?
97
+ @tc.flush
98
+ end
99
+
100
+ sleep(1)
101
+ end
102
+ end
103
+
104
+ def process(tag, es)
105
+ es.each do |time, record|
106
+ # 'time' is a Fluent::EventTime object or an Integer. Convert it to ruby Time object.
107
+ time_ruby = time.is_a?(Fluent::EventTime) ? Time.at(time.sec, time.nsec / 1000).utc : Time.at(time)
108
+ if @standard_schema
109
+ process_standard_schema_log record, time_ruby
110
+ else
111
+ process_non_standard_schema_log record, time_ruby
112
+ end
113
+ end
114
+ end
115
+
116
+ def process_standard_schema_log(record, time)
117
+ if record["data"] && record["data"].is_a?(Hash) && record["data"]["baseType"] && record["data"]["baseData"]
118
+ base_type = record["data"]["baseType"]
119
+
120
+ if TELEMETRY_TYPES.include? base_type
121
+ # If the record is processed by json parser plugin, e.g., in_http use it by default, the time property will be removed. Add it back in this case.
122
+ record["time"] ||= time.iso8601(7)
123
+ record["iKey"] = @instrumentation_key
124
+ set_name_property(record) if !record["name"]
125
+ set_context_standard_schema record
126
+
127
+ envelope = Channel::Contracts::Envelope.new
128
+ Channel::Contracts::Envelope.json_mappings.each do |attr, name|
129
+ property = record.delete(name)
130
+ envelope.send(:"#{attr}=", property) if property
131
+ end
132
+
133
+ # There could be extra properties added during the fluentd pipeline. Merge the extra properties so they are not lost.
134
+ merge_extra_properties_standard_schema record, envelope
135
+
136
+ @tc.channel.queue.push(envelope)
137
+ else
138
+ log.debug "Unknown telemetry type #{base_type}. Event will be treated as as non standard schema event."
139
+ process_non_standard_schema_log record, time
140
+ end
141
+ else
142
+ log.debug "The event does not meet the standard schema of Application Insights output. Missing data, baseType or baseData property. Event will be treated as as non standard schema event."
143
+ process_non_standard_schema_log record, time
144
+ end
145
+ end
146
+
147
+ def set_name_property(record)
148
+ normalizedIKey = @instrumentation_key.gsub("-", "")
149
+ normalizedIKey = normalizedIKey.empty? ? "" : normalizedIKey + "."
150
+ type = record["data"]["baseType"][0..-5]
151
+ record["name"] = "Microsoft.ApplicationInsights.#{normalizedIKey}#{type}"
152
+ end
153
+
154
+ def set_context_standard_schema(record)
155
+ return if @context_tag_sources.length == 0
156
+
157
+ record["tags"] = record["tags"] || {}
158
+ @context_tag_accessors.each do |tag, accessor|
159
+ tag_value = accessor.call(record)
160
+ record["tags"][tag] = tag_value if !tag_value.nil?
161
+ end
162
+ end
163
+
164
+ def merge_extra_properties_standard_schema(record, envelope)
165
+ return if record.empty?
166
+
167
+ envelope.data["baseData"]["properties"] ||= {}
168
+ envelope.data["baseData"]["properties"].merge!(record)
169
+ stringify_properties(envelope.data["baseData"]["properties"])
170
+ end
171
+
172
+ def process_non_standard_schema_log(record, time)
173
+ time = record.delete(@time_property) || time
174
+ context = get_context_non_standard_schema(record)
175
+ message = record.delete @message_property
176
+ severity_level_value = record.delete @severity_property
177
+ severity_level = severity_level_value ? @severity_level_mapping[severity_level_value.to_s.downcase] : nil
178
+ props = stringify_properties(record)
179
+
180
+ data = Channel::Contracts::MessageData.new(
181
+ :message => message || 'Null',
182
+ :severity_level => severity_level || Channel::Contracts::SeverityLevel::INFORMATION,
183
+ :properties => props || {}
184
+ )
185
+
186
+ @tc.channel.write(data, context, time)
187
+ end
188
+
189
+ def get_context_non_standard_schema(record)
190
+ context = Channel::TelemetryContext.new
191
+ context.instrumentation_key = @instrumentation_key
192
+ return context if @context_tag_sources.length == 0
193
+
194
+ @context_tag_accessors.each do |tag, accessor|
195
+ set_context_tag context, tag, accessor.call(record)
196
+ end
197
+
198
+ return context
199
+ end
200
+
201
+ def set_context_tag(context, tag_name, tag_value)
202
+ return if tag_value.nil?
203
+
204
+ context_set = [context.application, context.cloud, context.device, context.location, context.operation, context.session, context.user]
205
+ context_set.each do |c|
206
+ c.class.json_mappings.each do |attr, name|
207
+ if (name == tag_name)
208
+ c.send(:"#{attr}=", tag_value)
209
+ return
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ def stringify_properties(record)
216
+ # If the property value is a json object or array, e.g., {"prop": {"inner_prop": value}}, it needs to be serialized.
217
+ # Otherwise, the property will become {"prop": "[object Object]"} in the final telemetry.
218
+ # The stringified property can be queried as described here: https://docs.loganalytics.io/docs/Language-Reference/Scalar-functions/parse_json()
219
+ record.each do |key, value|
220
+ if value.is_a?(Hash) || value.is_a?(Array)
221
+ record[key] = JSON.generate(value)
222
+ end
223
+ end
224
+ record
225
+ end
226
+
227
+ end
228
+ end
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.expand_path("../../", __FILE__))
2
+ require "test-unit"
3
+ require "fluent/test"
4
+ require "fluent/test/driver/output"
5
+ require "fluent/test/helpers"
6
+
7
+ Test::Unit::TestCase.include(Fluent::Test::Helpers)
8
+ Test::Unit::TestCase.extend(Fluent::Test::Helpers)
@@ -0,0 +1,37 @@
1
+ require "application_insights"
2
+
3
+ class MockSender < Channel::SenderBase
4
+ def initialize
5
+ end
6
+
7
+ def start
8
+ end
9
+ end
10
+
11
+ class MockQueue
12
+ attr_accessor :sender
13
+ attr_accessor :queue
14
+
15
+ def initialize(sender)
16
+ @sender = sender
17
+ @queue = []
18
+ end
19
+
20
+ def push(item)
21
+ return unless item
22
+ @queue << item
23
+ end
24
+
25
+ def empty?
26
+ @queue.empty?
27
+ end
28
+
29
+ def [](index)
30
+ @queue[index]
31
+ end
32
+
33
+ def flush
34
+ @queue = []
35
+ end
36
+ end
37
+
@@ -0,0 +1,570 @@
1
+ require "helper"
2
+ require "fluent/plugin/out_application_insights.rb"
3
+ require_relative "mock_client.rb"
4
+
5
+ class ApplicationInsightsOutputTest < Test::Unit::TestCase
6
+ setup do
7
+ Fluent::Test.setup
8
+ end
9
+
10
+ CONFIG = %[
11
+ instrumentation_key ikey
12
+ ]
13
+
14
+ def create_driver(conf = CONFIG)
15
+ driver = Fluent::Test::Driver::Output.new(Fluent::Plugin::ApplicationInsightsOutput).configure(conf)
16
+ driver.instance.start
17
+
18
+ sender = MockSender.new
19
+ queue = MockQueue.new sender
20
+ channel = ApplicationInsights::Channel::TelemetryChannel.new nil, queue
21
+ driver.instance.tc = ApplicationInsights::TelemetryClient.new "iKey", channel
22
+
23
+ return driver
24
+ end
25
+
26
+ sub_test_case 'configure' do
27
+ test 'invalid context tag key' do
28
+ config = %[
29
+ instrumentation_key ikey
30
+ context_tag_sources invalid_tag_name:kubernetes_container_name
31
+ ]
32
+ assert_raise ArgumentError.new("Context tag 'invalid_tag_name' is invalid!") do
33
+ create_driver config
34
+ end
35
+ end
36
+ end
37
+
38
+ sub_test_case 'process standard schema event' do
39
+ setup do
40
+ config = %[
41
+ instrumentation_key ikey
42
+ standard_schema true
43
+ ]
44
+
45
+ @d = create_driver config
46
+ end
47
+
48
+ test 'ikey and timestamps are added if empty' do
49
+ time = event_time("2011-01-02 13:14:15 UTC")
50
+ @d.run(default_tag: 'test', shutdown: false) do
51
+ @d.feed(time, {"name" => "telemetry name", "data" => { "baseType" => "RequestData", "baseData" => {} }})
52
+ end
53
+
54
+ envelope = @d.instance.tc.channel.queue[0]
55
+ assert_equal "ikey", envelope.i_key
56
+ assert_equal "2011-01-02T13:14:15.0000000Z", envelope.time
57
+ end
58
+
59
+ test 'event missing required properties is treated as non standard schema' do
60
+ time = event_time("2011-01-02 13:14:15 UTC")
61
+ @d.instance.log.level = "debug"
62
+ @d.run(default_tag: 'test', shutdown: false) do
63
+ @d.feed(time, {})
64
+ @d.feed(time, {"data" => 2})
65
+ @d.feed(time, {"data" => {}})
66
+ @d.feed(time, {"data" => {"someprop" => "value"}})
67
+ @d.feed(time, {"data" => {"baseType" => "type"}})
68
+ @d.feed(time, {"data" => {"baseData" => "data"}})
69
+ end
70
+
71
+ logs = @d.instance.log.out.logs
72
+ assert_equal 6, logs.length
73
+ assert_true logs.all?{ |log| log.include?("The event does not meet the standard schema of Application Insights output. Missing data, baseType or baseData property.") }
74
+ end
75
+
76
+ test 'event of standard schema will fill in the name property if it is empty' do
77
+ config = %[
78
+ instrumentation_key 000-000-000
79
+ standard_schema true
80
+ ]
81
+ d = create_driver config
82
+
83
+ time = event_time("2011-01-02 13:14:15 UTC")
84
+ d.run(default_tag: 'test', shutdown: false) do
85
+ d.feed(time, {
86
+ "data" => { "baseType" => "RequestData", "baseData" => {} }
87
+ })
88
+ end
89
+
90
+ envelope = d.instance.tc.channel.queue[0]
91
+ assert_equal "Microsoft.ApplicationInsights.000000000.Request", envelope.name
92
+ end
93
+
94
+ test 'event of standard schema will fill in the name property if it is empty and instrumentation key is empty' do
95
+ config = %[
96
+ instrumentation_key ""
97
+ standard_schema true
98
+ ]
99
+ d = create_driver config
100
+
101
+ time = event_time("2011-01-02 13:14:15 UTC")
102
+ d.run(default_tag: 'test', shutdown: false) do
103
+ d.feed(time, {
104
+ "data" => { "baseType" => "RequestData", "baseData" => {} }
105
+ })
106
+ end
107
+
108
+ envelope = d.instance.tc.channel.queue[0]
109
+ assert_equal "Microsoft.ApplicationInsights.Request", envelope.name
110
+ end
111
+
112
+ test 'event with unknown data type is treated as non standard schema' do
113
+ time = event_time("2011-01-02 13:14:15 UTC")
114
+ @d.instance.log.level = "debug"
115
+ @d.run(default_tag: 'test', shutdown: false) do
116
+ @d.feed(time, {"name" => "telemetry name", "data" => {"baseType" => "unknown", "baseData" => {}}})
117
+ end
118
+
119
+ logs = @d.instance.log.out.logs
120
+ assert_true logs.length > 0
121
+ assert_true logs.all?{ |log| log.include?("Unknown telemetry type unknown") }
122
+ end
123
+
124
+ test 'extra properties are not lost after processing' do
125
+ time = event_time("2011-01-02 13:14:15 UTC")
126
+ @d.run(default_tag: 'test', shutdown: false) do
127
+ @d.feed(time, {"name" => "telemetry name", "data" => { "baseType" => "RequestData", "baseData" => {} }, "kubernetes" => { "pod_name" => "frontend" }})
128
+ end
129
+
130
+ envelope = @d.instance.tc.channel.queue[0]
131
+ assert_equal({ "kubernetes" => "{\"pod_name\":\"frontend\"}" }, envelope.data["baseData"]["properties"])
132
+ end
133
+ end
134
+
135
+ sub_test_case 'set context on standard schema event' do
136
+ test 'context tag sources is empty' do
137
+ config = %[
138
+ instrumentation_key ikey
139
+ standard_schema true
140
+ context_tag_sources {}
141
+ ]
142
+ d = create_driver config
143
+
144
+ time = event_time("2011-01-02 13:14:15 UTC")
145
+ d.run(default_tag: 'test', shutdown: false) do
146
+ d.feed(time, {
147
+ "name" => "telemetry name",
148
+ "data" => { "baseType" => "RequestData", "baseData" => {} },
149
+ "kubernetes_container_name" => "frontend"
150
+ })
151
+ end
152
+
153
+ envelope = d.instance.tc.channel.queue[0]
154
+ assert_true envelope.tags.length == 0
155
+ end
156
+
157
+ test 'context tag sources does not exist on record' do
158
+ config = %[
159
+ instrumentation_key ikey
160
+ standard_schema true
161
+ context_tag_sources ai.cloud.role:kubernetes_container_name
162
+ ]
163
+ d = create_driver config
164
+
165
+ time = event_time("2011-01-02 13:14:15 UTC")
166
+ d.run(default_tag: 'test', shutdown: false) do
167
+ d.feed(time, {
168
+ "name" => "telemetry name",
169
+ "data" => { "baseType" => "RequestData", "baseData" => {} }
170
+ })
171
+ end
172
+
173
+ envelope = d.instance.tc.channel.queue[0]
174
+ assert_not_nil envelope.tags
175
+ assert_nil envelope.tags["ai.cloud.role"]
176
+ end
177
+
178
+ test 'context is updated according to context tag keys' do
179
+ config = %[
180
+ instrumentation_key ikey
181
+ standard_schema true
182
+ context_tag_sources ai.cloud.role:kubernetes_container_name
183
+ ]
184
+ d = create_driver config
185
+
186
+ time = event_time("2011-01-02 13:14:15 UTC")
187
+ d.run(default_tag: 'test', shutdown: false) do
188
+ d.feed(time, {
189
+ "name" => "telemetry name",
190
+ "data" => { "baseType" => "RequestData", "baseData" => {} },
191
+ "kubernetes_container_name" => "frontend",
192
+ "other_prop" => "prop value"
193
+ })
194
+ end
195
+
196
+ envelope = d.instance.tc.channel.queue[0]
197
+ assert_not_nil envelope.tags
198
+ assert_equal "frontend", envelope.tags["ai.cloud.role"]
199
+
200
+ extra_properties = {
201
+ "kubernetes_container_name" => "frontend",
202
+ "other_prop" => "prop value"
203
+ }
204
+ assert_equal extra_properties, envelope.data["baseData"]["properties"]
205
+ end
206
+
207
+ test 'context tag source is nested property path' do
208
+ config = %[
209
+ instrumentation_key ikey
210
+ standard_schema true
211
+ context_tag_sources {
212
+ "ai.cloud.role": "$.kubernetes.container_name",
213
+ "ai.cloud.roleInstance": "$.kubernetes.not_exist"
214
+ }
215
+ ]
216
+ d = create_driver config
217
+
218
+ time = event_time("2011-01-02 13:14:15 UTC")
219
+ d.run(default_tag: 'test', shutdown: false) do
220
+ d.feed(time, {
221
+ "name" => "telemetry name",
222
+ "data" => { "baseType" => "RequestData", "baseData" => {} },
223
+ "kubernetes" => {
224
+ "container_name" => "frontend",
225
+ "container_id" => "c42c557e1615511dd3a3cb1d6e8f14984464bb0f"
226
+ }
227
+ })
228
+ end
229
+
230
+ envelope = d.instance.tc.channel.queue[0]
231
+ assert_not_nil envelope.tags
232
+ assert_equal "frontend", envelope.tags["ai.cloud.role"]
233
+ assert_nil envelope.tags["ai.cloud.roleInstance"]
234
+ end
235
+
236
+ test 'multiple context tag keys' do
237
+ config = %[
238
+ instrumentation_key ikey
239
+ standard_schema true
240
+ context_tag_sources {
241
+ "ai.cloud.role": "kubernetes_container_name",
242
+ "ai.cloud.roleInstance": "$.docker.container_id"
243
+ }
244
+ ]
245
+ d = create_driver config
246
+
247
+ time = event_time("2011-01-02 13:14:15 UTC")
248
+ d.run(default_tag: 'test', shutdown: false) do
249
+ d.feed(time, {
250
+ "name" => "telemetry name",
251
+ "data" => { "baseType" => "RequestData", "baseData" => {} },
252
+ "docker" => {
253
+ "container_id" => "c42c557e1615511dd3a3cb1d6e8f14984464bb0f"
254
+ },
255
+ "kubernetes_container_name" => "frontend"
256
+ })
257
+ end
258
+
259
+ envelope = d.instance.tc.channel.queue[0]
260
+ assert_not_nil envelope.tags
261
+ assert_equal "frontend", envelope.tags["ai.cloud.role"]
262
+ assert_equal "c42c557e1615511dd3a3cb1d6e8f14984464bb0f", envelope.tags["ai.cloud.roleInstance"]
263
+ end
264
+ end
265
+
266
+ sub_test_case 'process non standard schema event' do
267
+ test 'empty message' do
268
+ d = create_driver
269
+
270
+ time = event_time("2011-01-02 13:14:15 UTC")
271
+ d.run(default_tag: 'test', shutdown: false) do
272
+ d.feed(time, {"prop" => "value"})
273
+ end
274
+
275
+ assert_equal 1, d.instance.tc.channel.queue.queue.length
276
+ envelope = d.instance.tc.channel.queue[0]
277
+ assert_equal "Null", envelope.data.base_data.message
278
+ assert_equal envelope.data.base_data.properties, {"prop" => "value"}
279
+ end
280
+
281
+ test 'custom timestamp take precedence over fluentd timestamp' do
282
+ config = %[
283
+ instrumentation_key ikey
284
+ time_property time
285
+ ]
286
+ d = create_driver config
287
+
288
+ time = event_time("2011-01-02 13:14:15 UTC")
289
+ d.run(default_tag: 'test', shutdown: false) do
290
+ d.feed(time, {"time" => "2010-10-01"})
291
+ end
292
+
293
+ envelope = d.instance.tc.channel.queue[0]
294
+ assert_equal "2010-10-01", envelope.time
295
+ end
296
+
297
+ test 'custom timestamp format is not ensured to be valid' do
298
+ config = %[
299
+ instrumentation_key ikey
300
+ time_property time
301
+ ]
302
+ d = create_driver config
303
+
304
+ time = event_time("2011-01-02 13:14:15 UTC")
305
+ d.run(default_tag: 'test', shutdown: false) do
306
+ d.feed(time, {"time" => "custom time"})
307
+ end
308
+
309
+ envelope = d.instance.tc.channel.queue[0]
310
+ assert_equal "custom time", envelope.time
311
+ end
312
+
313
+ test 'timestamp is in iso8601 format' do
314
+ d = create_driver
315
+
316
+ time = event_time("2011-01-02 13:14:15 UTC")
317
+ d.run(default_tag: 'test', shutdown: false) do
318
+ d.feed(time, {"message" => "log message"})
319
+ end
320
+
321
+ envelope = d.instance.tc.channel.queue[0]
322
+ assert_equal "2011-01-02T13:14:15.0000000Z", envelope.time
323
+ end
324
+
325
+ test 'custom message property' do
326
+ config = %[
327
+ instrumentation_key ikey
328
+ message_property custom_message_property
329
+ ]
330
+ d = create_driver config
331
+
332
+ time = event_time("2011-01-02 13:14:15 UTC")
333
+ d.run(default_tag: 'test', shutdown: false) do
334
+ d.feed(time, {"custom_message_property" => "custom message", "message" => "my message"})
335
+ end
336
+
337
+ envelope = d.instance.tc.channel.queue[0]
338
+ assert_equal "custom message", envelope.data.base_data.message
339
+ end
340
+
341
+ test 'custom severity level mapping' do
342
+ config = %[
343
+ instrumentation_key ikey
344
+ severity_property custom_severity_property
345
+ severity_level_error 100
346
+ ]
347
+ d = create_driver config
348
+
349
+ time = event_time("2011-01-02 13:14:15 UTC")
350
+ d.run(default_tag: 'test', shutdown: false) do
351
+ d.feed(time, {"custom_severity_property" => 100, "message" => "my message"})
352
+ end
353
+
354
+ envelope = d.instance.tc.channel.queue[0]
355
+ assert_equal ApplicationInsights::Channel::Contracts::SeverityLevel::ERROR, envelope.data.base_data.severity_level
356
+ end
357
+
358
+ test 'multiple severity levels' do
359
+ config = %[
360
+ instrumentation_key ikey
361
+ severity_property custom_severity_property
362
+ severity_level_critical fatal, panic
363
+ ]
364
+ d = create_driver config
365
+
366
+ time = event_time("2011-01-02 13:14:15 UTC")
367
+ d.run(default_tag: 'test', shutdown: false) do
368
+ d.feed(time, {"custom_severity_property" => "panic", "message" => "my message"})
369
+ end
370
+
371
+ d.run(default_tag: 'test', shutdown: false) do
372
+ d.feed(time, {"custom_severity_property" => "fatal", "message" => "my message"})
373
+ end
374
+
375
+ d.run(default_tag: 'test', shutdown: false) do
376
+ d.feed(time, {"custom_severity_property" => "critical", "message" => "my message"})
377
+ end
378
+
379
+ envelope = d.instance.tc.channel.queue[0]
380
+ assert_equal ApplicationInsights::Channel::Contracts::SeverityLevel::CRITICAL, envelope.data.base_data.severity_level
381
+
382
+ envelope = d.instance.tc.channel.queue[1]
383
+ assert_equal ApplicationInsights::Channel::Contracts::SeverityLevel::CRITICAL, envelope.data.base_data.severity_level
384
+
385
+ envelope = d.instance.tc.channel.queue[2]
386
+ assert_not_equal ApplicationInsights::Channel::Contracts::SeverityLevel::CRITICAL, envelope.data.base_data.severity_level
387
+ end
388
+
389
+ test 'properties are stringified' do
390
+ d = create_driver
391
+
392
+ time = event_time("2011-01-02 13:14:15 UTC")
393
+ d.run(default_tag: 'test', shutdown: false) do
394
+ d.feed(time, {"prop" => {"inner_prop1" => "value1", "inner_prop2" => "value2"}})
395
+ end
396
+
397
+ envelope = d.instance.tc.channel.queue[0]
398
+ assert_equal 1, envelope.data.base_data.properties.length
399
+ assert_equal "{\"inner_prop1\":\"value1\",\"inner_prop2\":\"value2\"}", envelope.data.base_data.properties["prop"]
400
+ end
401
+ end
402
+
403
+ sub_test_case 'set context on non standard schema event' do
404
+ test 'context tag sources is empty' do
405
+ config = %[
406
+ instrumentation_key ikey
407
+ context_tag_sources {}
408
+ ]
409
+ d = create_driver config
410
+
411
+ time = event_time("2011-01-02 13:14:15 UTC")
412
+ d.run(default_tag: 'test', shutdown: false) do
413
+ d.feed(time, {
414
+ "message" => "my message",
415
+ "kubernetes_container_name" => "frontend"
416
+ })
417
+ end
418
+
419
+ envelope = d.instance.tc.channel.queue[0]
420
+
421
+ # The only tag is "ai.internal.sdkVersion", which is irrelevant to contaxt_tag_sources
422
+ assert_true envelope.tags.length == 1
423
+ assert_equal "ai.internal.sdkVersion", envelope.tags.keys[0]
424
+ assert_equal "ikey", envelope.i_key
425
+ end
426
+
427
+ test 'context tag sources does not exist on record' do
428
+ config = %[
429
+ instrumentation_key ikey
430
+ context_tag_sources ai.cloud.role:kubernetes_container_name
431
+ ]
432
+ d = create_driver config
433
+
434
+ time = event_time("2011-01-02 13:14:15 UTC")
435
+ d.run(default_tag: 'test', shutdown: false) do
436
+ d.feed(time, {
437
+ "message" => "my message"
438
+ })
439
+ end
440
+
441
+ envelope = d.instance.tc.channel.queue[0]
442
+ assert_not_nil envelope.tags
443
+ assert_nil envelope.tags["ai.cloud.role"]
444
+ end
445
+
446
+ test 'context is updated according to context tag keys' do
447
+ config = %[
448
+ instrumentation_key ikey
449
+ context_tag_sources ai.cloud.role:kubernetes_container_name
450
+ ]
451
+ d = create_driver config
452
+
453
+ time = event_time("2011-01-02 13:14:15 UTC")
454
+ d.run(default_tag: 'test', shutdown: false) do
455
+ d.feed(time, {
456
+ "message" => "my message",
457
+ "kubernetes_container_name" => "frontend",
458
+ "other_prop" => "prop value"
459
+ })
460
+ end
461
+
462
+ envelope = d.instance.tc.channel.queue[0]
463
+ assert_not_nil envelope.tags
464
+ assert_equal "frontend", envelope.tags["ai.cloud.role"]
465
+ assert_not_nil envelope.data.base_data.properties["kubernetes_container_name"]
466
+ assert_not_nil envelope.data.base_data.properties["other_prop"]
467
+ end
468
+
469
+ test 'context tag source is nested property path' do
470
+ config = %[
471
+ instrumentation_key ikey
472
+ context_tag_sources {
473
+ "ai.cloud.role": "$.kubernetes.container_name",
474
+ "ai.cloud.roleInstance": "$.kubernetes.not_exist"
475
+ }
476
+ ]
477
+ d = create_driver config
478
+
479
+ time = event_time("2011-01-02 13:14:15 UTC")
480
+ d.run(default_tag: 'test', shutdown: false) do
481
+ d.feed(time, {
482
+ "name" => "telemetry name",
483
+ "data" => { "baseType" => "RequestData", "baseData" => {} },
484
+ "kubernetes" => {
485
+ "container_name" => "frontend",
486
+ "container_id" => "c42c557e1615511dd3a3cb1d6e8f14984464bb0f"
487
+ }
488
+ })
489
+ end
490
+
491
+ envelope = d.instance.tc.channel.queue[0]
492
+ assert_not_nil envelope.tags
493
+ assert_equal "frontend", envelope.tags["ai.cloud.role"]
494
+ assert_nil envelope.tags["ai.cloud.roleInstance"]
495
+ end
496
+
497
+ test 'multiple context tag keys' do
498
+ config = %[
499
+ instrumentation_key ikey
500
+ context_tag_sources {
501
+ "ai.cloud.role": "kubernetes_container_name",
502
+ "ai.cloud.roleInstance": "$.docker.container_id"
503
+ }
504
+ ]
505
+ d = create_driver config
506
+
507
+ time = event_time("2011-01-02 13:14:15 UTC")
508
+ d.run(default_tag: 'test', shutdown: false) do
509
+ d.feed(time, {
510
+ "message" => "my message",
511
+ "docker" => {
512
+ "container_id" => "c42c557e1615511dd3a3cb1d6e8f14984464bb0f"
513
+ },
514
+ "kubernetes_container_name" => "frontend"
515
+ })
516
+ end
517
+
518
+ envelope = d.instance.tc.channel.queue[0]
519
+ assert_not_nil envelope.tags
520
+ assert_true envelope.tags.length == 3
521
+ assert_equal "frontend", envelope.tags["ai.cloud.role"]
522
+ assert_equal "c42c557e1615511dd3a3cb1d6e8f14984464bb0f", envelope.tags["ai.cloud.roleInstance"]
523
+ end
524
+ end
525
+
526
+ sub_test_case 'stringify_properties' do
527
+ test 'simple data type are not stringified' do
528
+ plugin = create_driver.instance
529
+
530
+ record = {prop1: 1, prop2: true, prop3: "value"}
531
+ actual = plugin.stringify_properties(record)
532
+ expected = {prop1: 1, prop2: true, prop3: "value"}
533
+ assert_equal expected, actual
534
+ end
535
+
536
+ test 'json and array property values are stringified' do
537
+ plugin = create_driver.instance
538
+
539
+ record = {prop1: 1, prop2: [1, 2, 3], prop3: {inner_prop: "value"}}
540
+ actual = plugin.stringify_properties(record)
541
+ expected = {prop1: 1, prop2: "[1,2,3]", prop3: "{\"inner_prop\":\"value\"}"}
542
+ assert_equal expected, actual
543
+ end
544
+
545
+ test 'stringify complicated property value' do
546
+ plugin = create_driver.instance
547
+
548
+ record = {
549
+ arr: [1, [2, [3, {inner: "value"}]]],
550
+ obj: {
551
+ arr: [1, {inarr: "inarr"}],
552
+ inobj: {
553
+ ininobj: {
554
+ prop: "value"
555
+ },
556
+ num: 3.14
557
+ }
558
+ }
559
+ }
560
+
561
+ actual = plugin.stringify_properties(record)
562
+ expected = {
563
+ :arr=> "[1,[2,[3,{\"inner\":\"value\"}]]]",
564
+ :obj=> "{\"arr\":[1,{\"inarr\":\"inarr\"}],\"inobj\":{\"ininobj\":{\"prop\":\"value\"},\"num\":3.14}}"
565
+ }
566
+ assert_equal expected, actual
567
+ end
568
+ end
569
+
570
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-application-insights-freiheit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.5
5
+ platform: ruby
6
+ authors:
7
+ - freiheit.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-11-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: fluentd
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: '2'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '1.0'
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '2'
75
+ - !ruby/object:Gem::Dependency
76
+ name: application_insights
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 0.5.5
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 0.5.5
89
+ description: Fluentd output plugin for Azure Application Insights.
90
+ email:
91
+ - xyz@freiheit.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - ".gitignore"
97
+ - ".travis.yml"
98
+ - CONTRIBUTING.md
99
+ - Gemfile
100
+ - LICENSE
101
+ - README.md
102
+ - Rakefile
103
+ - fluent-plugin-application-insights.gemspec
104
+ - lib/fluent/plugin/out_application_insights.rb
105
+ - test/helper.rb
106
+ - test/plugin/mock_client.rb
107
+ - test/plugin/test_out_application_insights.rb
108
+ homepage: https://github.com/dennis-tra/fluent-plugin-application-insights
109
+ licenses:
110
+ - MIT
111
+ metadata: {}
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.7.7
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: This is a fork of the fluentd output plugin for Azure Application Insights.
132
+ test_files:
133
+ - test/helper.rb
134
+ - test/plugin/mock_client.rb
135
+ - test/plugin/test_out_application_insights.rb