Advanced Log Parser Part 6 - Creating a Simple Custom Input Format Plug-In
In Part 4 of this series, I illustrated how to create a new COM-based input provider for Log Parser from a custom input format:
Advanced Log Parser Charts Part 4 - Adding Custom Input Formats
For the sample that I published in that blog, I wrote a plug-in that consumed FTP RSCA events, which is highly structured data, and it added a lot of complexity to my example. In the past ten months or so since I published my original blog, I've had several requests for additional information about how to get started writing COM-based input formats for Log Parser, so it occurred to me that perhaps I could have shown a simpler example to get people started instead of diving straight into parsing RSCA data. ;-)
With that in mind, I thought that I would write a couple of blog posts with simpler examples to help anyone who wants to get started writing custom input formats for Log Parser.
For this blog post, I will show you how to create a very basic COM-based input format provider for Log Parser that simply returns static data; you could use this sample as a template to quickly get up-and-running with the basic concepts. (I promise to follow this blog with another real-world example that is still easier-to-use than my RSCA example.)
A Reminder about Creating COM-based plug-ins for Log Parser
In the blog that I referred to earlier, I mentioned that a COM plug-in has to support the following public methods:
Method Name | Description |
---|---|
OpenInput |
Opens your data source and sets up any initial environment settings. |
GetFieldCount |
Returns the number of fields that your plug-in will provide. |
GetFieldName |
Returns the name of a specified field. |
GetFieldType |
Returns the datatype of a specified field. |
GetValue |
Returns the value of a specified field. |
ReadRecord |
Reads the next record from your data source. |
CloseInput |
Closes your data source and cleans up any environment settings. |
Once you have created and registered a COM plug-in, you call it by using something like the following syntax:
logparser.exe "SELECT * FROM FOO" -i:COM -iProgID:BAR
In the preceding example, FOO is a data source that makes sense to your plug-in, and BAR is the COM class name for your plug-in.
Creating a Simple COM plug-in for Log Parser
Once again, I'm going to demonstrate how to create a COM component by using a scriptlet, which I like to use for demos because they are quick to design, they're easily portable, and updates take place immediately since no compilation is required. (All of that being said, if I were writing a real COM plug-in for Log Parser, I would use C# or C++.)
To create the sample COM plug-in, copy the following code into a text file, and save that file as "Simple.LogParser.Scriptlet.sct" to your computer. (Note: The *.SCT file extension tells Windows that this is a scriptlet file.)
<SCRIPTLET> <registration Description="Simple Log Parser Scriptlet" Progid="Simple.LogParser.Scriptlet" Classid="{4e616d65-6f6e-6d65-6973-526f62657274}" Version="1.00" Remotable="False" /> <comment> EXAMPLE: logparser "SELECT * FROM FOOBAR" -i:COM -iProgID:Simple.LogParser.Scriptlet </comment> <implements id="Automation" type="Automation"> <method name="OpenInput"> <parameter name="strValue"/> </method> <method name="GetFieldCount" /> <method name="GetFieldName"> <parameter name="intFieldIndex"/> </method> <method name="GetFieldType"> <parameter name="intFieldIndex"/> </method> <method name="ReadRecord" /> <method name="GetValue"> <parameter name="intFieldIndex"/> </method> <method name="CloseInput"> <parameter name="blnAbort"/> </method> </implements> <SCRIPT LANGUAGE="VBScript"> Option Explicit Const MAX_RECORDS = 5 Dim intRecordCount ' -------------------------------------------------------------------------------- ' Open the input session. ' -------------------------------------------------------------------------------- Public Function OpenInput(strValue) intRecordCount = 0 End Function ' -------------------------------------------------------------------------------- ' Close the input session. ' -------------------------------------------------------------------------------- Public Function CloseInput(blnAbort) End Function ' -------------------------------------------------------------------------------- ' Return the count of fields. ' -------------------------------------------------------------------------------- Public Function GetFieldCount() GetFieldCount = 5 End Function ' -------------------------------------------------------------------------------- ' Return the specified field's name. ' -------------------------------------------------------------------------------- Public Function GetFieldName(intFieldIndex) Select Case CInt(intFieldIndex) Case 0: GetFieldName = "INTEGER" Case 1: GetFieldName = "REAL" Case 2: GetFieldName = "STRING" Case 3: GetFieldName = "TIMESTAMP" Case 4: GetFieldName = "NULL" Case Else GetFieldName = Null End Select End Function ' -------------------------------------------------------------------------------- ' Return the specified field's type. ' -------------------------------------------------------------------------------- Public Function GetFieldType(intFieldIndex) ' Define the field type constants. Const TYPE_INTEGER = 1 Const TYPE_REAL = 2 Const TYPE_STRING = 3 Const TYPE_TIMESTAMP = 4 Const TYPE_NULL = 5 Select Case CInt(intFieldIndex) Case 0: GetFieldType = TYPE_INTEGER Case 1: GetFieldType = TYPE_REAL Case 2: GetFieldType = TYPE_STRING Case 3: GetFieldType = TYPE_TIMESTAMP Case 4: GetFieldType = TYPE_NULL Case Else GetFieldType = Null End Select End Function ' -------------------------------------------------------------------------------- ' Return the specified field's value. ' -------------------------------------------------------------------------------- Public Function GetValue(intFieldIndex) Select Case CInt(intFieldIndex) Case 0: GetValue = 1 Case 1: GetValue = 1.0 Case 2: GetValue = "One" Case 3: GetValue = Now Case Else GetValue = Null End Select End Function ' -------------------------------------------------------------------------------- ' Read the next record, and return true or false if there is more data. ' -------------------------------------------------------------------------------- Public Function ReadRecord() intRecordCount = intRecordCount + 1 If intRecordCount <= MAX_RECORDS Then ReadRecord = True Else ReadRecord = False End If End Function </SCRIPT> </SCRIPTLET>
After you have saved the scriptlet code to your computer, you register it by using the following syntax:
regsvr32 Simple.LogParser.Scriptlet.sct
At the very minimum, you can now use the COM plug-in with Log Parser by using syntax like the following:
logparser "SELECT * FROM FOOBAR" -i:COM -iProgID:Simple.LogParser.Scriptlet
This will return results like the following:
INTEGER |
REAL |
STRING |
TIMESTAMP |
NULL |
---|---|---|---|---|
------- |
-------- |
------ |
------------------ |
---- |
1 |
1.000000 |
One |
2/26/2013 19:42:12 |
- |
1 |
1.000000 |
One |
2/26/2013 19:42:12 |
- |
1 |
1.000000 |
One |
2/26/2013 19:42:12 |
- |
1 |
1.000000 |
One |
2/26/2013 19:42:12 |
- |
1 |
1.000000 |
One |
2/26/2013 19:42:12 |
- |
Statistics: |
||||
----------- |
||||
Elements processed: |
5 |
|||
Elements output: |
5 |
|||
Execution time: |
0.01 seconds |
Next, let's analyze what this sample does.
Examining the Sample Scriptlet Contents in Detail
Here are the different parts of the scriptlet and what they do:
- The <registration> section of the scriptlet sets up the COM registration information; you'll notice the COM component class name and GUID, as well as version information and a general description. (Note that you should generate your own GUID for each scriptlet that you create.)
- The <implements> section declares the public methods that the COM plug-in has to support.
- The <script>section contains the actual implementation:
- The
OpenInput()
method opens your data source, although in this example it only initializes the record count. (Note that the value that is passed to the method will be ignored in this example.) - The
CloseInput()
method would normally clean up your session, (e.g. close a data file or database, etc.), but it doesn't do anything in this example. - The
GetFieldCount()
method returns the number of data fields in each record of your data, which is static in this example. - The
GetFieldName()
method returns the name of a field that is passed to the method as a number; the names are static in this example. - The
GetFieldType()
method returns the data type of a field that is passed to the method as a number, which are statically-defined in this example. As a reminder, Log Parser supports the following five data types for COM plug-ins:TYPE_INTEGER
,TYPE_REAL
,TYPE_STRING
,TYPE_TIMESTAMP
, andTYPE_NULL
. - The
GetValue()
method returns the data value of a field that is passed to the method as a number. Once again, the data values are statically-defined in this example. - The
ReadRecord()
method moves to the next record in your data set; this method returns True if there is data to read, or False when the end of data is reached. In this example, the method increments the record counter and sets the status based on whether the maximum number of records has been reached.
- The
Summary
That wraps up the simplest example that I could put together of a COM-based input provider for Log Parser. In my next blog, I'll show how to create a generic COM-based input provider for Log Parser that you can use to parse text-based log files.
(Cross-posted from http://blogs.msdn.com/robert_mcmurray/)