using log4net with scoped properties - log4net

I'm using on an existing application log4net with seq target.
During a code workflow (which is calling different methods) I wish to save an id as a property in order to keep a reference to that id (when looking on Seq It's easier to understand what happened)
is there a way of using something as
using(var log = ........ properties)
{
normal logging here
}
Thanks

Such a property can be stored on the ThreadContext or LogicalThreadContext via eg
log4net.ThreadContext.Properties["someId"] = "foo";
and be included in a LayoutPattern via %property{someId}
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger | %level | %property{someId} | %message%newline" />
</layout>
The code below
ILog logger = LogManager.GetLogger("SomeLogger");
logger.Info("Message 1");
then produces the following output
SomeLogger | INFO | foo | Message 1
This contextual data can be stacked because Log4net has the concept of context stacks.
The stack is stored in a context property, therefore stacks have names and more than one stack can exist in the same context. A property value set in a narrower context would override a stack with the same property name set in a wider scoped context.
The stack supports Push and Pop methods. As more contextual data is pushed onto the stack the stack grows. When the stack is rendered all the data pushed onto the stack is output with the most recent data to the right hand end of the string.
As the stack is just an object stored in the context properties it is also rendered using the same PatternLayout syntax: %property{name}. Where name is the name of the stack.
Calls the the stack's Push and Pop methods must be matched up so that each push has a corresponding pop. The Push method also returns an IDisposable object that will perform the required pop operation when it is disposed. This allows the C# using syntax to be used to automate the stack management.
A property value gets set on a stack via Push, which returns an IDisposable object that removes this property value from the stack on disposal, eg
using (ThreadContext.Stacks["someId"].Push("bar"))
{
logger.Info("Message 2");
using (ThreadContext.Stacks["someId"].Push("baz"))
{
logger.Info("Message 3");
}
}
with output
SomeLogger | INFO | bar | Message 2
SomeLogger | INFO | baz | Message 3

Related

ILogger.BeginScope(): persisting scope between calls to other methods

I need to log custom dimensions to Application Insights for which I'm using ILogger.BeginScope(). That works perfectly. That is:
using (logger.BeginScope(new Dictionary<string, object> { "key": "value" }))
{
logger.LogInformation("message");
}
My issue is that I need to call other methods in other classes, and I'm injecting the ILogger into all my classes. So how can I persist the logging scope among all my classes?
I could surely do ILogger.BeginScope() in all my classes, but I would need to pass the custom properties to classes that don't really need that information. Is there a pattern I could use?
If you call BeginScope multiple times in multiple classes within the same execution path, you will get one aggregated scope. There is no need to manually pass the properties. ILogger is injected as a singleton by default (see source code).
See also Since ILogger<T> is a singleton, how different threads can use BeginScope() without affecting others?
A call to BeginScope will put a new item onto that stack, and the adjoining Dispose will pop it off of that stack.
When the logger is invoked via LogInformation or otherwise, the data of the current stack object will be copied to write it to the console or whatever output that logger instance is configured to do.
LoggerExternalScopeProvider.cs#L14

How do I set the applicaiton insights operationID to my custom correlationID?

I have a .net6 worker service and I need the Request Telemetry OperationID set to a custom value. This value is my CorrelationID that is read from a message queue, and it's format is a guid with dashes.
TelemetryClient.StartOperation has an overload that takes an operationId, but it only works with a specific format. It will not work with my guid.
I have tried the code below, which appears to work in the debugger. However, the value that shows up in applicaiton insights is not what I set it to.
var client = new TelemetryClient();
client.Context.Operation.Id = internalId;
I have tried creating an ITelemetryInitializer. If I set the operationID in the initialize method will work. The problem here is getting the correlationID to the initializer in the correct dependency injection scope.
It seems as though this is a common scenario. I have seen where others ask the question but I have not found a suitable solution.
Has anyone solved this problem?
The problem is that since .Net 5 the default Id format is set to W3C standard instead of the Hierarchical Id format, see the docs:
Parent-Child relationships between Activities in the distributed trace tree are established using unique IDs. .NET's implementation of distributed tracing supports two ID schemes: the W3C standard TraceContext, which is the default in .NET 5+, and an older .NET convention called 'Hierarchical' that's available for backwards compatibility. Activity.DefaultIdFormat controls which ID scheme is used. In the W3C TraceContext standard, every trace is assigned a globally unique 16-byte trace-id (Activity.TraceId), and every Activity within the trace is assigned a unique 8-byte span-id (Activity.SpanId). Each Activity records the trace-id, its own span-id, and the span-id of its parent (Activity.ParentSpanId). Because distributed traces can track work across process boundaries, parent and child Activities may not be in the same process. The combination of a trace-id and parent span-id can uniquely identify the parent Activity globally, regardless of what process it resides in.
Activity.DefaultIdFormat controls which ID format is used for starting new traces, but by default adding a new Activity to an existing trace uses whatever format the parent Activity is using. Setting Activity.ForceDefaultIdFormat to true overrides this behavior and creates all new Activities with the DefaultIdFormat, even when the parent uses a different ID format.
When you set the Activity.DefaultIdFormat to ActivityIdFormat.Hierarchical you can specify any string as an operation Id as it does not have to conform to the W3C standard.
So the following code works like a charm:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Activity.DefaultIdFormat = ActivityIdFormat.Hierarchical;
int index = 0;
while (!stoppingToken.IsCancellationRequested)
{
++index;
using var operation = _telemetryClient.StartOperation<RequestTelemetry>($"op{index}", $"a-b-c-{index}");
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
but it might break the distributed trace flow for your api controllers if you want to trace the end-to-end flow between multiple seperate applications.
Another way is to just include your own correlation Id as a custom propery:
operation.Telemetry.Properties["MessageCorrelationId"] = "xxx";

What does the # next to a Class instance mean?

I have a question while using IntelliJ IDEA, Android Studio Debug.
What does the number next to #(at) mean? No matter how hard I look for development references, I can't find them.
There are a number in brackets and a number without brackets. They have different numbers. I am curious about the difference
I guess it is the unique hash value of the class instance, but I wonder what it means more accurately.
enter image description here
This is an object id.
It is controlled by the Settings (Preferences on macOS) | Build, Execution, Deployment | Debugger | Data Views | Java | Show | Object id option.
As pointed in the comment to another answer the objectID is:
Uniquely identifies an object in the target VM. A particular object will be identified by exactly one objectID in JDWP commands and replies throughout its lifetime (or until the objectID is explicitly disposed). An ObjectID is not reused to identify a different object unless it has been explicitly disposed, regardless of whether the referenced object has been garbage collected. An objectID of 0 represents a null object. Note that the existence of an object ID does not prevent the garbage collection of the object. Any attempt to access a a garbage collected object with its object ID will result in the INVALID_OBJECT error code. Garbage collection can be disabled with the DisableCollection command, but it is not usually necessary to do so.

Migrating the code from Bot Framework V3 to V4

I have more question while migrating the dialog from V3 to V4. below is our code.
In v3, we were using
Microsoft.Bot.Builder.Dialogs.Conversation.SendAsync(conversationContext.CurrentActivity, new RootDialog());
public class RootDialog : IDialog {
public RootDialog()
{
.....
}
public async Task StartAsync(IDialogContext context)
{
context.Wait(this.MessageReceivedAsync);
}
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
}
In the MessageReceivedAsync, we used the context.Wait(), context.Done() and context.PostAsync().
Can you recommend how to replace in the V4? And what's the alertnative for Microsoft.Bot.Builder.Dialogs.Conversation.SendAsync in V4?
These APIs are all gone. Here are the explanations of their replacements in V4:
context.Wait(…)
This method was used to tell the dialog system what method to invoke next on your class when a new activity arrived and is now gone. Instead you now subclass Dialog and override several methods for various lifecycle events:
BeginDialogAsync - called when the dialog is first pushed on the stack by bot code or another dialog calling BeginDialogAsync on the DialogContext.
ContinueDialogAsync - called when a new activity comes in and the bot calls ContinueDialog on the DialogContext.
ResumeDialogAsync - called when another dialog on the stack has completed and a dialog that was previously on the stack is now at the top of the stack.
RepromptDialogAsync - called when an explicit request has been made to reprompt the user. This is basically a way to tell the dialog that nothing has changed, but that it should pick up from where it left off again by sending whatever activity it last sent.
EndDialogAsync - called when the dialog has indicated its done and is being popped off the stack.
context.Done()/.Fail()
This was one of the way you reported the status of your dialog, but this is now accomplished by returning a DialogTurnResult from most of the aforementioned lifecycle methods. One of the properties is named Status and is of type DialogTurnStatus which has values that indicate the current state of the dialog. For example:
Waiting - the dialog sent some activities and is awaiting more input and should remain at the top of the stack.
Complete - the dialog has completed it's work and should be ended and popped off the stack. When this state is returned, callers can also investigate the output of the dialog (if it has one) which is passed back via the DialogTurnResult::Result property.
Cancelled - the dialog was cancelled part of the way through its work.
context.PostAsync()/Conversation.SendAsync
These were both used to respond back to the user. Both are now replaced by calling SendActivityAsync on the ITurnContext that is accessible via the Context property of the DialogContext instance that is passed into most of the aforementioned lifecycle methods as a parameter. NOTE: a couple of the lifecycle methods actually receive an ITurnContext parameter directly and then you just use that.

Enterprise Architect Communication Diagrams question

I want to achieve something like this in Enterprise Architect's Communication Diagrams:
start() ---------------- 1. create() ------------
------------> | RGController | ---------------> | U : User |
---------------- ------------
But I am facing 2 problems:
It seems I have to always make a
connection between 2 objects (I
can't have the start() message just
come out of nowhere, like I'd want).
I can't control the numbering as I'd
want. Is there any way I could just
set the numbering by myself? If I
define that an actor is calling
start() on RGController, it will
call it message 1 when I'd want
message 1 to be User.create().
A) Which Object is sending the start() message to RGController? Add it to the diagram and create the connection between these two objects.
Alternatively you could send the initial message from an Inital element (in the Activity toolbox).
You could also hack an invisible start element by creating an empty shape rendering script.
Just create a new Stereotype in Settings->UML...->Stereotypes, set the name to hidden, applied to , and set the shape script as:
shape main {
}
Then apply this stereotype to your initial element. It should be rendered as invisible.
B) I'm not sure how to manually set the message labeling. Are you aware of the message numbering settings?
right clicking one of the message
labels an selecting "Sequence
Communication Messages".
checking "start new group" in the
Message Properties->Sequnce
Expressions section.
Are you trying to draw an architecture which uses a Front-controller or Facade for handling incoming request?
If so in that case the client will call the start(). E.g. In case of an ASP.net application, it will be the UI code or presentation logic.
Client/User/Customer ---start()---> RGBController--- create() ---> u: User

Resources