Skip to content

Commit b60ba73

Browse files
authored
feat: [Java] EMF Exporter Implementation (#1209)
*Description of changes:* Java Version of these PRs: - aws-observability/aws-otel-python-instrumentation#382 - aws-observability/aws-otel-python-instrumentation#409 - aws-observability/aws-otel-python-instrumentation#410 - aws-observability/aws-otel-python-instrumentation#434 This PR introduces the complete CloudWatch EMF (Embedded Metric Format) exporter implementation for sending OpenTelemetry metrics directly to CloudWatch without requiring a Collector or Agent. In order to enable this exporter, users MUST set the following environment variables: - `OTEL_METRICS_EXPORTER=awsemf` - - `OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-aws-log-group=<log-group-name>,x-aws-log-stream=<log-stream-name>, x-aws-metric-namespace=<namespace>` - `AWS_REGION=<region>` OR `AWS_DEFAULT_REGION=<region>` This PR includes: - EMF MetricRecord translation for for unified representation of all OTel metric types with log creation with unit mapping from OpenTelemetry to CloudWatch-compatible units - Automatic log group and stream creation with retry logic - Supported CloudWatch Logs integration with batching and constraint handling (256KB event limit, 1MB request limit, timestamp limits) - Support for metric grouping by attributes and timestamps for EMF log generation - Support for Gauge, Sum, Histogram, and ExponentialHistogram metric types **TODO**: - On the next PR, will integrate the Console EMF exporter into AWS Lambda environments to validate EMF log formatting and ensure consistent behavior in Lambda runtime: aws-observability/aws-otel-python-instrumentation#437 **Testing**: - Added unit tests to validate EMF exporter configuration scenarios, including parameterized tests for both valid configurations (supporting AWS_REGION and AWS_DEFAULT_REGION) and invalid configurations (missing headers, wrong exporter type, missing region). The tests ensure the EMF exporter is correctly enabled only when all required environment variables are properly configured. - Manual end to end testing with the following environment variables to ensure the EMF logs show up: - `AWS_REGION=us-east-1` - `OTEL_METRICS_EXPORTER=awsemf` - `OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-aws-log-group=test,x-aws-log-stream=default,x-aws-metric-namespace=testNamespace` - `OTEL_RESOURCE_ATTRIBUTES=service.name=testService,aws.log.group.names=test,cloud.resource_id=agent-12345` - `OTEL_LOGS_EXPORTER=none` - `OTEL_TRACES_EXPORTER=none` Example EMF log emitted: ``` { "otel.resource.process.command_args": "[/Library/Java/JavaVirtualMachines/amazon-corretto-21.jdk/Contents/Home/bin/java, -javaagent:/Users/liustve/aws-otel-java-instrumentation/otelagent/build/libs/aws-opentelemetry-agent-2.18.0-SNAPSHOT.jar, -Dfile.encoding=UTF-8, -Dsun.stdout.encoding=UTF-8, -Dsun.stderr.encoding=UTF-8, -jar, /Users/liustve/aws-otel-java-instrumentation/sample-apps/springboot/build/libs/springboot-2.11.0-SNAPSHOT.jar]", "otel.resource.host.arch": "aarch64", "otel.resource.host.name": "7cf34dd812df", "otel.resource.service.instance.id": "a0399d3c-b856-43ae-b374-fe66dee41ce8", "otel.resource.aws.log.group.names": "test", "jvm.class.unloaded": 1, "otel.resource.service.name": "testSErvice", "_aws": { "CloudWatchMetrics": [ { "Metrics": [ { "Unit": "Count", "Name": "jvm.cpu.count" }, { "Unit": "Count", "Name": "jvm.class.loaded" }, { "Name": "jvm.cpu.recent_utilization" }, { "Unit": "Seconds", "Name": "jvm.cpu.time" }, { "Unit": "Count", "Name": "jvm.class.count" }, { "Unit": "Count", "Name": "jvm.class.unloaded" } ], "Namespace": "testNamespace" } ], "Timestamp": 1758761023497 }, "otel.resource.cloud.resource_id": "agent-12345", "jvm.class.count": 14264, "Version": "1", "otel.resource.process.pid": "46822", "otel.resource.os.description": "Mac OS X 15.6.1", "otel.resource.telemetry.distro.name": "opentelemetry-java-instrumentation", "otel.resource.os.type": "darwin", "otel.resource.telemetry.sdk.name": "opentelemetry", "otel.resource.telemetry.distro.version": "2.18.0-aws-SNAPSHOT", "otel.resource.process.runtime.description": "Amazon.com Inc. OpenJDK 64-Bit Server VM 21.0.8+9-LTS", "otel.resource.process.runtime.version": "21.0.8+9-LTS", "jvm.cpu.recent_utilization": 0, "otel.resource.process.executable.path": "/Library/Java/JavaVirtualMachines/amazon-corretto-21.jdk/Contents/Home/bin/java", "otel.resource.telemetry.sdk.version": "1.52.0", "jvm.cpu.count": 12, "jvm.class.loaded": 14265, "otel.resource.process.runtime.name": "OpenJDK Runtime Environment", "otel.resource.telemetry.sdk.language": "java", "jvm.cpu.time": 7.756965 } ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 2bde6be commit b60ba73

File tree

17 files changed

+2918
-483
lines changed

17 files changed

+2918
-483
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ If your change does not need a CHANGELOG entry, add the "skip changelog" label t
1717

1818
### Enhancements
1919

20+
- Add CloudWatch EMF metrics exporter with auto instrumentation configuration
21+
([#1209](https://github.com/aws-observability/aws-otel-java-instrumentation/pull/1209))
2022
- Support X-Ray Trace Id extraction from Lambda Context object, and respect user-configured OTEL_PROPAGATORS in AWS Lamdba instrumentation
2123
([#1191](https://github.com/aws-observability/aws-otel-java-instrumentation/pull/1191)) ([#1218](https://github.com/aws-observability/aws-otel-java-instrumentation/pull/1218))
2224
- Adaptive Sampling improvements: Ensure propagation of sampling rule across services and AWS accounts. Remove unnecessary B3 propagator.

awsagentprovider/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ dependencies {
5353
runtimeOnly("software.amazon.awssdk:sts")
5454
implementation("software.amazon.awssdk:auth")
5555
implementation("software.amazon.awssdk:http-auth-aws")
56+
// For EMF exporter
57+
implementation("software.amazon.awssdk:cloudwatchlogs")
5658

5759
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
5860
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,48 @@
1818
import static software.amazon.opentelemetry.javaagent.providers.AwsApplicationSignalsCustomizerProvider.*;
1919

2020
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
21-
import java.util.Arrays;
21+
import java.util.*;
2222
import java.util.logging.Level;
2323
import java.util.logging.Logger;
2424
import java.util.regex.Pattern;
25+
import java.util.stream.Collectors;
2526

2627
/** Utilities class to validate ADOT environment variable configuration. */
27-
public final class AwsApplicationSignalsConfigValidator {
28+
public final class AwsApplicationSignalsConfigUtils {
2829
private static final Logger logger =
2930
Logger.getLogger(AwsApplicationSignalsCustomizerProvider.class.getName());
3031

32+
/**
33+
* Removes "awsemf" from OTEL_METRICS_EXPORTER if present to prevent validation errors from OTel
34+
* dependencies which would try to load metric exporters. We will contribute emf exporter to
35+
* upstream for supporting OTel metrics in SDK
36+
*
37+
* @param configProps the configuration properties
38+
* @return Optional string containing the updated metrics exporter config with "awsemf" removed if
39+
* "awsemf" was one of the registered exporters, otherwise empty Optional if "awsemf" was not
40+
* a part of the registered exporters.
41+
*/
42+
static Optional<String> removeEmfExporterIfEnabled(ConfigProperties configProps) {
43+
String metricExporters = configProps.getString(OTEL_METRICS_EXPORTER);
44+
45+
if (metricExporters == null || !metricExporters.contains("awsemf")) {
46+
return Optional.empty();
47+
}
48+
49+
// Remove "awsemf" from exporters list. If "awsemf" is the only exporter, return empty
50+
// string instead of "none". While OTel's behavior when given an empty string for the exporter
51+
// is to default to the "otlp" exporter, we will deviate from this
52+
// because upstream will not call customizeMetricExporter if OTEL_METRICS_EXPORTER is set to
53+
// "none", which would prevent EMF exporter registration
54+
String filtered =
55+
Arrays.stream(metricExporters.split(","))
56+
.map(String::trim)
57+
.filter(exp -> !exp.equals("awsemf"))
58+
.collect(Collectors.joining(","));
59+
60+
return Optional.of(filtered);
61+
}
62+
3163
/**
3264
* Is the given configuration correct to enable SigV4 for Logs?
3365
*
@@ -61,27 +93,21 @@ static boolean isSigV4EnabledLogs(ConfigProperties config) {
6193

6294
if (logsHeaders == null || logsHeaders.isEmpty()) {
6395
logger.warning(
64-
"Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS to include x-aws-log-group and x-aws-log-stream");
96+
String.format(
97+
"Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS to include %s and %s",
98+
AWS_OTLP_LOGS_GROUP_HEADER, AWS_OTLP_LOGS_STREAM_HEADER));
6599

66100
return false;
67101
}
102+
Map<String, String> parsedHeaders =
103+
AwsApplicationSignalsConfigUtils.parseOtlpHeaders(logsHeaders);
68104

69-
long filteredLogHeaders =
70-
Arrays.stream(logsHeaders.split(","))
71-
.filter(
72-
pair -> {
73-
if (pair.contains("=")) {
74-
String key = pair.split("=", 2)[0];
75-
return key.equals(AWS_OTLP_LOGS_GROUP_HEADER)
76-
|| key.equals(AWS_OTLP_LOGS_STREAM_HEADER);
77-
}
78-
return false;
79-
})
80-
.count();
81-
82-
if (filteredLogHeaders != 2) {
105+
if (!(parsedHeaders.containsKey(AWS_OTLP_LOGS_GROUP_HEADER)
106+
&& parsedHeaders.containsKey(AWS_OTLP_LOGS_STREAM_HEADER))) {
83107
logger.warning(
84-
"Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS to have values for x-aws-log-group and x-aws-log-stream");
108+
String.format(
109+
"Improper configuration: Please configure the environment variable OTEL_EXPORTER_OTLP_LOGS_HEADERS to have values for %s and %s",
110+
AWS_OTLP_LOGS_GROUP_HEADER, AWS_OTLP_LOGS_STREAM_HEADER));
85111
return false;
86112
}
87113

@@ -168,4 +194,26 @@ private static boolean isSigv4ValidConfig(
168194

169195
return false;
170196
}
197+
198+
/**
199+
* Parse OTLP headers and return a map of header key to value. See: <a
200+
* href="https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_headers">...</a>
201+
*
202+
* @param headersString the headers string in format "key1=value1,key2=value2"
203+
* @return map of header keys to values
204+
*/
205+
static Map<String, String> parseOtlpHeaders(String headersString) {
206+
Map<String, String> headers = new HashMap<>();
207+
if (headersString == null || headersString.isEmpty()) {
208+
return headers;
209+
}
210+
211+
for (String pair : headersString.split(",")) {
212+
if (pair.contains("=")) {
213+
String[] keyValue = pair.split("=", 2);
214+
headers.put(keyValue[0].trim(), keyValue[1].trim());
215+
}
216+
}
217+
return headers;
218+
}
171219
}

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
import java.util.logging.Level;
6666
import java.util.logging.Logger;
6767
import javax.annotation.concurrent.Immutable;
68+
import software.amazon.opentelemetry.javaagent.providers.exporter.aws.metrics.AwsCloudWatchEmfExporter;
69+
import software.amazon.opentelemetry.javaagent.providers.exporter.aws.metrics.ConsoleEmfExporter;
6870
import software.amazon.opentelemetry.javaagent.providers.exporter.otlp.aws.logs.OtlpAwsLogsExporterBuilder;
6971
import software.amazon.opentelemetry.javaagent.providers.exporter.otlp.aws.traces.OtlpAwsSpanExporterBuilder;
7072

@@ -86,6 +88,9 @@
8688
@Immutable
8789
public final class AwsApplicationSignalsCustomizerProvider
8890
implements AutoConfigurationCustomizerProvider {
91+
// https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-envvars.html
92+
static final String AWS_REGION = "aws.region";
93+
static final String AWS_DEFAULT_REGION = "aws.default.region";
8994
static final String AWS_LAMBDA_FUNCTION_NAME_CONFIG = "AWS_LAMBDA_FUNCTION_NAME";
9095
static final String LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT =
9196
"LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT";
@@ -103,6 +108,7 @@ public final class AwsApplicationSignalsCustomizerProvider
103108
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-OTLPEndpoint.html#CloudWatch-LogsEndpoint
104109
static final String AWS_OTLP_LOGS_GROUP_HEADER = "x-aws-log-group";
105110
static final String AWS_OTLP_LOGS_STREAM_HEADER = "x-aws-log-stream";
111+
static final String AWS_EMF_METRICS_NAMESPACE = "x-aws-metric-namespace";
106112

107113
private static final String DEPRECATED_SMP_ENABLED_CONFIG = "otel.smp.enabled";
108114
private static final String DEPRECATED_APP_SIGNALS_ENABLED_CONFIG =
@@ -132,7 +138,7 @@ public final class AwsApplicationSignalsCustomizerProvider
132138
private static final String OTEL_BSP_MAX_EXPORT_BATCH_SIZE_CONFIG =
133139
"otel.bsp.max.export.batch.size";
134140

135-
private static final String OTEL_METRICS_EXPORTER = "otel.metrics.exporter";
141+
static final String OTEL_METRICS_EXPORTER = "otel.metrics.exporter";
136142
static final String OTEL_LOGS_EXPORTER = "otel.logs.exporter";
137143
static final String OTEL_TRACES_EXPORTER = "otel.traces.exporter";
138144
static final String OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = "otel.exporter.otlp.traces.protocol";
@@ -161,6 +167,7 @@ public final class AwsApplicationSignalsCustomizerProvider
161167
private static final int LAMBDA_SPAN_EXPORT_BATCH_SIZE = 10;
162168

163169
private Sampler sampler;
170+
private boolean isEmfExporterEnabled = false;
164171

165172
public void customize(AutoConfigurationCustomizer autoConfiguration) {
166173
autoConfiguration.addPropertiesCustomizer(this::customizeProperties);
@@ -171,6 +178,15 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) {
171178
autoConfiguration.addMeterProviderCustomizer(this::customizeMeterProvider);
172179
autoConfiguration.addSpanExporterCustomizer(this::customizeSpanExporter);
173180
autoConfiguration.addLogRecordExporterCustomizer(this::customizeLogsExporter);
181+
autoConfiguration.addMetricExporterCustomizer(this::customizeMetricExporter);
182+
}
183+
184+
private static Optional<String> getAwsRegionFromConfig(ConfigProperties configProps) {
185+
String region = configProps.getString(AWS_REGION);
186+
if (region != null) {
187+
return Optional.of(region);
188+
}
189+
return Optional.ofNullable(configProps.getString(AWS_DEFAULT_REGION));
174190
}
175191

176192
static boolean isLambdaEnvironment() {
@@ -190,10 +206,18 @@ private boolean isApplicationSignalsRuntimeEnabled(ConfigProperties configProps)
190206
&& configProps.getBoolean(APPLICATION_SIGNALS_RUNTIME_ENABLED_CONFIG, true);
191207
}
192208

193-
private Map<String, String> customizeProperties(ConfigProperties configProps) {
209+
Map<String, String> customizeProperties(ConfigProperties configProps) {
194210
Map<String, String> propsOverride = new HashMap<>();
195211
boolean isLambdaEnvironment = isLambdaEnvironment();
196212

213+
// Check if awsemf was specified and remove it from OTEL_METRICS_EXPORTER
214+
Optional<String> filteredExporters =
215+
AwsApplicationSignalsConfigUtils.removeEmfExporterIfEnabled(configProps);
216+
if (filteredExporters.isPresent()) {
217+
this.isEmfExporterEnabled = true;
218+
propsOverride.put(OTEL_METRICS_EXPORTER, filteredExporters.get());
219+
}
220+
197221
// Enable AWS Resource Providers
198222
propsOverride.put(OTEL_RESOURCE_PROVIDERS_AWS_ENABLED, "true");
199223

@@ -394,7 +418,6 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder(
394418

395419
private SdkMeterProviderBuilder customizeMeterProvider(
396420
SdkMeterProviderBuilder sdkMeterProviderBuilder, ConfigProperties configProps) {
397-
398421
if (isApplicationSignalsRuntimeEnabled(configProps)) {
399422
Set<String> registeredScopeNames = new HashSet<>(1);
400423
String jmxRuntimeScopeName = "io.opentelemetry.jmx";
@@ -434,7 +457,7 @@ SpanExporter customizeSpanExporter(SpanExporter spanExporter, ConfigProperties c
434457
}
435458
}
436459

437-
if (AwsApplicationSignalsConfigValidator.isSigV4EnabledTraces(configProps)) {
460+
if (AwsApplicationSignalsConfigUtils.isSigV4EnabledTraces(configProps)) {
438461
// can cast here since we've checked that the configuration for OTEL_TRACES_EXPORTER is otlp
439462
// and OTEL_EXPORTER_OTLP_TRACES_PROTOCOL is http/protobuf
440463
// so the given spanExporter will be an instance of OtlpHttpSpanExporter
@@ -480,7 +503,7 @@ private boolean isOtlpSpanExporter(SpanExporter spanExporter) {
480503

481504
LogRecordExporter customizeLogsExporter(
482505
LogRecordExporter logsExporter, ConfigProperties configProps) {
483-
if (AwsApplicationSignalsConfigValidator.isSigV4EnabledLogs(configProps)) {
506+
if (AwsApplicationSignalsConfigUtils.isSigV4EnabledLogs(configProps)) {
484507
// can cast here since we've checked that the configuration for OTEL_LOGS_EXPORTER is otlp and
485508
// OTEL_EXPORTER_OTLP_LOGS_PROTOCOL is http/protobuf
486509
// so the given logsExporter will be an instance of OtlpHttpLogRecorderExporter
@@ -509,6 +532,45 @@ LogRecordExporter customizeLogsExporter(
509532
return logsExporter;
510533
}
511534

535+
MetricExporter customizeMetricExporter(
536+
MetricExporter metricExporter, ConfigProperties configProps) {
537+
if (isEmfExporterEnabled) {
538+
Map<String, String> headers =
539+
AwsApplicationSignalsConfigUtils.parseOtlpHeaders(
540+
configProps.getString(OTEL_EXPORTER_OTLP_LOGS_HEADERS));
541+
Optional<String> awsRegion = getAwsRegionFromConfig(configProps);
542+
543+
if (awsRegion.isPresent()) {
544+
String namespace = headers.get(AWS_EMF_METRICS_NAMESPACE);
545+
546+
if (headers.containsKey(AWS_OTLP_LOGS_GROUP_HEADER)
547+
&& headers.containsKey(AWS_OTLP_LOGS_STREAM_HEADER)) {
548+
String logGroup = headers.get(AWS_OTLP_LOGS_GROUP_HEADER);
549+
String logStream = headers.get(AWS_OTLP_LOGS_STREAM_HEADER);
550+
return new AwsCloudWatchEmfExporter(namespace, logGroup, logStream, awsRegion.get());
551+
}
552+
if (isLambdaEnvironment()) {
553+
return new ConsoleEmfExporter(namespace);
554+
}
555+
logger.warning(
556+
String.format(
557+
"Improper EMF Exporter configuration: Please configure the environment variable %s to have values for %s, %s, and %s",
558+
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
559+
AWS_OTLP_LOGS_GROUP_HEADER,
560+
AWS_OTLP_LOGS_STREAM_HEADER,
561+
AWS_EMF_METRICS_NAMESPACE));
562+
563+
} else {
564+
logger.warning(
565+
String.format(
566+
"Improper EMF Exporter configuration: AWS region not found in environment variables please set %s or %s",
567+
AWS_REGION, AWS_DEFAULT_REGION));
568+
}
569+
}
570+
571+
return metricExporter;
572+
}
573+
512574
static AwsXrayAdaptiveSamplingConfig parseConfigString(String config)
513575
throws JsonProcessingException {
514576
if (config == null) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.opentelemetry.javaagent.providers.exporter.aws.metrics;
17+
18+
import io.opentelemetry.sdk.common.CompletableResultCode;
19+
import java.util.logging.Logger;
20+
import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient;
21+
import software.amazon.opentelemetry.javaagent.providers.exporter.aws.metrics.common.BaseEmfExporter;
22+
import software.amazon.opentelemetry.javaagent.providers.exporter.aws.metrics.common.emitter.CloudWatchLogsClientEmitter;
23+
import software.amazon.opentelemetry.javaagent.providers.exporter.aws.metrics.common.emitter.LogEventEmitter;
24+
25+
/**
26+
* EMF metrics exporter for sending data directly to CloudWatch Logs.
27+
*
28+
* <p>This exporter converts OTel metrics into CloudWatch EMF logs which are then sent to CloudWatch
29+
* Logs. CloudWatch Logs automatically extracts the metrics from the EMF logs.
30+
*
31+
* <p><a
32+
* href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html">...</a>
33+
*/
34+
public class AwsCloudWatchEmfExporter extends BaseEmfExporter<CloudWatchLogsClient> {
35+
private static final Logger logger = Logger.getLogger(AwsCloudWatchEmfExporter.class.getName());
36+
37+
/**
38+
* Initialize the CloudWatch EMF exporter.
39+
*
40+
* @param namespace CloudWatch namespace for metrics (default: "default")
41+
* @param logGroupName CloudWatch log group name
42+
* @param logStreamName CloudWatch log stream name (auto-generated if null)
43+
* @param awsRegion AWS region
44+
*/
45+
public AwsCloudWatchEmfExporter(
46+
String namespace, String logGroupName, String logStreamName, String awsRegion) {
47+
super(namespace, new CloudWatchLogsClientEmitter(logGroupName, logStreamName, awsRegion));
48+
}
49+
50+
/**
51+
* Initialize the CloudWatch EMF exporter with a custom emitter.
52+
*
53+
* @param namespace CloudWatch namespace for metrics
54+
* @param emitter Custom log emitter
55+
*/
56+
public AwsCloudWatchEmfExporter(String namespace, LogEventEmitter<CloudWatchLogsClient> emitter) {
57+
super(namespace, emitter);
58+
}
59+
60+
@Override
61+
public CompletableResultCode flush() {
62+
this.emitter.flushEvents();
63+
logger.fine("AwsCloudWatchEmfExporter force flushes the buffered metrics");
64+
return CompletableResultCode.ofSuccess();
65+
}
66+
67+
@Override
68+
public CompletableResultCode shutdown() {
69+
this.flush();
70+
logger.fine("AwsCloudWatchEmfExporter shutdown called");
71+
return CompletableResultCode.ofSuccess();
72+
}
73+
}

0 commit comments

Comments
 (0)