Skip to main content

Using Text Based Logs with Windows PowerShell (4 of 8)


Day 4: Creating a PowerShell Object out of a Simple Text Log
Last Friday we left off with a list of properties from our log. Today we will complete the final 3 steps in this process:
3.       Create an object that contains all possible property names.
4.       Read the log records into the object.
5.       Send to the pipeline.
Steps 3 and 4 are actually integrated together while Step 5 is an easy one liner.
We are going to add 3 new functions to our cmdlet.  The first function Import-LogData will actually call the function Create-EmptyObject which will call the function Get-Hash to support their individual purposes.  Let’s take a look at the shorter of the functions, Get-Hash
    Function Get-Hash
    {
    Param ($Name)
    # This brief function is used to used to reduce the amount of code
    # while building a new empty object.
 
        $Hash = @{MemberType = "NoteProperty";
                    Name = $Name;
                    Value = $Null}
 
        Write-Output $Hash
           
    } # End: Function Get-Hash
 
This function will cut down on a lot of typing.  It will be called while creating a new, empty object.  The Create-EmptyObject function will use this function for each property that needs to be added to the empty object being created.
    Function Create-EmptyObject
    {
    Param ($Prop)
    # Here the list of discovered properties is sent so that
    # a blank object can be created containing all of the potential
    # properties of the object.  this will allow for a consistent
    # set of members for the final object.
       
        # Create a blank object.           
        $Obj = New-Object -TypeName PSObject
           
        # Change the type name.
        $Obj.pstypenames.insert(0,'Custom.LogObject')
 
        # This will create a new property for each of the derived
        # properties.
        ForEach ($P in $Prop.prop)
        {
            $Hash = Get-Hash -Name $P 
            $Obj | Add-Member @Hash
        }
  
        # Add the TimeWritted Property.
        $Hash = Get-Hash -Name "TimeWritten" 
        $Obj | Add-Member @Hash
       
        # Return empty object to calling statement.
        Write-Output $Obj           
 
    } # End: Function Create-EmptyObject
 
Creating an empty object is the sole duty of Create-EmptyObject.  We will pass to it the list of properties that we derived from the log file earlier.  This will create an object of type LogObject.  This will also ensure that each instance of this object has the same property values.   Also, an additional property call TimeWritten is added to the empty object.  The ForEach loop will add the property names that we derived earlier in the Get-PropertyList function.  This one allows us to also add the time stamp information.
    Function Import-LogData
    {
    Param ($Records, $ObjProps)
    # This is where we put it all together. Each record is cycled through.
    # If the record contains a property that we derived earlier, a new object
    # is created containing the time stamp and the data for that property.
 
        ForEach ($R in $Records.Record)
        {
            $Obj = Create-EmptyObject -Prop $ObjProps
            ForEach ($O in $ObjProps.Prop)
            {
                If ($R -like "*$($O)*")
                {
                    # Add the data time stamp
                    $End = $R.IndexOf("]")
                    $Obj.TimeWritten = $R.Substring(1,$End-1)
               
                    # Add the property that caused the match.
                    $Obj.$O = $R.Remove(0,22).Replace("$($O):","").Trim()
                   
                }
            }
            Write-Output $Obj
   
        } # End: ForEach ($R in $Records)
    } # End: Function Import-LogData
 
Import-LogData is called from the main code to start the process of creating an object for the log data.  We pass to it the list of records created in the Get-Record function and the list of properties created in the Get-PropertyList function.
The first ForEach loop will cycle through each record one at a time. The second ForEach loop will compare the current record to see if it contains one of the properties that we gathered from Get-PropertyList.  If it does, it first adds the date/time information to the TimeWritten property.  It then adds the remaining string data to the property of the object that has the same name that caused the match. Finally, our object is sent to the pipeline.
Here is our final code:
Function Import-MyLog1
{
    # -----------------------------------------------------------------------
    Function Get-Log
    {
    # Reads the log file into memory.  Will display an error if the
    # file cannot be found.
 
        Try
        {
            Get-Content -path "\\Server1\Data.txt" `
                        -ErrorAction Stop |
            Write-Output
        }
        Catch
        {
            Write-Error "The data file is not present"
            BREAK
        }
    } # End: Function Get-Log
    # -----------------------------------------------------------------------
    Function Get-Record
    {
    Param ($Log)
    # Determines what is a record.  This eliminates any header information.
    # For this to work properly, you need to utilize some type of rule
    # to determine what is a new record.  In this case, a pattern
    # is being utilized that we will send to a regular expression.
    # The pattern is:
    # -- ::
    # Example - 2013-05-30 16:06:40
    #
    # See About_Regular_Expressions for more information on how to use
    # Regular Expressions in the built in PowerShell help files.
 
 
        # Array to hold all of the objects.
        $Object = @()
 
        # Cycle through each line of the log and look for a line that
        # matches the pattern that we are using to denote a new record.
        ForEach ($L in $Log)
        {
            If ($L -match "\d*-\d*-\d* \d*:\d*:\d*")
            {
                $Obj = New-Object -TypeName PSObject
 
                $Splat = @{NotePropertyName = "Record";
                          NotePropertyValue = "$($L)"}
                $Obj | Add-Member @Splat
                $Object += $Obj 
            }
        } # End: ForEach ($L in $Log)
       
        # Send the records to the calling statement.
        Write-Output $Object
 
    } # End: Function Get-Record
    # -----------------------------------------------------------------------
    Function Get-PropertyList
    {
    Param ($Records)
    # To help build a more flexible object, we need to identify possible
    # property names.  This function will cycle through each record and use
    # a rule to identify the name of a property.  Our rule is any string
    # after the "]" in the time stamp and before the next ":". And
    # object containing the property names is returned.
 
        # Array to hold all of the objects.
        $Object = @()
 
        <# Cycle through each record to get a list of properties. This is
           done to discover all the potential properties.
        #>
       
        ForEach ($R in $Records.record)
        {
            $Obj = New-Object -TypeName PSObject
            # Find the end of the Date time stamp and remove it along
            # with the leading space.
            $Start = $R.IndexOf("]")
            $Value = $R.Substring($Start+2)
 
            # Find the next colon which will mark the end of the
            # property.  Remove this colon and the rest of the string.
            $End = $Value.IndexOf(":")
            $value = $Value.Substring(0,$End)
           
            # Add the property to the list.
            $Splat = @{NotePropertyName = "Prop";
                        NotePropertyValue = "$($Value)"}
            $Obj | Add-Member @Splat
            $Object += $Obj              
        } # End: ForEach ($R in $Records.record)
 
        # Make sure that each property is unique.
 
        $ObjectList = $Object | Select-Object -Property Prop -Unique
 
        # Send the records to the calling statement.
        Write-Output $ObjectList
    } #End: Function Get-ADResourcePropertyList
 
    # -----------------------------------------------------------------------
    Function Get-Hash
    {
    Param ($Name)
    # This brief function is used to used to reduce the amount of code
    # while building a new empty object.
 
        $Hash = @{MemberType = "NoteProperty";
                    Name = $Name;
                    Value = $Null}
 
        Write-Output $Hash
           
    } # End: Function Get-Hash
 
    # -----------------------------------------------------------------------
    Function Create-EmptyObject
    {
    Param ($Prop)
    # Here the list of discovered properties is sent so that
    # a blank object can be created containing all of the potential
    # properties of the object.  this will allow for a consistent
    # set of members for the final object.
       
        # Create a blank object.           
        $Obj = New-Object -TypeName PSObject
           
        # Change the type name.
        $Obj.pstypenames.insert(0,'Custom.LogObject')
 
        # This will create a new property for each of the derived
        # properties.
        ForEach ($P in $Prop.prop)
        {
            $Hash = Get-Hash -Name $P 
            $Obj | Add-Member @Hash
        }
  
        # Add the TimeWritted Property.
        $Hash = Get-Hash -Name "TimeWritten" 
        $Obj | Add-Member @Hash
       
        # Return empty object to calling statement.
        Write-Output $Obj           
 
    } # End: Function Create-EmptyObject
 
    # -----------------------------------------------------------------------
 
    Function Import-LogData
    {
    Param ($Records, $ObjProps)
    # This is where we put it all together. Each record is cycled through.
    # If the record contains a property that we derived earlier, a new object
    # is created containing the time stamp and the data for that property.
 
        ForEach ($R in $Records.Record)
        {
            $Obj = Create-EmptyObject -Prop $ObjProps
            ForEach ($O in $ObjProps.Prop)
            {
                If ($R -like "*$($O)*")
                {
                    # Add the data time stamp
                    $End = $R.IndexOf("]")
                    $Obj.TimeWritten = $R.Substring(1,$End-1)
               
                    # Add the property that caused the match.
                    $Obj.$O = $R.Remove(0,22).Replace("$($O):","").Trim()
                   
                }
            }
            Write-Output $Obj
   
        } # End: ForEach ($R in $Records)
    } # End: Function Import-LogData
    # -----------------------------------------------------------------------
 
    # Load the log into memory
    $Log = Get-Log
 
    # Extract the records
    $Records = Get-Record -Log $Log
 
    # Get a list of all properties.
    $ObjProps = Get-PropertyList -Records $Records
 
    # Split the record into an PowerShell Object
    Import-LogData -Records $Records -ObjProps $ObjProps
 
} #End: Function Import-MyLog1
 
Here is our output:
Info                         Warning                      Critical                     TimeWritten                 
----                         -------                      --------                     -----------                
InstallHelper Start                                                                    2013-05-30 16:06:40        
ST_Drive::IsInternalDrive...                                                           2013-05-30 16:06:40        
IsTablePC return FALSE                                                                 2013-05-30 16:06:40        
InstallHelper End                                                                      2013-05-30 16:06:40        
                             InstallHelper Experienced...                              2013-05-30 16:10:40        
                                                          Help files are not availa... 2013-05-30 16:10:40    
 
Here is the member information:
    TypeName: Custom.LogObject
 
Name        MemberType   Definition                                  
----        ----------   ----------                                  
Equals      Method       bool Equals(System.Object obj)              
GetHashCode Method       int GetHashCode()                           
GetType     Method       type GetType()                              
ToString    Method       string ToString()                            
Critical    NoteProperty  Critical=null                              
Info        NoteProperty System.String Info=InstallHelper Start      
TimeWritten NoteProperty System.String TimeWritten=2013-05-30 16:06:40
Warning     NoteProperty  Warning=null                               
 
 
This code will have to be customized for each text based log that you want to use it with.  The general steps are all the same. 
Tomorrow we will begin this process all over again.  This time however, we will work with a log file that has records spread across multiple lines in the text file with property values that are both common, and unique to each record.
 

Comments

Popular posts from this blog

Adding a Comment to a GPO with PowerShell

As I'm writing this article, I'm also writing a customization for a PowerShell course I'm teaching next week in Phoenix.  This customization deals with Group Policy and PowerShell.  For those of you who attend my classes may already know this, but I sit their and try to ask the questions to myself that others may ask as I present the material.  I finished up my customization a few hours ago and then I realized that I did not add in how to put a comment on a GPO.  This is a feature that many Group Policy Administrators may not be aware of. This past summer I attended a presentation at TechEd on Group Policy.  One organization in the crowd had over 5,000 Group Policies.  In an environment like that, the comment section can be priceless.  I always like to write in the comment section why I created the policy so I know its purpose next week after I've completed 50 other tasks and can't remember what I did 5 minutes ago. In the Group Policy module for PowerShell V3, th

Return duplicate values from a collection with PowerShell

If you have a collection of objects and you want to remove any duplicate items, it is fairly simple. # Create a collection with duplicate values $Set1 = 1 , 1 , 2 , 2 , 3 , 4 , 5 , 6 , 7 , 1 , 2   # Remove the duplicate values. $Set1 | Select-Object -Unique 1 2 3 4 5 6 7 What if you want only the duplicate values and nothing else? # Create a collection with duplicate values $Set1 = 1 , 1 , 2 , 2 , 3 , 4 , 5 , 6 , 7 , 1 , 2   #Create a second collection with duplicate values removed. $Set2 = $Set1 | Select-Object -Unique   # Return only the duplicate values. ( Compare-Object -ReferenceObject $Set2 -DifferenceObject $Set1 ) . InputObject | Select-Object – Unique 1 2 This works with objects as well as numbers.  The first command creates a collection with 2 duplicates of both 1 and 2.   The second command creates another collection with the duplicates filtered out.  The Compare-Object cmdlet will first find items that are diffe

How to list all the AD LDS instances on a server

AD LDS allows you to provide directory services to applications that are free of the confines of Active Directory.  To list all the AD LDS instances on a server, follow this procedure: Log into the server in question Open a command prompt. Type dsdbutil and press Enter Type List Instances and press Enter . You will receive a list of the instance name, both the LDAP and SSL port numbers, the location of the database, and its status.