/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.usergrid.tools;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.usergrid.persistence.index.query.CounterResolution;
import org.apache.usergrid.management.ApplicationInfo;
import org.apache.usergrid.management.OrganizationInfo;
import org.apache.usergrid.management.UserInfo;
import org.apache.usergrid.persistence.AggregateCounter;
import org.apache.usergrid.tools.bean.MetricLine;
import org.apache.usergrid.tools.bean.MetricQuery;
import org.apache.usergrid.tools.bean.MetricSort;
import org.apache.usergrid.utils.TimeUtils;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.lang.time.DateUtils;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.BiMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Ordering;
/**
* Tools class which dumps metrics for tracking Usergrid developer adoption and high-level application usage.
* <p/>
* Can be called thusly: mvn exec:java -Dexec.mainClass="org.apache.usergrid.tools.Command" -Dexec.args="Metrics -host
* localhost -outputDir ./output"
*
* @author zznate
*/
public class Metrics extends ExportingToolBase {
private List<OrganizationInfo> organizations;
private ListMultimap<UUID, ApplicationInfo> orgApps = ArrayListMultimap.create();
private ListMultimap<Long, UUID> totalScore = ArrayListMultimap.create();
private Map<UUID, MetricLine> collector = new HashMap<UUID, MetricLine>();
private int reportThreshold = 100;
private long startDate;
private long endDate;
@Override
public void runTool( CommandLine line ) throws Exception {
startSpring();
setVerbose( line );
prepareBaseOutputFileName( line );
parseDuration( line );
applyOrgId( line );
parseDateRange( line );
outputDir = createOutputParentDir();
logger.info( "Export directory: {}", outputDir.getAbsolutePath() );
if ( orgId == null ) {
organizations = managementService.getOrganizations( null, 20000 );
for ( OrganizationInfo organization : organizations ) {
logger.info( "Org Name: {} key: {}", organization.getName(), organization.getUuid() );
//List<UserInfo> adminUsers = managementService.getAdminUsersForOrganization(orgId);
applicationsFor( organization.getUuid() );
}
}
else {
OrganizationInfo orgInfo = managementService.getOrganizationByUuid( orgId );
applicationsFor( orgInfo.getUuid() );
organizations = new ArrayList<OrganizationInfo>();
organizations.add( orgInfo );
}
Iterable<OrganizationInfo> workingOrgs = applyThreshold();
printReport( MetricSort.APP_REQ_COUNT, workingOrgs );
}
@Override
public Options createOptions() {
Options options = super.createOptions();
Option duration = OptionBuilder.hasArg().withDescription( "A duration signifying the previous time until now. "
+ "Supported forms: h,m,d eg. '30d' would be 30 days" ).create( "duration" );
Option startDate =
OptionBuilder.hasArg().withDescription( "The start date of the report" ).create( "startDate" );
Option endDate = OptionBuilder.hasArg().withDescription( "The end date of the report" ).create( "endDate" );
options.addOption( duration ).addOption( endDate ).addOption( startDate );
return options;
}
/** 30 days in milliseconds by default */
private void parseDuration( CommandLine line ) {
String duration = line.getOptionValue( "duration" );
if ( duration != null ) {
startDate = TimeUtils.millisFromDuration( duration );
endDate = System.currentTimeMillis();
}
}
private void parseDateRange( CommandLine line ) throws Exception {
if ( line.hasOption( "startDate" ) ) {
startDate = DateUtils.parseDate( line.getOptionValue( "startDate" ), new String[] { "yyyyMMdd-HHmm" } )
.getTime();
}
if ( line.hasOption( "endDate" ) ) {
endDate =
DateUtils.parseDate( line.getOptionValue( "endDate" ), new String[] { "yyyyMMdd-HHmm" } ).getTime();
}
}
private Iterable<OrganizationInfo> applyThreshold() throws Exception {
Set<OrganizationInfo> orgs = new HashSet<OrganizationInfo>( reportThreshold );
for ( Long l : Ordering.natural().greatestOf( totalScore.keys(), reportThreshold ) ) {
List<UUID> apps = totalScore.get( l );
for ( UUID appId : apps ) {
orgs.add( managementService.getOrganizationForApplication( appId ) );
}
}
return orgs;
}
private void printReport( MetricSort metricSort, Iterable<OrganizationInfo> workingOrgs ) throws Exception {
JsonGenerator jg = getJsonGenerator( createOutputFile( "metrics", metricSort.name().toLowerCase() ) );
jg.writeStartObject();
jg.writeStringField( "report", metricSort.name() );
jg.writeStringField( "date", new Date().toString() );
jg.writeArrayFieldStart( "orgs" );
for ( OrganizationInfo org : workingOrgs ) {
jg.writeStartObject();
jg.writeStringField( "org_id", org.getUuid().toString() );
jg.writeStringField( "org_name", org.getName() );
jg.writeArrayFieldStart( "admins" );
for ( UserInfo userInfo : managementService.getAdminUsersForOrganization( org.getUuid() ) ) {
jg.writeString( userInfo.getEmail() );
}
jg.writeEndArray();
writeAppLines( jg, org.getUuid() );
jg.writeEndObject();
}
jg.writeEndArray();
jg.writeEndObject();
jg.close();
}
private void writeAppLines( JsonGenerator jg, UUID orgId ) throws Exception {
jg.writeArrayFieldStart( "apps" );
for ( ApplicationInfo appInfo : orgApps.get( orgId ) ) {
jg.writeStartObject();
jg.writeStringField( "app_id", appInfo.getId().toString() );
jg.writeStringField( "app_name", appInfo.getName() );
jg.writeArrayFieldStart( "counts" );
MetricLine line = collector.get( appInfo.getId() );
if ( line != null ) {
jg.writeStartObject();
for ( AggregateCounter ag : line.getAggregateCounters() ) {
jg.writeStringField( new Date( ag.getTimestamp() ).toString(), Long.toString( ag.getValue() ) );
}
jg.writeEndObject();
}
jg.writeEndArray();
jg.writeEndObject();
}
jg.writeEndArray();
}
private void applicationsFor( UUID orgId ) throws Exception {
BiMap<UUID, String> applications = managementService.getApplicationsForOrganization( orgId );
for ( UUID uuid : applications.keySet() ) {
logger.info( "Checking app: {}", applications.get( uuid ) );
orgApps.put( orgId, new ApplicationInfo( uuid, applications.get( uuid ) ) );
collect( MetricQuery.getInstance( uuid, MetricSort.APP_REQ_COUNT ).resolution( CounterResolution.DAY )
.startDate( startDate ).endDate( endDate ).execute( emf.getEntityManager( uuid ) ) );
}
}
private void collect( MetricLine metricLine ) {
for ( AggregateCounter a : metricLine.getAggregateCounters() ) {
logger.info( "col: {} val: {}", new Date( a.getTimestamp() ), a.getValue() );
}
totalScore.put( metricLine.getCount(), metricLine.getAppId() );
collector.put( metricLine.getAppId(), metricLine );
}
// line format: {reportQuery: application.requests, date: date, startDate : startDate, endDate: endDate, orgs : [
// {orgId: guid, orgName: name, apps [{appId: guid, appName: name, dates: [{"[human date from ts]" : "[value]"},{...
}