2020import io .opentelemetry .contrib .awsxray .AlwaysRecordSampler ;
2121import io .opentelemetry .contrib .awsxray .ResourceHolder ;
2222import io .opentelemetry .exporter .otlp .http .metrics .OtlpHttpMetricExporter ;
23+ import io .opentelemetry .exporter .otlp .http .trace .OtlpHttpSpanExporter ;
2324import io .opentelemetry .exporter .otlp .internal .OtlpConfigUtil ;
2425import io .opentelemetry .exporter .otlp .metrics .OtlpGrpcMetricExporter ;
26+ import io .opentelemetry .exporter .otlp .trace .OtlpGrpcSpanExporter ;
2527import io .opentelemetry .sdk .autoconfigure .spi .AutoConfigurationCustomizer ;
2628import io .opentelemetry .sdk .autoconfigure .spi .AutoConfigurationCustomizerProvider ;
2729import io .opentelemetry .sdk .autoconfigure .spi .ConfigProperties ;
4547import java .util .HashSet ;
4648import java .util .List ;
4749import java .util .Map ;
50+ import java .util .Optional ;
4851import java .util .Set ;
4952import java .util .logging .Level ;
5053import java .util .logging .Logger ;
6669 */
6770public class AwsApplicationSignalsCustomizerProvider
6871 implements AutoConfigurationCustomizerProvider {
72+ static final String AWS_LAMBDA_FUNCTION_NAME_CONFIG = "AWS_LAMBDA_FUNCTION_NAME" ;
73+
6974 private static final Duration DEFAULT_METRIC_EXPORT_INTERVAL = Duration .ofMinutes (1 );
7075 private static final Logger logger =
7176 Logger .getLogger (AwsApplicationSignalsCustomizerProvider .class .getName ());
@@ -85,16 +90,34 @@ public class AwsApplicationSignalsCustomizerProvider
8590 "otel.aws.application.signals.exporter.endpoint" ;
8691
8792 private static final String OTEL_JMX_TARGET_SYSTEM_CONFIG = "otel.jmx.target.system" ;
93+ private static final String OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG =
94+ "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT" ;
95+ private static final String AWS_XRAY_DAEMON_ADDRESS_CONFIG = "AWS_XRAY_DAEMON_ADDRESS" ;
96+ private static final String DEFAULT_UDP_ENDPOINT = "127.0.0.1:2000" ;
97+ private static final String OTEL_DISABLED_RESOURCE_PROVIDERS_CONFIG =
98+ "otel.java.disabled.resource.providers" ;
99+ private static final String OTEL_BSP_MAX_EXPORT_BATCH_SIZE_CONFIG =
100+ "otel.bsp.max.export.batch.size" ;
101+
102+ // UDP packet can be upto 64KB. To limit the packet size, we limit the exported batch size.
103+ // This is a bit of a magic number, as there is no simple way to tell how many spans can make a
104+ // 64KB batch since spans can vary in size.
105+ private static final int LAMBDA_SPAN_EXPORT_BATCH_SIZE = 10 ;
88106
89107 public void customize (AutoConfigurationCustomizer autoConfiguration ) {
90108 autoConfiguration .addPropertiesCustomizer (this ::customizeProperties );
109+ autoConfiguration .addPropertiesCustomizer (this ::customizeLambdaEnvProperties );
91110 autoConfiguration .addResourceCustomizer (this ::customizeResource );
92111 autoConfiguration .addSamplerCustomizer (this ::customizeSampler );
93112 autoConfiguration .addTracerProviderCustomizer (this ::customizeTracerProviderBuilder );
94113 autoConfiguration .addMeterProviderCustomizer (this ::customizeMeterProvider );
95114 autoConfiguration .addSpanExporterCustomizer (this ::customizeSpanExporter );
96115 }
97116
117+ static boolean isLambdaEnvironment () {
118+ return System .getenv (AWS_LAMBDA_FUNCTION_NAME_CONFIG ) != null ;
119+ }
120+
98121 private boolean isApplicationSignalsEnabled (ConfigProperties configProps ) {
99122 return configProps .getBoolean (
100123 APPLICATION_SIGNALS_ENABLED_CONFIG ,
@@ -126,6 +149,30 @@ private Map<String, String> customizeProperties(ConfigProperties configProps) {
126149 return Collections .emptyMap ();
127150 }
128151
152+ private Map <String , String > customizeLambdaEnvProperties (ConfigProperties configProperties ) {
153+ if (isLambdaEnvironment ()) {
154+ Map <String , String > propsOverride = new HashMap <>(2 );
155+
156+ // Disable other AWS Resource Providers
157+ List <String > list = configProperties .getList (OTEL_DISABLED_RESOURCE_PROVIDERS_CONFIG );
158+ List <String > disabledResourceProviders = new ArrayList <>(list );
159+ disabledResourceProviders .add (
160+ "io.opentelemetry.contrib.aws.resource.BeanstalkResourceProvider" );
161+ disabledResourceProviders .add ("io.opentelemetry.contrib.aws.resource.Ec2ResourceProvider" );
162+ disabledResourceProviders .add ("io.opentelemetry.contrib.aws.resource.EcsResourceProvider" );
163+ disabledResourceProviders .add ("io.opentelemetry.contrib.aws.resource.EksResourceProvider" );
164+ propsOverride .put (
165+ OTEL_DISABLED_RESOURCE_PROVIDERS_CONFIG , String .join ("," , disabledResourceProviders ));
166+
167+ // Set the max export batch size for BatchSpanProcessors
168+ propsOverride .put (
169+ OTEL_BSP_MAX_EXPORT_BATCH_SIZE_CONFIG , String .valueOf (LAMBDA_SPAN_EXPORT_BATCH_SIZE ));
170+
171+ return propsOverride ;
172+ }
173+ return Collections .emptyMap ();
174+ }
175+
129176 private Resource customizeResource (Resource resource , ConfigProperties configProps ) {
130177 if (isApplicationSignalsEnabled (configProps )) {
131178 AttributesBuilder builder = Attributes .builder ();
@@ -156,6 +203,17 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder(
156203 // Construct and set local and remote attributes span processor
157204 tracerProviderBuilder .addSpanProcessor (
158205 AttributePropagatingSpanProcessorBuilder .create ().build ());
206+
207+ // If running on Lambda, we just need to export 100% spans and skip generating any Application
208+ // Signals metrics.
209+ if (isLambdaEnvironment ()) {
210+ tracerProviderBuilder .addSpanProcessor (
211+ AwsUnsampledOnlySpanProcessorBuilder .create ()
212+ .setMaxExportBatchSize (LAMBDA_SPAN_EXPORT_BATCH_SIZE )
213+ .build ());
214+ return tracerProviderBuilder ;
215+ }
216+
159217 // Construct meterProvider
160218 MetricExporter metricsExporter =
161219 ApplicationSignalsExporterProvider .INSTANCE .createExporter (configProps );
@@ -207,6 +265,21 @@ private SdkMeterProviderBuilder customizeMeterProvider(
207265
208266 private SpanExporter customizeSpanExporter (
209267 SpanExporter spanExporter , ConfigProperties configProps ) {
268+ // When running in Lambda, override the default OTLP exporter with UDP exporter
269+ if (isLambdaEnvironment ()) {
270+ if (isOtlpSpanExporter (spanExporter )
271+ && System .getenv (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG ) == null ) {
272+ String tracesEndpoint =
273+ Optional .ofNullable (System .getenv (AWS_XRAY_DAEMON_ADDRESS_CONFIG ))
274+ .orElse (DEFAULT_UDP_ENDPOINT );
275+ spanExporter =
276+ new OtlpUdpSpanExporterBuilder ()
277+ .setPayloadSampleDecision (TracePayloadSampleDecision .SAMPLED )
278+ .setEndpoint (tracesEndpoint )
279+ .build ();
280+ }
281+ }
282+
210283 if (isApplicationSignalsEnabled (configProps )) {
211284 return AwsMetricAttributesSpanExporterBuilder .create (
212285 spanExporter , ResourceHolder .getResource ())
@@ -216,6 +289,11 @@ private SpanExporter customizeSpanExporter(
216289 return spanExporter ;
217290 }
218291
292+ private boolean isOtlpSpanExporter (SpanExporter spanExporter ) {
293+ return spanExporter instanceof OtlpGrpcSpanExporter
294+ || spanExporter instanceof OtlpHttpSpanExporter ;
295+ }
296+
219297 private enum ApplicationSignalsExporterProvider {
220298 INSTANCE ;
221299
0 commit comments