I have a windows service that is processing Azure service bus messages. When I deploy this code to QA server, the first few messages received, telemetry works great. I see all the information including the tags I expect to see. Then if there is no messages published for say 15 min, any message received after 15 min there is no data in telemetry. Is there some idle timeout or something else I am missing?
I can confirm that the handleMessageActivity
object is null and the activitySource
still looks to be a valid object.
Same problem happens on both activitySource?.CreateActivity
and activitySource?.StartActivity
I cannot understand why activitySource
is valid object when the object is first deployed and then becomes null.
If it helps I have also pasted the windsor DI setup part at the end.
This is a framework 4.6.1 app
My code to setup telemetry
public class AzureOpenTelemetryLoggerService : IDisposable
{
private Dictionary<ActivitySourceEnum, ActivitySource> ActivitySourceCollection;
private bool disposedValue;
private readonly TracerProvider _tracerProvider;
public AzureOpenTelemetryLoggerService(string connectionString)
{
var sourcesArray = System.Enum.GetNames(typeof(ActivitySourceEnum));
AppContext.SetSwitch("Azure.Experimental.EnableActivitySource", true);
_tracerProvider = Sdk
.CreateTracerProviderBuilder()
.AddSource(sourcesArray)
.SetSampler(new AlwaysOnSampler())
.SetResourceBuilder(
ResourceBuilder.CreateDefault().AddService(
serviceName: "MyApp.Backend",
serviceVersion: "1.0.0")
)
.AddConsoleExporter(configure =>
{
})
.AddAzureMonitorTraceExporter(configure =>
{
configure.ConnectionString = connectionString;
})
.Build();
InitializeActivitySources();
}
private void InitializeActivitySources()
{
ActivitySourceCollection =
new Dictionary<ActivitySourceEnum, ActivitySource>();
foreach (var activitySourceItem
in Enum
.GetValues(typeof(ActivitySourceEnum))
.Cast<ActivitySourceEnum>())
{
ActivitySourceCollection.Add(
activitySourceItem,
new ActivitySource(activitySourceItem.ToString())
);
};
var activityListener = new ActivityListener
{
ShouldListenTo = s => true,
SampleUsingParentId =
(ref ActivityCreationOptions<string> activityOptions)
=> ActivitySamplingResult.AllData,
Sample =
(ref ActivityCreationOptions<ActivityContext> activityOptions)
=> ActivitySamplingResult.AllData
};
ActivitySource.AddActivityListener(activityListener);
}
public ActivitySource GetActivitySource(ActivitySourceEnum activitySource)
{
return ActivitySourceCollection[activitySource];
}
public enum ActivitySourceEnum
{
SomeActivitySourceToListenTo
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
_tracerProvider.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
// Do not change this code.
// Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
Client code to use this in some service class -- pseudoCode
Note I am injecting AzureOpenTelemetryLoggerService
via Windsor DI which works as expected
public Task Handle(SomeChangeMessage message, IMessageHandlerContext context)
{
using (var activitySource
= _azureOpenTelemetryLoggerService
.GetActivitySource(AzureOpenTelemetryLoggerService
.ActivitySourceEnum
.SomeActivitySourceToListenTo))
{
// this is always a valid object
Log.Debug("activitySource is created {@activitySource}", activitySource);
using (var handleMessageActivity
= activitySource?
.CreateActivity($"Handle SomeChangeMessage message",
ActivityKind.Internal))
{
// first time is valid object.
// after 10-15 min when I process another message,
// his object and below is null
// so no temetry gets written
Log.Debug("handleMessageActivity " +
"is created {@handleMessageActivity}",
handleMessageActivity);
handleMessageActivity?.SetTag("Tag1", "someVal");
ActivityContext? linkedContext = handleMessageActivity?.Context;
var links = new List<ActivityLink>();
if (linkedContext.HasValue)
{
links.Add(new ActivityLink(linkedContext.Value));
}
foreach (var personId in message.SomeList.Select(a => a.personId)
.Distinct())
{
using (var processPartyActivity = activitySource?
.StartActivity(kind: ActivityKind.Client,
links: links, name: "process party"))
{
Log.Information("Process start for {personId}", personId);
// more stuff
Log.Information("Process complete for {personId}", personId);
}
}
}
}
return Task.CompletedTask;
}
Windsor castle registration code:
container.Register(
Component.For<AzureOpenTelemetryLoggerService>()
.ImplementedBy<AzureOpenTelemetryLoggerService>()
.DependsOn(
Dependency.OnAppSettingsValue("connectionString",
"AzureAppInsightsConnectionString"))
.LifestyleSingleton())
AzureOpenTelemetryLoggerService class was correct in it's implementation. In the method public Task Handle ....do not add "use " when getting activity from activitySource collection. I put use there..which called dispose when finishing off the use block. Obvious but I clearly missed it. Also per documentation, if you use CreateActivity, then you have to manually start and stop it. Instead use StartActivity. To link parent activity and child activity, do something like this
if (handleMessageActivity != null)
{
processPartyActivity.SetParentId(handleMessageActivity?.Id);
}
just putting the link didnt help as much as I wished. Hope this helps others