Minimal API Logging Missing Event ID - api

I have a Minimal API web application and have enabled logging. Logging is working however the Event ID's don't show up in Azure logs.
I have a call to AddAzureWebAppDiagnostics() at the beginning of the application.
builder.Logging.AddAzureWebAppDiagnostics();
Within the various endpoints I call LogInformation and pass in an event id. It logs the event but the eventID is never shown in the logs. This call
app.Logger.LogInformation(Events.UserGet, "Getting UserInformation: {UserGUID}", UserGUID);
produces the following in the App Logs within Azure.
2023-02-13 00:19:51.662 +00:00 [Information] Admin: Getting UserInformation: c8e7f5ba-445c-42f7-9e61-8869c8f1ace0
Am I missing something? Ideally the event would include the corresponding Event ID. I've included an excerpt of the Event ID's class below.
public class Events
{
//...
public const int UserGet = 1601;
//...
}
Thanks

Related

SignalR, how to ensure that only one user can edit a given form at a time?

I have a dashboard with a list of items and a finite number of users. I want to show "an item is being edited" near said item to avoid simultaneous edits and overwrites of data.
This seems to me like updating a flag in the database and relatively simple signalr implementation with the javascript simply adding/removing a css class.
I have seen this:
Prevent multiple people from editing the same form
which describes a method with posting every X minutes and clearing the flag from the database when there are no more update messages from the user.
The issue is:
I was wondering if there was a signalr method (like disconnect; i know it exists but I don't know if it fits this scenario) to do that elegantly rather than running a timer function. If so, is it possible for the server to miss the event and permanently leave the flagged as "editing" when it is not?
you could implement a hub for this, here is a example:
public class ItemAccessHub : Hub
{
public override Task OnConnectedAsync()
{
// your logic to lock the object, set a state in the db
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
// your logic to unlock the object
return base.OnDisconnectedAsync(exception);
}
}
to get information from the query you can access the HttpContext:
Context.GetHttpContext().Request.Query.TryGetValue("item-id", out var itemId)
so you could start a connection when the user is accessing the form and send the id of the item in the query:
/hub/itemAccess?item-id=ITEM_ID
and when the user closes the form then disconnect the connection.
with this method the item is also unlocked when the user loses his network connection.
the on disconnect method is allays invoked when a client disconnects, so you can do your clean up in this method.
in this hub you can than also implement the update function
i hope this is what you are looking for

nservicebus sagas - stuck trying to understand the purpose and benefit

I have read multiple times the documentation on the website. I am reading again and again the same articles and I cannot understand what they are trying to achieve with sagas. Besides, there are almost no resources in internet related to this subject.
But I am completely stuck trying to understand the purpose and benefit of defining so called sagas. I understand handlers (IHandleMessages) - these are interceptors. But I can't understand what Saga is for. The language in the documentation assumes that I am supposed to know something special to grasp that idea, but I dont.
Can someone explain to me in simple words, hopefully with real-life example a situation where I must or should define Saga, and what is the benefit of doing so? I have created an app with multiple endpoints and Saga definition as shown in samples, it works (I guess) but I don't understand what these sagas were defined for... In many samples they use RequestTimeout() method in Saga class. Why, why would anyone want to cause a timeout intentionally? I dont want to put any code fragments here, because its unrelated, I need to understand why I would want to use "Sagas" whatever that means?
Thank you.
NServiceBus Saga is a variant of a Process Manager described in the Enterprise Integration Patterns book.
To understand when to use Saga, one has to need it. Let's assume you're using regular message handlers only to implement new user registration process. At some point in time, you discover that only 40% of the brand-new registrants confirm their email address and becoming active user accounts. There are two things you'd like to address.
Remind new registrants to confirm their email after 24 hours after registration by sending a reminder.
Remove registrant info (email for example) from the data store to be compliant with GDPR within 48 hours.
Now how do you do that with a regular message handler? A handler would receive the initial request (first message, m1) to kick off registration by generating an email with a confirmation link and that's it. Once the handler is done, it's done for good. But your process is not finished. It's a long-running logical process that has to span 48 hours before completed. It's no longer just a single message processing, but a workflow at this point. A workflow with multiple checkpoints. Similar to a state machine. To move from one state to another, a certain condition has to be fulfilled. In case of NServiceBus, those would be messages. A message to send a reminder after 24 hours (let's call it m2) is not going to be triggered by any user action. It's a "system" message. A timed message that should be kicked off automatically. So is with the message to instruct the system to remove registrant information if validation link was not activated. The theme can be observed: need to schedule messages in the future to re-hydrate the workflow and continue from the state it was left last time.
That's what timeouts are. Those are requests to re-hydrate/continue saga/workflow from the point it was left last time at a certain point in time - minutes, hours, days, months, years.
This is what this kind of workflow would look like as a saga (oversimplified and doesn't take into consideration all the edge cases).
class RegistrationWorkflow :
Saga<WorkflowState>,
IAmStartedByMessages<RegisterUser>,
IHandleMessages<ActivationReceived>,
IHandleTimeouts<NoResponseFor24Hours>,
IHandleTimeouts<NoResponseFor48Hours>
{
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<WorkflowState> mapper)
{
// omitted for simplicity, see message correlation
// https://docs.particular.net/nservicebus/sagas/message-correlation
}
public async Task Handle(RegisterUser message, IMessageHandlerContext context)
{
Data.RegistrationId = message.RegistrationEmail;
await RequestTimeout<NoResponseFor24Hours>(context, TimeSpan.FromHours(24));
}
public async Task Handle(ActivationReceived message, IMessageHandlerContext context)
{
Data.ConfirmationReceived = true;
// email was confirmed and account was activated
await context.Send(new PromoteCandidateToUser
{
CandidateEmail = Data.RegistrationEmail
});
MarkAsComplete()
}
public async Task Timeout(NoResponseFor24Hours timeout, IMessageHandlerContext context)
{
if (Data.ConfirmationReceived)
{
return;
}
await context.Send(new SendReminderEmailToActivateAccount { Email = Data.RegistrationEmail });
await RequestTimeout(context, TimeSpan.FromHours(24), new NoResponseFor48Hours());
}
public async Task Timeout(NoResponseFor48Hours timeout, IMessageHandlerContext context)
{
if (Data.ConfirmationReceived)
{
return;
}
context.Send(new CleanupRegistrationInformationForGDPRCompliancy
{
RegistrationEmail = Data.RegistrationEmail
});
MarkAsComplete();
}
}
Since this is a state machine, the state is persisted between Saga invocations. Invocation would be caused either by a message a saga can handle (RegisterUser and ActivationReceived) or by timeouts that are due (NoResponseFor24Hours and NoResponseFor48Hours). For this specific saga, the state is defined by the following POCO:
class WorkflowState : ContainSagaData
{
public string RegistrationEmail { get; set; }
public bool ConfirmationReceived { get; set; }
}
Timeouts are nothing but plain IMessages that get deferred. The timeouts used in this samples would be
class NoResponseFor24Hours : IMessage {}
class NoResponseFor48Hours : IMessage {}
Hope this clarifies the idea of Sagas in general, what Timeouts are and how they are used. I did not go into Message Correlation, Saga Concurrency, and some other details as those can be found at the documentation site you've referred to. Which bring us to the next point.
I have read multiple times the documentation on their website. It is absolutely terrible. I am reading again and again the same articles and I cannot comprehend what they are trying to achieve.
The site has a feedback mechanism you should absolutely provide.
Besides there almost no resources in internet related to this subject.
Hope to see you posting a blog (or a series of posts) on this topic. By doing so you'll have a positive contribution.
Full disclaimer: I work on NServiceBus

How do you register a behavior to execute after the "Handle" method in NServiceBus 6?

I have an Endpoint with a Handle method. I would like to do something immediately before and immediately following Handle. I was able to get the step working before by imolementing LogCommandEntryBehavior : Behavior<IIncomingLogicalMessageContext>. What needs to be implemented to happen immediately following Handle?
In NServiceBus Version 6, the pipeline consists of a series of Stages, each one nested inside the previous like a set of Russian Dolls. For an incoming message, the stages are (in order):
ITransportReceiveContext,
IIncomingPhysicalMessageContext,
IIncomingLogicalMessageContext, and
IInvokeHandlerContext
When you create a behavior within a stage, you get passed a delegate called next(). When you call next() you execute the next behavior in the pipeline (which may move the pipeline to the next stage). The call to next() returns a Task which indicates when this inner part of the pipeline is done.
That gives you the opportunity to invoke your code before moving on to the next stage, and invoke more code after the next stage has been completed like this:
public class LogCommandEntryBehavior : Behavior<IIncomingLogicalMessageContext>
{
public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
{
// custom logic before calling the next step in the pipeline.
await next().ConfigureAwait(false);
// custom logic after all inner steps in the pipeline completed.
}
}
If you want to log information about the handling of a message, I recommend looking at the IInvokeHandlerContext stage. It contains information about how the message was handled and will be called once for every handler that is invoked (in cases where you have multiple). If you don't need info at that level then IIncomingLogicalMessageContext is probably all you need.
You can read more about the Version 6 pipeline in the Particular Docs site:
Steps, Stages and Connectors
Manipulate Pipeline with Behaviors

You must call "WebSecurity.InitializeDatabaseConnection" ... But I already have

I've read through a number of SO questions on this topic, but all seem to be dealing with where you should put this call.
My problem is different: I have already done the WebSecurity.InitializeDatabaseConnection() call, and have set breakpoints so I know it's been executed. But I still get the invalid operation exception saying I have to call it.
Unlike most of the other questions which are encoutering it in an MVC controller action, I'm encountering it an HttpModule I have written to accomplish Authentication for a REST WebAPI controller. The Init call contains the WebSecurity.InitializeDatabaseConnection call. The OnAuthenticationRequest method then extracts username and password information from the request's Authorization header, and calls the ValidateUser method of the SimpleMembershipProvider. This is where I get the exception
You must call the WebSecurity.InitializeDatabaseConnection method
before you call any other method of theWebSecurityclass.
So
a) why am I getting this exception when I have already fulfilled the conditions not to get it.
b) what can be done about it?
After hours of facing the same problem and setting many many breakpoints I found a solution for my environment, I hope others can benefit from it:
inside Folder App_Start
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var attr=new InitializeSimpleMembershipAttribute();
// here is the important part
attr.OnActionExecuting(new ActionExecutingContext());
filters.Add(attr);
}
}
Background: In my app I have 2 views:
/Account/Register: this is part of the standard asp.mvc 4 templates and has the attribute [AllowAnonymous]
/Task: this is the "Index"-View of a todo-list, The TaskController has the attribute [Authorize(Roles="Administrator")]
I always had the problem in the following sitation:
debugging session in browser
successfull login
open the view "/Task" which requires authorization
switch to visual studio and keep the browser open
stop debugging
change some code inside a controller (setting some whitespace is enough)
start debugging session
press refresh in browser
At this point the method "OnActionExecuting" inside the class InitializeSimpleMembershipAttribute was not yet called (determined by setting breakpoints)!
But when I opened a View which doesn't require Authorization (i.e. /Account/Register) the Method "OnActionExecuting" was called. After this I could call /Task without errors.
So the solution was to make a "pseudocall" for "OnActionExecuting" on application-startup.
You should not need to init the database connection every time your HttpModule is called. Just do it once at application start up. Here is an article that explains how to use SimpleMembership for authentication/authorization of Web API calls. There is also a link to source code for the example project.

How to get tcmid of currently logged user in Tridion?

private void Subscribe()
{
EventSystem.Subscribe<User, LoadEventArgs>(GetInfo, EventPhases.Initiated);
}
public void GetInfo(User user, LoadEventArgs args, EventPhases phase)
{
TcmUri id = user.Id;
string name = user.Title;
Console.WriteLine(id.ToString());
Console.WriteLine(name);
}
I wrote above code and add the assembly in config file in Tridion server but no console window is coming on login of a user
The event you were initially subscribing to is the processed phase of any identifiable object with any of its actions, that will trigger basically on every transaction happening in the SDL Tridion CMS, so it won't give you any indication of when a user logs in (it's basically everything which happens all the time).
Probably one of the first things which is happening after a user logs in, is that its user info and application data is read. So what you should try is something along the lines of:
private void Subscribe()
{
EventSystem.Subscribe<User, LoadEventArgs>(GetInfo, EventPhases.Initiated);
}
public void GetInfo(User user, LoadEventArgs args, EventPhases phase)
{
TcmUri id = user.Id;
string name = user.Title;
}
But do keep in mind that this will also be triggered by other actions, things like viewing history, checking publish transactions and possibly a lot more. I don't know how you can distinguish this action to be part of a user login, since there isn't an event triggered specifically for that.
You might want to check out if you can find anything specific for a login in the LoadEventArgs for instance in its ContextVariables, EventStack, FormerLoadState or LoadFlags.
Edit:
Please note that the Event System is running inside the SDL Tridion core, so you won't ever see a console window popup from anywhere. If you want to log information, you can include the following using statement:
using Tridion.Logging;
After adding a reference to the Tridion.Logging.dll which you can find in your ..\Tridion\bin\client directory. Then you can use the following logging statement in your code:
Logger.Write("message", "name", LoggingCategory.General, TraceEventType.Information);
Which you will find back in your Tridion Event log (provided you have set the logging level to show information messages too).
But probably the best option here is to just debug your event system, so you can directly inspect your object when the event is triggered. Here you can find a nice blog article about how to setup debugging of your event system.
If you want to get the TCM URI of the current user, you can do so in a number of ways.
I would recommend one of these:
Using the Core Service, call GetCurrentUser and read the Id property.
Using TOM.NET, read the User.Id property of the current Session.
It looks like you want #2 in this case as your code is in the event system.