Send Logs with Libhoney for Java

Libhoney for Java is Honeycomb’s structured logging library for Java applications. It is a low-level library that helps you send structured events to Honeycomb’s Events API.

Tip
If you are instrumenting a new application for tracing, we recommend that you use OpenTelemetry instead.

Installation 

The SDK is available from Maven Central Repository. To get started, add the SDK to your dependencies:

compile group: 'io.honeycomb.libhoney', name: 'libhoney-java', version: '1.5.4'
<dependency>
    <groupId>io.honeycomb.libhoney</groupId>
    <artifactId>libhoney-java</artifactId>
    <version>1.5.4</version>
</dependency>

Initialization 

The LibHoney class is the entry point to the Honeycomb client library, and is used to create a HoneyClient.

The HoneyClient class provides functionality to construct and send events. The typical application will only have one HoneyClient instance.

Use LibHoney’s static methods to construct and configure an instance of HoneyClient, passing in your Team API key and the default dataset name to which it should send events.

public class Initialization {
    private HoneyClient honeyClient;

    public Initialization() {
        honeyClient = create(
            options()
                .setWriteKey("myTeamWriteKey")
                .setDataset("Cluster Dataset")
                .setSampleRate(2)
                .build()
        );
    }
}

Working With a Proxy 

To reach the Honeycomb API through a proxy server, you need to specify alternative TransportOptions when calling create, and provide the proxy address. For example, if your proxy is at https://myproxy.example.com:

public class Initialization {
    private HoneyClient honeyClient;

    public Initialization() {
        honeyClient = create(
            options()
                .setWriteKey("myTeamWriteKey")
                .setDataset("Cluster Dataset")
                .setSampleRate(2)
                .build(),
            transportOptions().setProxy(HttpHost.create("https://myproxy.example.com")).build()
        );
    }
}

Important notes on Libhoney:

  1. Use of one HoneyClient instance across threads is thread safe.
  2. Libhoney sends will not block your code - event sends are batched up and sent in a queue running in its own thread as the queue either fills to its limit or the default batch send interval is reached.
  3. Pending writes to Honeycomb will be flushed automatically when the JVM shuts down. You do not need to worry about flushing them yourself.

Building and Sending Events 

Once initialized, HoneyClient is ready to send events. In many cases, an event should be first customized with relevant data from its runtime context. For example, try putting a timer around a section of code, adding per-user information, or details about what it took to craft a response. You can add fields when and where you need to, or for some events but not others. (Error handlers are a good example of this.)

createEvent() creates such a customizable Event. You can add fields to the event with .addField() and then submit to the Honeycomb server via Event.send().

Sending an event is an asynchronous action and will avoid blocking by default.

    public static void main(String... args) {
        try (HoneyClient honeyClient = initializeClient()) {
            honeyClient
                .createEvent()
                .addField("userName", "Bob")
                .addField("userId", UUID.randomUUID().toString())
                .setTimestamp(System.currentTimeMillis())
                .send();
        }
    }

Alternatively, an EventFactory is useful when a grouping of events being sent shares common properties. This can be created through the buildEventFactory() method.

You can use an EventFactory to create many similar events as follows:

static class UserService {
        private final EventFactory localBuilder;

        UserService(HoneyClient libHoney) {
            int serviceLevelSampleRate = 2;
            localBuilder = libHoney.buildEventFactory()
                .addField("serviceName", "userService")
                .setSampleRate(serviceLevelSampleRate)
                .build();
        }

        void sendEvent(String username) {
            localBuilder
                .createEvent()
                .addField("userName", username)
                .addField("userId", UUID.randomUUID().toString())
                .setTimestamp(System.currentTimeMillis())
                .send();
        }
    }

All libraries set defaults that will allow your application to function as smoothly as possible during error conditions. When creating events faster than they can be sent, overflowed events will be dropped instead of backing up and slowing down your application. If you add the same key multiple times, only the last value added will be kept.

Configuring Libhoney to Disable Sending Events to Honeycomb 

For situations in which you would like to disable sending events to Honeycomb, such as a test environment or in unit tests, use HoneyClient’s constructor in which the Transport can be overridden. Create a mock Transport to substitute in that implements the Transport interface. One similar example can be seen here.

Handling Responses 

Sending an event is an asynchronous action and will avoid blocking by default. .send() will enqueue the event to be sent as soon as possible, meaning the return value of that method does not indicate that the event was successfully sent.

To see whether events are being successfully received by Honeycomb’s servers, register a ResponseObserver interface with HoneyClient. The ResponseObserver interface has four methods which are notified in different circumstances depending on the outcome of the sending of the event:

  • onServerAccepted will be notified for any event that was accepted on the server side
  • onServerRejected will be notified for any event that was rejected on the server side
  • onClientRejected will be notified for any event that was rejected on the client side
  • onUnknown will be notified for any event where the outcome was not clear

All responses have methods to extract the original event metadata, metrics collected during the event, and a message explaining the response from Honeycomb:

  • getEventMetadata: Get the event metadata that was originally passed in with the event. The metadata is not modified by the client and not sent to the Honeycomb server.
  • getMessage: Get a human readable message explaining the response.
  • getMetrics: Get any metrics that may have been collected during the lifetime of the event. These metrics will only be complete if a response from the Honeycomb server was received.

For responses to events either accepted by or rejected by the server, it is also possible to extract the event status code and raw HTTP response body.

  • getEventStatusCode​: Get the event-specific status code, if it is available, otherwise -1. For batched events this is the status code contained in the batch response body.
  • getRawHttpResponseBody​: Get a byte array of the raw response body.

In the case of responses to events rejected by the client, the exception and rejection reason are accessible.

  • getException: Get the exception that may have caused the rejection (returns null if no exception was the cause).
  • getReason: Get the reason for the event having been rejected.

Before sending an event, you have the option to attach metadata to that event. This metadata is not sent to Honeycomb; instead, it is used to help you match individual responses with sent events. When sending an event, HoneyClient will take the metadata from the event and attach it to the response for you to consume. Add metadata by calling .addMetadata(k, v) or addMetadata(Map<String,?> metadata) on an event.

You do not have to process responses if you are not interested in them—simply ignoring them is perfectly safe. Unread responses will be dropped.

Troubleshooting 

Refer to Common Issues with Sending Data in Honeycomb.

Contributions 

Features, bug fixes and other changes to libhoney are gladly accepted. Please open issues or a pull request with your change. Remember to add your name to the CONTRIBUTORS file!

All contributions will be released under the Apache License 2.0.