diff --git a/CHANGELOG.md b/CHANGELOG.md index 21ece27853..6d5b0663f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## dev -Version of the agent adds log-level filtering, an API to add custom attributes to logs, and updated instrumentation for Action Cable. It also provides fixes for how `Fiber` args are treated, Code-Level Metrics, and `NewRelic::Agent::Logging::DecoratingFormatter#clear_tags!` being incorrectly private. +Version of the agent adds log-level filtering, adds custom attributes for log events, and updates instrumentation for Action Cable. It also provides fixes for how `Fiber` args are treated, Code-Level Metrics, and `NewRelic::Agent::Logging::DecoratingFormatter#clear_tags!` being incorrectly private. - **Feature: Filter forwarded logs based on level** @@ -14,15 +14,22 @@ Version of the agent adds log-level filtering, an API to add custom attrib This setting uses [Ruby's Logger::Severity constants integer values](https://github.com/ruby/ruby/blob/master/lib/logger/severity.rb#L6-L17) to determine precedence. -- **Feature: Custom attributes for logs API** +- **Feature: Custom attributes for logs** - You can now add custom attributes to your log events using `NewRelic::Agent.add_custom_log_attributes`. + You can now add custom attributes to log events forwarded to New Relic! You can pass these attributes using an API and/or a configuration option. - For example: `NewRelic::Agent.add_custom_log_attributes(dyno: ENV['DYNO'], pod_name: ENV['POD_NAME'])`, will add the attributes `dyno` and `pod_name` to your log events. Attributes passed to this API will be added to all log events. + | Configuration name | Default | Behavior | + | --------------------------- | ------- | ------------------------------------------------------ | + | `application_logging.forwarding.custom_attributes` | `{}` | A hash with key/value pairs to add as custom attributes to all log events forwarded to New Relic. If sending using an environment variable, the value must be formatted like: "key1=value1,key2=value2" | + + + Call the API using `NewRelic::Agent.add_custom_log_attributes` and passing your attributes as a hash. For example, you could call: `NewRelic::Agent.add_custom_log_attributes(dyno: ENV['DYNO'], pod_name: ENV['POD_NAME'])`, to add the attributes `dyno` and `pod_name` to your log events. + + Attributes passed to the API or the configuration will be added to all log events. - Thanks to [@rajpawar02](https://github.com/rajpawar02) for raising this issue and [@askreet](https://github.com/askreet) for helping us with the solution. [Issue#1141](https://github.com/newrelic/newrelic-ruby-agent/issues/1141), [PR#2084](https://github.com/newrelic/newrelic-ruby-agent/pull/2084) + Thanks to [@rajpawar02](https://github.com/rajpawar02) for raising this issue and [@askreet](https://github.com/askreet) for helping us with the solution. [Issue#1141](https://github.com/newrelic/newrelic-ruby-agent/issues/1141), [PR#2084](https://github.com/newrelic/newrelic-ruby-agent/pull/2084), [PR#2087](https://github.com/newrelic/newrelic-ruby-agent/pull/2087) -- **Feature: Instrument transmit_subscription_* Action Cable actions** +- **Feature: Instrument transmit_subscription-related Action Cable actions** This change subscribes the agent to the Active Support notifications for: * `transmit_subscription_confirmation.action_cable` diff --git a/lib/new_relic/agent/configuration/default_source.rb b/lib/new_relic/agent/configuration/default_source.rb index 1ce5fd7910..a8d67d7fb8 100644 --- a/lib/new_relic/agent/configuration/default_source.rb +++ b/lib/new_relic/agent/configuration/default_source.rb @@ -211,6 +211,22 @@ def self.convert_to_list(value) end end + def self.convert_to_hash(value) + return value if value.is_a?(Hash) + + if value.is_a?(String) + return value.split(',').each_with_object({}) do |item, hash| + key, value = item.split('=') + hash[key] = value + end + end + + raise ArgumentError.new( + "Config value '#{value}' of " \ + "class #{value.class} couldn't be turned into a Hash." + ) + end + SEMICOLON = ';'.freeze def self.convert_to_list_on_semicolon(value) case value @@ -771,6 +787,14 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil) * "unknown" DESCRIPTION }, + :'application_logging.forwarding.custom_attributes' => { + :default => {}, + :public => true, + :type => Hash, + :transform => DefaultSource.method(:convert_to_hash), + :allowed_from_server => false, + :description => 'A hash with key/value pairs to add as custom attributes to all log events forwarded to New Relic. If sending using an environment variable, the value must be formatted like: "key1=value1,key2=value2"' + }, :'application_logging.forwarding.max_samples_stored' => { :default => 10000, :public => true, diff --git a/lib/new_relic/agent/log_event_aggregator.rb b/lib/new_relic/agent/log_event_aggregator.rb index ec49eae763..2af7cd622b 100644 --- a/lib/new_relic/agent/log_event_aggregator.rb +++ b/lib/new_relic/agent/log_event_aggregator.rb @@ -38,6 +38,7 @@ class LogEventAggregator < EventAggregator FORWARDING_ENABLED_KEY = :'application_logging.forwarding.enabled' DECORATING_ENABLED_KEY = :'application_logging.local_decorating.enabled' LOG_LEVEL_KEY = :'application_logging.forwarding.log_level' + CUSTOM_ATTRIBUTES_KEY = :'application_logging.forwarding.custom_attributes' attr_reader :attributes @@ -181,6 +182,8 @@ def register_for_done_configuring(events) record_configuration_metric(METRICS_SUPPORTABILITY_FORMAT, METRICS_ENABLED_KEY) record_configuration_metric(FORWARDING_SUPPORTABILITY_FORMAT, FORWARDING_ENABLED_KEY) record_configuration_metric(DECORATING_SUPPORTABILITY_FORMAT, DECORATING_ENABLED_KEY) + + add_custom_attributes(NewRelic::Agent.config[CUSTOM_ATTRIBUTES_KEY]) end end diff --git a/test/new_relic/agent/configuration/default_source_test.rb b/test/new_relic/agent/configuration/default_source_test.rb index bc6e0e8db5..a672567ec8 100644 --- a/test/new_relic/agent/configuration/default_source_test.rb +++ b/test/new_relic/agent/configuration/default_source_test.rb @@ -254,6 +254,25 @@ def test_instrumentation_logger_matches_application_logging_disabled end end + def test_convert_to_hash_returns_hash + result = {'key1' => 'value1', 'key2' => 'value2'} + + assert_equal(DefaultSource.convert_to_hash(result), result) + end + + def test_convert_to_hash_with_string + value = 'key1=value1,key2=value2' + result = {'key1' => 'value1', 'key2' => 'value2'} + + assert_equal(DefaultSource.convert_to_hash(value), result) + end + + def test_convert_to_hash_raises_error_with_wrong_data_type + value = [1, 2, 3] + + assert_raises(ArgumentError) { DefaultSource.convert_to_hash(value) } + end + def get_config_value_class(value) type = value.class diff --git a/test/new_relic/agent/log_event_attributes_test.rb b/test/new_relic/agent/log_event_attributes_test.rb index fca543630c..85eb0812f2 100644 --- a/test/new_relic/agent/log_event_attributes_test.rb +++ b/test/new_relic/agent/log_event_attributes_test.rb @@ -211,5 +211,18 @@ def test_drops_attribute_pair_if_invalid_value_class logger.verify end end + + def test_log_attributes_from_config + key = 'configured' + value = 'value' + + with_config( + :'application_logging.forwarding.custom_attributes' => {key => value} + ) do + NewRelic::Agent.config.notify_server_source_added + + assert_includes(common_attributes_from_melt, key) + end + end end end