Skip to content

Instantly share code, notes, and snippets.

@NickCraver
Last active October 29, 2023 00:48
Show Gist options
  • Save NickCraver/551ff8ad90d63344f0604442d33ad728 to your computer and use it in GitHub Desktop.
Save NickCraver/551ff8ad90d63344f0604442d33ad728 to your computer and use it in GitHub Desktop.
MetricsPrototyping
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System;
namespace MetricsPrototype
{
public class Example
{
public void Foo()
{
var counter = Counter.Create("my_counter", "requests", "asldjkhasjklhda");
counter.Increment();
var counterWithTags1 = Counter.Create("my_counter_with_tags", "requests", "asldjkhasjklhda", new Tag<HttpStatusCode>("status_code"));
counterWithTags1.Increment(HttpStatusCode.OK);
var counterWithTags2 = Counter.Create("my_counter_with_tags", "requests", "asldjkhasjklhda", new Tag<HttpStatusCode>("status_code"), new Tag<string>("route"));
counterWithTags2.Increment(HttpStatusCode.OK, "Questions/Show");
}
}
public abstract class MetricBase
{
protected MetricBase() { }
public MetricBase(string name, string unit, string description)
=> (Name, Unit, Description) = (name, unit, description);
public MetricBase(string name, string unit, string description, IReadOnlyDictionary<string, string> tags)
=> (Name, Unit, Description, Tags) = (name, unit, description, tags);
internal void Init(MetricBase baseProps, ImmutableDictionary<string, string>.Builder tagBuilder)
=> (Name, Unit, Description, Tags) = (baseProps.Name, baseProps.Unit, baseProps.Description, tagBuilder);
public string Name { get; protected set; }
public string Unit { get; protected set; }
public string Description { get; protected set; }
public IReadOnlyDictionary<string, string> Tags { get; protected set; }
}
public abstract class MetricBase<TMetric, TTag> : MetricBase where TMetric : MetricBase, new()
{
protected readonly ConcurrentDictionary<TTag, TMetric> Metrics = new ConcurrentDictionary<TTag, TMetric>();
public Tag<TTag> Tag { get; private set; }
public MetricBase(string name, string unit, string description, Tag<TTag> tag) : base(name, unit, description)
=> Tag = tag;
public TMetric Get(TTag value) => Metrics.GetOrAdd(value, key => GetFor(key));
private TMetric GetFor(TTag value)
{
var tags = ImmutableDictionary.CreateBuilder<string, string>();
tags.Add(Tag.Name, value.ToString());
var result = new TMetric();
result.Init(this, tags);
return result;
}
}
public abstract class MetricBase<TMetric, TTag1, TTag2> : MetricBase where TMetric : MetricBase, new()
{
protected readonly ConcurrentDictionary<(TTag1, TTag2), TMetric> Metrics = new ConcurrentDictionary<(TTag1, TTag2), TMetric>();
public Tag<TTag1> Tag1 { get; private set; }
public Tag<TTag2> Tag2 { get; private set; }
public MetricBase(string name, string unit, string description, Tag<TTag1> tag1, Tag<TTag2> tag2) : base(name, unit, description)
=> (Tag1, Tag2) = (tag1, tag2);
public TMetric Get(TTag1 value1, TTag2 value2) => Metrics.GetOrAdd((value1, value2), key => GetFor(key));
private TMetric GetFor((TTag1, TTag2) tagValues)
{
var tags = ImmutableDictionary.CreateBuilder<string, string>();
tags.Add(Tag1.Name, tagValues.Item1.ToString());
tags.Add(Tag2.Name, tagValues.Item2.ToString());
var result = new TMetric();
result.Init(this, tags);
return result;
}
}
public sealed class Counter : MetricBase
{
[Obsolete("For initialization only", true)]
public Counter() { }
private Counter(string name, string unit, string description) : base(name, unit, description, ImmutableDictionary<string, string>.Empty) { }
private long i;
public void Increment() => Interlocked.Increment(ref i);
public static Counter Create(string name, string unit, string description)
=> new Counter(name, unit, description);
public static Counter<T> Create<T>(string name, string unit, string description, Tag<T> tag)
=> new Counter<T>(name, unit, description, tag);
public static Counter<T1, T2> Create<T1, T2>(string name, string unit, string description, Tag<T1> tag1, Tag<T2> tag2)
=> new Counter<T1, T2>(name, unit, description, tag1, tag2);
}
public sealed class Counter<T> : MetricBase<Counter, T>
{
internal Counter(string name, string unit, string description, Tag<T> tag) : base(name, unit, description, tag) { }
public void Increment(T t) => Get(t).Increment();
}
public class Counter<T1, T2> : MetricBase<Counter, T1, T2>
{
internal Counter(string name, string unit, string description, Tag<T1> tag1, Tag<T2> tag2) : base (name, unit, description, tag1, tag2) { }
public void Increment(T1 t1, T2 t2) => Get(t1, t2).Increment();
}
public sealed class SnapshotGauge : MetricBase
{
[Obsolete("For initialization only", true)]
public SnapshotGauge() { }
private SnapshotGauge(string name, string unit, string description) : base(name, unit, description, ImmutableDictionary<string, string>.Empty) { }
public static SnapshotGauge Create(string name, string unit, string description)
=> new SnapshotGauge(name, unit, description);
public static SnapshotGauge<T> Create<T>(string name, string unit, string description, Tag<T> tag)
=> new SnapshotGauge<T>(name, unit, description, tag);
public static SnapshotGauge<T1, T2> Create<T1, T2>(string name, string unit, string description, Tag<T1> tag1, Tag<T2> tag2)
=> new SnapshotGauge<T1, T2>(name, unit, description, tag1, tag2);
}
public class SnapshotGauge<T> : MetricBase<SnapshotGauge, T>
{
internal SnapshotGauge(string name, string unit, string description, Tag<T> tag) : base(name, unit, description, tag) { }
}
public class SnapshotGauge<T1, T2> : MetricBase<SnapshotGauge, T1, T2>
{
internal SnapshotGauge(string name, string unit, string description, Tag<T1> tag1, Tag<T2> tag2) : base(name, unit, description, tag1, tag2) { }
}
public readonly struct Tag<T>
{
public string Name { get; }
public Tag(string name) => Name = name;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment