Skip to content

Commit 28b7bdb

Browse files
sighphyredaveleek
andauthored
feat: rest of impact metrics definitions for dotnet (#76)
Co-authored-by: David Leek <david@getunleash.io>
1 parent bec1737 commit 28b7bdb

File tree

5 files changed

+547
-16
lines changed

5 files changed

+547
-16
lines changed
Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
using System;
2-
using System.Text.Json;
1+
using System.Collections.Generic;
32
using NUnit.Framework;
43
using Yggdrasil;
5-
using Yggdrasil.Test;
64

75
public class YggdrasilImpactMetricsTest
86
{
9-
private JsonSerializerOptions options = new JsonSerializerOptions
10-
{
11-
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
12-
};
13-
147
[Test]
158
public void DefineCounter_Throws_Only_When_Invalid()
169
{
@@ -20,4 +13,77 @@ public void DefineCounter_Throws_Only_When_Invalid()
2013
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.DefineCounter(null!, "Measures time spent on server doing things"));
2114
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.DefineCounter(null!, null!));
2215
}
16+
17+
[Test]
18+
public void IncCounter_Throws_Only_When_Invalid()
19+
{
20+
var yggdrasilEngine = new YggdrasilEngine();
21+
yggdrasilEngine.DefineCounter("requests_total", "Total requests");
22+
23+
Assert.DoesNotThrow(() => yggdrasilEngine.IncCounter("requests_total"));
24+
Assert.DoesNotThrow(() => yggdrasilEngine.IncCounter("requests_total", 5));
25+
Assert.DoesNotThrow(() => yggdrasilEngine.IncCounter("requests_total", 3, new Dictionary<string, string> { { "env", "test" } }));
26+
27+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.IncCounter(null!));
28+
}
29+
30+
[Test]
31+
public void DefineGauge_Throws_Only_When_Invalid()
32+
{
33+
var yggdrasilEngine = new YggdrasilEngine();
34+
Assert.DoesNotThrow(() => yggdrasilEngine.DefineGauge("cpu_usage", "CPU usage"));
35+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.DefineGauge("cpu_usage", null!));
36+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.DefineGauge(null!, "CPU usage"));
37+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.DefineGauge(null!, null!));
38+
}
39+
40+
[Test]
41+
public void SetGauge_Throws_Only_When_Invalid()
42+
{
43+
var yggdrasilEngine = new YggdrasilEngine();
44+
yggdrasilEngine.DefineGauge("queue_depth", "Queue depth");
45+
46+
Assert.DoesNotThrow(() => yggdrasilEngine.SetGauge("queue_depth", 10.5));
47+
Assert.DoesNotThrow(() => yggdrasilEngine.SetGauge("queue_depth", 5.25, new Dictionary<string, string> { { "env", "prod" } }));
48+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.SetGauge(null!, 1.0));
49+
}
50+
51+
[Test]
52+
public void DefineHistogram_Throws_Only_When_Invalid()
53+
{
54+
var yggdrasilEngine = new YggdrasilEngine();
55+
Assert.DoesNotThrow(() => yggdrasilEngine.DefineHistogram("request_duration", "Request duration"));
56+
Assert.DoesNotThrow(() => yggdrasilEngine.DefineHistogram("request_duration_custom", "Request duration custom", new[] { 0.1, 0.5, 1.0, 5.0 }));
57+
58+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.DefineHistogram("request_duration", null!));
59+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.DefineHistogram(null!, "Request duration"));
60+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.DefineHistogram(null!, null!));
61+
}
62+
63+
[Test]
64+
public void ObserveHistogram_Throws_Only_When_Invalid()
65+
{
66+
var yggdrasilEngine = new YggdrasilEngine();
67+
yggdrasilEngine.DefineHistogram("request_duration", "Request duration", new[] { 0.1, 0.5, 1.0, 5.0 });
68+
69+
Assert.DoesNotThrow(() => yggdrasilEngine.ObserveHistogram("request_duration", 0.05));
70+
Assert.DoesNotThrow(() => yggdrasilEngine.ObserveHistogram("request_duration", 0.75, new Dictionary<string, string> { { "env", "test" } }));
71+
Assert.Throws<YggdrasilEngineException>(() => yggdrasilEngine.ObserveHistogram(null!, 1.0));
72+
}
73+
74+
[Test]
75+
public void ImpactMetrics_Methods_Can_Be_Used_Together()
76+
{
77+
var yggdrasilEngine = new YggdrasilEngine();
78+
79+
Assert.DoesNotThrow(() =>
80+
{
81+
yggdrasilEngine.DefineCounter("test_counter", "Test counter");
82+
yggdrasilEngine.IncCounter("test_counter", 10);
83+
yggdrasilEngine.DefineGauge("test_gauge", "Test gauge");
84+
yggdrasilEngine.SetGauge("test_gauge", 42);
85+
yggdrasilEngine.DefineHistogram("test_histogram", "Test histogram", new[] { 0.1, 0.5, 1.0 });
86+
yggdrasilEngine.ObserveHistogram("test_histogram", 0.25);
87+
});
88+
}
2389
}

dotnet-engine/Yggdrasil.Engine/Flat.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ static Flat()
1717
built_in_strategies = Marshal.GetDelegateForFunctionPointer<BuiltInStrategiesDelegate>(NativeLibLoader.LoadFunctionPointer(_libHandle, "flat_built_in_strategies"));
1818
get_metrics = Marshal.GetDelegateForFunctionPointer<GetMetricsDelegate>(NativeLibLoader.LoadFunctionPointer(_libHandle, "flat_get_metrics"));
1919
define_counter = Marshal.GetDelegateForFunctionPointer<DefineCounterDelegate>(NativeLibLoader.LoadFunctionPointer(_libHandle, "flat_define_counter"));
20+
inc_counter = Marshal.GetDelegateForFunctionPointer<IncCounterDelegate>(NativeLibLoader.LoadFunctionPointer(_libHandle, "flat_inc_counter"));
21+
define_gauge = Marshal.GetDelegateForFunctionPointer<DefineGaugeDelegate>(NativeLibLoader.LoadFunctionPointer(_libHandle, "flat_define_gauge"));
22+
set_gauge = Marshal.GetDelegateForFunctionPointer<SetGaugeDelegate>(NativeLibLoader.LoadFunctionPointer(_libHandle, "flat_set_gauge"));
23+
define_histogram = Marshal.GetDelegateForFunctionPointer<DefineHistogramDelegate>(NativeLibLoader.LoadFunctionPointer(_libHandle, "flat_define_histogram"));
24+
observe_histogram = Marshal.GetDelegateForFunctionPointer<ObserveHistogramDelegate>(NativeLibLoader.LoadFunctionPointer(_libHandle, "flat_observe_histogram"));
2025
}
2126

2227
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
@@ -41,6 +46,16 @@ static Flat()
4146

4247
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
4348
public delegate Buf DefineCounterDelegate(IntPtr enginePtr, IntPtr messagePtr, nuint messageLen);
49+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
50+
public delegate Buf IncCounterDelegate(IntPtr enginePtr, IntPtr messagePtr, nuint messageLen);
51+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
52+
public delegate Buf DefineGaugeDelegate(IntPtr enginePtr, IntPtr messagePtr, nuint messageLen);
53+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
54+
public delegate Buf SetGaugeDelegate(IntPtr enginePtr, IntPtr messagePtr, nuint messageLen);
55+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
56+
public delegate Buf DefineHistogramDelegate(IntPtr enginePtr, IntPtr messagePtr, nuint messageLen);
57+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
58+
public delegate Buf ObserveHistogramDelegate(IntPtr enginePtr, IntPtr messagePtr, nuint messageLen);
4459

4560
private static readonly TakeStateDelegate take_state;
4661
private static readonly FreeBufferDelegate free_buffer;
@@ -51,6 +66,11 @@ static Flat()
5166
private static readonly GetMetricsDelegate get_metrics;
5267

5368
private static readonly DefineCounterDelegate define_counter;
69+
private static readonly IncCounterDelegate inc_counter;
70+
private static readonly DefineGaugeDelegate define_gauge;
71+
private static readonly SetGaugeDelegate set_gauge;
72+
private static readonly DefineHistogramDelegate define_histogram;
73+
private static readonly ObserveHistogramDelegate observe_histogram;
5474

5575
public static Buf TakeState(IntPtr ptr, string json)
5676
{
@@ -86,6 +106,7 @@ public static Buf CheckVariant(IntPtr ptr, byte[] message)
86106
handle.Free();
87107
}
88108
}
109+
89110
public static Buf ListKnownToggles(IntPtr ptr)
90111
{
91112
return list_known_toggles(ptr);
@@ -116,6 +137,81 @@ public static Buf DefineCounter(IntPtr ptr, byte[] message)
116137
}
117138
}
118139

140+
public static Buf IncCounter(IntPtr ptr, byte[] message)
141+
{
142+
nuint len = (nuint)message.Length;
143+
GCHandle handle = GCHandle.Alloc(message, GCHandleType.Pinned);
144+
try
145+
{
146+
IntPtr msgPtr = handle.AddrOfPinnedObject();
147+
return inc_counter(ptr, msgPtr, len);
148+
}
149+
finally
150+
{
151+
handle.Free();
152+
}
153+
}
154+
155+
public static Buf DefineGauge(IntPtr ptr, byte[] message)
156+
{
157+
nuint len = (nuint)message.Length;
158+
GCHandle handle = GCHandle.Alloc(message, GCHandleType.Pinned);
159+
try
160+
{
161+
IntPtr msgPtr = handle.AddrOfPinnedObject();
162+
return define_gauge(ptr, msgPtr, len);
163+
}
164+
finally
165+
{
166+
handle.Free();
167+
}
168+
}
169+
170+
public static Buf SetGauge(IntPtr ptr, byte[] message)
171+
{
172+
nuint len = (nuint)message.Length;
173+
GCHandle handle = GCHandle.Alloc(message, GCHandleType.Pinned);
174+
try
175+
{
176+
IntPtr msgPtr = handle.AddrOfPinnedObject();
177+
return set_gauge(ptr, msgPtr, len);
178+
}
179+
finally
180+
{
181+
handle.Free();
182+
}
183+
}
184+
185+
public static Buf DefineHistogram(IntPtr ptr, byte[] message)
186+
{
187+
nuint len = (nuint)message.Length;
188+
GCHandle handle = GCHandle.Alloc(message, GCHandleType.Pinned);
189+
try
190+
{
191+
IntPtr msgPtr = handle.AddrOfPinnedObject();
192+
return define_histogram(ptr, msgPtr, len);
193+
}
194+
finally
195+
{
196+
handle.Free();
197+
}
198+
}
199+
200+
public static Buf ObserveHistogram(IntPtr ptr, byte[] message)
201+
{
202+
nuint len = (nuint)message.Length;
203+
GCHandle handle = GCHandle.Alloc(message, GCHandleType.Pinned);
204+
try
205+
{
206+
IntPtr msgPtr = handle.AddrOfPinnedObject();
207+
return observe_histogram(ptr, msgPtr, len);
208+
}
209+
finally
210+
{
211+
handle.Free();
212+
}
213+
}
214+
119215
public static void FreeBuf(Buf buf)
120216
{
121217
free_buffer(buf);

dotnet-engine/Yggdrasil.Engine/Flatbuffers.cs

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,118 @@ public static byte[] CreateDefineCounterBuffer(FlatBufferBuilder builder, string
6464
return builder.SizedByteArray();
6565
}
6666

67+
public static byte[] CreateIncCounterBuffer(FlatBufferBuilder builder, string name, long value, IDictionary<string, string>? labels = null)
68+
{
69+
var nameOffset = builder.CreateString(name);
70+
var labelsOffset = CreateSampleLabelsVector(builder, labels);
71+
72+
IncCounter.StartIncCounter(builder);
73+
IncCounter.AddName(builder, nameOffset);
74+
IncCounter.AddValue(builder, value);
75+
if (labelsOffset.HasValue)
76+
{
77+
IncCounter.AddLabels(builder, labelsOffset.Value);
78+
}
79+
80+
var incCounterMessage = IncCounter.EndIncCounter(builder);
81+
builder.Finish(incCounterMessage.Value);
82+
return builder.SizedByteArray();
83+
}
84+
85+
public static byte[] CreateDefineGaugeBuffer(FlatBufferBuilder builder, string name, string help)
86+
{
87+
var nameOffset = builder.CreateString(name);
88+
var helpOffset = builder.CreateString(help);
89+
90+
DefineGauge.StartDefineGauge(builder);
91+
DefineGauge.AddName(builder, nameOffset);
92+
DefineGauge.AddHelp(builder, helpOffset);
93+
94+
var defineGaugeMessage = DefineGauge.EndDefineGauge(builder);
95+
builder.Finish(defineGaugeMessage.Value);
96+
return builder.SizedByteArray();
97+
}
98+
99+
public static byte[] CreateSetGaugeBuffer(FlatBufferBuilder builder, string name, double value, IDictionary<string, string>? labels = null)
100+
{
101+
var nameOffset = builder.CreateString(name);
102+
var labelsOffset = CreateSampleLabelsVector(builder, labels);
103+
104+
SetGauge.StartSetGauge(builder);
105+
SetGauge.AddName(builder, nameOffset);
106+
SetGauge.AddValue(builder, value);
107+
if (labelsOffset.HasValue)
108+
{
109+
SetGauge.AddLabels(builder, labelsOffset.Value);
110+
}
111+
112+
var setGaugeMessage = SetGauge.EndSetGauge(builder);
113+
builder.Finish(setGaugeMessage.Value);
114+
return builder.SizedByteArray();
115+
}
116+
117+
public static byte[] CreateDefineHistogramBuffer(FlatBufferBuilder builder, string name, string help, IEnumerable<double>? buckets = null)
118+
{
119+
var nameOffset = builder.CreateString(name);
120+
var helpOffset = builder.CreateString(help);
121+
var bucketArray = (buckets ?? Enumerable.Empty<double>()).ToArray();
122+
var bucketsOffset = bucketArray.Length > 0
123+
? DefineHistogram.CreateBucketsVector(builder, bucketArray)
124+
: default(VectorOffset);
125+
126+
DefineHistogram.StartDefineHistogram(builder);
127+
DefineHistogram.AddName(builder, nameOffset);
128+
DefineHistogram.AddHelp(builder, helpOffset);
129+
if (bucketArray.Length > 0)
130+
{
131+
DefineHistogram.AddBuckets(builder, bucketsOffset);
132+
}
133+
134+
var defineHistogramMessage = DefineHistogram.EndDefineHistogram(builder);
135+
builder.Finish(defineHistogramMessage.Value);
136+
return builder.SizedByteArray();
137+
}
138+
139+
public static byte[] CreateObserveHistogramBuffer(FlatBufferBuilder builder, string name, double value, IDictionary<string, string>? labels = null)
140+
{
141+
var nameOffset = builder.CreateString(name);
142+
var labelsOffset = CreateSampleLabelsVector(builder, labels);
143+
144+
ObserveHistogram.StartObserveHistogram(builder);
145+
ObserveHistogram.AddName(builder, nameOffset);
146+
ObserveHistogram.AddValue(builder, value);
147+
if (labelsOffset.HasValue)
148+
{
149+
ObserveHistogram.AddLabels(builder, labelsOffset.Value);
150+
}
151+
152+
var observeHistogramMessage = ObserveHistogram.EndObserveHistogram(builder);
153+
builder.Finish(observeHistogramMessage.Value);
154+
return builder.SizedByteArray();
155+
}
156+
157+
private static VectorOffset? CreateSampleLabelsVector(FlatBufferBuilder builder, IDictionary<string, string>? labels)
158+
{
159+
if (labels == null || labels.Count == 0)
160+
{
161+
return null;
162+
}
163+
164+
var labelEntries = new Offset<SampleLabelEntry>[labels.Count];
165+
var index = 0;
166+
foreach (var kvp in labels)
167+
{
168+
labelEntries[index] = SampleLabelEntry.CreateSampleLabelEntry(
169+
builder,
170+
builder.CreateString(kvp.Key),
171+
builder.CreateString(kvp.Value)
172+
);
173+
index++;
174+
}
175+
176+
return IncCounter.CreateLabelsVector(builder, labelEntries);
177+
}
178+
67179
internal static VectorOffset CreatePropertiesVector(FlatBufferBuilder builder, Context context)
68180
{
69181
var propertyEntries = new Offset<PropertyEntry>[context.Properties?.Count ?? 0];
@@ -238,4 +350,4 @@ private static Dictionary<string, long> GetVariantCounts(ToggleStats stats)
238350
.ToDictionary(x => x.Key, v => v.Value);
239351
}
240352

241-
}
353+
}

dotnet-engine/Yggdrasil.Engine/YggdrasilEngine.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,46 @@ public void DefineCounter(string name, string help)
9090
finally { Flat.FreeBuf(buf); }
9191
}
9292

93+
public void IncCounter(string name, long value = 1, IDictionary<string, string>? labels = null)
94+
{
95+
var messageBuffer = Flatbuffers.CreateIncCounterBuffer(new FlatBufferBuilder(128), name, value, labels);
96+
var buf = Flat.IncCounter(state, messageBuffer);
97+
try { Flatbuffers.ParseVoidAndThrow(buf); }
98+
finally { Flat.FreeBuf(buf); }
99+
}
100+
101+
public void DefineGauge(string name, string help)
102+
{
103+
var messageBuffer = Flatbuffers.CreateDefineGaugeBuffer(new FlatBufferBuilder(128), name, help);
104+
var buf = Flat.DefineGauge(state, messageBuffer);
105+
try { Flatbuffers.ParseVoidAndThrow(buf); }
106+
finally { Flat.FreeBuf(buf); }
107+
}
108+
109+
public void SetGauge(string name, double value, IDictionary<string, string>? labels = null)
110+
{
111+
var messageBuffer = Flatbuffers.CreateSetGaugeBuffer(new FlatBufferBuilder(128), name, value, labels);
112+
var buf = Flat.SetGauge(state, messageBuffer);
113+
try { Flatbuffers.ParseVoidAndThrow(buf); }
114+
finally { Flat.FreeBuf(buf); }
115+
}
116+
117+
public void DefineHistogram(string name, string help, IEnumerable<double>? buckets = null)
118+
{
119+
var messageBuffer = Flatbuffers.CreateDefineHistogramBuffer(new FlatBufferBuilder(128), name, help, buckets);
120+
var buf = Flat.DefineHistogram(state, messageBuffer);
121+
try { Flatbuffers.ParseVoidAndThrow(buf); }
122+
finally { Flat.FreeBuf(buf); }
123+
}
124+
125+
public void ObserveHistogram(string name, double value, IDictionary<string, string>? labels = null)
126+
{
127+
var messageBuffer = Flatbuffers.CreateObserveHistogramBuffer(new FlatBufferBuilder(128), name, value, labels);
128+
var buf = Flat.ObserveHistogram(state, messageBuffer);
129+
try { Flatbuffers.ParseVoidAndThrow(buf); }
130+
finally { Flat.FreeBuf(buf); }
131+
}
132+
93133
public ICollection<FeatureDefinition> ListKnownToggles()
94134
{
95135
var buf = Flat.ListKnownToggles(state);

0 commit comments

Comments
 (0)