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

org.graylog.plugins.beats.BeatsCodec Maven / Gradle / Ivy

There is a newer version: 6.1.4
Show newest version
/*
 * Copyright (C) 2020 Graylog, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * .
 */
package org.graylog.plugins.beats;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import org.graylog2.jackson.TypeReferences;
import org.graylog2.plugin.Message;
import org.graylog2.plugin.MessageFactory;
import org.graylog2.plugin.Tools;
import org.graylog2.plugin.configuration.Configuration;
import org.graylog2.plugin.inputs.annotations.Codec;
import org.graylog2.plugin.inputs.annotations.ConfigClass;
import org.graylog2.plugin.inputs.annotations.FactoryClass;
import org.graylog2.plugin.inputs.codecs.AbstractCodec;
import org.graylog2.plugin.journal.RawMessage;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.requireNonNull;

@Codec(name = "beats-deprecated", displayName = "Beats (deprecated)")
public class BeatsCodec extends AbstractCodec {
    private static final Logger LOG = LoggerFactory.getLogger(BeatsCodec.class);
    private static final String MAP_KEY_SEPARATOR = "_";

    private final ObjectMapper objectMapper;
    private final MessageFactory messageFactory;

    @Inject
    public BeatsCodec(@Assisted Configuration configuration, ObjectMapper objectMapper, MessageFactory messageFactory) {
        super(configuration);
        this.objectMapper = requireNonNull(objectMapper);
        this.messageFactory = messageFactory;
    }

    @Nullable
    @Override
    public Message decode(@Nonnull RawMessage rawMessage) {
        final byte[] payload = rawMessage.getPayload();
        final Map event;
        try {
            event = objectMapper.readValue(payload, TypeReferences.MAP_STRING_OBJECT);
        } catch (IOException e) {
            LOG.error("Couldn't decode raw message {}", rawMessage);
            return null;
        }

        return parseEvent(event);
    }

    @Nullable
    private Message parseEvent(Map event) {
        @SuppressWarnings("unchecked")
        final Map metadata = (HashMap) event.remove("@metadata");
        final String type;
        if (metadata == null) {
            LOG.warn("Couldn't recognize Beats type");
            type = "unknown";
        } else {
            type = metadata.get("beat");
        }
        final Message gelfMessage;
        switch (type) {
            case "filebeat":
                gelfMessage = parseFilebeat(event);
                break;
            case "topbeat":
                gelfMessage = parseTopbeat(event);
                break;
            case "metricbeat":
                gelfMessage = parseMetricbeat(event);
                break;
            case "packetbeat":
                gelfMessage = parsePacketbeat(event);
                break;
            case "winlogbeat":
                gelfMessage = parseWinlogbeat(event);
                break;
            default:
                LOG.debug("Unknown beats type {}. Using generic handler.", type);
                gelfMessage = parseGenericBeat(event);
                break;
        }

        return gelfMessage;
    }

    private Message createMessage(String message, Map event) {
        @SuppressWarnings("unchecked")
        final Map beat = (Map) event.remove("beat");
        final String hostname;
        final String name;
        if (beat == null) {
            hostname = "unknown";
            name = "unknown";
        } else {
            hostname = String.valueOf(beat.get("hostname"));
            name = String.valueOf(beat.get("name"));
        }
        final String timestampField = String.valueOf(event.remove("@timestamp"));
        final DateTime timestamp = Tools.dateTimeFromString(timestampField);
        final String type = String.valueOf(event.get("type"));
        final Object tags = event.get("tags");

        final Message result = messageFactory.createMessage(message, hostname, timestamp);
        result.addField("name", name);
        result.addField("type", type);
        result.addField("tags", tags);

        @SuppressWarnings("unchecked")
        final Map fields = (Map) event.get("fields");
        if (fields != null) {
            result.addFields(fields);
        }

        return result;
    }

    /**
     * @see Filebeat Exported Fields
     */
    private Message parseFilebeat(Map event) {
        final String message = String.valueOf(event.get("message"));
        final Message gelfMessage = createMessage(message, event);
        gelfMessage.addField("facility", "filebeat");
        gelfMessage.addField("file", event.get("source"));
        gelfMessage.addField("input_type", event.get("input_type"));
        gelfMessage.addField("count", event.get("count"));
        gelfMessage.addField("offset", event.get("offset"));

        return gelfMessage;
    }

    /**
     * @see Topbeat Exported Fields
     */
    private Message parseTopbeat(Map event) {
        final Message gelfMessage = createMessage("-", event);
        gelfMessage.addField("facility", "topbeat");
        final Map flattened = MapUtils.flatten(event, "topbeat", MAP_KEY_SEPARATOR);

        // Fix field names containing dots, like "cpu.name"
        final Map withoutDots = MapUtils.replaceKeyCharacter(flattened, '.', MAP_KEY_SEPARATOR.charAt(0));
        gelfMessage.addFields(withoutDots);
        return gelfMessage;
    }

    /**
     * @see Metricbeat Exported Fields
     */
    private Message parseMetricbeat(Map event) {
        final Message gelfMessage = createMessage("-", event);
        gelfMessage.addField("facility", "metricbeat");
        final Map flattened = MapUtils.flatten(event, "metricbeat", MAP_KEY_SEPARATOR);

        // Fix field names containing dots, like "cpu.name"
        final Map withoutDots = MapUtils.replaceKeyCharacter(flattened, '.', MAP_KEY_SEPARATOR.charAt(0));
        gelfMessage.addFields(withoutDots);
        return gelfMessage;
    }

    /**
     * @see Packetbeat Exported Fields
     */
    private Message parsePacketbeat(Map event) {
        final Message gelfMessage = createMessage("-", event);
        gelfMessage.addField("facility", "packetbeat");
        final Map flattened = MapUtils.flatten(event, "packetbeat", MAP_KEY_SEPARATOR);

        // Fix field names containing dots, like "icmp.version"
        final Map withoutDots = MapUtils.replaceKeyCharacter(flattened, '.', MAP_KEY_SEPARATOR.charAt(0));
        gelfMessage.addFields(withoutDots);

        return gelfMessage;
    }

    /**
     * @see Winlogbeat Exported Fields
     */
    private Message parseWinlogbeat(Map event) {
        final String message = String.valueOf(event.remove("message"));
        final Message gelfMessage = createMessage(message, event);
        gelfMessage.addField("facility", "winlogbeat");
        final Map flattened = MapUtils.flatten(event, "winlogbeat", MAP_KEY_SEPARATOR);

        // Fix field names containing dots, like "user.name"
        final Map withoutDots = MapUtils.replaceKeyCharacter(flattened, '.', MAP_KEY_SEPARATOR.charAt(0));
        gelfMessage.addFields(withoutDots);
        return gelfMessage;
    }

    private Message parseGenericBeat(Map event) {
        final String message = String.valueOf(event.remove("message"));
        final Message gelfMessage = createMessage(message, event);
        gelfMessage.addField("facility", "genericbeat");
        final Map flattened = MapUtils.flatten(event, "beat", MAP_KEY_SEPARATOR);

        // Fix field names containing dots
        final Map withoutDots = MapUtils.replaceKeyCharacter(flattened, '.', MAP_KEY_SEPARATOR.charAt(0));
        gelfMessage.addFields(withoutDots);
        return gelfMessage;
    }


    @FactoryClass
    public interface Factory extends AbstractCodec.Factory {
        @Override
        BeatsCodec create(Configuration configuration);

        @Override
        Config getConfig();

        @Override
        Descriptor getDescriptor();
    }

    @ConfigClass
    public static class Config extends AbstractCodec.Config {
    }


    public static class Descriptor extends AbstractCodec.Descriptor {
        @Inject
        public Descriptor() {
            super(BeatsCodec.class.getAnnotation(Codec.class).displayName());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy