The complete MoSKito integration guide – Step 4 – Central storage

Today we are going to unlock even more MoSKito power: more persisting, more centralising.

As you know, we can access MoSKito producer’s statistics while application is running: http://localhost:8080/burgershop/mui/mskShowAllProducers.

But what if: 

  • we want to check statistics for some time ago, like the last night, when we were sleeping?
  • we want to have and check statistics for all our applications, not only a single burgershop (as good businessmen, we surely have various projects to be safe from total fail one day!)?
  • we want to automatically analyse the collected (in time and space) data and make
    some strategical changes, based on results of this analysis?

The answer to all these if’s is: MoSKito-Central.

In a few words, MoSKito-Central is a service (remote or embedded) that receives your MoSKito statistics and stores it in the place of your choice (Filesystem, Database, …).

MoSKito-Central Overview

And… yahoo, we are going to integrate it into our burgershop in embedded mode right now! One fine day we plan to switch it to remote mode, of course.

Before we start, make sure you have the burgershop project locally in a proper state (from step 3), or catch up with us, using the following commands:

git clone https://github.com/dvayanu/burgershop
cd burgershop
git checkout moskito-integration-guide-step3

I. Adding dependencies

<properties>
    ...
    <moskito-central.version>1.1.0</moskito-central.version>
</properties>
...
<dependency>
    <groupId>org.moskito</groupId>
    <artifactId>moskito-central-embedded-connector</artifactId>
    <version>${moskito-central.version}</version>
</dependency>
<dependency>
    <groupId>org.moskito</groupId>
    <artifactId>moskito-central-storages</artifactId>
    <version>${moskito-central.version}</version>
</dependency>

We have added moskito-central.version property, dependencies to the embedded connector and implementations of storages.

II. Adding configurations

Now we’re going to say:

  • “Burgershop, please accept MoSKito-Central as a part of your…”
  • “MoSKito-Central, please store MoSKito statistics in the filesystem like this and like that…”

Let’s create resources directory and put some new config files there.

mkdir ./src/main/resources

moskito.json

{
    "@pluginsConfig": {
        "@plugins": [
            {
                "name": "EmbeddedCentralConnector",
                "configurationName": "none",
                "className": "org.moskito.central.connectors.embedded.EmbeddedConnector"
            }
        ]
    }
},

This plugs the embedded MoSKito-Central connector to the MoSKitorized application.

moskito-central.json

{
    "@storages": [
        {
            "name": "json-file",
            "clazz": "org.moskito.central.storage.fs.FileSystemStorage",
            "configName": "moskito-fs"
        },
        {
            "name": "csv-file",
            "clazz": "org.moskito.central.storage.fs.CSVFileStorage",
            "configName": "moskito-csv"
        }
    ]
}

This configures the list of storages (config file name and implementation class). For simplicity, we added two file storages (json and csv). For a full storages list, please check documentation or sources.

moskito-fs.json

{
    "pattern": "/tmp/moskito-central/json/{host}/{component}/{producer}/{interval}/{date}/{date}_{time}_{producer}.json",
    "serializer": "org.moskito.central.storage.serializer.GsonSerializer",
    "includeIntervals": "*",
    "excludeIntervals": "15m",
    "includeProducers": "*",
    "excludeProducers": ""
}

With this, we configure the storage path, json serializer implementation and statistics intervals for json storage.

moskito-csv.json

{
    "pattern": "/tmp/moskito-central/csv/{host}/{component}/{producer}/{interval}/{producer}_{stat}.csv",
    "@entries": [
        {
            "includedProducers": "SessionCount",
            "includedStats": "*",
            "includedIntervals": "*"
        },
        {
            "includedProducers": "RequestURIFilter",
            "includedStats": "cumulated",
            "includedIntervals": "5m,1h"
        },
        {
            "includedProducers": "sales",
            "includedStats": "*",
            "includedIntervals": "1m,1h"
        }
    ]
}

This is to configure the storage path, producers and intervals for csv storage.

III. Implement get sales values

To have a complete sales statistics stored in the central, we have to say what values it supports, by implementing getAvailableValueNames() and getValueByNameAsString(..) methods. Check updated SalesStats:

package de.zaunberg.burgershop.service.stats;

import net.anotheria.moskito.core.predefined.Constants;
import net.anotheria.moskito.core.producers.GenericStats;
import net.anotheria.moskito.core.stats.StatValue;
import net.anotheria.moskito.core.stats.TimeUnit;
import net.anotheria.moskito.core.stats.impl.StatValueFactory;
import net.anotheria.moskito.webui.decorators.DecoratorRegistryFactory;

import java.util.ArrayList;
import java.util.List;

public class SalesStats extends GenericStats{

	public static enum StatDef {
		NUMBER("Number"),
		VOLUME("Volume");

		private String statName;

		private StatDef(final String aStatName) {
			statName = aStatName;
		}

		public String getStatName() {
			return statName;
		}

		public static List<String> getStatNames() {
			List<String> ret = new ArrayList<String>(StatDef.values().length);
			for (StatDef value : StatDef.values()) {
				ret.add(value.getStatName());
			}
			return ret;
		}

		public static StatDef getValueByName(String statName) {
			for (StatDef value : StatDef.values()) {
				if (value.getStatName().equals(statName)) {
					return value;
				}
			}
			throw new IllegalArgumentException("No such value with name: " + statName);
		}
	}

	static{
		DecoratorRegistryFactory.getDecoratorRegistry().addDecorator(SalesStats.class, new SalesStatsDecorator());
	}

	/**
	 * The number of sales.
	 */
	private StatValue number;

	/**
	 * The volume of sales.
	 */
	private StatValue volume;

	public SalesStats(String name) {
		super(name);

		number = StatValueFactory.createStatValue(Long.valueOf(0), StatDef.NUMBER.getStatName(), Constants.getDefaultIntervals());
		volume = StatValueFactory.createStatValue(Long.valueOf(0), StatDef.VOLUME.getStatName(), Constants.getDefaultIntervals());
	}

	public void addSale(int priceInCents){
		number.increase();
		volume.increaseByInt(priceInCents);
	}

	public long getNumber(String intervalName){
		return number.getValueAsLong(intervalName);
	}

	public long getVolume(String intervalName){
		return volume.getValueAsLong(intervalName);
	}

	public double getAverageVolume(String intervalName){
		return (double)getVolume(intervalName) / getNumber(intervalName);
	}

	@Override
	public String toStatsString(String s, TimeUnit timeUnit) {
		return null;
	}

	@Override
	public String getValueByNameAsString(String valueName, String intervalName, TimeUnit timeUnit) {
		StatDef statDef = StatDef.getValueByName(valueName);
		switch (statDef) {
			case NUMBER:
				return number.getValueAsString(intervalName);
			case VOLUME:
				return volume.getValueAsString(intervalName);
			default:
				return super.getValueByNameAsString(valueName, intervalName, timeUnit);
		}
	}

	@Override
	public List<String> getAvailableValueNames() {
		return StatDef.getStatNames();
	}
}

IV. Run-run-run!

Rebuild, deploy your project and order some new burgers, even if you are not so hungry ;):

http://localhost:8080/burgershop/order.html?choice1=brioche&choice2=dog&choice3=cockroach
http://localhost:8080/burgershop/order.html?choice1=brioche&choice2=lamb&choice3=cockroach

And… after a few minutes, you will find the new data files and directories in your local /tmp/moskito-central folder. It will look like this:

moskito-central-storage

31_01_2014_10_23_sales.json

{
  "metaData": {
    "producerId": "sales",
    "componentName": "app",
    "hostName": "vkazhdan-book",
    "intervalName": "1m",
    "creationTimestamp": 1391156610369,
    "arrivalTimestamp": 1391156610401,
    "category": "business",
    "subsystem": "sales",
    "statClassName": "de.zaunberg.burgershop.service.stats.SalesStats"
  },
  "stats": {
    "cumulated": {
      "Number": "2",
      "Volume": "7509"
    },
    "brioche": {
      "Number": "2",
      "Volume": "7509"
    },
    "cockroach": {
      "Number": "2",
      "Volume": "7509"
    },
    "lamb": {
      "Number": "1",
      "Volume": "4254"
    },
    "dog": {
      "Number": "1",
      "Volume": "3255"
    }
  }
}

sales_brioche.csv

sales_brioche.csv

So, that’s it for today’s class. We’ve learned how to deploy the Embedded MoSKito-Central, with the burgershop (same for any other) application.

Enjoy!

3 thoughts on “The complete MoSKito integration guide – Step 4 – Central storage

  1. Yes, you can, Michael, using storage config like this:
    moskito-central.json:

    {
    “name”: “psql-db”,
    “clazz”: “org.moskito.central.storage.psql.PSQLStorage”,
    “configName”: “moskito-psql-hibernate”
    },

    moskito-psql-hibernate.json:
    {
    driver:”org.hsqldb.jdbc.JDBCDriver”,
    url:”jdbc:hsqldb:/tmp/hsqldb-central”,
    userName:”daa”,
    password:”daa”,
    persistenceUnitName:”hsqldbSnapshotStorage”,
    “@mappings” : [
    {
    “producerName”: “SessionCount”,
    “statEntityClass”:”org.moskito.central.storage.psql.HttpSessionStatisticsEntity”
    },
    {
    “producerName”: “testProducerId,*API,*Service*”,
    “statEntityClass”: “org.moskito.central.storage.psql.ServiceStatsEntity”
    },
    ]
    }

    Please, check https://confluence.opensource.anotheria.net/display/MSK/Configure+MoSKito-Central+Storage
    for up-to-date information

  2. Pingback: The complete MoSKito integration guide – Step 5 – MoSKito-Central in remote mode | anotheria devblog

Leave a Reply

Your email address will not be published. Required fields are marked *

*