feat: Enhance telemetry metrics recording with sealed mode tagging

This commit is contained in:
StellaOps Bot
2025-12-05 01:00:29 +02:00
parent 175b750e29
commit 0de3c8a3f0
4 changed files with 152 additions and 27 deletions

View File

@@ -81,41 +81,29 @@ internal static class CliMetrics
new("status", string.IsNullOrWhiteSpace(status) ? "queued" : status)));
public static void RecordPolicySimulation(string outcome)
=> PolicySimulationCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)
});
=> PolicySimulationCounter.Add(1, WithSealedModeTag(
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)));
public static void RecordTaskRunnerSimulation(string outcome)
=> TaskRunnerSimulationCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)
});
=> TaskRunnerSimulationCounter.Add(1, WithSealedModeTag(
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)));
public static void RecordPolicyActivation(string outcome)
=> PolicyActivationCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)
});
=> PolicyActivationCounter.Add(1, WithSealedModeTag(
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)));
public static void RecordAdvisoryRun(string taskType, string outcome)
=> AdvisoryRunCounter.Add(1, new KeyValuePair<string, object?>[]
{
=> AdvisoryRunCounter.Add(1, WithSealedModeTag(
new("task", string.IsNullOrWhiteSpace(taskType) ? "unknown" : taskType.ToLowerInvariant()),
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)
});
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)));
public static void RecordSourcesDryRun(string status)
=> SourcesDryRunCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("status", string.IsNullOrWhiteSpace(status) ? "unknown" : status)
});
=> SourcesDryRunCounter.Add(1, WithSealedModeTag(
new("status", string.IsNullOrWhiteSpace(status) ? "unknown" : status)));
public static void RecordAocVerify(string outcome)
=> AocVerifyCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)
});
=> AocVerifyCounter.Add(1, WithSealedModeTag(
new("outcome", string.IsNullOrWhiteSpace(outcome) ? "unknown" : outcome)));
public static void RecordPolicyFindingsList(string outcome)
=> PolicyFindingsListCounter.Add(1, new KeyValuePair<string, object?>[]

View File

@@ -0,0 +1,132 @@
using System.Text.Json;
namespace StellaOps.Microservice;
/// <summary>
/// Adapts typed endpoint handlers to the raw endpoint interface.
/// </summary>
public static class TypedEndpointAdapter
{
private static readonly JsonSerializerOptions DefaultJsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true
};
/// <summary>
/// Creates a raw handler function from a typed endpoint with request and response.
/// </summary>
/// <typeparam name="TRequest">The request type.</typeparam>
/// <typeparam name="TResponse">The response type.</typeparam>
/// <param name="handler">The typed handler.</param>
/// <param name="jsonOptions">Optional JSON serialization options.</param>
/// <returns>A raw handler function.</returns>
public static Func<RawRequestContext, CancellationToken, Task<RawResponse>> Adapt<TRequest, TResponse>(
IStellaEndpoint<TRequest, TResponse> handler,
JsonSerializerOptions? jsonOptions = null)
{
var options = jsonOptions ?? DefaultJsonOptions;
return async (context, cancellationToken) =>
{
try
{
// Deserialize request
TRequest? request;
if (context.Body == Stream.Null || context.Body.Length == 0)
{
request = default;
}
else
{
request = await JsonSerializer.DeserializeAsync<TRequest>(
context.Body,
options,
cancellationToken);
}
if (request is null)
{
return RawResponse.BadRequest("Invalid request body");
}
// Call handler
var response = await handler.HandleAsync(request, cancellationToken);
// Serialize response
return SerializeResponse(response, options);
}
catch (JsonException ex)
{
return RawResponse.BadRequest($"Invalid JSON: {ex.Message}");
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
return RawResponse.InternalError(ex.Message);
}
};
}
/// <summary>
/// Creates a raw handler function from a typed endpoint with response only.
/// </summary>
/// <typeparam name="TResponse">The response type.</typeparam>
/// <param name="handler">The typed handler.</param>
/// <param name="jsonOptions">Optional JSON serialization options.</param>
/// <returns>A raw handler function.</returns>
public static Func<RawRequestContext, CancellationToken, Task<RawResponse>> Adapt<TResponse>(
IStellaEndpoint<TResponse> handler,
JsonSerializerOptions? jsonOptions = null)
{
var options = jsonOptions ?? DefaultJsonOptions;
return async (context, cancellationToken) =>
{
try
{
// Call handler
var response = await handler.HandleAsync(cancellationToken);
// Serialize response
return SerializeResponse(response, options);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
return RawResponse.InternalError(ex.Message);
}
};
}
/// <summary>
/// Creates a raw handler function from a raw endpoint.
/// </summary>
/// <param name="handler">The raw handler.</param>
/// <returns>A raw handler function.</returns>
public static Func<RawRequestContext, CancellationToken, Task<RawResponse>> Adapt(
IRawStellaEndpoint handler)
{
return handler.HandleAsync;
}
private static RawResponse SerializeResponse<TResponse>(TResponse response, JsonSerializerOptions options)
{
var json = JsonSerializer.SerializeToUtf8Bytes(response, options);
var headers = new HeaderCollection();
headers.Set("Content-Type", "application/json; charset=utf-8");
return new RawResponse
{
StatusCode = 200,
Headers = headers,
Body = new MemoryStream(json)
};
}
}