按日期合并多个日志文件,包括多行

Posted

技术标签:

【中文标题】按日期合并多个日志文件,包括多行【英文标题】:Merging multiple log files by date including multilines 【发布时间】:2013-03-29 19:22:25 【问题描述】:

我有几条日志,其中包含所有以时间戳开头的行,因此以下内容可以按预期合并它们:

cat myLog1.txt myLog2.txt | sort -n > combined.txt

问题是,myLog2.txt 也可以包含没有时间戳的行(例如 java 堆栈跟踪)。是否有一种无需任何自定义脚本的简单方法仍然可以合并它们并保留多行内容?

示例 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

示例 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

预期输出

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

谢谢 马可

【问题讨论】:

感谢@marco 的示例。帮助我自己解决了同样的问题。 【参考方案1】:

不——不能用简单的命令 IMMHO 来完成。

但是 - 这是一个脚本来做这件事(这是一个挑战......)

@ECHO OFF
SETLOCAL
:: First log to tempfile
COPY /y mylog.txt "%temp%\combinedlogs.tmp" >NUL
(
FOR /f "delims=" %%i IN (mylog2.txt) DO (
 SET line=%%i
 ECHO %%i|FINDSTR /b /r "[012][0-9]:[0-5][0-9]:[0-5][0-9]\.[0-9][0-9][0-9]" >NUL
 IF ERRORLEVEL 1 (
  SETLOCAL ENABLEDELAYEDEXPANSION
 ECHO(!stamp:~0,12!!count!!line!
  ENDLOCAL
  SET /a count+=1
 ) ELSE (
 SET /a count=100
 ECHO %%i
 SET stamp=%%i
 )
)
)>>"%temp%\combinedlogs.tmp"
(
FOR /f "delims=" %%i IN ('SORT "%temp%\combinedlogs.tmp"') DO (
 SET line=%%i
 SETLOCAL enabledelayedexpansion
 IF "!line:~12,1!"==" " (ECHO(%%i) ELSE (ECHO(!line:~15!)
 ENDLOCAL
)
)>combinedlogs.txt
DEL "%temp%\combinedlogs.tmp" /F /Q

将带有所有时间戳条目的第一个日志复制到临时文件 通过

处理第二个文件 直接输出任何带时间戳的行,保存时间戳行并设置3位计数器 为其他行输出印章部分+计数器+原始文本和 撞柜台

因此是临时文件

Timestamp1 line1 from file1
..
Timestampn linen from file1
timestampA line1 from file2 with timestamp
timestampA100 UNtimestamped line2from file2
timestampA101 UNtimestamped line3from file2
timestampB line4 from file2 with timestamp
timestampB100 UNtimestamped line5from file2
timestampB101 UNtimestamped line6from file2
...

对结果进行排序并重新处理 第 13 个字符中没有空格的行是第二个文件中没有时间戳的行,所以

输出除前 15 个字符(时间戳 12 个字符 + 3 个 计数器) 否则,时间戳行,所以全部输出。

完成!

【讨论】:

【参考方案2】:

您应该在日志文件中使用mergestableignore-leading-blanksnumeric-sort 和易于排序的日期时间格式(例如yyyyMMddHHmmssSSS)。

所以,我将您的日志格式更改为更易于排序,从而得到sort -bsnm log1 log2

 $ cat -n log1 log2 && sort -m -b -n -s log1 log2
      1 114818825 [main] INFO  org.hibernate.cfg.Environment - HHH000206 hibernate.properties not found
      2 114855784 [main] INFO  o.h.tool.hbm2ddl.SchemaUpdate - HHH000396 Updating schema
      1 114835377 [qtp1484319352-19] ERROR c.w.b.c.ControllerErrorHandler -
      2 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]"
      3     at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java68) ~[spring-beans-3.2.1.RELEASE.jar3.2.1.RELEASE]
      4 at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java45) ~[spring-beans-3.2.1.RELEASE.jar3.2.1.RELEASE]
      5 at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java595) ~[spring-context-3.2.1.RELEASE.jar3.2.1.RELEASE]
      6 at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java98) ~[spring-web-3.2.1.RELEASE.jar3.2.1.RELEASE]
      7 at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java77) ~[spring-web-3.2.1.RELEASE.jar3.2.1.RELEASE]
      8 at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java162) ~[spring-web-3.2.1.RELEAS
      9 
 114818825 [main] INFO  org.hibernate.cfg.Environment - HHH000206 hibernate.properties not found
 114835377 [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.java68) ~[spring-beans-3.2.1.RELEASE.jar3.2.1.RELEASE]
 at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java45) ~[spring-beans-3.2.1.RELEASE.jar3.2.1.RELEASE]
 at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java595) ~[spring-context-3.2.1.RELEASE.jar3.2.1.RELEASE]
 at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java98) ~[spring-web-3.2.1.RELEASE.jar3.2.1.RELEASE]
 at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java77) ~[spring-web-3.2.1.RELEASE.jar3.2.1.RELEASE]
 at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java162) ~[spring-web-3.2.1.RELEAS

 114855784 [main] INFO  o.h.tool.hbm2ddl.SchemaUpdate - HHH000396 Updating schema

正如@Magoo's answer 中所说,您的日志日期时间当前的格式很难排序。

【讨论】:

这将通过“排序”堆栈跟踪来破坏多行日志条目,不是吗? 嗨@topr,有一段时间了,但据我记得看到,多行堆栈跟踪与sort -bsnm log1 log2... 一起保留。如果有极端情况,我不会感到惊讶,但是对于@marco 提供的示例和我自己的用法,这效果非常好。但是,如果您需要更复杂的东西,可以使用各种日志查看器工具。 感谢您的回答。 Stacktraces 被保留了,但是有了这些开关,所有的行都被保留了。根本没有排序,只是合并。我正在使用日志查看工具,但我需要与排序合并是因为我有多个日志文件,并且使用单个文件提供工具更方便。 嗨@topr,考虑编写您自己的*** 问题,并附上您正在寻找的示例。至少对于这个问题中的示例,它应该可以工作。请记住,每一行必须以时间的可排序数字表示开头。请记住,日志应以易于排序的日期时间格式(例如 yyyyMMddHHmmssSSS)开头。也就是说,日志1的日志行以114818825114855784开头,而日志2以114835377开头。自 18825114818825、114835377114855784 不确定新问题是否不会重复,因为它与这种情况相同。我的日志文件行以日期和时间开头,例如:2014-11-25 06:43:20,991 INFO... 这是一种可排序的格式,不是吗?但是,也有与上面示例完全相同的带有堆栈跟踪的日志条目行。不确定 -m 应该做什么,但它完全禁用排序(与使用 cat 没有什么不同)。如果没有 -m 它会排序,但会将所有 stracktrace 的所有行聚集在顶部,这使得这种排序毫无用处。我没有得到您的答案,因为 sort 似乎无法对包含多行条目的日志文件进行排序。【参考方案3】:

这是在 bash shell 中执行此操作的一种方法,文件的简单合并(而不是昂贵的手段 - 因为日志文件已经排序)。这对于数百兆字节或更大的大文件很重要,现实世界的日志文件也是如此。

此解决方案假定您的日志中没有 NUL 字节,这对于我遇到的每个日志文件都是如此,具有各种字符集。

基本思路:

    通过将每个输入文件中的换行符替换为 NUL 将所有多行连接到单行 对替换的文件执行sort -m 以合并它们 将 NUL 替换回合并结果上的换行符

由于第一步多次完成,我给它起了一个别名:

alias a="awk ' if (match(\$0, /^[0-9]2:[0-9]2:[0-9]2\\./, _))\
 if (NR == 1) printf \"%s\", \$0; else printf \"\\n%s\", \$0 \
else printf \"\\0%s\", \$0  END  print \"\" '"

这是执行所有 3 个步骤的命令:

sort -m <(a myLog1.txt) <(a myLog2.txt) | tr '\0' '\n'

欲了解更多信息,请参阅https://superuser.com/a/838446/125379

【讨论】:

【参考方案4】:

我在同样的问题上苦苦挣扎,最后我想我明白了。尝试这样做:

sort -nbms -k1.1,1.2 -k1.4,1.5 -k1.7,1.8 -k1.10,1.12 myLog1.txt myLog2.txt &gt; combined.txt

我自己还不是很清楚,不过我会尝试给出一些解释。根据手册页使用的开关意味着:

-n, --numeric-sort - 根据字符串数值比较。

-b, --ignore-leading-blanks - 忽略前导空格。

-s, --stable - 通过禁用最后的比较来稳定排序

-m, --merge - 合并已排序的文件;不排序

-k, --key=POS1[,POS2] - 在 POS1(原点 1)开始一个键,在 POS2 结束它(默认行尾)

日志文件已经排序,所以我们不需要再次排序,只需要确定合并时哪一行去哪里。这就是-m 的原因。防止堆栈跟踪被打乱至关重要。 在这种情况下,-b 不是必需的,因为 -n-m 组合在一起可以防止堆栈跟踪行聚集。我留下了它以防万一大多数堆栈跟踪行都以空格开头。 -n 显然会在密钥中有非数字字符时停止比较密钥。这是保持堆栈跟踪到位的第二个关键位。重要的是,如果它是-n -k1,1,它只会按小时对日志文件进行排序,因为冒号是非数字的。除此之外,-n 加快了数字比较,所以无论如何我们都希望拥有它。 上一点提到的问题是通过指向每个键中的特定字符位置来解决的,这就是为什么-k1.1,1.2(小时的第一位和第二位)-k1.4,1.5(分钟的第一位和第二位)等等。点之前的第一个数字始终是“1”,因为它指向文件行的第一列(在我们的例子中是时间)。很快它就是-kA,B,其中AB 是给定行中的列位置(默认情况下,行由空格分隔)。使用的 A 和 B 的格式是 .. 请记住,只要在 AB 之间有一个非数字字符,则与使用 -n 相比,它后面的所有内容都将被忽略。 -s 禁用默认行为,即:每当进行比较的键是相同的行的完整字符串比较时。我们不希望这样保留原始日志条目的顺序。不确定是否有必要使用-m

【讨论】:

干得好@topr!如果您的日志格式不是简单的数字排序,则必须与 -k 标志搏斗。我发现将时间戳修改为易于排序的日期时间格式(例如 yyyyMMddHHmmssSSS)更简单,但它更难阅读。 关于如何在此处保留堆栈跟踪的任何想法?使用 sort -nbs -k1.1,1.4 -k1.6,1.7 -k1.9,1.10 test.log 对日志行的这一位进行正确排序 2021-09-13 但是如果有没有日期的堆栈跟踪,它将把它们扔到输出的顶部,而不是它们所属的内联。【参考方案5】:

开源工具 (Java GitHub) 允许您将不同格式的日志文件(包括多行)合并到一个合并文件中。

该工具允许更改日志文件中记录的时间。当文件来自不同时区时,它会很有用。

它还允许为合并文件生成附加信息,例如统一格式的应用程序名称或时间戳,请参阅the example。

该工具可用作command line tool 或Java library。 注意:我是作者。

【讨论】:

以上是关于按日期合并多个日志文件,包括多行的主要内容,如果未能解决你的问题,请参考以下文章

python脚本实现访问日志合并

在 Spark 中组合来自多个目录的日志

Windows下将Tomcat日志输出到文件之后。按日期切分日志

如何用log4j输出多个自定义日志文件

log4j配置参数详解——按日志文件大小日期切分日志文件

求一个在linux下的函数:将日志打印到文件中 (重点实现要求1、2即可)