How to get the current TraceId and SpanId - asp.net-core

This article, https://devblogs.microsoft.com/aspnet/improvements-in-net-core-3-0-for-troubleshooting-and-monitoring-distributed-apps/, tells me that the field TraceId is available as a correlation id, which is great!
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
=> ConnectionId:0HLR1BR0PL1CH
=> RequestPath:/weatherforecastproxy
RequestId:0HLR1BR0PL1CH:00000001,
SpanId:|363a800a-4cf070ad93fe3bd8.,
TraceId:363a800a-4cf070ad93fe3bd8,
ParentId: Executed endpoint 'FrontEndApp.Controllers.WeatherForecastProxyController.Get
(FrontEndApp)'
In fact, I can see that in our log sink this works as advertised: When web application A serves a request and in doing so invokes web application B, both of them write the same TraceId value to the log.
As far as I understand, any ASP.NET Core application that receives an incoming Request-Id header will attach the same header to outgoing requests, but if the header does not exist on the incoming request, an new value will be generated for the outgoing request.
We have been asked to add that value to the response from web application A, but it is (not surprisingly) not available on the incoming request.
I have been looking at the System.Diagnostics.Activity class, but accessing Activity.Current isn't giving me an instance with anything useful - the TraceID is just {} - i.e. empty.
My question is this: How can I access the TraceId value in the context of a web application?
-S

I had the same problem when I tried to add a header with TraceId value.
Doing some tests with ModelValidation, I saw then in this kind of error response the "traceId" value was correct, but I couldn't obtain this value from http context variable in any way.
Then I went to net core source code to see DefaultProblemDetailsFactory implementation and surprise! The "traceId" value is obtained doing this:
var traceId = Activity.Current?.Id ?? httpContext?.TraceIdentifier;
Yes, you can get THE traceId using Activity static variable.

You can get tracid and spanid in dictionary.
using var subject = _tracer.BuildSpan($"Operation").StartActive();
var spanContext = subject.Span.Context;
var dictionary = new Dictionary<string, string>();
_tracer.Inject(spanContext, BuiltinFormats.TextMap, new TextMapInjectAdapter(dictionary));

Related

How to extract sessionId from Citrus HttpRequest

I tried to test the a set of REST services using Citrus Java DSL. After authentication the services expect the same, valid session id of the first request.
On the server side I can see, that there exists a random session-id, but at the second request, the session-id is null.
I've tried to set handleCookies to true in the endpoint configuration and tried to extract some header information (set-cookie) but without success. The EndpointConfiguration is reused during the different requests.
CitrusEndpoints.http()
.client()
.handleCookies(true)
How can I force the Endpoint to reuse the negotiated session-id or how can I extract it from the request / response?
Thanks in advance for any ideas and hints.
The response to your 1st request should have a header set
Set-Cookie: JSESSIONID=ABCDEFG;path=/api/foo
You can extract this information in your receive operation
http()
.client(todoClient)
.receive()
.response(HttpStatus.OK)
.extractFromHeader("Set-Cookie", "cookie")
.payload("{ \"foo\": \"bar\" }");
After that we have to post process the new ${cookie} value in order to extract the actual session id name and value into a new variable ${sessionId}.
createVariable("sessionId", "citrus:substringBefore(${cookie}, ';')");
Now we have a variable ${sessionId} that only contains the name and value of the session id - in our example this is JSESSIONID=ABCDEFG.
In further requests you can use the variable in order to set proper Cookie header information
http()
.client(todoClient)
.send()
.get("/api/foo")
.header("Cookie", "${sessionId}")
.accept(ContentType.APPLICATION_JSON.getMimeType());

how to set a property globally in wso2 ESB

I am trying to figure out how to implement session management in wso2 esb.So i have written a class mediator which generates session_ID that i want to store.For storing the session id I am using following code as:
org.apache.axis2.context.ServiceContext serviceContext = org.apache.axis2.context.MessageContext
.getCurrentMessageContext().getServiceContext();
serviceContext.setProperty("SessionIDGlobal", uuid);
But while running it in my esb's proxy it throws null pointer exception at getCurrentMessageContext part.I have followed another approach where-in i am storing the sessionID in property mediator and tried to get its value but when i click postRequest operation after generateSessionID operation from try-it. all the property gets reset and my sessionID property gives me null value. What should i do to rectify this problem? Is there any alternate way?
You have to create servicecontext like this;
ConfigurationContext cfgCtx =(((Axis2MessageContext) synCtx).getAxis2MessageContext(). getConfigurationContext();
cfgCtx.getOperationContext().getServiceContext();
You should store in the Message context but you have stored in the service Context. please refer this to understand how you can set the properties at different scopes. Synapase (default), Axis2, Transport etc.
Please refer this blog post for complete details.
http://blog.facilelogin.com/2011/02/wso2-esb-property-mediator-different.html

WCF Unique ID for each service method call

I'm logging using log4net, and I want to log a id that is unique for each serice method call. I dont need it unique across service calls, just within a method call. Is there any built in id i can use in wcf? I don't want to manually create a guid or something at the start of the method call.
e.g.
wcfMethod(int x)
{
log("xxx");
somework
log("yyy");
}
private log(string message)
{
var frame = new StackFrame(1);
var method = frame.GetMethod();
var type = method.DeclaringType;
var name = method.Name;
var log = LogManager.GetLogger(type);
// LOG ID HERE
ThreadContext.Properties["MessageId"] = OperationContext.Current.IncomingMessageHeaders.MessageId; // SOMETHING HERE
}
I've tried OperationContext.Current.IncomingMessageHeaders.MessageId but thats always null.
I've read about wcf instance correlation but i don't need something that complicated (e.g. unique across different method calls).
Please if anyone can help that would be much apprieciated. Thanks in advance.
Plain SOAP or REST has no such identification included in messages. You must use some additional feature or transport protocol (for example MSMQ) supporting identifications of messages. In case of MessageId you have to use SOAP service with WS-Addressing and this information must be passed from client.

Accessing the HTTP headers from a WCF Service

I need to access the HTTP response headers that are to be returned to the client from a WCF Service. Accessing the HTTPContext is easy(through HttpContext.Current.Response), but what is the event/extension/behavior that is executed lastly, when the StatusCode is already set (for ex. if the status is 500)?
EDIT: Message Inspectors don't seem to be a good solution here, because at the time they run, the status code isn't set yet. (At least in my trial that was the case)
You can access all headers on WebOperationContext.Current.IncomingRequest, like this:
IncomingWebRequestContext request = WebOperationContext.Current.IncomingRequest;
WebHeaderCollection headers = request.Headers;
Console.WriteLine("-------------------------------------------------------");
foreach (string headerName in headers.AllKeys)
{
Console.WriteLine(headerName + ": " + headers[headerName]);
}
Console.WriteLine("-------------------------------------------------------");
See here
Simplest way for having control on the Headers is to use Message contracts.
Use Message Inspectors to monitor the message right after receiving it at the Service end.
In an extreme case, where you are not satisfied with any other standard routes, you can go for POX (Plain Old XML) type operations where you would be dealing with raw XML message.

WCF routing -- how to correctly add filter table programmatically

I am using the WCF 4 routing service, and need to configure the service programmatically (as opposed to via config). The examples I have seen of doing so, which are rare, create a MessageFilterTable as follows:
var filterTable=new MessageFilterTable<IEnumerable<ServiceEndpoint>>();
But, the generic parameter to that method is supposed to be TFilterData (the type of data you are filtering on)? I have my own custom filter that accepts a string -- can I still create the filter table this way?
If this will work...will the routing infrastructure create client endpoints out of the list I pass in?
I have created a WCF 4 routing service and configured it programmatically. My code is a bit more spaced out than it needs to be (maintainability for others being a concern, hence the comments), but it definitely works. This has two filters: one filters some specific Actions to a given endpoint, and the second sends the remaining actions to a generic endpoint.
// Create the message filter table used for routing messages
MessageFilterTable<IEnumerable<ServiceEndpoint>> filterTable = new MessageFilterTable<IEnumerable<ServiceEndpoint>>();
// If we're processing a subscribe or unsubscribe, send to the subscription endpoint
filterTable.Add(
new ActionMessageFilter(
"http://etcetcetc/ISubscription/Subscribe",
"http://etcetcetc/ISubscription/KeepAlive",
"http://etcetcetc/ISubscription/Unsubscribe"),
new List<ServiceEndpoint>()
{
new ServiceEndpoint(
new ContractDescription("ISubscription", "http://etcetcetc/"),
binding,
new EndpointAddress(String.Format("{0}{1}{2}", TCPPrefix, HostName, SubscriptionSuffix)))
},
HighRoutingPriority);
// Otherwise, send all other packets to the routing endpoint
MatchAllMessageFilter filter = new MatchAllMessageFilter();
filterTable.Add(
filter,
new List<ServiceEndpoint>()
{
new ServiceEndpoint(
new ContractDescription("IRouter", "http://etcetcetc/"),
binding,
new EndpointAddress(String.Format("{0}{1}{2}", TCPPrefix, HostName, RouterSuffix)))
},
LowRoutingPriority);
// Then attach the filter table as part of a RoutingBehaviour to the host
_routingHost.Description.Behaviors.Add(
new RoutingBehavior(new RoutingConfiguration(filterTable, false)));
You can find a good example on MSDN here: How To: Dynamic Update Routing Table
Note how they dont directly create an instance of the MessageFilterTable, but instead use the 'FilterTable' property provided by a new RoutingConfiguration instance.
If you have written a custom filter, then you will add it like this:
rc.FilterTable.Add(new CustomMessageFilter("customStringParameter"), new List<ServiceEndpoint> { physicalServiceEndpoint });
The CustomMessageFilter will be your filter, and the "customStringParameter" is the string that (I believe) you are talking about.
When the Router receives a connection request, it will attempt to map it via this table entry, if this is successful, then you are right, the router will create a client endpoint to talk to the ServiceEndpoint that you provided.