/** * 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.lens.server.metrics; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; import org.apache.lens.api.query.QueryStatus.Status; import org.apache.lens.server.BaseLensService; import org.apache.lens.server.EventServiceImpl; import org.apache.lens.server.LensServices; import org.apache.lens.server.api.LensConfConstants; import org.apache.lens.server.api.events.AsyncEventListener; import org.apache.lens.server.api.events.LensEventService; import org.apache.lens.server.api.health.HealthStatus; import org.apache.lens.server.api.metastore.CubeMetastoreService; import org.apache.lens.server.api.metrics.*; import org.apache.lens.server.api.query.QueryExecutionService; import org.apache.lens.server.api.query.events.StatusChange; import org.apache.lens.server.api.session.*; import org.apache.lens.server.healthcheck.LensServiceHealthCheck; import org.apache.lens.server.query.QueryExecutionServiceImpl; import org.apache.lens.server.quota.QuotaServiceImpl; import org.apache.lens.server.scheduler.AlarmService; import org.apache.lens.server.scheduler.SchedulerServiceImpl; import org.apache.lens.server.session.DatabaseResourceService; import org.apache.lens.server.session.HiveSessionService; import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hive.service.AbstractService; import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.model.ResourceMethod; import com.codahale.metrics.*; import com.codahale.metrics.ganglia.GangliaReporter; import com.codahale.metrics.graphite.Graphite; import com.codahale.metrics.graphite.GraphiteReporter; import com.codahale.metrics.health.HealthCheckRegistry; import com.codahale.metrics.jvm.GarbageCollectorMetricSet; import com.codahale.metrics.jvm.MemoryUsageGaugeSet; import com.codahale.metrics.jvm.ThreadStatesGaugeSet; import info.ganglia.gmetric4j.gmetric.GMetric; import info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode; import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** * The Class MetricsServiceImpl. */ @Slf4j public class MetricsServiceImpl extends AbstractService implements MetricsService { /** The query status listener. */ private AsyncEventListener<StatusChange> queryStatusListener; private AsyncEventListener<SessionEvent> sessionEventListener; /** The metric registry. */ @Getter private MetricRegistry metricRegistry; /** The reporters. */ @Getter private List<ScheduledReporter> reporters; /** The health check. */ @Getter private HealthCheckRegistry healthCheck; /** The total opened sessions*/ private Counter totalOpenedSessions; /** The total closed sessions*/ private Counter totalClosedSessions; /** The total expired sessions*/ private Counter totalExpiredSessions; /** The total errors while persisting server state */ private Counter totalServerStatePersistenceErrors; /** The total accepted queries. */ private Counter totalAcceptedQueries; /** The total successful queries. */ private Counter totalSuccessfulQueries; /** The total finished queries. */ private Counter totalFinishedQueries; /** The total failed queries. */ private Counter totalFailedQueries; /** The total cancelled queries. */ private Counter totalCancelledQueries; /** The total errors while loading database resources */ private Counter totalDatabaseResourceLoadErrors; /** The opened sessions */ private Gauge<Integer> activeSessions; /** The queued queries. */ private Gauge<Long> queuedQueries; /** The running queries. */ private Gauge<Long> runningQueries; /** The waiting queries. */ private Gauge<Long> waitingQueries; /** The queries being launched. */ private Gauge<Long> launchingQueries; /** The finished queries. */ private Gauge<Long> finishedQueries; /** All method meters. Factory for creation + caching */ @Getter private MethodMetricsFactory methodMetricsFactory; @Getter private boolean enableResourceMethodMetering; public void setEnableResourceMethodMetering(boolean enableResourceMethodMetering) { log.info("setEnableResourceMethodMetering: " + enableResourceMethodMetering); this.enableResourceMethodMetering = enableResourceMethodMetering; if (!enableResourceMethodMetering) { methodMetricsFactory.clear(); } } /** * The listener interface for receiving asyncQueryStatus events. The class that is interested in processing a * asyncQueryStatus event implements this interface, and the object created with that class is registered with a * component using the component's <code>addAsyncQueryStatusListener<code> method. * When the asyncQueryStatus event occurs, that object's appropriate method is invoked. */ public class AsyncQueryStatusListener extends AsyncEventListener<StatusChange> { /* * (non-Javadoc) * * @see org.apache.lens.server.api.events.AsyncEventListener#process(org.apache.lens.server.api.events.LensEvent) */ @Override public void process(StatusChange event) { processCurrentStatus(event.getCurrentValue()); } /** * Process current status. * * @param currentValue the current value */ protected void processCurrentStatus(Status currentValue) { switch (currentValue) { case QUEUED: totalAcceptedQueries.inc(); break; case CANCELED: totalCancelledQueries.inc(); totalFinishedQueries.inc(); break; case FAILED: totalFailedQueries.inc(); totalFinishedQueries.inc(); break; case SUCCESSFUL: totalSuccessfulQueries.inc(); totalFinishedQueries.inc(); break; default: break; } } } /** * The listener interface for receiving asyncSession events. The class that is interested in processing a * asyncSession event implements this interface, and the object created with that class is registered with a * component using the component's <code>addAsyncSessionEventListener<code> method. * When the asyncSessionEvent event occurs, that object's appropriate method is invoked. */ public class AsyncSessionEventListener extends AsyncEventListener<SessionEvent> { /* * (non-Javadoc) * * @see org.apache.lens.server.api.events.AsyncEventListener#process(org.apache.lens.server.api.events.LensEvent) */ @Override public void process(SessionEvent event) { if (event instanceof SessionOpened) { totalOpenedSessions.inc(); } else if (event instanceof SessionExpired) { totalExpiredSessions.inc(); totalClosedSessions.inc(); } else if (event instanceof SessionClosed) { totalClosedSessions.inc(); } } } /** * Instantiates a new metrics service impl. * * @param name the name */ public MetricsServiceImpl(String name) { super(NAME); } private QueryExecutionService getQuerySvc() { return LensServices.get().getService(QueryExecutionService.NAME); } /** The time between polls. */ private static int timeBetweenPolls = 10; /* * (non-Javadoc) * * @see org.apache.hive.service.AbstractService#init(org.apache.hadoop.hive.conf.HiveConf) */ @Override public synchronized void init(HiveConf hiveConf) { queryStatusListener = new AsyncQueryStatusListener(); sessionEventListener = new AsyncSessionEventListener(); LensEventService eventService = LensServices.get().getService(LensEventService.NAME); eventService.addListenerForType(queryStatusListener, StatusChange.class); eventService.addListenerForType(sessionEventListener, SessionEvent.class); metricRegistry = LensMetricsRegistry.getStaticRegistry(); methodMetricsFactory = new MethodMetricsFactory(metricRegistry); setEnableResourceMethodMetering(hiveConf.getBoolean(LensConfConstants.ENABLE_RESOURCE_METHOD_METERING, false)); healthCheck = new HealthCheckRegistry(); healthCheck.register(CubeMetastoreService.NAME, new LensServiceHealthCheck(CubeMetastoreService.NAME)); healthCheck.register(HiveSessionService.NAME, new LensServiceHealthCheck(HiveSessionService.NAME)); healthCheck.register(QueryExecutionServiceImpl.NAME, new LensServiceHealthCheck(QueryExecutionServiceImpl.NAME)); healthCheck.register(SchedulerServiceImpl.NAME, new LensServiceHealthCheck(SchedulerServiceImpl.NAME)); healthCheck.register(QuotaServiceImpl.NAME, new LensServiceHealthCheck(QuotaServiceImpl.NAME)); healthCheck.register(MetricsServiceImpl.NAME, new LensServiceHealthCheck(MetricsServiceImpl.NAME)); healthCheck.register(EventServiceImpl.NAME, new LensServiceHealthCheck(EventServiceImpl.NAME)); healthCheck.register(AlarmService.NAME, new LensServiceHealthCheck(AlarmService.NAME)); initCounters(); timeBetweenPolls = hiveConf.getInt(LensConfConstants.REPORTING_PERIOD, 10); reporters = new ArrayList<ScheduledReporter>(); if (hiveConf.getBoolean(LensConfConstants.ENABLE_CONSOLE_METRICS, false)) { // Start console reporter ConsoleReporter reporter = ConsoleReporter.forRegistry(metricRegistry).convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS).build(); reporters.add(reporter); } if (hiveConf.getBoolean(LensConfConstants.ENABLE_CSV_METRICS, false)) { File f = new File(hiveConf.get(LensConfConstants.CSV_METRICS_DIRECTORY_PATH, "metrics/")); f.mkdirs(); CsvReporter reporter = CsvReporter.forRegistry(metricRegistry) .formatFor(Locale.US) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(f); reporters.add(reporter); } if (hiveConf.getBoolean(LensConfConstants.ENABLE_GANGLIA_METRICS, false)) { GMetric ganglia; try { ganglia = new GMetric(hiveConf.get(LensConfConstants.GANGLIA_SERVERNAME), hiveConf.getInt( LensConfConstants.GANGLIA_PORT, LensConfConstants.DEFAULT_GANGLIA_PORT), UDPAddressingMode.MULTICAST, 1); GangliaReporter greporter = GangliaReporter.forRegistry(metricRegistry).convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS).build(ganglia); reporters.add(greporter); } catch (IOException e) { log.error("Could not start ganglia reporter", e); } } if (hiveConf.getBoolean(LensConfConstants.ENABLE_GRAPHITE_METRICS, false)) { final GraphiteReporter reporter; try { Graphite graphite = new Graphite(new InetSocketAddress(hiveConf.get(LensConfConstants.GRAPHITE_SERVERNAME), hiveConf.getInt(LensConfConstants.GRAPHITE_PORT, LensConfConstants.DEFAULT_GRAPHITE_PORT))); reporter = GraphiteReporter.forRegistry(metricRegistry) .prefixedWith(InetAddress.getLocalHost().getHostName()) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .build(graphite); reporters.add(reporter); } catch (UnknownHostException e) { log.error("Couldn't get localhost. So couldn't setup graphite reporting", e); } } log.info("Started metrics service"); super.init(hiveConf); } /** * Inits the counters. */ protected void initCounters() { activeSessions = metricRegistry.register(MetricRegistry.name(SessionService.class, ACTIVE_SESSIONS), new Gauge<Integer>() { @Override public Integer getValue() { return BaseLensService.getNumberOfSessions(); } }); queuedQueries = metricRegistry.register(MetricRegistry.name(QueryExecutionService.class, QUEUED_QUERIES), new Gauge<Long>() { @Override public Long getValue() { return getQuerySvc().getQueuedQueriesCount(); } }); runningQueries = metricRegistry.register(MetricRegistry.name(QueryExecutionService.class, RUNNING_QUERIES), new Gauge<Long>() { @Override public Long getValue() { return getQuerySvc().getRunningQueriesCount(); } }); waitingQueries = metricRegistry.register(MetricRegistry.name(QueryExecutionService.class, WAITING_QUERIES), new Gauge<Long>() { @Override public Long getValue() { return getQuerySvc().getWaitingQueriesCount(); } }); launchingQueries = metricRegistry.register(MetricRegistry.name(QueryExecutionService.class, LAUNCHING_QUERIES), new Gauge<Long>() { @Override public Long getValue() { return getQuerySvc().getLaunchingQueriesCount(); } }); finishedQueries = metricRegistry.register(MetricRegistry.name(QueryExecutionService.class, FINISHED_QUERIES), new Gauge<Long>() { @Override public Long getValue() { return getQuerySvc().getFinishedQueriesCount(); } }); totalDatabaseResourceLoadErrors = metricRegistry.counter(MetricRegistry.name(DatabaseResourceService.class, DatabaseResourceService.LOAD_RESOURCES_ERRORS)); totalAcceptedQueries = metricRegistry.counter(MetricRegistry.name(QueryExecutionService.class, "total-" + ACCEPTED_QUERIES)); totalSuccessfulQueries = metricRegistry.counter(MetricRegistry.name(QueryExecutionService.class, "total-success-queries")); totalFinishedQueries = metricRegistry.counter(MetricRegistry.name(QueryExecutionService.class, "total-" + FINISHED_QUERIES)); totalFailedQueries = metricRegistry.counter(MetricRegistry.name(QueryExecutionService.class, "total-" + FAILED_QUERIES)); totalCancelledQueries = metricRegistry.counter(MetricRegistry.name(QueryExecutionService.class, "total-" + CANCELLED_QUERIES)); totalOpenedSessions = metricRegistry.counter(MetricRegistry.name(QueryExecutionService.class, "total-" + OPENED_SESSIONS)); totalClosedSessions = metricRegistry.counter(MetricRegistry.name(QueryExecutionService.class, "total-" + CLOSED_SESSIONS)); totalExpiredSessions = metricRegistry.counter(MetricRegistry.name(QueryExecutionService.class, "total-" + EXPIRED_SESSIONS)); totalServerStatePersistenceErrors = metricRegistry.counter(MetricRegistry.name(LensServices.class, LensServices.SERVER_STATE_PERSISTENCE_ERRORS)); metricRegistry.register("gc", new GarbageCollectorMetricSet()); metricRegistry.register("memory", new MemoryUsageGaugeSet()); metricRegistry.register("threads", new ThreadStatesGaugeSet()); metricRegistry.register("jvm", new JvmAttributeGaugeSet()); } /** * Called by LensRequestListener in start event. marks the method meter, starts a timer, returns timer context * returned by the start timer call. * * @param method the resource method * @param containerRequest container request * @return * @see org.apache.lens.server.api.metrics.MetricsService#getMethodMetricsContext( *org.glassfish.jersey.server.model.ResourceMethod, org.glassfish.jersey.server.ContainerRequest) */ @Override public MethodMetricsContext getMethodMetricsContext(ResourceMethod method, ContainerRequest containerRequest) { // if method is null then it means no matching resource method was found. return enableResourceMethodMetering ? methodMetricsFactory.get(method, containerRequest).newContext() : DisabledMethodMetricsContext.getInstance(); } /* * (non-Javadoc) * * @see org.apache.hive.service.AbstractService#start() */ @Override public synchronized void start() { for (ScheduledReporter reporter : reporters) { reporter.start(timeBetweenPolls, TimeUnit.SECONDS); } super.start(); } /* * (non-Javadoc) * * @see org.apache.hive.service.AbstractService#stop() */ @Override public synchronized void stop() { // unregister LensEventService eventService = LensServices.get().getService(LensEventService.NAME); if (eventService != null) { eventService.removeListener(queryStatusListener); } if (queryStatusListener != null) { queryStatusListener.stop(); } if (reporters != null) { for (ScheduledReporter reporter : reporters) { reporter.stop(); } } log.info("Stopped metrics service"); super.stop(); } /* * (non-Javadoc) * * @see org.apache.lens.server.api.metrics.MetricsService#incrCounter(java.lang.String) */ @Override public void incrCounter(String counter) { incrCounter(MetricsService.class, counter); } /* * (non-Javadoc) * * @see org.apache.lens.server.api.metrics.MetricsService#decrCounter(java.lang.String) */ @Override public void decrCounter(String counter) { decrCounter(MetricsService.class, counter); } /* * (non-Javadoc) * * @see org.apache.lens.server.api.metrics.MetricsService#incrCounter(java.lang.Class, java.lang.String) */ @Override public void incrCounter(Class<?> cls, String counter) { metricRegistry.counter(MetricRegistry.name(cls, counter)).inc(); } /* * (non-Javadoc) * * @see org.apache.lens.server.api.metrics.MetricsService#decrCounter(java.lang.Class, java.lang.String) */ @Override public void decrCounter(Class<?> cls, String counter) { metricRegistry.counter(MetricRegistry.name(cls, counter)).dec(); } /* * (non-Javadoc) * * @see org.apache.lens.server.api.metrics.MetricsService#getCounter(java.lang.String) */ @Override public long getCounter(String counter) { return getCounter(MetricsService.class, counter); } /* * (non-Javadoc) * * @see org.apache.lens.server.api.metrics.MetricsService#getCounter(java.lang.Class, java.lang.String) */ @Override public long getCounter(Class<?> cls, String counter) { return metricRegistry.counter(MetricRegistry.name(cls, counter)).getCount(); } @Override public long getTotalDatabaseResourceLoadErrors() { return totalDatabaseResourceLoadErrors.getCount(); } @Override public long getQueuedQueries() { return queuedQueries.getValue(); } @Override public long getRunningQueries() { return runningQueries.getValue(); } @Override public long getWaitingQueries() { return waitingQueries.getValue(); } @Override public long getFinishedQueries() { return finishedQueries.getValue(); } @Override public long getTotalAcceptedQueries() { return totalAcceptedQueries.getCount(); } @Override public long getTotalFinishedQueries() { return totalFinishedQueries.getCount(); } @Override public long getTotalCancelledQueries() { return totalCancelledQueries.getCount(); } @Override public long getTotalFailedQueries() { return totalFailedQueries.getCount(); } @Override public int getActiveSessions() { return activeSessions.getValue(); } @Override public HealthStatus getHealthStatus() { boolean isHealthy = true; StringBuilder details = new StringBuilder(); if (!this.getServiceState().equals(STATE.STARTED)) { details.append("Metric service is down."); isHealthy = false; } if (!isHealthy) { log.error(details.toString()); } return isHealthy ? new HealthStatus(true, "Metric service is healthy.") : new HealthStatus(false, details.toString()); } @Override public long getTotalOpenedSessions() { return totalOpenedSessions.getCount(); } @Override public long getTotalClosedSessions() { return totalClosedSessions.getCount(); } @Override public long getTotalExpiredSessions() { return totalExpiredSessions.getCount(); } @Override public long getTotalServerStatePersistenceErrors() { return totalServerStatePersistenceErrors.getCount(); } /* * (non-Javadoc) * * @see org.apache.lens.server.api.metrics.MetricsService#publishReport() */ @Override public void publishReport() { if (reporters != null) { for (ScheduledReporter reporter : reporters) { reporter.report(); } } } @Override public long getTotalSuccessfulQueries() { return totalSuccessfulQueries.getCount(); } }