More complicated log parsing

Hey Guys,

I am trying to grep within a file to find and output certain parts of lines to other file(s). The output files need to have a dynamic file name based on a field in the main log.

The problem is that every line of the log is not the same, and often not even similar.

To explain further, the lines in the log look like:

2007-06-05 14:03:48,337 INFO  External- PXgcGllGX1TMdFCXrKyc8GQTwvLlfQ6B9wYQLyGXTQpKX5yxW8FC!-1784053810!1181066628296|>> [HandleRequest] QService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:03:51,236 INFO  External- PXgcGllGX1TMdFCXrKyc8GQTwvLlfQ6B9wYQLyGXTQpKX5yxW8FC!-1784053810!1181066628296|<< [HandleResponse] QService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2007-06-05 14:03:56,900 INFO  External- |||>> [HandleRequest] QService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:03:58,492 INFO  External- |||<< [HandleResponse] QService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2007-06-05 14:11:09,570 INFO  External- |02-20070605-510669||>> [HandleRequest] LService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:11:12,752 INFO  External- |02-20070605-510669||<< [HandleResponse] LService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2007-06-05 14:11:22,997 INFO  External- |02-20070605-510669||>> [HandleRequest] AService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:11:38,191 INFO  External- |02-20070605-510669||<< [HandleResponse] AService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

What I want to do is ignore everything before the first pipe and take the '02-YYYYMMDD-XXXXXX' format between the pipes as the $FILENAME, then append everything after the XService (ie. <?xml to end of line) to the new file.

I would appreciate any suggestions, thanks in advance.

try this, not tested though

while read line
do
  filename=`echo "$line" | sed 's/^.* |\(.*\)||\(.*\)/\1/'`
  echo "filename is $filename"
  echo "$line" | sed 's/^.*<?\(.*\)/<?\1>/' > $filename
done < source_file

Thanks for the amazingly quick response.

As you said, untested, but so far good start.
Two main issues
First, it doesn't quite work. I neglected to mention that I only want to get what is after the HandleRequest for AService ([HandleRequest] AService).
It does make files in the current state, but selectively, and not the correct handle/service.

Second, is the millions of other files that are created named after random tags that are on their own lines. All the other garbage files all start with "<", what would the if statement in the brackets look like (if [ $filename != <* ] ?).

Try this awk program :

#!/usr/bin/awk -f
# Awk script: extract.awk

BEGIN {
   FS = "|";
}
$2 ~ /^02-[0-9]+-[0-9]+$/ {
   if (file && file != $2) close(file);
   file = $2;
   sub(/^.*.Service/, "", $0);
   print $0 >> file;
}

Output with your datas:

$ ls 02-*
/bin/ls: cannot access 02-*: No such file or directory
$ awk -f extract.awk logfile
$ ls 02-*
02-20070605-510669
$ cat 02-*
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
$ 

Jean-Pierre.

Thanks for your response Jean-Pierre,

The script does work with my sample file but not every line has the same format. For example these lines are thrown in between the previous ones I posted.

So for my actual logs there is no output.

2007-06-06 11:05:32,863 INFO  External- 4164445555|01-20070606-280684||WService dueDate call with requestXML=
2007-06-06 11:05:32,863 INFO  External- 4164445555|01-20070606-280684||<?xml version="1.0" encoding="ISO-8859-1"?>
    <requestHeader>
        <sourceSystemTimestamp>
        <requestType>
            <miscServices>dueDate</miscServices>
        </requestType>
        <asyncIndr>no</asyncIndr>
        <responseReqtList>
            <responseReqt>
                <responseType>confirmation</responseType>
                <responseMode>http</responseMode>
                <responseAddress>
                <responseLanguage>E</responseLanguage>
            </responseReqt>
            <totalResponseReqts>1</totalResponseReqts>
        </responseReqtList>
        <sourceRequestIdList>
            <sourceRequestId>01-20070606-280684</sourceRequestId>
            <totalSourceRequestIds>1</totalSourceRequestIds>
        </sourceRequestIdList>
    </requestHeader>
    <telephoneNumber>
    <serviceAddressRequest>
    <dueDateRequestList>
        <dueDateRequestListItem>
            <dueDateRequestMode>query</dueDateRequestMode>
        </dueDateRequestListItem>
        <totalDueDateRequestListItems>1</totalDueDateRequestListItems>
    </dueDateRequestList>
</dueDateRequest>

It may be better to do an intial sweep to duplicate the log, then sweep through and remove them, then process with your initial script.

Also, I do not need all entries for a specific 02-YYYYMMDD-XXXXXX, only the ones that have '[HandleRequest] AService'.

perhaps :

#!/usr/bin/awk -f
# Awk script: extract.awk

BEGIN {
   FS = "|";
}
$2 ~ /^02-[0-9]+-[0-9]+$/ && /[HandleRequest] AService/ {
   if (file && file != $2) close(file);
   file = $2;
   sub(/^.*.Service/, "", $0);
   print $0 >> file;
}

Input file:

2007-06-05 14:03:48,337 INFO  External- PXgcGllGX1TMdFCXrKyc8GQTwvLlfQ6B9wYQLyGXTQpKX5yxW8FC!-1784053810!1181066628296|>> [HandleRequest] QService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:03:51,236 INFO  External- PXgcGllGX1TMdFCXrKyc8GQTwvLlfQ6B9wYQLyGXTQpKX5yxW8FC!-1784053810!1181066628296|<< [HandleResponse] QService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2007-06-05 14:03:56,900 INFO  External- |||>> [HandleRequest] QService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:03:58,492 INFO  External- |||<< [HandleResponse] QService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2007-06-05 14:11:09,570 INFO  External- |02-20070605-510669||>> [HandleRequest] LService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:11:12,752 INFO  External- |02-20070605-510669||<< [HandleResponse] LService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2007-06-05 14:11:22,997 INFO  External- |02-20070605-510669||>> [HandleRequest] AService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:11:38,191 INFO  External- |02-20070605-510669||<< [HandleResponse] AService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2007-06-06 11:05:32,863 INFO  External- 4164445555|01-20070606-280684||WService dueDate call with requestXML=
2007-06-06 11:05:32,863 INFO  External- 4164445555|01-20070606-280684||<?xml version="1.0" encoding="ISO-8859-1"?>
    <requestHeader>
        <sourceSystemTimestamp>
        <requestType>
            <miscServices>dueDate</miscServices>
        </requestType>
        <asyncIndr>no</asyncIndr>
        <responseReqtList>
            <responseReqt>
                <responseType>confirmation</responseType>
                <responseMode>http</responseMode>
                <responseAddress>
                <responseLanguage>E</responseLanguage>
            </responseReqt>
            <totalResponseReqts>1</totalResponseReqts>
        </responseReqtList>
        <sourceRequestIdList>
            <sourceRequestId>01-20070606-280684</sourceRequestId>
            <totalSourceRequestIds>1</totalSourceRequestIds>
        </sourceRequestIdList>
    </requestHeader>
    <telephoneNumber>
    <serviceAddressRequest>
    <dueDateRequestList>
        <dueDateRequestListItem>
            <dueDateRequestMode>query</dueDateRequestMode>
        </dueDateRequestListItem>
        <totalDueDateRequestListItems>1</totalDueDateRequestListItems>
    </dueDateRequestList>
</dueDateRequest>

Output file 02-20070605-510669:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml version="1.0" encoding="utf-8" standalone="yes"?>

Jean-Pierre.

Continued thanks for all your support Jean-Pierre,

but I still get no output at all from the latest input file (as pasted by yourself), and the latest extract script

awk '{  
         if ( b= match($0,"xml")) {
           xml = substr($0,b-2)
         }
         else {
          next
         }
         n=split($0,line," ")
         m=split(line[5],file,"|")         
         if ( file[2] ~ /^[0-9]/) {
             filename=file[2]
             print xml > filename
         }     
       
      }

' "file"

Thanks ghostdog,

It is interesting to see the different methods everyone has to tackle the same parse.

Ghostdog: I tried to add a bit to the if statement to only output lines that had '[HandleRequest] AService' in them (so that I would have only one xml, per 01/02-YYYYMMDD-XXXXXX), but I could not get it to work successfully.
Could you please post up this addition?

Jean-Pierre: Your awk script is very short and seems efficient, but with the addition of '&& /[HandleRequest] AService/', I cannot get any output. I have tried on many different servers, and while your original script creates the outputs, for some reason no matter what I try with the second one, I cannot get any output files. Do you have any suggestions? It is strange that it works for you but not for me at all. Is there an awk log or hidden verbosity I could enable to trace the activity? Also, how are you getting both of the AService lines output, one is a request and one is a reply, so one should definitely not pass your filters(?).

awk '/HandleRequest.*AService/{  
         if ( b= match($0,"xml") ) {
           xml = substr($0,b-2)
           print $0
         }
         else {
          next
         }
         n=split($0,line," ")
         m=split(line[5],file,"|")         
         if ( file[2] ~ /^[0-9]/) {
             filename=file[2]
             print xml > filename
         }     
      }
' "file"
#!/usr/bin/awk -f
# Awk script: extract.awk

BEGIN {
   FS = "|";
}
$2 ~ /^02-[0-9]+-[0-9]+$/ && /\[HandleRequest\] AService/ {
   if (file && file != $2) close(file);
   file = $2;
   sub(/^.*.Service/, "", $0);
   print $0 >> file;
}

Jean-Pierre.

A small modification as,

echo "$line" | sed 's/^.*<?\(.*\)/<?\1/' >> $filename
>cat input
2007-06-05 14:11:09,570 INFO  External- |02-20070605-510669||>> [HandleRequest] LService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:11:12,752 INFO  External- |02-20070605-510669||<< [HandleResponse] LService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2007-06-05 14:11:22,997 INFO  External- |02-20070605-510669||>> [HandleRequest] AService<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2007-06-05 14:11:38,191 INFO  External- |02-20070605-510669||<< [HandleResponse] AService<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
>running the script
filename is 02-20070605-510669
filename is 02-20070605-510669
filename is 02-20070605-510669
filename is 02-20070605-510669
>cat 02-20070605-510669
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

Thanks for all of your help ghostdog, Jean-Pierre, and matrixmadhan!

ghostdog: I have adjusted your second edit, and made it into a script, and it works with fantastic results on my very large log files.
How would I encorporate an if ( ! -f filename), as well as directory filing by date 01-YYYYMMDD-XXXXX into the corresponding YYYYMMDD directory) into the awk script?

Jean-Pierre: With your latest edit it works very well as well on the most complicated sample log I have provided, however my actual logs have more garbage in them that causes your script not to work. I have not been able to isolate what causes it to stop working. Also, when running with full very long xml code after the service search, the code is strangely truncated. I would like to send you some additional logs if you would not mind continuing to help me.

matrixmadhan: Your latest edit does work with my initial simple log, but also does stop functioning when it encounters more difficult log files, such as the one I posted in response to Jean-Pierre's early script. Additionally I only need to see the Request from Aservice.

ermm sorry i don't understand.care to elaborate more?

Sorry, it seemed clearer to me.

Since this script will be run frequently on the same log file(s), I would like to:

-Add a check to make sure the script does not overwrite existing xml files (a check to see if the file exists, before it is written). I know how to do this as a shell script, but I am not sure how to do it in the awk script.

-While writing the new xml file (whose filename follow the format of 01-YYYYMMDD-XXXXXX - as read from the original log), move the xml into an appropriate directory (that can share the same YYYYMMDD format from the xml filename). If the filename does not follow the format (as above), then put it in an arbitrary saftey directory (any name).

its not clear why you want to do it in an awk script, but anyway, you can use getline. something like this snippet...

awk 'BEGIN { 
       if ((getline < "ljalsdfls") == -1) {
            print "not exists"
        } 
 } '

just an example only. apply the concepts to your code as needed.

awk 'BEGIN { 
      string="01-YYYYMMDD-XXXXXX"
      n=split(string,array,"-")
      print array[2] #this contains your format.
      cmd="mkdir -p " array[2] 
      print cmd
      cmd | getline result #execute mkdir
      close(cmd)
      print "your xml lines " > array[2]"/"string
 } '

Thanks alot for all your help ghostdog! :slight_smile:

My combined script looks like:

#!/usr/bin/awk -f
# Awk script: extract3.awk

/HandleRequest.*OrderControlService/{  
    if ( b= match($0,"xml") ) {
      xml = substr($0,b-2)
      print $0
    }
    else {
      next
    }
         
    n=split($0,line," ")
    m=split(line[5],file,"|")     
      
    if ( file[2] ~ /^[0-9]/) {
        filename=file[2]

       if ((getline < filename) == -1) {
          n=split(filename,array,"-")
          print array[2]
          cmd="mkdir -p " array[2] 
          print cmd
          cmd | getline result
          close(cmd)
          print xml > array[2]"/"filename".xml"
       }
       else {
       print filename " - File Exists"
       }
        
    }     
}

Without the directory creation it works fine, however with this full version, when I run it I get:

It works on some shells, and gives me a mkdir missing operand error, but on the log server it just gives me the above error and does nothing.
What is wrong?

what is your OS platform, and awk version? what shell are you using? any such information will be good

running
AIX 5.3, bash,
and I don't know what version of awk (there is no version flag?)

I fixed the original error I posted above, as my version of awk seems to have a problem with spaces in the script, so that is resolved for now.

But I have one more issue, which will hopefully be the last.

The way in which directories are created is by splitting the 01-YYYYMMDD-XXXXX by '-' then putting each piece in an array then creating a directory based on the second piece.

However there are some entries between the pipes that do not follow the format and have no '-' in them to give them a second piece.

For example there are some lines that look like:

2007-06-06 16:52:38,805 INFO  External- |3676482||>> [HandleRequest] AService<?xml version="1.0" encoding="utf-8" standalone="yes"?>

I am not sure what would be easier, simply to put them in the same folder as the previous one that was created (rolling over the folder filename to the next loop), or to just put all all these seven digit standalone numbers that do not follow the array format into a fixed existing directory (by checking the size of array[2] before mkdir?).

it depends on what you are comfortable with. and you are on the right track. one way is to check the array[2], if empty then put to another defined directory..