DEV Community

Cover image for CloudWatch OTLP metrics exporter using .Net Lambda
ohalay
ohalay

Posted on • Edited on

CloudWatch OTLP metrics exporter using .Net Lambda

We will configure the open telemetry protocol(OTLP) metrics exporter to AWS CloudWatch using AWS Lambda with .NET 8 runtime. We will start from the infrastructure using AWS CDK.

Lambda infrastructure

To support Lambda for open telemetry, we need to configure the next thing. Add a lambda layer for AWS Distro for OpenTelemetry(ADOT); layer versions are in GitHub repository. Also, add collector.yaml path to the environment variables.

const string version = "ver-0-115-0:2";
var lambda = new Function("otlp-lambda", new FunctionProps
{
  ...
  Runtime = Runtime.DOTNET_8,
  Environment = new Dictionary<string, string>
  {
    ["OPENTELEMETRY_COLLECTOR_CONFIG_URI"] = "/var/task/collector.yaml";
     },
  Layers = [LayerVersion.FromLayerVersionArn(
     this,
     "otel-lambda-layer",
     Fn.Join("", ["arn:aws:lambda:", Aws.REGION, $":901920570463:layer:aws-otel-collector-arm64-{version}"]))
  ]
}
Enter fullscreen mode Exit fullscreen mode

Collector config

The collector file is a configuration to receive, process, and export telemetry data. An important thing is NodeName that we will path later, during OTLP configuration. ADOT collector configuration here.

receivers:
  otlp:
    protocols:
      grpc:
      http:
exporters:
  debug:
    verbosity: normal
  awsemf:
    log_group_name: '/aws/lambda/{NodeName}'
service:
  pipelines:
    metrics:
      receivers: [ otlp ]
      exporters: [ awsemf ]
Enter fullscreen mode Exit fullscreen mode

.NET part

First of all, we need to register open telemetry metrics that we want to collect and add an exporter. AWS_LAMBDA_FUNCTION_NAME - environment variable that is managed by AWS.

.AddOpenTelemetry()
  .WithMetrics(builder =>
  {
    var lambdaName = Environment.GetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME")
      ?? "unknown";
    var resourceBuilder = ResourceBuilder.CreateDefault()
      .AddService("otlp-metrics-sample")
      .AddAttributes([new KeyValuePair<string, object>("NodeName", lambdaName!)]);

    builder
    .SetResourceBuilder(resourceBuilder)
    .AddMeter(api.test.operation);
    .AddOtlpExporter((_, readerOptions) =>
    {
        readerOptions.TemporalityPreference = MetricReaderTemporalityPreference.Delta;
    })
  });
Enter fullscreen mode Exit fullscreen mode

Next, we need to integrate metrics with the DI container .AddMetrics().
The last thing - record our metric

app.MapPost("api/metric", (IMeterFactory meterFactory) =>
{
  var metter = meterFactory.Create("api.test.operation");
  var instrument = metter.CreateCounter<int>("sample_counter");
  instrument.Add(1);

  return Results.Ok();
});
Enter fullscreen mode Exit fullscreen mode

CloudWatch

ADOT collector will create a separate stream in the AWS Lambda log group with the prefix otel-stream- and put all metrics there. After that, we can use those metrics with CloudWatch tools(dashboards, alarms, etc).

"OTelLib": "api.test.operation",
"Version": "1",
"_aws": {
  "CloudWatchMetrics": [
    {
      "Namespace": "otlp-metrics-sample",
      "Dimensions": [
        [ "OTelLib" ]
      ],
      "Metrics": [
        {
          "Name": "sample_counter"
        }
      ]
    }
  ],
  "Timestamp": 1722233676723
},
"sample_counter": 2
Enter fullscreen mode Exit fullscreen mode

Conclusion

  1. Open telemetry - language agnostic protocol, and using the built-in exporter, we may easily migrate from AWS CloudWatch to other supported exporters.
  2. Using .NET abstraction IMeterFactory for OTLP, we may easily migrate from AWS Lambda to other computed services(Any Cloud, On-Prem, etc), with no vendor lock.

Help links

Top comments (0)