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

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.

How to run GPResult on a remote client with PowerShell

In the past, to run the GPResult command, you would need to either physically visit this client, have the user do it, or use and RDP connection.  In all cases, this will disrupt the user.  First, you need PowerShell remoting enabled on the target machine.  You can do this via Group Policy . Open PowerShell and type this command. Invoke-Command –ScriptBlock {GPResult /r} –ComputerName <ComputerName> Replace <ComputerName> with the name of the target.  Remember, the target needs to be online and accessible to you.

Error icon when creating a GPO Preference drive map

You may not have an error at all.  Take a look at the drive mapping below. The red triangle is what threw us off.  It is not an error.  It is simply a color representation of the Replace option of the Action field in the properties of the drive mappings. Create action This give you a green triangle. The Create action creates a new mapped drive for users. Replace Action The Replace action gives you a red triangle.  This action will delete and recreate mapped drives for users. The net result of the Replace action is to overwrite all existing settings associated with the mapped drive. If the drive mapping does not exist, then the Replace action creates a new drive mapping. Update Action The Update action will have a yellow triangle. Update will modify settings of an existing mapped drive for users. This action differs from Replace in that it only updates settings defined within the preference item. All other settings remain as configured on the mapped drive. If the