I have several logs containing lines all starting with a timestamp, so that the following works as expected to merge them:
cat myLog1.txt myLog2.txt | sort -n > combined.txt
Problem is, that myLog2.txt can also contain lines without a timestamp (e.g. java stack traces). Is there an easy way without any custom scripts to still merge them and preserve the multiline content?
Example myLog1.txt
11:48:18.825 [main] INFO org.hibernate.cfg.Environment - HHH000206: hibernate.properties not found
11:48:55.784 [main] INFO o.h.tool.hbm2ddl.SchemaUpdate - HHH000396: Updating schema
Example myLog2.txt
11:48:35.377 [qtp1484319352-19] ERROR c.w.b.c.ControllerErrorHandler -
org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'org.joda.time.LocalDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.web.bind.annotation.RequestParam @org.springframework.format.annotation.DateTimeFormat org.joda.time.LocalDate for value '[2013-03-26]'; nested exception is java.lang.IllegalArgumentException: Invalid format: " [2013-03-26]"
at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:68) ~[spring-beans-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45) ~[spring-beans-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:595) ~[spring-context-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:98) ~[spring-web-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77) ~[spring-web-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162) ~[spring-web-3.2.1.RELEAS
Expected output
11:48:18.825 [main] INFO org.hibernate.cfg.Environment - HHH000206: hibernate.properties not found
11:48:35.377 [qtp1484319352-19] ERROR c.w.b.c.ControllerErrorHandler -
org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'org.joda.time.LocalDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.web.bind.annotation.RequestParam @org.springframework.format.annotation.DateTimeFormat org.joda.time.LocalDate for value '[2013-03-26]'; nested exception is java.lang.IllegalArgumentException: Invalid format: " [2013-03-26]"
at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:68) ~[spring-beans-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45) ~[spring-beans-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:595) ~[spring-context-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:98) ~[spring-web-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77) ~[spring-web-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162) ~[spring-web-3.2.1.RELEAS
11:48:55.784 [main] INFO o.h.tool.hbm2ddl.SchemaUpdate - HHH000396: Updating schema
Thanks Marco
Here's one way to do it in a bash shell with simple merging of the files (rather than expensive resorting - as log files are already sorted). This is important for huge files in the hundreds of megabytes or more, as often is the case with real world log files.
This solution assumes that there are no NUL bytes in your logs, which is true for every log file that I've come across, with various character sets.
The basic idea:
sorn -m
on the replaced files to merge themAs the first step is done multiple times, I've given it an alias:
Here's the command that performs all 3 steps:
For more, see https://superuser.com/a/838446/125379
I was struggling with the same issue and finally I think I've got it. Try do it like:
sort -nbms -k1.1,1.2 -k1.4,1.5 -k1.7,1.8 -k1.10,1.12 myLog1.txt myLog2.txt > combined.txt
It's still not fully clear to myself, I'll try to give some explanation though. According to the man pages used switches mean:
-m
. It's crucial to keep stacktraces from getting scrambled.-b
is not necessary in this case as somehow-n
and-m
combined keeps stacktrace lines from getting clustered. I left it just in case as most of stacktrace lines starts with blanks.-n
apparently stops comparing key whenever there is a non-numeric character in the key. That's the second crucial bit for keeping stacktraces in place. Important is if it was-n -k1,1
it would only sort the log files by hour as colon is non-numeric. Apart from that-n
speeds up numeric comparison so we would like to have it anyway.-k1.1,1.2
(first and second digit of hour)-k1.4,1.5
(first and second digit of minutes) and so on. The first digit before the dot is always '1' as it points to the first column of the file line (which in our case is time). Shortly it's-kA,B
whereA
andB
are column positions in a given line (by default lines are delimited by blanks). Format of A and B used is .. Keep in mind that whenever there is a non-numeric character betweenA
andB
everything after it will be ignored in comparison if-n
used.-s
disables default behaviour which is: whenever keys by which comparison is being done are the same full string comparison of the lines is done. We don't want that to preserve original log entries order. Not sure if it's necessary with-m
though.Nope - can't be done with a simple command IMMHO.
But - here's a script to do it (it was a challenge...)
Copy the first log with all-timestamped entries to a tempfile
Process the second file by
Tempfile thus is
Sorting the result and reprocessing
A line with a non-space in the 13th character is an untimestamped line from the second file, so
Done!
You should use the
merge
,stable
,ignore-leading-blanks
,numeric-sort
, and an easily sortable datetime format (such asyyyyMMddHHmmssSSS
) in your log files.So, I changed your log format to be more easily sortable, resulting in
sort -bsnm log1 log2
:As said in @Magoo's answer, the way your logs' datetime is currently formatted is hard to sort.