I have a log file whose lines start like this...
2016-01-06 16:06:52,778 [1]DEBUG ...blah blah blah
I want to parse this file and add the elapsed time from the previous log line onto the start of the next. For example, if I have lines like this...
2016-01-06 16:06:52,000 [1]DEBUG ...
2016-01-06 16:06:52,100 [1]DEBUG ...
2016-01-06 16:06:52,030 [1]DEBUG ...
...then I would like to produce the following...
0 2016-01-06 16:06:52,000 [1]DEBUG ...
100 2016-01-06 16:06:52,100 [1]DEBUG ...
30 2016-01-06 16:06:52,130 [1]DEBUG ...
...where the first number on each line is the number of milliseconds since the previous line.
Now in C#, I would use a (mutable) variable to hold the previous time, then subtract the new time from that, giving me the lapse. However, I think mutables are something you're supposed to avoid in F#, so was wondering how it should be done.
So far, I have a function that reads the file into a sequence, and the following function to extract the time from a single line (I'm not worried about the date in this case, as all timings will be on the same day)...
let parseDate (s:string) =
let time = ((s.Split [|' '|]) |> Seq.nth 1).Replace(",", ".")
DateTime.Parse(time)
I can do the following, which adds the total number of milliseconds since the first entry...
let start = logLines |> Seq.head |> parseDate
let linesWithTimes = logLines |> Seq.map (fun l ->
(((parseDate l) - start).TotalMilliseconds).ToString() + " "+ l
)
How do I add the elapsed time since the previous log entry?
Hope that was clear. I'm new to F#, so if there's a better way to do it, please let me know.
There are many ways, here's a solution using Seq.pairwise
:
let linesWithTimes =
logLines
|> Seq.map (fun x -> parseDate x, x)
|> Seq.pairwise
|> Seq.map (fun ((dt1, x1), (dt2, x2)) -> string (dt2 - dt1).TotalMilliseconds + " " + x2)
|> Seq.append (seq ["0 " + Seq.head logLines])
Another way to do a mapping but combining values between the elements is to use a fold:
let linesWithTimes =
logLines
|> Seq.fold (fun s t ->
let dt = parseDate t
match s with
| None , _ -> Some dt, ["0 " + t]
| Some d, lst -> Some dt, lst @ [string (dt - d).TotalMilliseconds + " " + t]
) (None , [])
|> snd
|> List.toSeq
And here's a solution using sequence expressions, it's longer and it uses a mutable but it might be more appropriate in scenarios when the enumerable is very long and slow to traverse:
let linesWithTimes =
let mutable prev = None : DateTime option
seq {
for e in logLines do
let dt = parseDate e
let diff =
match prev with
| None -> 0.
| Some t -> (dt - t).TotalMilliseconds
yield string diff + " " + e
prev <- Some dt
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With