In today's world, the amount of log data generated by any application is
so much that it is not possible to open the said log files in a text
editor of choice. The modern logging framework like log4net make it
possible for the log files to be split based on size, so that, one can atleast
open them in a text editor. But the problem with this is, there is most often,
a case of explosion of number of log files and pretty soon we will have GBs
worth of log files all over the place. The whole logging issue becomes a mess
once we start to have a distributed system. Looking through multiple log
files from different sources and making sense of them is a pain.
Recently, I faced exactly the same situation. I found myself sifting
through GBs of log data generated by a system which had been running for
many days.
In a multi threaded environment, logs are written to a log file by
multiple threads and sequence of operations from a single thread will not
be available in consecutive lines. If the logging is extensive, then the
sequence of operations that we are interested in, might also be a subset
of the entire log file. Sifting through the log file for this is very time
consuming if done manually. Especially, if the text editor used is a very
basic one, with no advanced features to do marking, highlighting and
searching.
I use VIM, so I get by very well, for sifting through huge log files.
Nevertheless, I found myself doing this activity so often, that I had to think
of making a small tool for this.
The basic set of needs that I had were :
- Should be able to look into multiple files
- should be able to look into large files
- should be able to filter out only those lines that I am interested in based
on REGEX pattern matching.
- should produce a consolidated result file.
So I came up with this small utility class to do this for me :
The way to use this class is as follows :
As you can see above, it is very convenient to use this. No matter how
many files I have, it will filter out the unwanted lines and generate
a single file from it. Of course, if the filter is letting through a lot
of lines then the number of lines in the generated file is going to be
large. This means the generated file will also be large. We can split the
writing to the output file and make multiple files and this is pretty easy
to do. In the above example I have used the REGEX for filtering the GUID.
I am a big fan of REGEX, particularly for the searching of log files.
A big use of this log filtering utility is that I can take output of the
tool and feed it to a graphing software to generate graphs. All logs have
a timestamp as their first column. So if we can filter logs indicating
some type of start and end of actions, then the generated log file will
look like:
This can used by the graphing software to generate graphs of when
"task A" started and ended and how long "task A" took.
This class is not perfect, but it does the job for me. I see many improvements
that can be done to this utility class :
- Splitting of the output file into many files if the size of the output
file is large.
- Speeding up the whole process by using multiple
threads.
- Reordering the lines of the generated log file based on
further pattern matching.
I have ideas with respect to 3rd point above, I shall write about it soon..
I only wrote this small utility to scratch my own itch. Of course
I recommend a better logging strategy,like logstash.net for highly
distributed systems . The utility here is only for making offline processing of
log files and generate meaning from them. For example, go through 4 days
of worker logs and make a graph of the timings of actions.
so much that it is not possible to open the said log files in a text
editor of choice. The modern logging framework like log4net make it
possible for the log files to be split based on size, so that, one can atleast
open them in a text editor. But the problem with this is, there is most often,
a case of explosion of number of log files and pretty soon we will have GBs
worth of log files all over the place. The whole logging issue becomes a mess
once we start to have a distributed system. Looking through multiple log
files from different sources and making sense of them is a pain.
Recently, I faced exactly the same situation. I found myself sifting
through GBs of log data generated by a system which had been running for
many days.
In a multi threaded environment, logs are written to a log file by
multiple threads and sequence of operations from a single thread will not
be available in consecutive lines. If the logging is extensive, then the
sequence of operations that we are interested in, might also be a subset
of the entire log file. Sifting through the log file for this is very time
consuming if done manually. Especially, if the text editor used is a very
basic one, with no advanced features to do marking, highlighting and
searching.
I use VIM, so I get by very well, for sifting through huge log files.
Nevertheless, I found myself doing this activity so often, that I had to think
of making a small tool for this.
The basic set of needs that I had were :
- Should be able to look into multiple files
- should be able to look into large files
- should be able to filter out only those lines that I am interested in based
on REGEX pattern matching.
- should produce a consolidated result file.
So I came up with this small utility class to do this for me :
Code
- public class LineFilter : ILineFilter
- {
- private readonly string[] fileNames;
- private readonly List<string> searchFilter = new List<string>();
- private LineFilter(params string[] fileNames)
- {
- this.fileNames = fileNames;
- }
- public ILineFilter WithFilter(string filter)
- {
- searchFilter.Add(filter);
- return this;
- }
- public void WriteResults(string outFileName)
- {
- var regex = string.Join("|", searchFilter);
- var g = new Regex(regex, RegexOptions.IgnoreCase);
- var linesToWrite = new List<string>();
- foreach (var f in fileNames)
- {
- Console.WriteLine(f);
- var lines = File.ReadAllLines(f);
- foreach (var line in lines)
- {
- var m = g.Match(line);
- if (m.Success)
- {
- linesToWrite.Add(line);
- }
- }
- }
- File.WriteAllLines(outFileName, linesToWrite);
- }
- public static ILineFilter CreateLineFilter(params string[] fileNames)
- {
- return new LineFilter(fileNames);
- }
- }
The way to use this class is as follows :
Code
- LineFilter.CreateLineFilter(
- @"d:\MyLogFile-Debug-2015-04-29.11.txt",
- @"d:\MyLogFile-Debug-2015-04-29.10.txt",
- @"d:\MyLogFile-Debug-2015-04-29.9.txt",
- @"d:\MyLogFile-Debug-2015-04-29.8.txt",
- @"d:\MyLogFile-Debug-2015-04-29.7.txt",
- @"d:\MyLogFile-Debug-2015-04-29.6.txt",
- @"d:\MyLogFile-Debug-2015-04-29.5.txt",
- @"d:\MyLogFile-Debug-2015-04-29.4.txt",
- @"d:\MyLogFile-Debug-2015-04-29.3.txt",
- @"d:\MyLogFile-Debug-2015-04-29.2.txt",
- @"d:\MyLogFile-Debug-2015-04-29.1.txt",
- @"d:\MyLogFile-Debug-2015-04-29.txt")
- .WithFilter(@"starting task A [A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}")
- .WithFilter(@"total DB connections are")
- .WithFilter(@"task A [A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12} actions")
- .WithFilter(@"task A [A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12} ends")
- .WriteResults(FileName);
As you can see above, it is very convenient to use this. No matter how
many files I have, it will filter out the unwanted lines and generate
a single file from it. Of course, if the filter is letting through a lot
of lines then the number of lines in the generated file is going to be
large. This means the generated file will also be large. We can split the
writing to the output file and make multiple files and this is pretty easy
to do. In the above example I have used the REGEX for filtering the GUID.
I am a big fan of REGEX, particularly for the searching of log files.
A big use of this log filtering utility is that I can take output of the
tool and feed it to a graphing software to generate graphs. All logs have
a timestamp as their first column. So if we can filter logs indicating
some type of start and end of actions, then the generated log file will
look like:
Log
- 2015-04-29 13:54:12,123 [Thread 1] Starting Task A 9e8fa525-5eeb-4653-b9f6-a489017eada8:
- 2015-04-29 13:54:42,123 [Thread 1] Task A 9e8fa525-5eeb-4653-b9f6-a489017eada8 ends
This can used by the graphing software to generate graphs of when
"task A" started and ended and how long "task A" took.
This class is not perfect, but it does the job for me. I see many improvements
that can be done to this utility class :
- Splitting of the output file into many files if the size of the output
file is large.
- Speeding up the whole process by using multiple
threads.
- Reordering the lines of the generated log file based on
further pattern matching.
I have ideas with respect to 3rd point above, I shall write about it soon..
I only wrote this small utility to scratch my own itch. Of course
I recommend a better logging strategy,like logstash.net for highly
distributed systems . The utility here is only for making offline processing of
log files and generate meaning from them. For example, go through 4 days
of worker logs and make a graph of the timings of actions.
