Latest Replies
Monday
Dec132010

Helpful Domain Logs of CQRS

Domain-aware logger with simple color patterns can speed up the maintenance of various distributed solutions, while providing you with additional insight of what's going on inside. Let's see how.

This article continues Lifehacks from Lokad Production mini-series of Cloud CQRS (xLim 4) body of knowledge. Previous articles:

Let's continue where Use Consistent Color Coding left off in part 2.

Right now we have an easy and consistent way to visually differentiate commands, events and failures that stream from the nodes of our distributed system in (almost) real-time. We use colors for this.

Domain Log Simple

Yet the picture is still a bit confusing. This page of the Domain Log contains sessions of two users and a saga process, spawned by the second user. Can you identify which message belongs to which category?

Actually it is impossible to figure this out without actually looking into the message contents. In situations with multiple concurrent processes this means a lot of clicking and thinking.

However, we can make our life much simpler if we:

  • Follow some common conventions in domain message contracts (commands and events alike);
  • Define domain-aware analyzer that can take any message as an input and tell if it belongs to a certain category (i.e.: "user-x", "saga-x", "unknown");
  • Add a little bit of color to the grid, to visually bind categories together.

On the UI level everything works out like this:

Domain Log with Colors

I think now it's easier to distinguish between two user sessions and a saga process.

Implementation Details

Below is dead-simple code to figure out primary background color for the type of message. It works with my definitions of domain messages. Your case will, obviously, require different handling.

static readonly Color CommandColor = Color.FromArgb(204, 255, 204);
static readonly Color EventColor = Color.FromArgb(204, 255, 255);
static readonly Color FailureColor = Color.FromArgb(255, 204, 204);

public static Color GetBackgroundColorForContract(object message)
{
    var domainCommand = message as IDomainCommand;

    if (domainCommand != null)
    {
        var mailCommand = domainCommand as SendMailCommand;

        if (mailCommand != null)
        {
            if (mailCommand.Subject.Contains("Error"))
            {
                return FailureColor;
            }
        }
        return CommandColor;
    }

    var domainEvent = message as IDomainEvent;

    if (domainEvent != null)
    {
        return EventColor;
    }
    return Color.LightGray;
}

Category name for each message (i.e.: user-x or sync-y) is generated by another straightforward code as well. It simply enumerates properties of the message type, looking for known conventions, and then generates a delegate to map this message to a category:

var property = member as PropertyInfo;

if (property != null)
{
    switch (property.Name)
    {
        case "Security":
            return m => MapToSecurity((SecurityContext) 
              property.GetValue(m, null));
        case "SyncId":
            return m => MapToSyncId((Guid) 
              property.GetValue(m, null));
        case "SyncReference":
            return m => MapToReference((string) 
              property.GetValue(m, null));
    }
}

From the snippet above you probably can identify a few conventions that we follow in message contracts for this specific distributed sub-system.

Note that this approach is not as fragile as it might seem. If a new message is defined, that does not follow convention, it will simply have an empty grey cell in the domain log. This will look inconvenient enough to urge developer to fix it. Renaming properties/fields usually does the trick right away, if you do use evolution-friendly message serializer.

Delegates produced by the snippet above are cached by message type and used like this:

public string GetCategoryName(IMessage message)
{
    var type = message.GetType();
    Func<IMessage, string> map;
    if (!_mapperCache.TryGetValue(type, out map))
    {
        map = GenerateTypeSpecificMapper(type);
        _mapperCache.Add(type, map);
    }
    return map(message);
}

Below is the code snippet to generate nice pseudo-random background color for a given category name. Strings with the same hash code will create same color.

static Color GetBackgroundColorForCategory(string category)
{
    if (string.IsNullOrEmpty(category))
        return Color.LightGray;

    var hashCode = category.GetHashCode();
    var b = BitConverter.GetBytes(hashCode);
    return Color.FromArgb(b[0] | 128, b[1] | 128, b[2] | 128);
}

Everything else is just the system-specific code to:

  • wire domain log retrieval from an endpoint of Cloud CQRS system (this part should be extremely simple by design, otherwise endpoint management might start choking your distributed system with unnecessary complexity and friction);
  • bind domain log to a DataGrid, while applying our colors.

Summary

This was an article from Cloud CQRS body of knowledge (series on production Lifehacks from Lokad). We highlighted a quick and simple approach of categorizing messages from the domain log and then visually differentiating them with hash-based background colors.

How does it look? Does the approach feel simple to you as well? How do you get insight into your distributed systems (cloud and on-premises deployments alike)?

PS: More articles are planned in the series. In order to stay tuned to the updates, you can subscribe to RSS or follow on Twitter.

« Introducing Distributed Podcast | Main | Cloud CQRS Lifehacks From Lokad - Part 2 »

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>