All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graylog2.telemetry.TelemetryPeriodical Maven / Gradle / Ivy

package org.graylog2.telemetry;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.EvictingQueue;
import com.google.common.collect.Lists;
import com.google.common.net.HttpHeaders;
import com.google.common.primitives.Ints;
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import org.graylog2.plugin.Version;
import org.graylog2.plugin.periodical.Periodical;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.List;

import static com.google.common.base.Preconditions.checkNotNull;

public abstract class TelemetryPeriodical extends Periodical {
    private static final MediaType CONTENT_TYPE = MediaType.parse("application/x-jackson-smile");
    private static final String TELEMETRY_VERSION = TelemetryMetaData.VERSION.toString();
    private static final String USER_AGENT = "Graylog " + Version.CURRENT_CLASSPATH;

    protected final TelemetryConfiguration config;
    protected final EvictingQueue cachedRequestsQueue;
    protected final ObjectMapper objectMapper;
    protected final OkHttpClient httpClient;
    protected final String credentials;
    protected final URL telemetryUrl;
    protected final String filenamePattern;

    private final Logger log = getLogger();

    protected TelemetryPeriodical(TelemetryConfiguration config,
                                  EvictingQueue telemetryRequestsQueue,
                                  OkHttpClient httpClient,
                                  ObjectMapper objectMapper,
                                  URL telemetryUrl,
                                  String filenamePattern) {
        this.config = checkNotNull(config);
        this.cachedRequestsQueue = checkNotNull(telemetryRequestsQueue);
        this.httpClient = checkNotNull(httpClient);
        this.objectMapper = checkNotNull(objectMapper);
        this.credentials = Credentials.basic("telemetry", config.getToken());
        this.telemetryUrl = checkNotNull(telemetryUrl);
        this.filenamePattern = checkNotNull(filenamePattern);
    }

    protected abstract byte[] buildPayload();

    @Override
    public void doRun() {
        log.debug("Telemetry is activated: Transmitting node data sets.");
        final byte[] requestBody = buildPayload();

        if (config.isOfflineMode()) {
            final String filename = String.format(filenamePattern, DateTime.now(DateTimeZone.UTC).getMillis());

            if (!config.getDirectory().exists()) {
                boolean success = config.getDirectory().mkdirs();
                if (!success) {
                    log.error("Couldn't create directory {}", config.getDirectory().getAbsolutePath());
                    return;
                }
            }

            final File file = new File(config.getDirectory(), filename);

            log.debug("Telemetry is in offline mode. Writing data into {}", file);
            try (final OutputStream outputStream = new FileOutputStream(file)) {
                outputStream.write(requestBody);
            } catch (IOException e) {
                log.warn("Couldn't write telemetry data into " + file, e);
            }
        } else {
            final Headers headers = new Headers.Builder()
                    .add(HttpHeaders.AUTHORIZATION, credentials)
                    .add(HttpHeaders.USER_AGENT, USER_AGENT)
                    .add("X-Telemetry-Version", TELEMETRY_VERSION)
                    .build();

            final TelemetryRequest request = TelemetryRequest.create(headers, requestBody);

            boolean success = uploadDataSet(request);
            if (!success) {
                cachedRequestsQueue.add(request);
            } else if (!cachedRequestsQueue.isEmpty()) {
                log.debug("Trying to upload {} queued data sets", cachedRequestsQueue.size());
                uploadQueuedDataSets();
            }
        }
    }

    protected void uploadQueuedDataSets() {
        final List unsuccessfulRequests = Lists.newArrayListWithCapacity(cachedRequestsQueue.size());

        while (!cachedRequestsQueue.isEmpty()) {
            final TelemetryRequest request = cachedRequestsQueue.poll();
            boolean success = uploadDataSet(request);

            if (!success) {
                log.debug("Couldn't successfully upload telemetry data set, re-queueing data");
                unsuccessfulRequests.add(request);
            }
        }

        cachedRequestsQueue.addAll(unsuccessfulRequests);
    }

    protected boolean uploadDataSet(TelemetryRequest telemetryRequest) {
        final Request request = new Request.Builder()
                .url(telemetryUrl)
                .headers(telemetryRequest.headers())
                .post(RequestBody.create(CONTENT_TYPE, telemetryRequest.body()))
                .build();

        final Response response;
        try {
            response = httpClient.newCall(request).execute();
        } catch (IOException e) {
            log.error("Error while uploading telemetry data");
            log.debug("Error details", e);
            return false;
        }

        if (!response.isSuccessful()) {
            log.warn("Couldn't successfully upload telemetry data set: {}", response);
            return false;
        }

        return true;
    }

    @Override
    public boolean runsForever() {
        return false;
    }

    @Override
    public boolean stopOnGracefulShutdown() {
        return true;
    }

    @Override
    public boolean masterOnly() {
        return false;
    }

    @Override
    public boolean isDaemon() {
        return true;
    }

    @Override
    public int getInitialDelaySeconds() {
        return 60;
    }

    @Override
    public int getPeriodSeconds() {
        return Ints.saturatedCast(config.getReportInterval().toSeconds());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy