Skip to content

Commit f969c9d

Browse files
committed
Documentation updates. [skip ci]
- CreateMetric - PopulateFromEnum - Updated MetricGroup examples.
1 parent 1d232e9 commit f969c9d

File tree

4 files changed

+60
-34
lines changed

4 files changed

+60
-34
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var collector = new MetricsCollector(new BosunOptions()
2525
Create a counter with only the default tags:
2626

2727
```csharp
28-
var counter = collector.GetMetric<Counter>("my_counter", "units", "description");
28+
var counter = collector.CreateMetric<Counter>("my_counter", "units", "description");
2929
```
3030

3131
Increment the counter by 1:

docs/MetricGroup.md

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
# MetricGroup
22

3-
Because one of the goals of BosunReporter.NET is to encourage using tags to differentiate subsets of the same data (instead of separate metric names), metric groups are built into the library. A metric group is a collection of metrics with the same name, but with different tag values. For example, let's say we have a counter which counts every request. We might want to tag this counter to indicate how many requests were successful versus how many encountered an error. Start by creating a class for our counter, as usual.
3+
Because one of the goals of BosunReporter.NET is to encourage using tags to differentiate subsets of the same data (instead of separate metric names), metric groups are built into the library. A metric group is a collection of metrics with the same name, but with different tag values. For example, let's say we have a counter which counts every request. We might want to tag this counter to indicate how many requests were successful versus how many encountered an error.
4+
5+
Let's start by defining an enum of the result types we want to track.
6+
7+
```csharp
8+
public enum Result
9+
{
10+
Error,
11+
Success
12+
}
13+
```
14+
15+
Then we'll create a class for our counter.
416

517
```csharp
6-
public RequestCounter : Counter
18+
public class RequestCounter : Counter
719
{
820
[BosunTag]
9-
public readonly string Result;
21+
public readonly Result Result;
1022
public RequestCounter(string result) { Result = result; }
1123
}
1224
```
1325

14-
Now, instead of manually creating a metric for each of the result values we want ("success", "error", etc), we can simply create a group which will enable us to create those metrics implicitly.
26+
Now, instead of manually creating a metric for each of the result values we want (Success and Error), we can simply create a group which will enable us to create those metrics implicitly.
1527

1628
```csharp
17-
var requestCounter = collector.GetMetricGroup<string, RequestCounter>(
29+
var requestCounter = collector.GetMetricGroup<Result, RequestCounter>(
1830
"requests", // name
1931
"requests", // unit
2032
"A count of the number of requests", // description
@@ -23,21 +35,18 @@ var requestCounter = collector.GetMetricGroup<string, RequestCounter>(
2335
// MetricGroup.Add() creates a metric if it does not already exist, and returns that metric.
2436
// Best-practice is to call Add() as close to application-startup as possible to avoid
2537
// "Unknown" Bosun alerts.
26-
requestCounter.Add("success");
27-
requestCounter.Add("error");
28-
29-
...
38+
requestCounter.Add(Result.Success);
39+
requestCounter.Add(Result.Error);
3040

31-
// as long as Add() has been previously called for each tag value, you can use indexer syntax
32-
if (error == null)
33-
requestCounter["success"].Increment();
34-
else
35-
requestCounter["error"].Increment();
41+
// once they've been added, we can use indexer syntax to access those metrics. For example...
42+
requestCounter[Result.Success].Increment();
3643
```
3744

38-
Note that we used the generic type arguments `<string, RequestCounter>` in the example above. This determines the signature of the factory, which is `Func<string, RequestCounter>` in this example because it takes a single string argument, and returns a `RequestCounter`. It also determines the method signature of `Add()`.
45+
Note that we used the generic type arguments `<Result, RequestCounter>` in the example above. This determines the signature of the factory, which is `Func<Result, RequestCounter>` in this example because it takes a single `Result` argument, and returns a `RequestCounter`. It also determines the method signature of `Add()`.
3946

40-
> Because `RequestCounter` has a constructor which matches the type signature provided (accepts a single string, and returns an instance of RequestCounter), we could have omitted the factory argument. The metric group will use that constructor to generate a default factory.
47+
> Because `RequestCounter` has a constructor which matches the type signature provided (accepts a single Result, and returns an instance of RequestCounter), we could have omitted the factory argument. The metric group will use that constructor to generate a default factory.
48+
49+
## Multiple Tags
4150

4251
You can also create metric groups which is differentiated by more than one tag (MetricGroup currently supports up to 5, though splitting on more than 2 or 3 may be a code-smell). Simply add additional type arguments to the MetricGroup generic type constructor. Let's use a three-tag counter as an example:
4352

@@ -46,13 +55,13 @@ public ThreeTagCounter : Counter
4655
{
4756
[BosunTag] public readonly string One;
4857
[BosunTag] public readonly string Two;
49-
[BosunTag] public readonly string Three;
58+
[BosunTag] public readonly SomeEnum Three;
5059

5160
public ThreeTagCounter(string one, int two, SomeEnum three)
5261
{
5362
One = one;
5463
Two = two.ToString();
55-
Three = three.ToString();
64+
Three = three;
5665
}
5766
}
5867
```
@@ -85,4 +94,21 @@ helloGroup.Add(2, SomeEnum.MyValue).Increment();
8594
helloGroup.Add(7, SomeEnum.AnotherValue).Increment();
8695
```
8796

88-
> Value types or strings are always best for the metric group arguments. Objects are okay as long as they play well as dictionary keys or values inside a Tuple which is used as a dictionary key. This generally means any object used as a metric group argument should implement IEquatable<T> and have a good GetHashCode implementation.
97+
> Value types or strings are always best for the metric group arguments. Objects are okay as long as they play well as dictionary keys or values inside a Tuple which is used as a dictionary key. This generally means any object used as a metric group argument should implement IEquatable<T> and have a good GetHashCode implementation.
98+
99+
## Auto-Populate from Enum Values
100+
101+
In the first example, we populated all of the values of our `Result` enum into the group using:
102+
103+
```csharp
104+
requestCounter.Add(Result.Success);
105+
requestCounter.Add(Result.Error);
106+
```
107+
108+
Since it's very common to want one metric per enum value, there is a helper method which does just that. The code below is functionally equivalent to the code above.
109+
110+
```csharp
111+
requestCounter.PopulateFromEnum();
112+
```
113+
114+
However, this helper method is only available when there is only one input type parameter, and that type parameter is an enum type. For example, you could use `PopulateFromEnum` on a group with the type signature `MetricGroup<SomeEnum, SomeMetric>`, but not on `MetricGroup<SomeEnum, int, SomeMetric>` because it has more than one input type parameter (`SomeEnum` and `int`).

docs/MetricTypes.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Counters are for _counting_ things. The most common use case is to increment a c
99
This is the basic counter type. It uses a `long` value and calls `Interlocked.Add()` internally to incrementing the value.
1010

1111
```csharp
12-
var counter = collector.GetMetric<Counter>("my_counter", "units", "description");
12+
var counter = collector.CreateMetric<Counter>("my_counter", "units", "description");
1313

1414
// increment by 1
1515
counter.Increment();
@@ -24,7 +24,7 @@ A snapshot counter is useful when you only care about updating the counter once
2424

2525
```csharp
2626
var count = 0;
27-
collector.GetMetric("name", "unit", "desc", new SnapshotCounter(() => count++));
27+
collector.CreateMetric("name", "unit", "desc", new SnapshotCounter(() => count++));
2828
```
2929

3030
## Gauges
@@ -36,7 +36,7 @@ Gauges describe a measurement at a point in time. A good example would be measur
3636
These are great for metrics where you want to record snapshots of a value, like CPU or memory usage. Pretend we have a method called `GetMemoryUsage` which returns a double. Now, let's write a snapshot gauge which calls that automatically at every metrics reporting interval.
3737

3838
```csharp
39-
collector.GetMetric("memory_usage", units, desc, new SnapshotGauge(() => GetMemoryUsage()));
39+
collector.CreateMetric("memory_usage", units, desc, new SnapshotGauge(() => GetMemoryUsage()));
4040
```
4141

4242
That's it. There's no reason to even assign the gauge to a variable.
@@ -48,7 +48,7 @@ That's it. There's no reason to even assign the gauge to a variable.
4848
These are ideal for low-volume event-based data where it's practical to send all of the data points to Bosun. If you have a measurable event which occurs once every few seconds, then, instead of aggregating, you may want to use an event gauge. Every time you call `.Record()` on an event gauge, the metric will be serialized and queued. The queued metrics will be sent to Bosun on the normal reporting interval, like all other metrics.
4949

5050
```csharp
51-
var myEvent = collector.GetMetric<EventGauge>("my_event", units, desc);
51+
var myEvent = collector.CreateMetric<EventGauge>("my_event", units, desc);
5252
someObject.OnSomeEvent += (sender, e) => myEvent.Record(someObject.Value);
5353
```
5454

@@ -89,7 +89,7 @@ public class RouteTimingGauge : AggregateGauge
8989
Then, instantiate the gauge for our route, and record timings to it.
9090

9191
```csharp
92-
var testRouteTiming = collector.GetMetric(
92+
var testRouteTiming = collector.CreateMetric(
9393
"route_tr",
9494
units,
9595
desc,
@@ -112,7 +112,7 @@ A sampling gauge simply reports the last recorded value at every reporting inter
112112
If the last recorded value is `Double.NaN`, then nothing will be reported to Bosun.
113113

114114
```csharp
115-
var sampler = collector.GetMetric<SamplingGauge>("my_sampler", units, desc);
115+
var sampler = collector.CreateMetric<SamplingGauge>("my_sampler", units, desc);
116116
sampler.Record(1.2);
117117
```
118118

docs/Tags.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,24 @@ public class RouteCounter : Counter
2525
And then let's instantiate one.
2626

2727
```csharp
28-
var testRouteOkCounter = collector.GetMetric(
29-
"hits",
30-
"units",
31-
"description",
28+
var testRouteOkCounter = collector.CreateMetric(
29+
"hits", // metric name
30+
"http requests", // units
31+
"description", // meaningful description
3232
new RouteCounter("Test/Route", true));
3333
```
3434

3535
The metric name `hits` has now been bound to the `RouteCounter` type. If we try to use that metric name with any other type, the library will throw an exception. However, we can use that metric name with as many different instances of `RouteCounter` as we'd like. For example:
3636

3737
```csharp
38-
var testRouteErrorCounter = collector.GetMetric(
38+
var testRouteErrorCounter = collector.CreateMetric(
3939
"hits",
4040
"units",
4141
"description",
4242
new RouteCounter("Test/Route", false));
4343
```
4444

45-
It it worth noting that the `GetMetric()` method is idempotent, so you'll never end up with duplicate metrics.
45+
It is worth noting that `CreateMetric()` will throw an exception if you try to create a duplicate metric. That behavior is generally desirable since multiple attempts to create the same metric likely represent a mistake. However, there is also a `GetMetric()` method (with identical signatures) which is idempotent. It still won't allow you to create duplicate metrics, but instead of throwing an exception if an identical metric already exists, it will return the existing metric.
4646

4747
```csharp
4848
var one = collector.GetMetric("hits", units, desc, new RouteCounter("Test/Route", true));
@@ -51,6 +51,6 @@ var two = collector.GetMetric("hits", units, desc, new RouteCounter("Test/Route"
5151
Console.WriteLine(one == two); // outputs True
5252
```
5353

54-
This, like the rest of the library, is thread safe. You could use this method to always instantiate and use metrics on-demand. Although, if you're concerned with performance, it is computationally cheaper to store the metrics in variables or a hash rather than calling `GetMetric()` every time you need it.
54+
This, like the rest of the library, is thread safe. You _could_ use this method to always instantiate and use metrics on-demand. However, if you care about performance or allocations, it is cheaper to store the metrics in variables or a hash rather than calling `GetMetric()` every time you need it. Or even better, consider using a [Metric Group](https://github.com/bretcope/BosunReporter.NET/blob/master/docs/MetricGroup.md) to create your metrics.
5555

56-
This `RouteCounter` type we just created, and any other BosunMetric type, can be used with as many metric names as you'd like. __You don't have to create a class for every metric you use__ if they share common tag lists. In fact, using common tag lists is a great idea which will help encourage consistency in your metrics conventions.
56+
> This `RouteCounter` type we just created, and any other BosunMetric type, can be used with as many metric names as you'd like. __You don't have to create a class for every metric you use__ if they share common tag lists. In fact, using common tag lists is a great idea which will help encourage consistency in your metrics conventions.

0 commit comments

Comments
 (0)