Add StartOperation method to expose ContextCorrelationManager.runWithContext for non-HTTP
We leverage Azure Service Bus heavily in our applications. We write a lot of code that semantically looks like this:
while(true){
const message = servicebus.getMessage();
if(message){
ProcessMessage(message);
}
}
function ProcessMessage(message){
request.get('http://some-service/get-stuff', (res) => {
if(res.statusCode == 200){
request.post('http://some-service/do-stuff');
}
});
}
I'd like to integrate Application Insights into these offline workloads. I'd like to:
- Correlate all trace/event/dependency messages produced by processing each message
- Tie each message back to it's parent operation if it exists.
Similar to this article for .NET.
Looking around I don't see anything exposed in this SDK to manually establish context.
The ContextCorrelationManager caught my eye but that does not appear to be exposed by the library.
Could you:
- Confirm that we don't currently have a built-in way of doing this
- If so, provide suggestions for what we might be able to do in the interim?
Many Thanks
You're correct that there isn't a built-in way of doing this. Your scenario is somewhat similar to what's described in #386. As I mentioned there, ideally this SDK should add a startOperation method like the .NET SDK has, that will allow you to manually create correlation contexts within your code.
In the interim, you can use the code shown in #386 to manually supply operation_Id and operation_ParentId fields, however this may become complicated if your scenario processes several events in parallel, as you'll need to take advantage of some kind of context propagation to know what the correct id fields are at each track time.
A somewhat hacky solution is to take advantage of the SDK's existing context propagation, even though it's not currently exposed. While hacky, it shouldn't break anything. Some sample (untested but the right idea) code:
const CorrelationContextManager = require('applicationinsights/out/AutoCollection/CorrelationContextManager').CorrelationContextManager;
function startOperation(cb) {
const correlationContext =CorrelationContextManager.generateContextObject(
operationId, // Use a guid here
parentIdIfExists, // Supply the item id of the parent telemetry item this context should be a "child" of, or null
operationName // Use a string here
);
CorrelationContextManager.runWithContext(correlationContext, cb);
}
while(true){
const message = servicebus.getMessage();
if(message){
startOperation(() => ProcessMessage(message));
}
}
function ProcessMessage(message){
request.get('http://some-service/get-stuff', (res) => {
if(res.statusCode == 200){
request.post('http://some-service/do-stuff');
}
});
}
One thing to note about this approach as-is, is that there isn't currently a "root" telemetry item (like a Request would be in HTTP server secnarios), so in the transaction details in the Azure Portal UI, your timeline will show all correlated dependencies, but they wont be nested under a parent. If there is a "root" item that makes sense, just record it manually and use its id as the ParentId in parentIdIfExists.
another problem of this approach is that you lose the return value of the wrapped callback.
that would be nice to make CorrelationContextManager.runWithContext return that instead of void
One thing to note about this approach as-is, is that there isn't currently a "root" telemetry item (like a Request would be in HTTP server secnarios), ... If there is a "root" item that makes sense, just record it manually and use its id as the ParentId in parentIdIfExists
I'm currently trying to use app insights for a websocket gateway which performs http requests as dependencies.( in other words i'm simply replacing http server with websocket).
Would it be possible to write a code like this (using the Context Manager "hack")?: NOTE: I based my assumption after analyzing how http server incoming request is handled by app insights
socket.on('event', msg => {
startOperation(() => processMsg(socket, msg));
});
function processMsg(socket, msg) {
// 1. This will be automatically tracked as dependency
request.get('http://some-service/get-stuff', (res) => {
if(res.statusCode == 200){
socket.emit('eventResponse', res.body);
// 2. This will explicitly set root item (like in http server)
appInsights.defaultClient.trackRequest({...});
}
}
}
Thanks Tomasz
Can we use async function for the callback function "cb"?
CorrelationContextManager.runWithContext(correlationContext, cb);
For an async operation I managed to get it working with:
await appInsights.wrapWithCorrelationContext(() => asyncCallBackFunc(parameters), correlationContext)();
where asyncCallBackFunc is an async function