Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gelf writer #590

Merged
merged 15 commits into from Jul 18, 2017
87 changes: 87 additions & 0 deletions jmxtrans-output/jmxtrans-output-gelf/pom.xml
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

The MIT License
Copyright 漏 2010 JmxTrans team

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jmxtrans</groupId>
<artifactId>jmxtrans-output</artifactId>
<version>266-SNAPSHOT</version>
</parent>

<artifactId>jmxtrans-output-gelf</artifactId>

<name>JmxTrans - GELF output writers</name>

<properties>
<verify.mutationThreshold>32</verify.mutationThreshold>
<verify.totalBranchRate>37</verify.totalBranchRate>
<verify.totalLineRate>96</verify.totalLineRate>
</properties>

<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.graylog2</groupId>
<artifactId>gelfclient</artifactId>
</dependency>
<dependency>
<groupId>org.jmxtrans</groupId>
<artifactId>jmxtrans-core</artifactId>
</dependency>
<dependency>
<groupId>org.jmxtrans</groupId>
<artifactId>jmxtrans-output-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jmxtrans</groupId>
<artifactId>jmxtrans-test-utils</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,118 @@
/**
* The MIT License
* Copyright 漏 2010 JmxTrans team
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.googlecode.jmxtrans.model.output.gelf;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.googlecode.jmxtrans.model.OutputWriterAdapter;
import com.googlecode.jmxtrans.model.Query;
import com.googlecode.jmxtrans.model.Result;
import com.googlecode.jmxtrans.model.Server;
import com.googlecode.jmxtrans.model.naming.KeyUtils;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang.StringUtils;
import org.graylog2.gelfclient.GelfMessageBuilder;
import org.graylog2.gelfclient.transport.GelfTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@EqualsAndHashCode
public class GelfWriter extends OutputWriterAdapter {

private static final Logger log = LoggerFactory.getLogger(GelfWriter.class);

private final Map<String, Object> additionalFields;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really minor... This map should probably be an ImmutableMap<String, String>. As it is coming from json, I doubt that values of anything else than a String make much sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ImmutableMap maybe, but <String, Object> is correct, because JSON could contain integers or booleans.

private final GelfTransport gelfTransport;
private final ImmutableList<String> typeNames;

public GelfWriter(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not absolutely required, but it would be nice if this class implements a meaningful equals and hashCode. Lombok can help.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

鉁旓笍

final ImmutableList<String> typeNames,
final Map<String, Object> additionalFields,
final GelfTransport gelfTransport
) {
this.typeNames = typeNames;
this.additionalFields = additionalFields;
this.gelfTransport = gelfTransport;
}

@Override
public void doWrite(final Server server, final Query query, final Iterable<Result> results)
throws Exception
{
final GelfMessageBuilder messageBuilder = new GelfMessageBuilder(
"",
server.getHost()
);

final List<String> messages = new ArrayList<>();

for (final Result result : results) {
log.debug("Query result: [{}]", result);
for (final Map.Entry<String, Object> values : result.getValues()
.entrySet()) {
final String
key =
KeyUtils.getKeyString(query, result, values, this.typeNames);

messages.add(
String.format(
"%s=%s",
key,
values.getValue()
)
);

messageBuilder.additionalField(key, values.getValue());
}
}

if (additionalFields != null) {
for (final Map.Entry<String, Object> additionalField : additionalFields
.entrySet()) {
messageBuilder.additionalField(additionalField.getKey(),
additionalField.getValue());
}
}

final String message = StringUtils.join(messages, " ");

messageBuilder.message(message);
messageBuilder.fullMessage(message);

log.debug(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use String.format() in log messages, use the SLF4J placeholders ({}).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

鉁旓笍

"Sending GELF message: {}",
messageBuilder.build().toString()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you are building the message twice. Storing it in a local variable would be nicer (I do expect that building this message is cheap, so this is not much of an optimization, feel free to ignore).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

鉁旓笍

);

this.gelfTransport.send(messageBuilder.build());
}

@VisibleForTesting
public Map<String, Object> getAdditionalFields() {
return additionalFields;
}
}
@@ -0,0 +1,175 @@
/**
* The MIT License
* Copyright 漏 2010 JmxTrans team
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.googlecode.jmxtrans.model.output.gelf;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.googlecode.jmxtrans.model.OutputWriterFactory;
import lombok.EqualsAndHashCode;
import org.graylog2.gelfclient.GelfConfiguration;
import org.graylog2.gelfclient.GelfTransports;
import org.graylog2.gelfclient.transport.GelfTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.io.File;
import java.util.Map;

@EqualsAndHashCode
public class GelfWriterFactory implements OutputWriterFactory {

private final ImmutableList<String> typeNames;
private final GelfConfiguration gelfConfiguration;
private final Map<String, Object> additionalFields;
private static final Logger log = LoggerFactory.getLogger(GelfWriter.class);

public GelfWriterFactory(
@JsonProperty("typeNames") final ImmutableList<String> typeNames,
@JsonProperty("booleanAsNumber") final boolean booleanAsNumber,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

booleanAsNumber should be used by wrapping the generated OutputWriter in the appropriate transformer. There are some other OutputWriter that already use that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it as I was not using it. (Or should I use it?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like booleanAsNumber is still there. I'm not sure about your use case, and converting boleans to numbers probably does not make all that much sense for GELF. So probably remove it.

@JsonProperty("debugEnabled") final Boolean debugEnabled,
@JsonProperty("settings") final Map<String, Object> settings,
@JsonProperty("host") final String host,
@JsonProperty("port") final Integer port,
@JsonProperty("additionalFields")
final Map<String, Object> additionalFields,
@JsonProperty("transport") final String transport,
@JsonProperty("queueSize") final Integer queueSize,
@JsonProperty("connectTimeout") final Integer connectTimeout,
@JsonProperty("reconnectDelay") final Integer reconnectDelay,
@JsonProperty("tcpNoDelay") final Boolean tcpNoDelay,
@JsonProperty("sendBufferSize") final Integer sendBufferSize,
@JsonProperty("tlsEnabled") final Boolean tlsEnabled,
@JsonProperty("tlsTrustCertChainFile")
final String tlsTrustCertChainFile,
@JsonProperty("tlsCertVerificationEnabled")
final Boolean tlsCertVerificationEnabled,
@JsonProperty("tcpKeepAlive") final Boolean tcpKeepAlive,
@JsonProperty("maxInflightSends") final Integer maxInflightSends
)
{
this.typeNames = typeNames;

this.additionalFields = additionalFields;

if (host == null) {
throw new NullPointerException("Host can not be null");
}

if (port != null) {
this.gelfConfiguration = new GelfConfiguration(host, port);
} else {
this.gelfConfiguration = new GelfConfiguration(host);
}

if (transport != null) {
final GelfTransports gelfTransports;

switch (transport.toUpperCase()) {
case "TCP":
gelfTransports = GelfTransports.TCP;
break;
case "UDP":
gelfTransports = GelfTransports.UDP;
break;
default:
gelfTransports = GelfTransports.TCP;
}

gelfConfiguration.transport(gelfTransports);
}

if (queueSize != null) {
gelfConfiguration.queueSize(queueSize);
}

if (connectTimeout != null) {
gelfConfiguration.connectTimeout(connectTimeout);
}

if (reconnectDelay != null) {
gelfConfiguration.reconnectDelay(reconnectDelay);
}

if (tcpNoDelay != null) {
gelfConfiguration.tcpNoDelay(tcpNoDelay);
}

if (sendBufferSize != null) {
gelfConfiguration.sendBufferSize(sendBufferSize);
}

if (tlsEnabled != null) {
if (tlsEnabled) {
gelfConfiguration.enableTls();
} else {
gelfConfiguration.disableTls();
}
}

if (tlsCertVerificationEnabled != null) {
if (tlsCertVerificationEnabled) {
gelfConfiguration.enableTlsCertVerification();
} else {
gelfConfiguration.disableTlsCertVerification();
}
}

if (tlsTrustCertChainFile != null) {
gelfConfiguration.tlsTrustCertChainFile(new File(
tlsTrustCertChainFile));
}

if (tcpKeepAlive != null) {
gelfConfiguration.tcpKeepAlive(tcpKeepAlive);
}

if (maxInflightSends != null) {
gelfConfiguration.maxInflightSends(maxInflightSends);
}

log.debug("Created gelf configuration: {}",
gelfConfiguration.toString());

}

@Nonnull
@Override
public GelfWriter create() {
final GelfTransport gelfTransport = GelfTransports.create(
gelfConfiguration
);

return new GelfWriter(
this.typeNames,
this.additionalFields,
gelfTransport
);
}

@VisibleForTesting
public GelfConfiguration getGelfConfiguration() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shoudl probably be marked as @VisibleForTesting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay. 鉁旓笍

return gelfConfiguration;
}
}