Chat Support
Loupe - Log - Monitor - Resolve
Loupe / Developer's Guide / For .NET Framework / For ASP.NET MVC / Developer's Guide - Capturing Application Users
Developer's Guide - Capturing Application Users

By default, Loupe captures the user name of the Current Principal associated with each thread as it writes log entries.  This provides at best a simple user name, typically in the form of <Domain>\<User> or <User Name>.  This is a good start, but there is only so far Loupe can do with this limited amount of information.

To maximize the benefit you get from Loupe's Application User tracking you'll want to provide additional information for each unique user of your application.  To do this, you need to:

  1. Subscribe to the ResolveApplicationUser Event
  2. Translate each IPrincipal (which includes the user name and other data your security system may be storing) into a full user profile.
This feature is disabled when the Agent is running in Anonymous mode.  In Anonymous mode no user-identifiable information is gathered which would  be defeated by this feature.

Application User Information

When responding to the ResolveApplicationUser Event you can provide additional details about the user including:

Field Name Description Default Value
Key Optional.  The authoritative key provided by the Agent for this user. None
FullyQualifiedUserName Read Only.  The fully qualified user name as originally provided (E.g. DOMAIN\User or user@domain) The user name from the IPrincipal
Caption Optional.  A display caption for the user - often their full name. None. The server will attempt to fill by parsing FullyQualifiedUserName
EmailAddress Optional.  The email address used to communicate with the user. None. The server will use FullyQualifiedUserName if it appears to be an email address
Phone Optional.  The phone number or other telecommunication alias for the user. None
Organization Optional. The organization this user belongs to - such as a customer. None.  The server will attempt to fill by parsing FullyQualifiedUserName
Title Optional.  A title for the user (e.g. job title) None
TimeZoneCode Optional.  The time zone the user is associated with None
Tenant Optional.  The specific tenant for the user account in a multitenant system.  Application-specific. None
Role Optional.  The primary role of the user (such as Administrator or User or Guest).  Application-specific. None
Properties Optional.  A collection of name/value pairs to be recorded with the user. An empty Dictionary

This information will be sent along with each log message and other user-specific data to the Server where it will be merged with other information available about the same user to provide the most complete picture possible for your support staff.

Application User information provided in this event will overwrite server-derived values for Caption, EmailAddress, and Organization.  The most current information for all non-key fields will update information in the server, allowing additional information to be stored over time and automatically updated as the user is changed in your application.

How Application Users are Matched

To match up user information between multiple applications the FullyQualifiedUserName is assumed to be globally unique.  This means that all users with the name "MYCOMPANY\AUser" will be considered the same person, similarly auser@mycompany.com

If this is inadequate for your purposes (for example if every installation has the same basic users and there is no domain to qualify the information) you can override this match by providing a Key.  If a Key is specified then only an exact match by Key will be considered the same user - even another user with the same FullyQualifiedUserName but no Key will not be merged with the entry with the Key.  The Key field is never displayed to the user so it can be something that's hard to read like a GUID.  Good examples would be a database GUID primary key (but not an identity key since that will duplicate between databases) and a Windows SID. 

Custom Properties

You can record custom name/value pairs for each user to add information that doesn't fit well into one of the built-in fields.  These properties will be merged with existing properties on the server based on a case-insensitive name comparison. 

Names can be dot-delimited (.) and may contain spaces. 

Setting a value to null will remove the property from the user when merged with the server record. 

Since the user properties are shared across all applications on the Loupe Server, prefix application-specific values with the display name of the application followed by a period (.) for best display.

Basic Example of Providing User Information

Multi-User Application User Resolution Example
Copy Code
private void OnResolveApplicationUser(object sender, ApplicationUserResolutionEventArgs e)
{
    //In this example our application has a custom IUserPrincipal called CustomUserPrincipal.
    //Your application will likely have some other approach you're using for common user info
    var ourApplicationPrincipal = e.Principal as CustomUserPrincipal;

    if (ourApplicationPrincipal == null)
        //it must be a regular windows user from a background thread or
        //some other scenario where we aren't getting the true app user
        return;

    //Now that we know we have something, populate the Loupe Application User
    var newUser = e.GetUser();
    newUser.Caption = ourApplicationPrincipal.FullName;
    newUser.Organization = ourApplicationPrincipal.Customer;
    newUser.EmailAddress = ourApplicationPrincipal.EmailAddress;
    newUser.Phone = ourApplicationPrincipal.PrimaryPhoneNumber;
}

Performance Implications of providing Application Users

The Loupe Agent will raise the ResolveApplicationUser event each time it sees a FullyQualifiedUserName that it doesn't yet have details for, provided there are any subscribers to this event.  This is done asynchronously from logging so the event will not be raised on the same thread that recorded the log message.  This means that it normally will have no effect on the responsiveness of the application unless there is a dramatic slowdown in providing user information compared to the rate of logging.  That said, it's recommended to make this method as fast as feasible, particularly if there is any synchronous logging in your application.

While Loupe is multithreaded, the ResolveApplicationUser event will only be raised by one thread so there will only be one outstanding invocation at a time. 

Best Practices for Providing User Information

It's recommended that your application have already loaded all of the information you want to record onto the object you have that implements IPrincipal.  These interfaces are designed to allow you to provide user profile information via claims or your own custom implementation.  If you follow this approach, when the event is raised you can cast your IPrincipal object to the correct type and interrogate for all of the relevant information without having to do any additional database calls.

Since the ResolveApplicationUser event is asynchronous relative to recording data with the Loupe Agent it's possible to log messages and metrics while attempting to resolve the user (such as might happen if you call a database to retrieve information and are recording database activity to Loupe) and there are protections to avoid deadlocking in the extreme case where these log messages would have to be recorded synchronously.  Despite this, it's recommended for performance reasons to minimize the work done during this event.

Gather information necessary to populate the Application User before calling the GetUser Method to start setting the information.  Once this method has been invoked, the resulting application user will be recorded and locked in that configuration for the duration of the session.  Therefore, if you may not be able to contact a data store to retrieve the user information you're better off deferring specifying the user information until it is all available.

See Also