6565import java .util .logging .Level ;
6666import java .util .logging .Logger ;
6767import javax .annotation .concurrent .Immutable ;
68+ import software .amazon .opentelemetry .javaagent .providers .exporter .aws .metrics .AwsCloudWatchEmfExporter ;
6869import software .amazon .opentelemetry .javaagent .providers .exporter .otlp .aws .logs .OtlpAwsLogsExporterBuilder ;
6970import software .amazon .opentelemetry .javaagent .providers .exporter .otlp .aws .traces .OtlpAwsSpanExporterBuilder ;
7071
8687@ Immutable
8788public final class AwsApplicationSignalsCustomizerProvider
8889 implements AutoConfigurationCustomizerProvider {
90+ // https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-envvars.html
91+ static final String AWS_REGION = "AWS_REGION" ;
92+ static final String AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION" ;
8993 static final String AWS_LAMBDA_FUNCTION_NAME_CONFIG = "AWS_LAMBDA_FUNCTION_NAME" ;
9094 static final String LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT =
9195 "LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT" ;
@@ -103,6 +107,7 @@ public final class AwsApplicationSignalsCustomizerProvider
103107 // https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-OTLPEndpoint.html#CloudWatch-LogsEndpoint
104108 static final String AWS_OTLP_LOGS_GROUP_HEADER = "x-aws-log-group" ;
105109 static final String AWS_OTLP_LOGS_STREAM_HEADER = "x-aws-log-stream" ;
110+ static final String AWS_EMF_METRICS_NAMESPACE = "x-aws-metric-namespace" ;
106111
107112 private static final String DEPRECATED_SMP_ENABLED_CONFIG = "otel.smp.enabled" ;
108113 private static final String DEPRECATED_APP_SIGNALS_ENABLED_CONFIG =
@@ -132,7 +137,7 @@ public final class AwsApplicationSignalsCustomizerProvider
132137 private static final String OTEL_BSP_MAX_EXPORT_BATCH_SIZE_CONFIG =
133138 "otel.bsp.max.export.batch.size" ;
134139
135- private static final String OTEL_METRICS_EXPORTER = "otel.metrics.exporter" ;
140+ static final String OTEL_METRICS_EXPORTER = "otel.metrics.exporter" ;
136141 static final String OTEL_LOGS_EXPORTER = "otel.logs.exporter" ;
137142 static final String OTEL_TRACES_EXPORTER = "otel.traces.exporter" ;
138143 static final String OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = "otel.exporter.otlp.traces.protocol" ;
@@ -161,6 +166,7 @@ public final class AwsApplicationSignalsCustomizerProvider
161166 private static final int LAMBDA_SPAN_EXPORT_BATCH_SIZE = 10 ;
162167
163168 private Sampler sampler ;
169+ private boolean isEmfExporterEnabled = false ;
164170
165171 public void customize (AutoConfigurationCustomizer autoConfiguration ) {
166172 autoConfiguration .addPropertiesCustomizer (this ::customizeProperties );
@@ -171,6 +177,15 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) {
171177 autoConfiguration .addMeterProviderCustomizer (this ::customizeMeterProvider );
172178 autoConfiguration .addSpanExporterCustomizer (this ::customizeSpanExporter );
173179 autoConfiguration .addLogRecordExporterCustomizer (this ::customizeLogsExporter );
180+ autoConfiguration .addMetricExporterCustomizer (this ::customizeMetricExporter );
181+ }
182+
183+ private static Optional <String > getAwsRegionFromEnvironment () {
184+ String region = System .getenv (AWS_REGION );
185+ if (region != null ) {
186+ return Optional .of (region );
187+ }
188+ return Optional .ofNullable (System .getenv (AWS_DEFAULT_REGION ));
174189 }
175190
176191 static boolean isLambdaEnvironment () {
@@ -194,6 +209,14 @@ private Map<String, String> customizeProperties(ConfigProperties configProps) {
194209 Map <String , String > propsOverride = new HashMap <>();
195210 boolean isLambdaEnvironment = isLambdaEnvironment ();
196211
212+ // Check if awsemf was specified and remove it from OTEL_METRICS_EXPORTER
213+ String filteredExporters =
214+ AwsApplicationSignalsConfigUtils .removeEmfExporterIfEnabled (configProps );
215+ if (filteredExporters != null ) {
216+ this .isEmfExporterEnabled = true ;
217+ propsOverride .put (OTEL_METRICS_EXPORTER , filteredExporters );
218+ }
219+
197220 // Enable AWS Resource Providers
198221 propsOverride .put (OTEL_RESOURCE_PROVIDERS_AWS_ENABLED , "true" );
199222
@@ -394,7 +417,6 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder(
394417
395418 private SdkMeterProviderBuilder customizeMeterProvider (
396419 SdkMeterProviderBuilder sdkMeterProviderBuilder , ConfigProperties configProps ) {
397-
398420 if (isApplicationSignalsRuntimeEnabled (configProps )) {
399421 Set <String > registeredScopeNames = new HashSet <>(1 );
400422 String jmxRuntimeScopeName = "io.opentelemetry.jmx" ;
@@ -434,7 +456,7 @@ SpanExporter customizeSpanExporter(SpanExporter spanExporter, ConfigProperties c
434456 }
435457 }
436458
437- if (AwsApplicationSignalsConfigValidator .isSigV4EnabledTraces (configProps )) {
459+ if (AwsApplicationSignalsConfigUtils .isSigV4EnabledTraces (configProps )) {
438460 // can cast here since we've checked that the configuration for OTEL_TRACES_EXPORTER is otlp
439461 // and OTEL_EXPORTER_OTLP_TRACES_PROTOCOL is http/protobuf
440462 // so the given spanExporter will be an instance of OtlpHttpSpanExporter
@@ -480,7 +502,7 @@ private boolean isOtlpSpanExporter(SpanExporter spanExporter) {
480502
481503 LogRecordExporter customizeLogsExporter (
482504 LogRecordExporter logsExporter , ConfigProperties configProps ) {
483- if (AwsApplicationSignalsConfigValidator .isSigV4EnabledLogs (configProps )) {
505+ if (AwsApplicationSignalsConfigUtils .isSigV4EnabledLogs (configProps )) {
484506 // can cast here since we've checked that the configuration for OTEL_LOGS_EXPORTER is otlp and
485507 // OTEL_EXPORTER_OTLP_LOGS_PROTOCOL is http/protobuf
486508 // so the given logsExporter will be an instance of OtlpHttpLogRecorderExporter
@@ -509,6 +531,40 @@ LogRecordExporter customizeLogsExporter(
509531 return logsExporter ;
510532 }
511533
534+ MetricExporter customizeMetricExporter (
535+ MetricExporter metricExporter , ConfigProperties configProps ) {
536+ if (isEmfExporterEnabled ) {
537+ Map <String , String > headers =
538+ AwsApplicationSignalsConfigUtils .parseOtlpHeaders (
539+ configProps .getString (OTEL_EXPORTER_OTLP_LOGS_HEADERS ));
540+ Optional <String > awsRegion = getAwsRegionFromEnvironment ();
541+ if (awsRegion .isPresent ()) {
542+ if (headers .containsKey (AWS_OTLP_LOGS_GROUP_HEADER )
543+ && headers .containsKey (AWS_OTLP_LOGS_STREAM_HEADER )
544+ && headers .containsKey (AWS_EMF_METRICS_NAMESPACE )) {
545+ String namespace = headers .get (AWS_EMF_METRICS_NAMESPACE );
546+ String logGroup = headers .get (AWS_OTLP_LOGS_GROUP_HEADER );
547+ String logStream = headers .get (AWS_OTLP_LOGS_STREAM_HEADER );
548+ return new AwsCloudWatchEmfExporter (namespace , logGroup , logStream , awsRegion .get ());
549+ }
550+ logger .warning (
551+ String .format (
552+ "Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS to have values for %s, %s, and %s" ,
553+ AWS_OTLP_LOGS_GROUP_HEADER ,
554+ AWS_OTLP_LOGS_STREAM_HEADER ,
555+ AWS_EMF_METRICS_NAMESPACE ));
556+
557+ } else {
558+ logger .warning (
559+ String .format (
560+ "Improper configuration: AWS region not found in environment variables please set %s or %s" ,
561+ AWS_REGION , AWS_DEFAULT_REGION ));
562+ }
563+ }
564+
565+ return metricExporter ;
566+ }
567+
512568 static AwsXrayAdaptiveSamplingConfig parseConfigString (String config )
513569 throws JsonProcessingException {
514570 if (config == null ) {
0 commit comments