Loupe - Log - Monitor - Resolve
Loupe / Developer's Guide / For .NET Framework / For ASP.NET MVC / Developer's Guide - Capturing Application Users
In This Topic
    Developer's Guide - Capturing Application Users
    In This Topic

    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