/*
* 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.system;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.usergrid.utils.JsonUtils;
import org.apache.usergrid.utils.MapUtils;
import org.apache.usergrid.utils.TimeUtils;
import me.prettyprint.hector.api.Cluster;
import me.prettyprint.hector.api.exceptions.HectorException;
/**
* Provide a single spot for monitoring usergrid system health
*
* @author zznate
*/
public class UsergridSystemMonitor {
private static final String TIMER_THRESHOLD_TRIGGERED_MSG =
"TimerThreshold triggered on duration: %d \n%s\n----------------";
private static final Logger logger = LoggerFactory.getLogger( UsergridSystemMonitor.class );
private final String buildNumber;
private final Cluster cluster;
/** The trigger point for printing debugging information. {@see #maybeLogPayload} */
private long timerLogThreshold = 15 * 1000;
public static final String LOG_THRESHOLD_PROPERTY = "metering.request.timer.log.threshold";
/**
* Must be instantiated with a build number and a cluster to be of any use. Properties can be null. Threshold
* property must be a form compatible with {@link TimeUtils#millisFromDuration(String)}
*/
public UsergridSystemMonitor( String buildNumber, Cluster cluster, Properties properties ) {
this.buildNumber = buildNumber;
this.cluster = cluster;
if ( properties != null ) {
timerLogThreshold = TimeUtils.millisFromDuration( properties.getProperty( LOG_THRESHOLD_PROPERTY, "15s" ) );
}
}
/**
* Wraps "describe_thrift_version API call as this hits a static string in Cassandra. This is the most lightweight
* way to assure that Hector is alive and talking to the cluster.
*
* @return true if we have a lit connection to the cluster.
*/
public boolean getIsCassandraAlive() {
boolean isAlive = false;
try {
isAlive = cluster.describeThriftVersion() != null;
}
catch ( HectorException he ) {
logger.error( "Could not communicate with Cassandra cluster", he );
}
return isAlive;
}
/** @return a string representing the build number */
public String getBuildNumber() {
return buildNumber;
}
/**
* Uses {@link JsonUtils#mapToFormattedJsonString(Object)} against the object if the duration is greater than {@link
* #timerLogThreshold}. When using the varargs form, the number of elements must be even such that key,value,key,
* value mapping via {@link MapUtils#map(Object...)} can collect all the elements.
* <p/>
* Conversion to a map this way let's us lazy create the map if and only if the triggering threshold is true or we
* are in debug mode.
*/
public void maybeLogPayload( long duration, Object... objects ) {
if ( duration > timerLogThreshold || logger.isInfoEnabled() ) {
String message;
if ( objects.length > 1 ) {
message = formatMessage( duration, MapUtils.map( objects ) );
}
else {
message = formatMessage( duration, objects );
}
logger.info( message );
}
}
static String formatMessage( long duration, Object object ) {
return String.format( TIMER_THRESHOLD_TRIGGERED_MSG, duration, JsonUtils.mapToFormattedJsonString( object ) );
}
}