From d7286570185fd841dbba6c863fed96c3a31da8ff Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 1 Mar 2024 15:06:32 -0800 Subject: [PATCH] [exporter/azuremonitor] Add environment variable support for connection string (#31523) This PR introduces the ability to configure the Azure Monitor Exporter connection string through the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable. Updated tests & readme. --- ...orter-connectionstring-envvar-support.yaml | 27 +++++++++++++++++ exporter/azuremonitorexporter/README.md | 17 +++++++++++ .../connection_string_parser.go | 19 ++++++++---- .../connection_string_parser_test.go | 30 +++++++++++++++++++ 4 files changed, 88 insertions(+), 5 deletions(-) create mode 100755 .chloggen/azuremonitorexporter-connectionstring-envvar-support.yaml diff --git a/.chloggen/azuremonitorexporter-connectionstring-envvar-support.yaml b/.chloggen/azuremonitorexporter-connectionstring-envvar-support.yaml new file mode 100755 index 000000000000..25c614ae76e0 --- /dev/null +++ b/.chloggen/azuremonitorexporter-connectionstring-envvar-support.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: azuremonitorexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Added support for configuring the Azure Monitor Exporter connection string via the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [31523] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/exporter/azuremonitorexporter/README.md b/exporter/azuremonitorexporter/README.md index d2d5f1ed4f25..168639863801 100644 --- a/exporter/azuremonitorexporter/README.md +++ b/exporter/azuremonitorexporter/README.md @@ -22,6 +22,14 @@ To configure the Azure Monitor Exporter, you must specify one of the following s - `connection_string` (recommended): The Azure Application Insights Connection String is required to send telemetry data to the monitoring service. It is the recommended method for configuring the exporter, aligning with Azure Monitor's best practices. If you need guidance on creating Azure resources, please refer to the step-by-step guides to [Create an Application Insights resource](https://docs.microsoft.com/azure/azure-monitor/app/create-new-resource) and [find your connection string](https://docs.microsoft.com/azure/azure-monitor/app/sdk-connection-string?tabs=net#find-your-connection-string). - `instrumentation_key`: Application Insights instrumentation key, which can be found in the Application Insights resource in the Azure Portal. While it is currently supported, its use is discouraged and it is slated for deprecation. It is highly encouraged to use the `connection_string` setting for new configurations and migrate existing configurations to use the `connection_string` as soon as possible. +### Environment Variable Support + +In addition to the above configuration options, the Azure Monitor Exporter now supports setting the connection string via the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable. This method is particularly useful for cloud or containerized environments where managing configuration through environment variables is standard practice. + +**Note:** If both the environment variable and the `connection_string` configuration option are provided, the environment variable takes precedence. + +### Configuration Options + **Important**: Only one of `connection_string` or `instrumentation_key` should be specified in your configuration. If both are provided, `connection_string` will be used as the priority setting. The following settings can be optionally configured: @@ -58,6 +66,15 @@ exporters: instrumentation_key: b1cd0778-85fc-4677-a3fa-79d3c23e0efd ``` +Example using environment variable: + +Ensure `APPLICATIONINSIGHTS_CONNECTION_STRING` is set in your environment, then configure the exporter without specifying a connection string or instrumentation key: + +```yaml +exporters: + azuremonitor: +``` + ## Attribute mapping ### Traces diff --git a/exporter/azuremonitorexporter/connection_string_parser.go b/exporter/azuremonitorexporter/connection_string_parser.go index fd1a7d00e862..064f09c8a6f6 100644 --- a/exporter/azuremonitorexporter/connection_string_parser.go +++ b/exporter/azuremonitorexporter/connection_string_parser.go @@ -6,6 +6,7 @@ package azuremonitorexporter // import "github.com/open-telemetry/opentelemetry- import ( "fmt" "net/url" + "os" "path" "strings" ) @@ -16,14 +17,22 @@ type ConnectionVars struct { } const ( - DefaultIngestionEndpoint = "https://dc.services.visualstudio.com/" - IngestionEndpointKey = "IngestionEndpoint" - InstrumentationKey = "InstrumentationKey" - ConnectionStringMaxLength = 4096 + ApplicationInsightsConnectionString = "APPLICATIONINSIGHTS_CONNECTION_STRING" + DefaultIngestionEndpoint = "https://dc.services.visualstudio.com/" + IngestionEndpointKey = "IngestionEndpoint" + InstrumentationKey = "InstrumentationKey" + ConnectionStringMaxLength = 4096 ) func parseConnectionString(exporterConfig *Config) (*ConnectionVars, error) { - connectionString := string(exporterConfig.ConnectionString) + // First, try to get the connection string from the environment variable + connectionString := os.Getenv(ApplicationInsightsConnectionString) + + // If not found in the environment, use the one from the configuration + if connectionString == "" { + connectionString = string(exporterConfig.ConnectionString) + } + instrumentationKey := string(exporterConfig.InstrumentationKey) connectionVars := &ConnectionVars{} diff --git a/exporter/azuremonitorexporter/connection_string_parser_test.go b/exporter/azuremonitorexporter/connection_string_parser_test.go index cff893d4cd46..dd0adb373c0b 100644 --- a/exporter/azuremonitorexporter/connection_string_parser_test.go +++ b/exporter/azuremonitorexporter/connection_string_parser_test.go @@ -4,6 +4,7 @@ package azuremonitorexporter import ( + "os" "strings" "testing" @@ -15,6 +16,7 @@ import ( func TestParseConnectionString(t *testing.T) { tests := []struct { name string + envValue string config *Config want *ConnectionVars wantError bool @@ -116,10 +118,38 @@ func TestParseConnectionString(t *testing.T) { want: nil, wantError: true, }, + { + name: "Environment variable only", + envValue: "InstrumentationKey=00000000-0000-0000-0000-00000000ENV;IngestionEndpoint=https://ingestion.env.azuremonitor.com/", + config: &Config{}, + want: &ConnectionVars{ + InstrumentationKey: "00000000-0000-0000-0000-00000000ENV", + IngestionURL: "https://ingestion.env.azuremonitor.com/v2.1/track", + }, + wantError: false, + }, + { + name: "Environment variable override", + envValue: "InstrumentationKey=00000000-0000-0000-0000-00000000ENV;IngestionEndpoint=https://ingestion.override.azuremonitor.com/", + config: &Config{ + ConnectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://ingestion.azuremonitor.com/", + InstrumentationKey: "00000000-0000-0000-0000-00000000IKEY", + }, + want: &ConnectionVars{ + InstrumentationKey: "00000000-0000-0000-0000-00000000ENV", + IngestionURL: "https://ingestion.override.azuremonitor.com/v2.1/track", + }, + wantError: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if tt.envValue != "" { + os.Setenv(ApplicationInsightsConnectionString, tt.envValue) + defer os.Unsetenv(ApplicationInsightsConnectionString) + } + got, err := parseConnectionString(tt.config) if tt.wantError { require.Error(t, err, "Expected an error but got none")