如何以编程方式将数字字段记录到 Graylog 中?
How can I programmatically log numeric fields into Graylog?
情况
我编写库来处理计算服务器。
我正在记录计算时间(例如,工作的开始和停止)。
我想独立于客户端应用程序的日志框架配置记录这些值。我通过以编程方式使用 Graylog 实现了这一点。所以我从我的库中配置和初始化我的记录器,而客户端仍然没有注意到。
这是我的初始化代码
import org.graylog2.log.GelfAppender;
void init() {
final GelfAppender appender = new GelfAppender();
appender.setName("MyServerAppender");
appender.setGraylogHost("bigcpuserver");
appender.setGraylogPort(2020);
appender.setExtractStacktrace(true);
appender.setAddExtendedInformation(true);
appender.setAdditionalFields("{'environment': 'TEST',"
+ "'ip_address': '10.20.30.40',"
+ "'serverName': 'devPc',"
+ "'libVersion': '1.0.0',"
+ "'application': 'loggingTestApp',"
+ "'appversion': '2.0.7'}");
appender.activateOptions();
final org.apache.log4j.Logger myLog =
org.apache.log4j.Logger.getLogger(MyHiddenLoggerClass.class);
myLog.addAppender(appender);
}
到目前为止一切顺利。我可以通过以下行将日志从代码中的任何位置发送到 Graylog:
org.apache.log4j.Logger.getLogger(MyHiddenLoggerClass.class).info("message");
问题
我想在我的日志中添加数字字段,这样 Graylog 就可以 运行 对它们进行统计。例如,查找哪些作业耗时最长——可能需要算法优化——或最短——可能根本不需要计算服务器——
我可以按以下方式向 MDC 添加字段:
org.apache.log4j.MDC.put("cpuTime", startTimeMillis-endTimeMillis);
并且,从那时起,所有日志都将包含具有该值的 cpuTime
字段。我会在发送特定日志条目后将其删除,以防止在后续日志条目中携带它:
org.apache.log4j.MDC.remove("cpuTime");
但是,对于 Graylog 来说,那些是字符串,所以它只能对它们进行计数,看看有多少不同。
问题
我如何告诉 Graylog "cpuTime" 将永远是 long
?
目前的尝试次数
我尝试在 init()
时预先配置字段。
为此,我也测试了其他 class 层次结构,例如 me.moocar
。例如,我尝试了以下方法:
final me.moocar.logbackgelf.GelfAppender appender =
new me.moocar.logbackgelf.GelfAppender();
appender.setName("MyServerAppender");
appender.setIncludeFullMDC(true);
appender.setGraylog2ServerHost("bigcpuserver");
appender.setGraylog2ServerPort(2020);
final Map<String, String> additionalFields = new HashMap<String, String>();
final Map<String, String> fieldTypes = new HashMap<String, String>();
additionalFields.put("cpuTime", "42");
fieldTypes.put("cpuTime", "long");
appender.setAdditionalFields(additionalFields);
appender.setFieldTypes(fieldTypes);
appender.start();
final ch.qos.logback.classic.LoggerContext logCtx =
(LoggerContext) LoggerFactory.getILoggerFactory();
appender.setContext(logCtx);
ch.qos.logback.classic.Logger logToConfigure =
logCtx.getLogger(MyHiddenLoggerClass.class);
logToConfigure.addAppender(appender);
因此我可以在我的测试代码中执行以下操作:
final ch.qos.logback.classic.Logger log =
logCtx.getLogger(MyHiddenLoggerClass.class);
MDC.put("cpuTime", totalTime);
log.error("task finished.");
并且在 Graylog 中,日志 "task finished." 将伴随一个名为 "cpuTime" 的字段,并包含 一个字符串 ,其值为 totalTime
在日志时刻。我只希望包含的值是 number。任何人都可以帮助我吗?
如果有帮助,我可以将整体更改为 me.moocar
层次结构。但是到目前为止,me.moocar
和org.graylog2.log
都没有给我想要的结果;使用后者,我可以更轻松地将附加程序附加到记录器。
更新
me.moocar
似乎仅限于 String
作为附加字段的类型。因此,如果我想要数字字段,我必须选择其他选项。
您需要根据数据类型发送值。有区别
{'cpuTime':'42'}
和
{'cpuTime':42}
我不知道 Graylog 中是否有可用的转换器(您可以通过 filters
在 logstash 中处理字段转换)。如果您想在记录器级别解决问题,请查看 http://logging.paluch.biz/examples/logback.html,特别是 additionalFieldTypes
.
我遇到了同样的问题,发现 https://mvnrepository.com/artifact/de.siegmar/logback-gelf/4.0.0 的最新版本实现了将类似数字的值作为数字发送到 Graylog 的功能。
它默认是禁用的,你可以通过设置标志 numbersAsString
来启用它,例如 logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include
resource="org/springframework/boot/logging/logback/base.xml" />
<appender name="GELF"
class="de.siegmar.logbackgelf.GelfUdpAppender">
<graylogHost>${graylog-host}</graylogHost>
<graylogPort>${graylog-port></graylogPort>
<encoder class="de.siegmar.logbackgelf.GelfEncoder">
<numbersAsString>false</numbersAsString>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="GELF" />
</root>
</configuration>
通过上述设置,可解析值作为数字发送到 Graylog,它由 de.siegmar.logbackgelf.GelfEncoder
中的这段代码处理 - 将 String 转换为 BigDecimal
private Object processValue(final String value) {
if (!numbersAsString) {
try {
return new BigDecimal(value);
} catch (final NumberFormatException e) {
return value;
}
}
return value;
}
稍后编码为 Json 作为 de.siegmar.logbackgelf.SimpleJsonEncoder
中的数字
SimpleJsonEncoder appendToJSON(final String key, final Object value) {
if (closed) {
throw new IllegalStateException("Encoder already closed");
}
if (value != null) {
appendKey(key);
if (value instanceof Number) {
sb.append(value.toString());
} else {
sb.append(QUOTE).append(escapeString(value.toString())).append(QUOTE);
}
}
return this;
}
情况
我编写库来处理计算服务器。 我正在记录计算时间(例如,工作的开始和停止)。 我想独立于客户端应用程序的日志框架配置记录这些值。我通过以编程方式使用 Graylog 实现了这一点。所以我从我的库中配置和初始化我的记录器,而客户端仍然没有注意到。
这是我的初始化代码
import org.graylog2.log.GelfAppender;
void init() {
final GelfAppender appender = new GelfAppender();
appender.setName("MyServerAppender");
appender.setGraylogHost("bigcpuserver");
appender.setGraylogPort(2020);
appender.setExtractStacktrace(true);
appender.setAddExtendedInformation(true);
appender.setAdditionalFields("{'environment': 'TEST',"
+ "'ip_address': '10.20.30.40',"
+ "'serverName': 'devPc',"
+ "'libVersion': '1.0.0',"
+ "'application': 'loggingTestApp',"
+ "'appversion': '2.0.7'}");
appender.activateOptions();
final org.apache.log4j.Logger myLog =
org.apache.log4j.Logger.getLogger(MyHiddenLoggerClass.class);
myLog.addAppender(appender);
}
到目前为止一切顺利。我可以通过以下行将日志从代码中的任何位置发送到 Graylog:
org.apache.log4j.Logger.getLogger(MyHiddenLoggerClass.class).info("message");
问题
我想在我的日志中添加数字字段,这样 Graylog 就可以 运行 对它们进行统计。例如,查找哪些作业耗时最长——可能需要算法优化——或最短——可能根本不需要计算服务器——
我可以按以下方式向 MDC 添加字段:
org.apache.log4j.MDC.put("cpuTime", startTimeMillis-endTimeMillis);
并且,从那时起,所有日志都将包含具有该值的 cpuTime
字段。我会在发送特定日志条目后将其删除,以防止在后续日志条目中携带它:
org.apache.log4j.MDC.remove("cpuTime");
但是,对于 Graylog 来说,那些是字符串,所以它只能对它们进行计数,看看有多少不同。
问题
我如何告诉 Graylog "cpuTime" 将永远是 long
?
目前的尝试次数
我尝试在 init()
时预先配置字段。
为此,我也测试了其他 class 层次结构,例如 me.moocar
。例如,我尝试了以下方法:
final me.moocar.logbackgelf.GelfAppender appender =
new me.moocar.logbackgelf.GelfAppender();
appender.setName("MyServerAppender");
appender.setIncludeFullMDC(true);
appender.setGraylog2ServerHost("bigcpuserver");
appender.setGraylog2ServerPort(2020);
final Map<String, String> additionalFields = new HashMap<String, String>();
final Map<String, String> fieldTypes = new HashMap<String, String>();
additionalFields.put("cpuTime", "42");
fieldTypes.put("cpuTime", "long");
appender.setAdditionalFields(additionalFields);
appender.setFieldTypes(fieldTypes);
appender.start();
final ch.qos.logback.classic.LoggerContext logCtx =
(LoggerContext) LoggerFactory.getILoggerFactory();
appender.setContext(logCtx);
ch.qos.logback.classic.Logger logToConfigure =
logCtx.getLogger(MyHiddenLoggerClass.class);
logToConfigure.addAppender(appender);
因此我可以在我的测试代码中执行以下操作:
final ch.qos.logback.classic.Logger log =
logCtx.getLogger(MyHiddenLoggerClass.class);
MDC.put("cpuTime", totalTime);
log.error("task finished.");
并且在 Graylog 中,日志 "task finished." 将伴随一个名为 "cpuTime" 的字段,并包含 一个字符串 ,其值为 totalTime
在日志时刻。我只希望包含的值是 number。任何人都可以帮助我吗?
如果有帮助,我可以将整体更改为 me.moocar
层次结构。但是到目前为止,me.moocar
和org.graylog2.log
都没有给我想要的结果;使用后者,我可以更轻松地将附加程序附加到记录器。
更新
me.moocar
似乎仅限于 String
作为附加字段的类型。因此,如果我想要数字字段,我必须选择其他选项。
您需要根据数据类型发送值。有区别
{'cpuTime':'42'}
和
{'cpuTime':42}
我不知道 Graylog 中是否有可用的转换器(您可以通过 filters
在 logstash 中处理字段转换)。如果您想在记录器级别解决问题,请查看 http://logging.paluch.biz/examples/logback.html,特别是 additionalFieldTypes
.
我遇到了同样的问题,发现 https://mvnrepository.com/artifact/de.siegmar/logback-gelf/4.0.0 的最新版本实现了将类似数字的值作为数字发送到 Graylog 的功能。
它默认是禁用的,你可以通过设置标志 numbersAsString
来启用它,例如 logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include
resource="org/springframework/boot/logging/logback/base.xml" />
<appender name="GELF"
class="de.siegmar.logbackgelf.GelfUdpAppender">
<graylogHost>${graylog-host}</graylogHost>
<graylogPort>${graylog-port></graylogPort>
<encoder class="de.siegmar.logbackgelf.GelfEncoder">
<numbersAsString>false</numbersAsString>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="GELF" />
</root>
</configuration>
通过上述设置,可解析值作为数字发送到 Graylog,它由 de.siegmar.logbackgelf.GelfEncoder
中的这段代码处理 - 将 String 转换为 BigDecimal
private Object processValue(final String value) {
if (!numbersAsString) {
try {
return new BigDecimal(value);
} catch (final NumberFormatException e) {
return value;
}
}
return value;
}
稍后编码为 Json 作为 de.siegmar.logbackgelf.SimpleJsonEncoder
SimpleJsonEncoder appendToJSON(final String key, final Object value) {
if (closed) {
throw new IllegalStateException("Encoder already closed");
}
if (value != null) {
appendKey(key);
if (value instanceof Number) {
sb.append(value.toString());
} else {
sb.append(QUOTE).append(escapeString(value.toString())).append(QUOTE);
}
}
return this;
}