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
Merged

Gelf writer #590

merged 15 commits into from Jul 18, 2017

Conversation

dploeger
Copy link
Contributor

Fixes #589

Have fun. 馃槈

@dploeger
Copy link
Contributor Author

The current build problems doesn't seem to relate to the new writer. Could you please look at them?

@gehel
Copy link
Member

gehel commented Jul 15, 2017

The build is failing because mutation testing is a bit low (30%). The default is 100% (which you are not expected to achieve). Have a look at other modules to see how to decrease that threshold.

Copy link
Member

@gehel gehel left a comment

Choose a reason for hiding this comment

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

Looks good! Thanks for the pull request! A few things need to be improved...

<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
Copy link
Member

Choose a reason for hiding this comment

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

The need to use powermock is a design smell in my book (I have not yet looked at the rest of the code, so there might be a good reason to use it here).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, this actually is a leftover. I'm not using Powermock anymore and will remove it.

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

public class GelfWriter extends BaseOutputWriter {
Copy link
Member

Choose a reason for hiding this comment

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

Splitting this class with OutputWriterFactory and an OutputWriter would split the responsibilities of creating the writer vs running the writer. Have a look at GraphiteWriter2 / GraphiteWriterFactory for an example. In particular, it would allow you to get rid of the mock transport in the writer and inject it during tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

Copy link
Member

Choose a reason for hiding this comment

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

Now that the responsibilities are split, this class should implement directly OutputWriter or extend OutputWriterAdapter.This would allow to drop a few unused methods (validateSetup() for one).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But the OutputWriter interface includes a validateSetup() method. If I implement that, I have to implement lots of other methods like start() and close, which BaseOutputWriter already does. Are you sure about this one?

Copy link
Member

Choose a reason for hiding this comment

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

OutputWriterAdapter provides default implementations for those methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

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.

鉁旓笍


@Before
public void setUp() throws Exception {
this.result = new Result(1, "attributeName", "className", "objDomain", "classNameAlias", "typeName", ImmutableMap
Copy link
Member

Choose a reason for hiding this comment

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

There are a bunch of ResultFixtures already available that you could reuse.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

null,
null
);
} catch (final NullPointerException e) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure that catching NPE here adds a lot of clarity. If you really want to do it, please rethrow a new NPE, not a RuntimeException (we are clearly in a case of a null pointer exception).

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 referenced the Elastic tests here, which do the same, but I will change 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.

鉁旓笍

}
gelfWriter.start();
gelfWriter.doWrite(dummyServer(), dummyQuery(), ImmutableList.<Result>of(result));
Assert.assertTrue(
Copy link
Member

Choose a reason for hiding this comment

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

AssertJ would make those assertions easier to read (assertThat(MockGelfTransport.calls).size().isNotZero()).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

pom.xml Outdated
@@ -110,15 +112,17 @@

<scm>
<connection>scm:git:git@github.com:jmxtrans/jmxtrans.git</connection>
<developerConnection>scm:git:git@github.com:jmxtrans/jmxtrans.git</developerConnection>
<developerConnection>scm:git:git@github.com:jmxtrans/jmxtrans.git
</developerConnection>
Copy link
Member

Choose a reason for hiding this comment

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

I don't like those new lines... probably a too eager reformat?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍


log.debug(
"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.

鉁旓笍

public GelfWriter(
ImmutableList<String> typeNames,
boolean booleanAsNumber,
Boolean debugEnabled,
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 debugEnabled isn't used anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I implemented that, because I thought it would be needed. BaseOutputWriter has that in the constructor. I don't need it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed it now. 鉁旓笍

import java.io.File;
import java.util.Map;

public class GelfWriterFactory implements OutputWriterFactory{
Copy link
Member

Choose a reason for hiding this comment

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

This class should implement a meaningfull equals and hashCode. Lombok can help you here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm. Why? It doesn't seem to be a child of Comparable. My IntelliJ usually recommends and creates that, if the class is a Comparable. Are WriterFactories compared somewhere in the code?

Copy link
Member

Choose a reason for hiding this comment

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

Nope, they are not Comparable, but they are deduplicated using a HashMap, which requires a meaningfull equals and hashCode. Don't look at that code to closely, it is ugly :) here lies dragons...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, will do then. 馃槈

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

private final Map<String, Object> additionalFields;
private final GelfTransport gelfTransport;

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.

鉁旓笍


@Before
public void setUp() throws Exception {
this.result = ResultFixtures.numericResult();
Copy link
Member

Choose a reason for hiding this comment

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

This could probably be inlined everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

public class GelfWriterTests {
Copy link
Member

Choose a reason for hiding this comment

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

The tests in this class have mostly no assertions. This explains the fairly low mutation test coverage. It would be nice to check a bit more that not only the code does not throw any exceptions, but that is actually does what it is supposed to do. It might also make sense to split test for the Writer and tests for the Factory.

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 Map<String, Object> additionalFields = new HashMap<>();

additionalFields.put("test", "test");
Copy link
Member

Choose a reason for hiding this comment

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

Adding additional fields to test that they are working correctly is good! Testing that they are actually sent would be much better. You should probably create a mock transport, capture the message and do a few assertions on it. The same kind of additional assertions could be added to most of those tests.

Side note: ImmutableMap.of() would be a much shorter notation to declare those additionalFields.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@gehel
Copy link
Member

gehel commented Jul 17, 2017

Note that you can sort the pom.xml with mvn sortpom:sort (I know, it is a pain, but it makes diffs so much more readable when things are in a predictable order, and thus so much easier to code review).

<dependency>
<groupId>org.graylog2</groupId>
<artifactId>gelfclient</artifactId>
<version>1.4.0</version>
Copy link
Member

Choose a reason for hiding this comment

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

Could you move version declaration to the parent pom? (in the dependencyManagement section)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍


private final ImmutableList<String> typeNames;
private final boolean booleanAsNumber;
private final Boolean debugEnabled;
Copy link
Member

Choose a reason for hiding this comment

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

debugEnabled and settings don't seem to be used. They should be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍


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.

);
}

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. 鉁旓笍

assertThat(
gelfWriterFactory.getGelfConfiguration()
).is(
new Condition<GelfConfiguration>() {
Copy link
Member

Choose a reason for hiding this comment

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

I'm really not sure that using a Condition is more readable than testing individual fields. I would go for a series of:

assertThat(gelfConfiguration.getQueueSize()).isEqualsTo(10)
...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm. I found the (rather long) series of assertThat not really readable. Maybe move it to another class as I did with GelfDefaultConfigurationCondition?

Copy link
Member

Choose a reason for hiding this comment

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

I would argue that the current version isn't much readable either. I don't think that extracting assertions to another class would help readability much, as assertions are very much part of your test. In any case, that's really a minor style issue...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, actually. I noticed that the output given when using Conditions isn't really helping when trying to find the bug. I'm moving to assertThat-lines.

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 GelfTransport gelfTransport = Mockito.mock(GelfTcpTransport.class);

doNothing().when(gelfTransport).send(
Copy link
Member

Choose a reason for hiding this comment

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

Using a captor instead of an argument matcher would allow a more readable test IMHO.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

@dploeger
Copy link
Contributor Author

@gehel I think, I fixed all your requested changes and the build is successful.


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.

ArgumentCaptor<GelfMessage> messageArgumentCaptor;

@Before
public void setUp() throws Exception {
Copy link
Member

Choose a reason for hiding this comment

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

This could be replaced by @RunWith(MockitoJUnitRunner.class) (which I find more readable, but that's personal style).

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 GelfTransport
gelfTransport =
Mockito.mock(GelfTcpTransport.class);
Copy link
Member

Choose a reason for hiding this comment

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

Since you are already using @Captor, using @Mock would be nicer. Or at least have a static import for Mockito.mock().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍


gelfWriter = new GelfWriter(
typenames,
ImmutableMap.of("test", (Object) "test"),
Copy link
Member

Choose a reason for hiding this comment

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

Having test values a bit more descriptive than "test" would help document what is going on. Could you provide an example of something you would actually use? Or at least a "additional_test_field" / "additional_test_value"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

gelfWriter.start();
gelfWriter.doWrite(dummyServer(),
dummyQuery(),
ImmutableList.of(ResultFixtures.numericResult()));
Copy link
Member

Choose a reason for hiding this comment

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

There is a singleNumericResult fixture which does exactly what you are doing here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

鉁旓笍

@gehel
Copy link
Member

gehel commented Jul 17, 2017

This is looking really good! I have a few more comments (I am a pain in the ..., yes I know). Feel free to let me know if I'm too much of a pain.

Mutation coverage is still on the low side. But most of the nominal paths are correctly asserted, so I'm fine with merging as is.

@dploeger
Copy link
Contributor Author

@gehel I will look into your suggestions tomorrow, please wait with merging. Thanks for all the good tips and suggestions.

@dploeger
Copy link
Contributor Author

Done.

@gehel
Copy link
Member

gehel commented Jul 18, 2017

The booleanAsNumber and settings parameters on GelfWriterFactory are still unused.

@dploeger
Copy link
Contributor Author

Damn. Corrected that as well.

@gehel gehel merged commit e960b25 into jmxtrans:master Jul 18, 2017
@gehel
Copy link
Member

gehel commented Jul 18, 2017

Looks good! Merging!

Thanks a lot for the efforts!

@dploeger
Copy link
Contributor Author

You're welcome. Added wiki documentation as well. Can you tell me, when this will be available in an official release?

@dploeger
Copy link
Contributor Author

@gehel The GELF-Writer doesn't seem to be in 266-SNAPSHOT. Any clues why?

@gehel
Copy link
Member

gehel commented Jul 20, 2017

The jar itself seems to be published (https://oss.sonatype.org/content/repositories/snapshots/org/jmxtrans/jmxtrans-output-gelf/266-SNAPSHOT/). I have not checked the main ueber jar, but I suspect just some delay with sonatype. If that's not the case, let me know, I'll investigate some more.

I can make a release tonight...

@gehel
Copy link
Member

gehel commented Jul 20, 2017

Release in progress: https://travis-ci.org/jmxtrans/jmxtrans/builds/255795067 (I'll check on it tomorrow, but it should just complete with no problem).

@dploeger
Copy link
Contributor Author

I just downloaded the latest nightly all.jar from https://oss.sonatype.org/content/repositories/snapshots/org/jmxtrans/jmxtrans/266-SNAPSHOT/ and it was missing there. Thanks.

@dploeger dploeger deleted the gelf-writer branch August 9, 2018 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants