DFSR Debug Analysis with Message Analyzer – Part 6, Parsing Specific Multi-line Messages


This post continues the series that started here.

Last time I discussed parsing multi-line messages but the way I handled them was to append everything into a generic MessageText field. In this post I want to examine the handling I’ve used for specific multi-line messages where the extended logging has additional field data. Let’s start with an example of such a message –

20120522 11:01:55.858 2600 JOIN  1201 Join::SubmitUpdate LDB Updating ID Record:
+    fid                             0x200000000F503
+    usn                             0x600d5e48
+    uidVisible                      1
+    filtered                        0
+    journalWrapped                  0
+    slowRecoverCheck                0
+    pendingTombstone                0
+    internalUpdate                  0
+    dirtyShutdownMismatch           0
+    meetInstallUpdate               0
+    meetReanimated                  0
+    recUpdateTime                   20120522 00:31:11.500 GMT
+    present                         1
+    nameConflict                    0
+    attributes                      0x20
+    ghostedHeader                   0
+    data                            0
+    gvsn                            {5442ADD7-04C7-486B-B665-2CB036997A67}-v899394
+    uid                             {5442ADD7-04C7-486B-B665-2CB036997A67}-v598754
+    parent                          {8A6CF487-2D5A-456C-A235-09F312D631C8}-v1
+    fence                           Default (3)
+    clockDecrementedInDirtyShutdown 0
+    clock                           20120522 00:31:11.500 GMT (0x1cd37b230245303)
+    createTime                      20120516 00:30:40.306 GMT
+    csId                            {8A6CF487-2D5A-456C-A235-09F312D631C8}
+    hash                            3C379B81-28E4F2A8-52F091A5-0D4CFAB8
+    similarity                      2E233A14-253F363F-3E110515-22120C04
+    name                            file0000004161.txt
+

For the sake of simplicity, I’ll edit the example to –

20120522 11:01:55.858 2600 JOIN  1201 Join::SubmitUpdate LDB Updating ID Record:
+    fid                             0x200000000F503
+    name                            file0000004161.txt

This will make the code samples that follow less verbose. In practice I’ve extended these to deal with all fields.

Custom Methods

I’ve discovered that Open Parser Notation (OPN) has no contains method. This would be useful for checking MessageText to see when I’m parsing a particular message type. I’ve created a contains method and dropped it at the end of my parser file –

bool Contains(this string str, string subStr)
{
    return str.IndexOf(subStr) >= 0;
}

Utility Parsers

Looking at the multi-line continuation of the message, I can see that it occurs as name-value pairs. I’ll need to create what I refer to as a utility parser to handle this. The utility parser will never be invoked against a standalone message but I’ll call on it in my multi-line parser code to extract these name-value pairs. Once again, I drop this code at the end of my parser file –

message name_value_pair with
    EntryInfo { Regex = @"\+\s*(?<n>[A-Za-z]+)\s*(?<v>.*)", Priority = 2 } : LogEntry
{
    string n;
    string v;
}

The RegEx looks for

  1. A + at the start of the line followed by any number of spaces – \+\s*
  2. One or more alpha characters which are injected into the “n” string – (?<n>[A-Za-z]+)
  3. Any number of spaces – \s*
  4. Any characters which are injected into the “v” string – (?<v>.*)

Custom Data Type

When I parse these name-value pairs I’ll want to inject them into a data type for that type of message. I define it like this –

type ldbupdateinfo
{
    optional string fid;
    optional string name;

    bool parse(string line, LineContext context, any message m)
    {
        line = line.Trim();
        var nvp = Decode<name_value_pair>(line, context);
       
        if (nvp == null) return false;
       
        switch (nvp.n)
        {
            // cases for an LDB change
            case "fid" => fid = (nvp.v as string);
            case "name" => name = (nvp.v as string);
            default => return false;
        }
       
        return true;
    }
}

Here my custom data type, ldbupdateinfo has two optional string fields – fid and name.

type ldbupdateinfo
{
    optional string fid;
    optional string name;

It has a parse method which calls the name_value_pair utility parser and attempts to populate the fid and name fields. If it is successful it returns true to indicate the line is parsed or false if unsuccessful –

    bool parse(string line, LineContext context, any message m)
    {
        line = line.Trim();
        var nvp = Decode<name_value_pair>(line, context);
       
        if (nvp == null) return false;
       
        switch (nvp.n)
        {
            // cases for an LDB change
            case "fid" => fid = (nvp.v as string);
            case "name" => name = (nvp.v as string);
            default => return false;
        }
       
        return true;
    }

Modify Multi-line Parser

The multi-line parser from last post contained code –

else
{
    if (line.Count > 0 && line[0] == '+')
    {
        MessageText = MessageText + " " + (line.Segment(1)).Trim();
        return true;
    }
    else
    {
        return false;
    }
}

I modify this to handle our specific multi-line message –

else
{
    // line starting with "+" is considered continuation
    // of the current message
    if (line.Count > 0 && line[0] == '+')
    {
        if (MessageText.Contains("LDB Updating ID Record:"))
        {
            // if there isn't already an ldbupdateinfo node, create one
            var ldbui = LDBUpdateInformation == nothing ? new ldbupdateinfo() : LDBUpdateInformation as ldbupdateinfo;
       
            // attempt to parse this line as an ldbupdateinfo node and extract name value pairs
            if (!ldbui.parse(line, context, this))
            {
                // if the line could not be parsed as a name-value pair
                // just append the whole line to the MessageText field
                // and confirm the line is parsed
                MessageText = MessageText + " " + (line.Segment(1)).Trim();
                return true;
            }
          
            LDBUpdateInformation = ldbui;
            return true;
        }
        else()
        {
            MessageText = MessageText + " " + (line.Segment(1)).Trim();
            return true;
        }
    }
    else
    {
        return false;
    }
}

And lastly, add an optional LDBUpdateInformation field to the message definition –

uint Thread;
string ModuleID;
uint SourceCodeLine;
string Namespace;
string Class;
string Method;
string Level;
string MessageText;
optional ldbupdateinfo LDBUpdateInformation;

Result

After saving all of these changes to the parser file (and expanding code to include all fields for the message type), a multi-line message of this type is parsed as follows –

MA10

Progress Report

To make it easier for you to consume what I’ve done so far, here’s a version of my parser that includes everything discussed to this point –

DFSRDebug-01.config

Next Up

Dealing with Message Analyzer Limitations

Comments (0)

Skip to main content