-
Notifications
You must be signed in to change notification settings - Fork 3
Home
Generates ActivitySource
, ILogger
, and Metrics
based telemetry from methods you define on an interface.
This approach allows for:
- Faster iteration cycles - simply create the method on your interface and the implementation will be automatically generated
- Easy mocking/ substitution for testing - a full sample project, including tests can be found here
- Built-in dependency injection helper generation
Use the latest version available on NuGet, which supports the following frameworks:
- .NET Framework 4.7.2, or higher
- .NET 8 or higher
For more information see:
- Activities generation target
-
Logging generation target
- Newer Generation v2
- Previous Generation v1
- Metrics generation target
- Multi-Targeting information
- Generation options
- Generated Output examples
- Diagnostics
- Sample Application
You can also see a list of the breaking changes between major versions here.
All marker attributes are generated as conditional, meaning they will not be present in your build. However, you can define PURVIEW_TELEMETRY_ATTRIBUTES
as a build constant to retain them. They are generated as internal to avoid exposing them outside of the assembly.
Reference in your .csproj
or Directory.Build.props
file:
<PackageReference Include="Purview.Telemetry.SourceGenerator" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
The following examples all contain explicit definitions, meaning each example explicitly applies the appropriate attributes. Inferring certain actions or values is also supported and will be detailed in each sub-section.
The documentation for each generation target (activity, logging and metrics) contains information on what can be inferred.
By default each interface used as a source for generation includes an extension method for registering it with an IServiceCollection
instance. More details can be found in Generation.
Tip
You can mix-and-match generation targets within a single interface, however the ability to infer is more limited. This is called multi-targeting.
Note
In .NET, Activities, Events, and Metrics refer to additional properties captured at creation, recording, or observation time as tags. However, in Open Telemetry these are referred to as attributes. As this source generator makes extensive use of marker attributes to control source code generation, we will use the term tags to describe these properties, and attributes as the .NET Attribute type not as the Open Telemetry definition.
An example project is available in the samples folder. Information can be found here.
Basic example of an activity-based telemetry interface.
There is one Activity (GettingItemFromCache
) and four events. Calling these will add an ActivityEvent
to the activity
parameter. Alternatively, if no Activity
is passed in, the Activity.Current
will be used in it's place.
There is also a 'context' method, that will add its properties as either tags or baggage to the current Activity
.
The [Tag]
and [Baggage]
attributes on the parameters will add the values to the Activity
or ActivityEvent
.
[ActivitySource("some-activity")]
interface IActivityTelemetry
{
[Activity]
Activity? GettingItemFromCache([Baggage]string key, [Tag]string itemType);
[Event("cachemiss")]
void Miss(Activity? activity);
[Event("cachehit")]
void Hit(Activity? activity);
[Event]
void Error(Activity? activity, Exception ex);
[Event]
void Finished(Activity? activity, [Tag]TimeSpan duration);
[Context]
void AdditionalInfo(Activity? activity, string state);
}
More information can be found here.
Basic example of a structured logging-based interface.
Note
The ProcessingWorkItem
method returns an IDisposable?
, this is a scoped log entry.
All of the parameters are passed into the logger methods as properties.
[Logger]
interface ILoggingTelemetry
{
[Log]
IDisposable? ProcessingWorkItem(Guid id);
[Log(LogLevel.Trace)]
void ProcessingItemType(ItemTypes itemType);
[Error]
void FailedToProcessWorkItem(Exception ex);
[Info]
void ProcessingComplete(bool success, TimeSpan duration);
}
More information can be found here, including the different types of code generation, and how to disable logging generation when the Microsoft.Extensions.Logging
types are unavailable.
This example shows each meter type currently supported. Note the Counter
attribute is demoed twice. Once with AutoIncrement
set to true
, this means the measurement value is automatically set to increment by 1 each time the method is called. In the other example (where AutoIncrement
is false
, which is the default) the measurement value is specified explicitly as a parameter using the InstrumentMeasurementAttribute
.
Important
Non-auto increment meters must specify a measurement with one of the valid types: byte
, short
, int
, long
, float
, double
, and decimal
.
Note
Observable types must always have a System.Func<>
with one of the following supported types:
- Any one of the following supported measurement types:
byte
,short
,int
,long
,float
,double
, ordecimal
-
Measurement<T>
whereT
is one of valid measurement types above. -
IEnumerable<Measurement<T>>
whereT
is one of valid measurement types above.
As with activities, you can add a [Tag]
to the parameters and they'll be included at recording time for the instrument.
[Meter]
interface IMeterTelemetry
{
[AutoCounter]
void AutoIncrementMeter([Tag]string someValue);
[Counter(AutoIncrement = true)]
void AutoIncrementCounterMeter([Tag]string someValue);
[Counter]
void CounterMeter([InstrumentMeasurement]int measurement, [Tag]float someValue);
[AutoCounter]
void AutoCounterMeter([Tag]float someValue);
[Histogram]
void HistogramMeter([InstrumentMeasurement]int measurement, [Tag]int someValue, [Tag]bool anotherValue);
[ObservableCounter]
void ObservableCounterMeter(Func<float> measurement, [Tag]double someValue);
[ObservableGauge]
void ObservableGaugeMeter(Func<Measurement<float>> measurement, [Tag]double someValue);
[ObservableUpDownCounter]
void ObservableUpDownCounter(Func<IEnumerable<Measurement<byte>>> measurement, [Tag]double someValue);
[UpDownCounter]
void UpDownCounterMeter([InstrumentMeasurement]decimal measurement, [Tag]byte someValue);
}
More information can be found here.
In this example, all method-based targets are explicitly set as inferring their usage is not support when using multi-targeting.
[ActivitySource("multi-targeting")]
[Logger]
[Meter]
interface IServiceTelemetry
{
[Activity]
Activity? StartAnActivity(string tagStringParam, [Baggage]int entityId);
[Event]
void AnInterestingEvent(Activity? activity, float aTagValue);
[Context]
void InterestingInfo(Activity? activity, float anotherTagValue, int intTagValue);
[Trace]
void ProcessingEntity(int entityId, string property1);
[Counter(AutoIncrement = true)]
void AnAutoIncrement([Tag]int value);
}
More information can be found here.
Important
Consider helping children around the world affected by conflict. You can donate any amount to War Child here - any amount can help save a life.