-
Notifications
You must be signed in to change notification settings - Fork 32
Add OTLP Proxy Endpoint for Frontend Telemetry #2245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
reakaleek
wants to merge
6
commits into
main
Choose a base branch
from
feature/otlp-proxy
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
33db2a1
Add OTLP proxy endpoint
reakaleek a8c7d98
Fix OtlpProxyOptions
reakaleek 1abb9bd
Refactor
reakaleek 006b096
Fix CodeQL
reakaleek 05fbd75
Refactor and dispose disposable
reakaleek 0d57789
Merge branch 'main' into feature/otlp-proxy
reakaleek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
src/api/Elastic.Documentation.Api.Core/Telemetry/IOtlpGateway.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Licensed to Elasticsearch B.V under one or more agreements. | ||
| // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
| // See the LICENSE file in the project root for more information | ||
|
|
||
| namespace Elastic.Documentation.Api.Core.Telemetry; | ||
|
|
||
| /// <summary> | ||
| /// Gateway for forwarding OTLP telemetry to a collector. | ||
| /// </summary> | ||
| public interface IOtlpGateway | ||
| { | ||
| /// <summary> | ||
| /// Forwards OTLP telemetry data to the collector. | ||
| /// </summary> | ||
| /// <param name="signalType">The OTLP signal type (traces, logs, or metrics)</param> | ||
| /// <param name="requestBody">The raw OTLP payload stream</param> | ||
| /// <param name="contentType">Content-Type of the payload</param> | ||
| /// <param name="ctx">Cancellation token</param> | ||
| /// <returns>HTTP status code and response content</returns> | ||
| Task<(int StatusCode, string? Content)> ForwardOtlp( | ||
| OtlpSignalType signalType, | ||
| Stream requestBody, | ||
| string contentType, | ||
| Cancel ctx = default); | ||
| } |
48 changes: 48 additions & 0 deletions
48
src/api/Elastic.Documentation.Api.Core/Telemetry/OtlpProxyOptions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| // Licensed to Elasticsearch B.V under one or more agreements. | ||
| // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
| // See the LICENSE file in the project root for more information | ||
|
|
||
| using Microsoft.Extensions.Configuration; | ||
|
|
||
| namespace Elastic.Documentation.Api.Core.Telemetry; | ||
|
|
||
| /// <summary> | ||
| /// Configuration options for the OTLP proxy. | ||
| /// The proxy forwards telemetry to a local OTLP collector (typically ADOT Lambda Layer). | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// ADOT Lambda Layer runs a local OpenTelemetry Collector that accepts OTLP/HTTP on: | ||
| /// - localhost:4318 (HTTP/JSON and HTTP/protobuf) | ||
| /// - localhost:4317 (gRPC) | ||
| /// | ||
| /// Configuration priority: | ||
| /// 1. OtlpProxy:Endpoint in IConfiguration (for tests/overrides) | ||
| /// 2. OTEL_EXPORTER_OTLP_ENDPOINT environment variable | ||
| /// 3. Default: http://localhost:4318 | ||
| /// | ||
| /// The proxy will return 503 if the collector is not available. | ||
| /// </remarks> | ||
| public class OtlpProxyOptions | ||
| { | ||
| /// <summary> | ||
| /// OTLP endpoint URL for the local ADOT collector. | ||
| /// Defaults to localhost:4318 when running in Lambda with ADOT layer. | ||
| /// </summary> | ||
| public string Endpoint { get; } | ||
|
|
||
| public OtlpProxyOptions(IConfiguration configuration) | ||
| { | ||
| // Check for explicit configuration override first (for tests or custom deployments) | ||
| var configEndpoint = configuration["OtlpProxy:Endpoint"]; | ||
| if (!string.IsNullOrEmpty(configEndpoint)) | ||
| { | ||
| Endpoint = configEndpoint; | ||
| return; | ||
| } | ||
|
|
||
| // Default to localhost:4318 - this is where ADOT Lambda Layer collector runs | ||
| // If ADOT layer is not present, the proxy will fail gracefully and return 503 | ||
| Endpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") | ||
| ?? "http://localhost:4318"; | ||
| } | ||
| } |
46 changes: 46 additions & 0 deletions
46
src/api/Elastic.Documentation.Api.Core/Telemetry/OtlpProxyRequest.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // Licensed to Elasticsearch B.V under one or more agreements. | ||
| // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
| // See the LICENSE file in the project root for more information | ||
|
|
||
| using System.ComponentModel.DataAnnotations; | ||
| using NetEscapades.EnumGenerators; | ||
|
|
||
| namespace Elastic.Documentation.Api.Core.Telemetry; | ||
|
|
||
| /// <summary> | ||
| /// OTLP signal types supported by the proxy. | ||
| /// The Display names match the OTLP path segments (lowercase). | ||
| /// </summary> | ||
| [EnumExtensions] | ||
| public enum OtlpSignalType | ||
| { | ||
| /// <summary> | ||
| /// Distributed traces - maps to /v1/traces | ||
| /// </summary> | ||
| [Display(Name = "traces")] | ||
| Traces, | ||
|
|
||
| /// <summary> | ||
| /// Log records - maps to /v1/logs | ||
| /// </summary> | ||
| [Display(Name = "logs")] | ||
| Logs, | ||
|
|
||
| /// <summary> | ||
| /// Metrics data - maps to /v1/metrics | ||
| /// </summary> | ||
| [Display(Name = "metrics")] | ||
| Metrics | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Request model for OTLP proxy endpoint. | ||
| /// Accepts raw OTLP payload from frontend and forwards to configured OTLP endpoint. | ||
| /// </summary> | ||
| public class OtlpProxyRequest | ||
| { | ||
| /// <summary> | ||
| /// The OTLP signal type: traces, logs, or metrics | ||
| /// </summary> | ||
| public required string SignalType { get; init; } | ||
| } |
36 changes: 36 additions & 0 deletions
36
src/api/Elastic.Documentation.Api.Core/Telemetry/OtlpProxyUsecase.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // Licensed to Elasticsearch B.V under one or more agreements. | ||
| // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
| // See the LICENSE file in the project root for more information | ||
|
|
||
| using System.Diagnostics; | ||
|
|
||
| namespace Elastic.Documentation.Api.Core.Telemetry; | ||
|
|
||
| /// <summary> | ||
| /// Proxies OTLP telemetry from the frontend to the local ADOT Lambda Layer collector. | ||
| /// The ADOT layer handles authentication and forwarding to the backend. | ||
| /// </summary> | ||
| public class OtlpProxyUsecase(IOtlpGateway gateway) | ||
| { | ||
| private static readonly ActivitySource ActivitySource = new(TelemetryConstants.OtlpProxySourceName); | ||
|
|
||
| /// <summary> | ||
| /// Proxies OTLP data from the frontend to the local ADOT collector. | ||
| /// </summary> | ||
| /// <param name="signalType">The OTLP signal type (traces, logs, or metrics)</param> | ||
| /// <param name="requestBody">The raw OTLP payload (JSON or protobuf)</param> | ||
| /// <param name="contentType">Content-Type header from the original request</param> | ||
| /// <param name="ctx">Cancellation token</param> | ||
| /// <returns>HTTP status code and response content</returns> | ||
| public async Task<(int StatusCode, string? Content)> ProxyOtlp( | ||
| OtlpSignalType signalType, | ||
| Stream requestBody, | ||
| string contentType, | ||
| Cancel ctx = default) | ||
| { | ||
| using var activity = ActivitySource.StartActivity("ProxyOtlp", ActivityKind.Client); | ||
|
|
||
| // Forward to gateway | ||
| return await gateway.ForwardOtlp(signalType, requestBody, contentType, ctx); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
src/api/Elastic.Documentation.Api.Infrastructure/Adapters/Telemetry/AdotOtlpGateway.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // Licensed to Elasticsearch B.V under one or more agreements. | ||
| // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
| // See the LICENSE file in the project root for more information | ||
|
|
||
| using Elastic.Documentation.Api.Core.Telemetry; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
||
| namespace Elastic.Documentation.Api.Infrastructure.Adapters.Telemetry; | ||
|
|
||
| /// <summary> | ||
| /// Gateway that forwards OTLP telemetry to the ADOT Lambda Layer collector. | ||
| /// </summary> | ||
| public class AdotOtlpGateway( | ||
| IHttpClientFactory httpClientFactory, | ||
| OtlpProxyOptions options, | ||
| ILogger<AdotOtlpGateway> logger) : IOtlpGateway | ||
| { | ||
| public const string HttpClientName = "OtlpProxy"; | ||
| private readonly HttpClient _httpClient = httpClientFactory.CreateClient(HttpClientName); | ||
|
|
||
| /// <inheritdoc /> | ||
| public async Task<(int StatusCode, string? Content)> ForwardOtlp( | ||
| OtlpSignalType signalType, | ||
| Stream requestBody, | ||
| string contentType, | ||
| Cancel ctx = default) | ||
| { | ||
| try | ||
| { | ||
| // Build the target URL: http://localhost:4318/v1/{signalType} | ||
| // Use ToStringFast(true) from generated enum extensions (returns Display name: "traces", "logs", "metrics") | ||
| var targetUrl = $"{options.Endpoint.TrimEnd('/')}/v1/{signalType.ToStringFast(true)}"; | ||
|
|
||
| logger.LogDebug("Forwarding OTLP {SignalType} to ADOT collector at {TargetUrl}", signalType, targetUrl); | ||
|
|
||
| using var request = new HttpRequestMessage(HttpMethod.Post, targetUrl); | ||
|
|
||
| // Forward the content with the original content type | ||
| request.Content = new StreamContent(requestBody); | ||
| _ = request.Content.Headers.TryAddWithoutValidation("Content-Type", contentType); | ||
|
|
||
| // No need to add authentication headers - ADOT layer handles auth to backend | ||
| // Just forward the telemetry to the local collector | ||
|
|
||
| // Forward to ADOT collector | ||
| using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, ctx); | ||
| var responseContent = response.Content.Headers.ContentLength > 0 | ||
| ? await response.Content.ReadAsStringAsync(ctx) | ||
| : string.Empty; | ||
|
|
||
| if (!response.IsSuccessStatusCode) | ||
| { | ||
| logger.LogError("OTLP forward to ADOT failed with status {StatusCode}: {Content}", | ||
| response.StatusCode, responseContent); | ||
| } | ||
| else | ||
| { | ||
| logger.LogDebug("Successfully forwarded OTLP {SignalType} to ADOT collector", signalType); | ||
| } | ||
|
|
||
| return ((int)response.StatusCode, responseContent); | ||
| } | ||
| catch (HttpRequestException ex) when (ex.Message.Contains("Connection refused") || ex.InnerException?.Message?.Contains("Connection refused") == true) | ||
| { | ||
| logger.LogError(ex, "Failed to connect to ADOT collector at {Endpoint}. Is ADOT Lambda Layer enabled?", options.Endpoint); | ||
| return (503, "ADOT collector not available. Ensure AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument is set"); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| logger.LogError(ex, "Error forwarding OTLP {SignalType}", signalType); | ||
| return (500, $"Error forwarding OTLP: {ex.Message}"); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.