Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
60ecef4
Refactor logging to use and new function queue
nharper285 Apr 12, 2023
e9a1ce2
Testing setup of custom metric.
nharper285 Apr 13, 2023
1830f47
Changing host.json
nharper285 Apr 14, 2023
519f9b4
Merge branch 'main' into user/noharper/custom-metrics-refactor
nharper285 Apr 18, 2023
90a9270
Updating log interface.
nharper285 Apr 18, 2023
388bcc9
Merge branch 'user/noharper/custom-metrics-refactor' of https://githu…
nharper285 Apr 18, 2023
2dd4341
changes.
nharper285 Apr 19, 2023
41cc646
Fix encoding.
nharper285 Apr 19, 2023
17f777f
Merge branch 'main' into user/noharper/custom-metrics-refactor
nharper285 Apr 19, 2023
ab5358d
Updating.
nharper285 Apr 19, 2023
bc53750
Updating tests.
nharper285 Apr 19, 2023
86b7803
Adding metrics to program
nharper285 Apr 19, 2023
55d2529
Merge branch 'main' into user/noharper/custom-metrics-refactor
nharper285 Apr 20, 2023
f35923c
Pushing latest changes.
nharper285 Apr 20, 2023
5c82dc4
Merge branch 'user/noharper/custom-metrics-refactor' of https://githu…
nharper285 Apr 20, 2023
e6dfbcc
Update interface references.
nharper285 Apr 20, 2023
fb0da50
Removing string type.
nharper285 Apr 20, 2023
18bffa4
Add string back.
nharper285 Apr 20, 2023
605c6f6
Getting additional data for task heartbeat.
nharper285 Apr 21, 2023
9e95769
Removing additional fields.
nharper285 Apr 27, 2023
98dc451
Removing containers.
nharper285 Apr 27, 2023
5d21f34
Cleaning up.
nharper285 Apr 27, 2023
ac7aff4
Adding feature flag.
nharper285 Apr 27, 2023
11023f5
Merge branch 'main' into user/noharper/custom-metrics-refactor
nharper285 Apr 27, 2023
73041a2
Adding bicep changes.
nharper285 Apr 27, 2023
4b4465e
Fixing tests.
nharper285 Apr 27, 2023
dd4899a
Fixing test metrics.
nharper285 Apr 27, 2023
747b32b
Removing most of tests.
nharper285 Apr 27, 2023
959d391
Telemetry Refact Round 2.
nharper285 May 3, 2023
3472eb1
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 3, 2023
fdbfb53
Updated metrics.
nharper285 May 5, 2023
0c5bf9e
Merge branch 'user/noharper/custom-metrics-refactor-fork' of https://…
nharper285 May 5, 2023
9b876db
Remove custom metric function.
nharper285 May 5, 2023
556099b
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 5, 2023
3671f98
Syncing events.cs
nharper285 May 5, 2023
2a536d6
Making optional.
nharper285 May 5, 2023
f62367f
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 8, 2023
cd5a430
Using events as metric dimensions.
nharper285 May 10, 2023
090b396
Merge branch 'user/noharper/custom-metrics-refactor-fork' of https://…
nharper285 May 10, 2023
650bd25
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 10, 2023
bec622f
Fixing ORM tests.
nharper285 May 10, 2023
a21e158
Merge branch 'user/noharper/custom-metrics-refactor-fork' of https://…
nharper285 May 10, 2023
4d70cae
Remove metric records.
nharper285 May 10, 2023
e8f4ff2
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 10, 2023
2570643
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 11, 2023
0ff2cf1
Removing bad test.
nharper285 May 11, 2023
77c3fd8
Merge branch 'user/noharper/custom-metrics-refactor-fork' of https://…
nharper285 May 11, 2023
ccb3493
Remove testmetrics.'
nharper285 May 11, 2023
d491349
Adding test back.
nharper285 May 11, 2023
86bc52f
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 16, 2023
d32a3e0
Improving custom dimensions serialization.
nharper285 May 16, 2023
5800d4e
Update src/ApiService/ApiService/onefuzzlib/Metrics.cs
nharper285 May 16, 2023
acc202d
Reverting change.
nharper285 May 16, 2023
475f1c1
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 16, 2023
641cda1
Merge branch 'main' into user/noharper/custom-metrics-refactor-fork
nharper285 May 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ApiService/ApiService/FeatureFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public static class FeatureFlagConstants {
public const string RenderOnlyScribanTemplates = "RenderOnlyScribanTemplates";
public const string EnableNodeDecommissionStrategy = "EnableNodeDecommissionStrategy";
public const string SemanticNotificationConfigValidation = "SemanticNotificationConfigValidation";
public const string EnableCustomMetricTelemetry = "EnableCustomMetricTelemetry";
}
11 changes: 9 additions & 2 deletions src/ApiService/ApiService/Functions/QueueNodeHeartbeat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ public QueueNodeHearbeat(ILogTracer log, IOnefuzzContext context) {

[Function("QueueNodeHeartbeat")]
public async Async.Task Run([QueueTrigger("node-heartbeat", Connection = "AzureWebJobsStorage")] string msg) {
_log.Info($"heartbeat: {msg}");

var nodes = _context.NodeOperations;
var events = _context.Events;
var metrics = _context.Metrics;

_log.Info($"heartbeat: {msg}");
var hb = JsonSerializer.Deserialize<NodeHeartbeatEntry>(msg, EntityConverter.GetJsonSerializerOptions()).EnsureNotNull($"wrong data {msg}");
var node = await nodes.GetByMachineId(hb.NodeId);

Expand All @@ -35,7 +37,12 @@ public async Async.Task Run([QueueTrigger("node-heartbeat", Connection = "AzureW
_log.WithHttpStatus(r.ErrorV).Error($"Failed to replace heartbeat: {hb.NodeId:Tag:NodeId}");
}

var nodeHeartbeatEvent = new EventNodeHeartbeat(node.MachineId, node.ScalesetId, node.PoolName, node.State);
// TODO: do we still send event if we fail do update the table ?
await events.SendEvent(new EventNodeHeartbeat(node.MachineId, node.ScalesetId, node.PoolName));
await events.SendEvent(nodeHeartbeatEvent);
if (await _context.FeatureManagerSnapshot.IsEnabledAsync(FeatureFlagConstants.EnableCustomMetricTelemetry)) {
metrics.SendMetric(1, nodeHeartbeatEvent);
}

}
}
29 changes: 20 additions & 9 deletions src/ApiService/ApiService/Functions/QueueTaskHeartbeat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,45 @@ namespace Microsoft.OneFuzz.Service.Functions;

public class QueueTaskHearbeat {
private readonly ILogTracer _log;
private readonly IOnefuzzContext _context;

private readonly IEvents _events;
private readonly ITaskOperations _tasks;

public QueueTaskHearbeat(ILogTracer logTracer, ITaskOperations tasks, IEvents events) {
public QueueTaskHearbeat(ILogTracer logTracer, IOnefuzzContext context) {
_log = logTracer;
_tasks = tasks;
_events = events;
_context = context;
}

[Function("QueueTaskHeartbeat")]
public async Async.Task Run([QueueTrigger("task-heartbeat", Connection = "AzureWebJobsStorage")] string msg) {
_log.Info($"heartbeat: {msg}");

var _tasks = _context.TaskOperations;
var _jobs = _context.JobOperations;
var _events = _context.Events;
var _metrics = _context.Metrics;

_log.Info($"heartbeat: {msg}");
var hb = JsonSerializer.Deserialize<TaskHeartbeatEntry>(msg, EntityConverter.GetJsonSerializerOptions()).EnsureNotNull($"wrong data {msg}");

var task = await _tasks.GetByTaskId(hb.TaskId);

if (task == null) {
_log.Warning($"invalid {hb.TaskId:Tag:TaskId}");
return;
}

var job = await _jobs.Get(task.JobId);
if (job == null) {
_log.Warning($"invalid {task.JobId:Tag:JobId}");
return;
}
var newTask = task with { Heartbeat = DateTimeOffset.UtcNow };
var r = await _tasks.Replace(newTask);
if (!r.IsOk) {
_log.WithHttpStatus(r.ErrorV).Error($"failed to replace with new task {hb.TaskId:Tag:TaskId}");
}
await _events.SendEvent(new EventTaskHeartbeat(newTask.JobId, newTask.TaskId, newTask.Config));

var taskHeartBeatEvent = new EventTaskHeartbeat(newTask.JobId, newTask.TaskId, job.Config.Project, job.Config.Name, newTask.State, newTask.Config);
await _events.SendEvent(taskHeartBeatEvent);
if (await _context.FeatureManagerSnapshot.IsEnabledAsync(FeatureFlagConstants.EnableCustomMetricTelemetry)) {
_metrics.SendMetric(1, taskHeartBeatEvent);
}
}
}
27 changes: 27 additions & 0 deletions src/ApiService/ApiService/Log.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public void AppendFormatted<T>(T message, string? format) {
public interface ILog {
void Log(Guid correlationId, LogStringHandler message, SeverityLevel level, IReadOnlyDictionary<string, string> tags, string? caller);
void LogEvent(Guid correlationId, LogStringHandler evt, IReadOnlyDictionary<string, string> tags, IReadOnlyDictionary<string, double>? metrics, string? caller);
void LogMetric(Guid correlationId, LogStringHandler metric, int value, IReadOnlyDictionary<string, string>? customDimensions, IReadOnlyDictionary<string, string> tags, string? caller);

void LogException(Guid correlationId, Exception ex, LogStringHandler message, IReadOnlyDictionary<string, string> tags, IReadOnlyDictionary<string, double>? metrics, string? caller);
void Flush();
}
Expand Down Expand Up @@ -103,6 +105,17 @@ public void LogEvent(Guid correlationId, LogStringHandler evt, IReadOnlyDictiona
_telemetryClient.TrackEvent(telemetry);
}

public void LogMetric(Guid correlationId, LogStringHandler metric, int value, IReadOnlyDictionary<string, string>? customDimensions, IReadOnlyDictionary<string, string> tags, string? caller) {
var telemetry = new MetricTelemetry(metric.ToString(), value, value, value, value, value);
// copy properties
Copy(telemetry.Properties, customDimensions);
telemetry.Properties["CorrelationId"] = correlationId.ToString();
if (caller is not null) telemetry.Properties["CalledBy"] = caller;
Copy(telemetry.Properties, metric.Tags);

_telemetryClient.TrackMetric(telemetry);
}

public void LogException(Guid correlationId, Exception ex, LogStringHandler message, IReadOnlyDictionary<string, string> tags, IReadOnlyDictionary<string, double>? metrics, string? caller) {
{
var telemetry = new ExceptionTelemetry(ex);
Expand Down Expand Up @@ -160,11 +173,17 @@ public void Log(Guid correlationId, LogStringHandler message, SeverityLevel leve
}
}

public void LogMetric(Guid correlationId, LogStringHandler metric, int value, IReadOnlyDictionary<string, string>? customDimensions, IReadOnlyDictionary<string, string> tags, string? caller) {
System.Console.Out.WriteLine($"[{correlationId}][Metric] {metric}");
LogTags(correlationId, tags);
}

public void LogEvent(Guid correlationId, LogStringHandler evt, IReadOnlyDictionary<string, string> tags, IReadOnlyDictionary<string, double>? metrics, string? caller) {
System.Console.Out.WriteLine($"[{correlationId}][Event] {evt}");
LogTags(correlationId, tags);
LogMetrics(correlationId, metrics);
}

public void LogException(Guid correlationId, Exception ex, LogStringHandler message, IReadOnlyDictionary<string, string> tags, IReadOnlyDictionary<string, double>? metrics, string? caller) {
System.Console.Out.WriteLine($"[{correlationId}][Exception] {message}:{ex}");
LogTags(correlationId, tags);
Expand All @@ -183,6 +202,7 @@ public interface ILogTracer {

void Error(Error error);
void Event(LogStringHandler evt, IReadOnlyDictionary<string, double>? metrics = null);
void Metric(LogStringHandler metric, int value, IReadOnlyDictionary<string, string>? customDimensions);
void Exception(Exception ex, LogStringHandler message = $"", IReadOnlyDictionary<string, double>? metrics = null);
void ForceFlush();
void Info(LogStringHandler message);
Expand Down Expand Up @@ -327,6 +347,13 @@ public void Event(LogStringHandler evt, IReadOnlyDictionary<string, double>? met
}
}

public void Metric(LogStringHandler metric, int value, IReadOnlyDictionary<string, string>? customDimensions) {
var caller = GetCaller();
foreach (var logger in _loggers) {
logger.LogMetric(CorrelationId, metric, value, customDimensions, Tags, caller);
}
}

public void Exception(Exception ex, LogStringHandler message, IReadOnlyDictionary<string, double>? metrics) {
var caller = GetCaller();
foreach (var logger in _loggers) {
Expand Down
9 changes: 6 additions & 3 deletions src/ApiService/ApiService/OneFuzzTypes/Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,14 @@ public record EventTaskStateUpdated(
TaskConfig Config
) : BaseEvent();


[EventType(EventType.TaskHeartbeat)]
public record EventTaskHeartbeat(
Guid JobId,
Guid TaskId,
TaskConfig Config
string? Project,
string? Name,
TaskState? State,
TaskConfig? Config
) : BaseEvent();

[EventType(EventType.Ping)]
Expand Down Expand Up @@ -273,7 +275,8 @@ PoolName PoolName
public record EventNodeHeartbeat(
Guid MachineId,
Guid? ScalesetId,
PoolName PoolName
PoolName PoolName,
NodeState state
) : BaseEvent();


Expand Down
1 change: 1 addition & 0 deletions src/ApiService/ApiService/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public static async Async.Task Main() {
})
.AddScoped<IAutoScaleOperations, AutoScaleOperations>()
.AddScoped<INodeOperations, NodeOperations>()
.AddScoped<IMetrics, Metrics>()
.AddScoped<IEvents, Events>()
.AddScoped<IWebhookOperations, WebhookOperations>()
.AddScoped<IWebhookMessageLogOperations, WebhookMessageLogOperations>()
Expand Down
4 changes: 2 additions & 2 deletions src/ApiService/ApiService/host.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
"isEnabled": false,
"excludedTypes": "Request;Trace;Dependency;Event;Exception"
}
}
}
Expand Down
1 change: 0 additions & 1 deletion src/ApiService/ApiService/onefuzzlib/Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public SignalREvent Truncate(int maxLength) {

public interface IEvents {
Async.Task SendEvent(BaseEvent anEvent);

Async.Task QueueSignalrEvent(DownloadableEventMessage message);

void LogEvent(BaseEvent anEvent);
Expand Down
54 changes: 54 additions & 0 deletions src/ApiService/ApiService/onefuzzlib/Metrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;

namespace Microsoft.OneFuzz.Service {

public record CustomMetric(
string name,
int value,
Dictionary<string, string> customDimensions
);


public interface IMetrics {
void SendMetric(int metricValue, BaseEvent customDimensions);

void LogMetric(BaseEvent metric);
}

public class Metrics : IMetrics {
private readonly ILogTracer _log;
private readonly IOnefuzzContext _context;
private readonly JsonSerializerOptions _options;

public Metrics(ILogTracer log, IOnefuzzContext context) {
_context = context;
_log = log;
_options = new JsonSerializerOptions(EntityConverter.GetJsonSerializerOptions()) {
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
_options.Converters.Add(new RemoveUserInfo());
}

public void SendMetric(int metricValue, BaseEvent customDimensions) {
var metricType = customDimensions.GetEventType();

_ = _options.PropertyNamingPolicy ?? throw new ArgumentException("Serializer _options not available.");

var metricTypeSnakeCase = _options.PropertyNamingPolicy.ConvertName($"{metricType}");

var dimensionNode = JsonSerializer.SerializeToNode(customDimensions, customDimensions.GetType(), _options);
_ = dimensionNode ?? throw new JsonException("Was not able to properly serialize the custom dimensions.");
var dimensionDict = dimensionNode.AsObject().ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value is not null ? kvp.Value.ToString() : "");

_log.Metric($"{metricTypeSnakeCase}", metricValue, dimensionDict);
LogMetric(customDimensions);
}

public void LogMetric(BaseEvent metric) {
var serializedMetric = JsonSerializer.Serialize(metric, metric.GetType(), _options);
_log.Info($"sending metric: {metric.GetEventType():Tag:MetricType} - {serializedMetric}");
}
}
}
2 changes: 2 additions & 0 deletions src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public interface IOnefuzzContext {
ICreds Creds { get; }
IDiskOperations DiskOperations { get; }
IEvents Events { get; }
IMetrics Metrics { get; }
IExtensions Extensions { get; }
IIpOperations IpOperations { get; }
IJobOperations JobOperations { get; }
Expand Down Expand Up @@ -62,6 +63,7 @@ public OnefuzzContext(IServiceProvider serviceProvider) {
public IAutoScaleOperations AutoScaleOperations => _serviceProvider.GetRequiredService<IAutoScaleOperations>();
public INodeOperations NodeOperations => _serviceProvider.GetRequiredService<INodeOperations>();
public IEvents Events => _serviceProvider.GetRequiredService<IEvents>();
public IMetrics Metrics => _serviceProvider.GetRequiredService<IMetrics>();
public IWebhookOperations WebhookOperations => _serviceProvider.GetRequiredService<IWebhookOperations>();
public IWebhookMessageLogOperations WebhookMessageLogOperations => _serviceProvider.GetRequiredService<IWebhookMessageLogOperations>();
public ITaskOperations TaskOperations => _serviceProvider.GetRequiredService<ITaskOperations>();
Expand Down
2 changes: 2 additions & 0 deletions src/ApiService/IntegrationTests/Fakes/TestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public TestContext(IHttpClientFactory httpClientFactory, ILogTracer logTracer, I
FeatureManagerSnapshot = new TestFeatureManagerSnapshot();
WebhookOperations = new TestWebhookOperations(httpClientFactory, logTracer, this);
Events = new TestEvents(logTracer, this);
Metrics = new TestMetrics(logTracer, this);
WebhookMessageLogOperations = new TestWebhookMessageLogOperations(logTracer, this);
}

Expand All @@ -67,6 +68,7 @@ public Async.Task InsertAll(params EntityBase[] objs)
// Implementations:

public IEvents Events { get; }
public IMetrics Metrics { get; }

public IServiceConfig ServiceConfiguration { get; }

Expand Down
12 changes: 12 additions & 0 deletions src/ApiService/IntegrationTests/Fakes/TestMetrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using Microsoft.OneFuzz.Service;

namespace IntegrationTests.Fakes;

public sealed class TestMetrics : Metrics {

public List<BaseEvent> Metrics { get; } = new();
public List<EventMessage> CustomMetrics { get; } = new();
public TestMetrics(ILogTracer log, IOnefuzzContext context)
: base(log, context) { }
}
5 changes: 5 additions & 0 deletions src/ApiService/IntegrationTests/TestLogTracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public void Event(LogStringHandler evt, IReadOnlyDictionary<string, double>? met
_output.WriteLine($"[Event] [{evt}]");
}

public void Metric(LogStringHandler metric, int value, IReadOnlyDictionary<string, string>? customDimensions) {
// TODO: metrics
_output.WriteLine($"[Event] [{metric}]");
}

public void Exception(Exception ex, LogStringHandler message = $"", IReadOnlyDictionary<string, double>? metrics = null) {
// TODO: metrics
_output.WriteLine($"[Error] {message} {ex}");
Expand Down
2 changes: 1 addition & 1 deletion src/ApiService/Tests/OrmTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public void TestConvertSnakeToPAscalCase() {

[Fact]
public void TestEventSerialization() {
var expectedEvent = new EventMessage(Guid.NewGuid(), EventType.NodeHeartbeat, new EventNodeHeartbeat(Guid.NewGuid(), Guid.NewGuid(), PoolName.Parse("test-Poool")), Guid.NewGuid(), "test", DateTime.UtcNow);
var expectedEvent = new EventMessage(Guid.NewGuid(), EventType.NodeHeartbeat, new EventNodeHeartbeat(Guid.NewGuid(), Guid.NewGuid(), PoolName.Parse("test-Poool"), NodeState.Busy), Guid.NewGuid(), "test", DateTime.UtcNow);
var serialized = JsonSerializer.Serialize(expectedEvent, EntityConverter.GetJsonSerializerOptions());
var actualEvent = JsonSerializer.Deserialize<EventMessage>((string)serialized, EntityConverter.GetJsonSerializerOptions());
Assert.Equal(expectedEvent, actualEvent);
Expand Down
13 changes: 13 additions & 0 deletions src/deployment/bicep-templates/feature-flags.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,17 @@ resource validateNotificationConfigSemantics 'Microsoft.AppConfiguration/configu
}
}

resource enableCustomMetricFeatureFlag 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = {
parent: featureFlags
name: '.appconfig.featureflag~2FEnableCustomMetricTelemetry'
properties: {
value: string({
id: 'EnableCustomMetricTelemetry'
description: 'Allow custom metrics to be sent.'
enabled: false
})
contentType: 'application/vnd.microsoft.appconfig.ff+json;charset=utf-8'
}
}

output AppConfigEndpoint string = 'https://${appConfigName}.azconfig.io'
1 change: 1 addition & 0 deletions src/deployment/bicep-templates/storageAccounts.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var storageAccountFuncQueuesParams = [
'update-queue'
'webhooks'
'signalr-events'
'custom-metrics'
]
var fileChangesQueueIndex = 0

Expand Down