Package com.streamreduce.core.transformer.message

Source Code of com.streamreduce.core.transformer.message.NodebellyMessageTransformer

/*
* Copyright 2012 Nodeable Inc
*
*    Licensed under the Apache License, Version 2.0 (the "License");
*    you may not use this file except in compliance with the License.
*    You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS,
*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*    See the License for the specific language governing permissions and
*    limitations under the License.
*/

package com.streamreduce.core.transformer.message;

import com.streamreduce.core.event.EventId;
import com.streamreduce.core.model.Event;
import com.streamreduce.core.model.messages.details.SobaMessageDetails;
import com.streamreduce.core.model.messages.details.nodebelly.NodebellyMessageDetails;
import com.streamreduce.core.model.messages.details.nodebelly.NodebellySummaryMessageDetails;
import com.streamreduce.util.MessageUtils;
import com.streamreduce.util.Pair;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.MessageFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
* This is annoying right now because we have to build both the plain text string and MessageDetails object at the same time
*/
public class NodebellyMessageTransformer extends SobaMessageTransformer implements MessageTransformer {

    protected transient Logger logger = LoggerFactory.getLogger(getClass());
    private JSONObject metricConfig;

    public NodebellyMessageTransformer(Properties messageProperties, SobaMessageDetails messageDetails, JSONObject metricConfig) {
        super(messageProperties, messageDetails);
        this.metricConfig  = metricConfig;
    }

    /*
     * This helper function grabs a subfield out of the JSON configuration
     * for each metricname->RESOURCE_ID.METRIC_ID in METRIC_CONFIG_JSON.
     */
    private String getMetricConfigValue(String metricName, Map<String, String> metricCriteria, String name) {
        JSONObject item = (JSONObject) metricConfig.get(metricName);
        if (item != null) {
            String key2 = "";
            if (metricCriteria != null && metricCriteria.containsKey("RESOURCE_ID") && metricCriteria.containsKey("METRIC_ID")) {
                key2 = metricCriteria.get("RESOURCE_ID") + "." + metricCriteria.get("METRIC_ID");
            }
            item = (JSONObject) item.get(key2);
            if (item != null) {
                return item.getString(name);
            }
        }
        logger.error("NB: metricNameMapping: miss: getMetricConfigValue:" + metricName + ", " + metricCriteria);
        return null;
    }

    /*
     * Wraps getMetricConfigValue() to return an empty non null string when null.
     */
    private String getMetricConfigValueNotNull(String metricName, Map<String, String> metricCriteria, String name) {
        String value = getMetricConfigValue(metricName, metricCriteria, name);
        if(value == null)
            value = "";
        return value;
    }

    /*
     * Formats the units string according to the metic and its value.
     */
    protected Pair getUnitsLabel(String metricName, Map<String, String> metricCriteria, double value, boolean space) {
        boolean plural = true;
        if (value == 1.0) {
            plural = false;
        }

        String unit = getMetricConfigValueNotNull(metricName, metricCriteria, (plural ? "units" : "unit"));

        /*
         * Divide down the value while stepping through incrementally
         * larger units until it's no longer > 1K of the units.
         */
        if (unit != null && unit.startsWith("Byte")) {
            String prefixes = "KMGTPEZY";
            for(int i = 0; i < prefixes.length(); i++) {
                if (Math.abs(value / 1024.0) > 1.0) {
                    value = value / 1024.0;
                    unit = prefixes.substring(i, i+1) + "b";
                }
            }
        }

        if (space && unit.length() > 0 && !unit.equals("%")) {
            unit = " " + unit;
        }

        return new Pair(value, unit);
    }

    @Override
    public String doTransform(Event event) {

        final EventId eventId = event.getEventId();
        final Map<String, Object> meta = event.getMetadata();
        final Date eventDate = new Date((Long) meta.get("timestamp"));

        String title;
        String details = "";
       
        Map<String, String> topMetricCriteria = (Map<String, String>) meta.get("metricCriteria");

        switch (eventId) {
            case NODEBELLY_STATUS:
            case NODEBELLY_SUMMARY:

                String providerId = (String) meta.get("targetProviderId");

                title = MessageFormat.format((String) messageProperties.get("message.nodebelly.summary"),
                        eventDate,
                        providerId);

                // not all status/summary messages have been aggregated
                if (meta.containsKey("items")) {

                    // make the names of each item human readable
                    List<Map<String, Object>> items = (List<Map<String, Object>>) meta.get("items");
                    boolean first = true;
                    for (Map<String, Object> item : items) {
                        Map<String, String> metricCriteria = (Map<String, String>) item.get("metricCriteria");
                        String metricName = (String) item.get("name");
                        float origValue = ((Number) item.get("value")).floatValue();
                        float origDiff = ((Number) item.get("diff")).floatValue();

                        Pair pair = getUnitsLabel(metricName, metricCriteria, ((Number) item.get("value")).doubleValue(), true);
                        item.put("value", ((Number)pair.first).floatValue());

                        Pair pairDiff = getUnitsLabel(metricName, metricCriteria, ((Number) item.get("diff")).doubleValue(), true);
                        item.put("diff", ((Number)pairDiff.first).floatValue());

                        /*
                        * Just render an explanation subheader for the first item since it
                        * has the highest stddev and will be selected by the client
                        */
                        if (first) {
                            first = false;
                            String explanation = getMetricConfigValueNotNull(metricName, metricCriteria, "explanation");
                            double previous = origValue - origDiff;
                            if (metricName.equals("CONNECTION_RESOURCE_USAGE") && metricCriteria.containsKey("RESOURCE_ID")) { // TODO hack for IMG
                                explanation = metricCriteria.get("RESOURCE_ID") + " was previously at %.2f and is now at %.2f";
                            }
                            if (explanation.length() > 0) {
                                explanation = MessageFormat.format(explanation, String.valueOf(previous), String.valueOf(pair.first) );
                            }
                            item.put("subheader", explanation);
                        }

                        item.put("name", metricTypeNameReadable(metricName, metricCriteria));
                        item.put("metricname", metricName); // needed for debugging
                        item.put("unit", pair.second);
                    }

                    HashMap<String, Object> structure = new HashMap<>();
                    structure.put("accountId", meta.get("account"));
                    structure.put("total", meta.get("total"));
                    structure.put("diff", meta.get("diff"));
                    structure.put("type", providerId);
                    structure.put("items", items); // add the updated items
                    structure.put("granularity", meta.get("granularity"));

                    // set rich formatting properties
                    // the client will render the table in "structure" how it wants to
                    messageDetails = new NodebellySummaryMessageDetails.Builder()
                            .title(title)
                            .structure(structure)
                            .build();

                    // just print the key/value pairs for the plain text version
                    details = ((NodebellySummaryMessageDetails) messageDetails).toPlainText();
                }

                break;

            case NODEBELLY_ANOMALY:

                String connectionName = "";
                if (meta.containsKey("targetConnectionAlias")) {
                    connectionName = ((String) meta.get("targetConnectionAlias"));
                }
               
                String inventoryName = "";
                if (meta.containsKey("targetAlias")) {
                    inventoryName =  " for " + ((String) meta.get("targetAlias"));
                }
               
                providerId = (String) meta.get("targetProviderId");
                String metricName = (String) meta.get("name");
                float fValue = ((Number) meta.get("value")).floatValue();
                float fMean = ((Number) meta.get("mean")).floatValue();
                float fStddev = ((Number) meta.get("stddev")).floatValue();

                int nStdDev = Double.valueOf(Math.floor(Math.abs(fValue - fMean) / fStddev)).intValue();

                Pair pair1 = getUnitsLabel(metricName, topMetricCriteria, fValue, true);
                //Pair pair2 = getUnitsLabel(metricName, topMetricCriteria, fStddev, true);
                Pair pair3 = getUnitsLabel(metricName, topMetricCriteria, fMean, true);

                int severity = getSeverityLevel(fValue, fMean, fStddev);
                title = MessageFormat.format((String) messageProperties.get("message.nodebelly.anomalyseverity" + severity),
                        metricTypeNameReadable(metricName, topMetricCriteria),
                        connectionName);

                String numericDetails = MessageFormat.format((String) messageProperties.get("message.nodebelly.anomaly.numericdetails"),
                        MessageUtils.roundAndTruncate(((Number) pair1.first).doubleValue(), 2),
                        pair1.second,
                        nStdDev,
                        MessageUtils.roundAndTruncate(((Number) pair3.first).doubleValue(), 2),
                        pair3.second);

                if (nStdDev > 25) {
                    numericDetails = MessageFormat.format((String) messageProperties.get("message.nodebelly.anomaly.numericdetailsBig"),
                            MessageUtils.roundAndTruncate(((Number) pair1.first).doubleValue(), 2),
                            pair1.second,
                            MessageUtils.roundAndTruncate(((Number) pair3.first).doubleValue(), 2),
                            pair3.second);
                }

                details = MessageFormat.format((String) messageProperties.get("message.nodebelly.anomaly.details"),
                        metricTypeNameReadable(metricName, topMetricCriteria),
                        numericDetails,
                        inventoryName);

                HashMap<String, Object> structure = new HashMap<>();
                structure.put("accountId", meta.get("account"));
                structure.put("name", metricTypeNameReadable(metricName, topMetricCriteria));
                structure.put("metricName", metricName);
                structure.put("metricCriteria", topMetricCriteria);
                structure.put("granularity", meta.get("granularity"));
                structure.put("value", meta.get("value"));
                structure.put("mean", meta.get("mean"));
                structure.put("stddev", meta.get("stddev"));
                structure.put("min", meta.get("min"));
                structure.put("max", meta.get("max"));
                structure.put("diff", meta.get("diff"));
                structure.put("unit", pair1.first);
               
                // set rich formatting properties
                // the client will render the table in "structure" how it wants to
                messageDetails = new NodebellyMessageDetails.Builder()
                        .title(title)
                        .details(details)
                        .structure(structure)
                        .build();

                break;

            default:
                // there really isn't this....
                title = super.doTransform(event);
        }

        // this is the plain text version, the rich message is set in the MessageDetails object
        return title + " " + details;
    }

    /*
     * Used for indexing into the correct headline for anomaly messages.
     * The returned severity is 0, 1 or 2
     */
    private int getSeverityLevel(float value, float mean, float stddev) {
        if(Math.abs(mean - value) > (stddev * 3))
            return 2;
        if(Math.abs(mean - value) > (stddev * 2))
            return 1;
        return 0;
    }

    /*
     * Make friendly names for the following. The rest are being
     * blacklisted by JuggaloaderMessageGeneratorBolt
     *
     * INVENTORY_ITEM_RESOURCE_USAGE.ID.CPUUtilization.average
     * INVENTORY_ITEM_RESOURCE_USAGE.ID.DiskReadBytes.average
     * INVENTORY_ITEM_RESOURCE_USAGE.ID.DiskReadOps.average
     * INVENTORY_ITEM_RESOURCE_USAGE.ID.WriteReadBytes.average
     * INVENTORY_ITEM_RESOURCE_USAGE.ID.DiskWriteOps.average
     * INVENTORY_ITEM_RESOURCE_USAGE.ID.NetworkIn.average
     * INVENTORY_ITEM_RESOURCE_USAGE.ID.NetworkOut.average
     * CONNECTION_ACTIVITY_COUNT.ID
     * INVENTORY_ITEM_COUNT.ID.total
     * USER_COUNT
     *
     */
    private String metricTypeNameReadable(String metricName, Map<String, String> metricCriteria) {
        // IMG Connections
        // TODO how do we handle explanation text and units?
        if (metricName.equals("CONNECTION_ACTIVITY_COUNT") && "gateway".equals(metricCriteria.get("PROVIDER_TYPE")))
            return "Custom Connection Activity";
        if (metricName.equals("CONNECTION_RESOURCE_USAGE") && metricCriteria.containsKey("RESOURCE_ID"))
            return metricCriteria.get("RESOURCE_ID");

        // Appcelerator hack. Don't remove unless you clear it with @NJH.
        if (metricCriteria.containsKey("RESOURCE_ID") && (metricCriteria.get("RESOURCE_ID").startsWith("cloud.") || metricCriteria.get("RESOURCE_ID").startsWith("ti."))) {
            return metricCriteria.get("RESOURCE_ID");
        }

        String nameFromJson = getMetricConfigValue(metricName, metricCriteria, "name");
        if (nameFromJson != null)
            return nameFromJson;

        if (metricName.startsWith("ACCOUNT_COUNT")) {
            metricName = "Account Count";
        } else if (metricName.equals("CONNECTION_ACTIVITY_COUNT") && "rss".equals(metricCriteria.get("PROVIDER_ID"))) {
            metricName = "RSS Activity";
        } else if (metricName.equals("CONNECTION_ACTIVITY_COUNT") && "github".equals(metricCriteria.get("PROVIDER_ID"))) {
            metricName = "Github Activity";
        } else if (metricName.equals("CONNECTION_ACTIVITY_COUNT") && "jira".equals(metricCriteria.get("PROVIDER_ID"))) {
            metricName = "Jira Activity";
        } else if (metricName.equals("CONNECTION_ACTIVITY_COUNT") && "aws".equals(metricCriteria.get("PROVIDER_ID"))) {
            metricName = "AWS Activity";
        } else if (metricName.equals("CONNECTION_ACTIVITY_COUNT") && metricCriteria.containsKey("CONNECTION_ID")) {
            metricName = "Connection Activity";
        } else if (metricName.equals("CONNECTION_ACTIVITY_COUNT")) {
            metricName = "Account Level Connection Activity";
        } else if (metricName.equals("CONNECTION_COUNT")) {
            metricName = "Connection Count";
        } else if (metricName.equals("INVENTORY_ITEM_COUNT")) {
            metricName = "Inventory Item Count";
        } else if (metricName.equals("INVENTORY_ITEM_ACTIVITY_COUNT")) {
            metricName = "Inventory Activity Count";
        } else if (metricName.equals("PENDING_USER_COUNT")) {
            metricName = "Pending User Count";
        } else if (metricName.equals("USER_COUNT")) {
            metricName = "User Count";
        }
        logger.error("NB: metricNameMapping: miss: metricTypeNameReadable:" + metricName + ", " + metricCriteria);
        return metricName;
    }

}
TOP

Related Classes of com.streamreduce.core.transformer.message.NodebellyMessageTransformer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.