001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021package org.nuxeo.ecm.core.management.events;
022
023import java.util.Collections;
024import java.util.Map;
025import java.util.Map.Entry;
026import java.util.concurrent.ConcurrentHashMap;
027
028import org.nuxeo.ecm.core.event.impl.EventListenerDescriptor;
029
030/**
031 * Helper class to store statistics about listeners calls.
032 *
033 * @author Thierry Delprat
034 */
035public class EventStatsHolder {
036
037    protected static boolean collectAsyncHandlersExecTime = false;
038
039    protected static boolean collectSyncHandlersExecTime = false;
040
041    protected static Map<String, CallStat> syncStats = new ConcurrentHashMap<>();
042
043    protected static Map<String, CallStat> aSyncStats = new ConcurrentHashMap<>();
044
045    private EventStatsHolder() {
046    }
047
048    public static boolean isCollectAsyncHandlersExecTime() {
049        return collectAsyncHandlersExecTime;
050    }
051
052    public static void setCollectAsyncHandlersExecTime(boolean collectAsyncHandlersExecTime) {
053        EventStatsHolder.collectAsyncHandlersExecTime = collectAsyncHandlersExecTime;
054    }
055
056    public static boolean isCollectSyncHandlersExecTime() {
057        return collectSyncHandlersExecTime;
058    }
059
060    public static void setCollectSyncHandlersExecTime(boolean collectSyncHandlersExecTime) {
061        EventStatsHolder.collectSyncHandlersExecTime = collectSyncHandlersExecTime;
062    }
063
064    /**
065     * @since 5.6
066     */
067    public static void clearStats() {
068        syncStats.clear();
069        aSyncStats.clear();
070    }
071
072    public static void logAsyncExec(EventListenerDescriptor desc, long delta) {
073        if (!collectAsyncHandlersExecTime) {
074            return;
075        }
076        String name = desc.getName();
077        synchronized (aSyncStats) {
078            CallStat stat = aSyncStats.get(name);
079            if (stat == null) {
080                String label = desc.asPostCommitListener().getClass().getSimpleName();
081                if (desc.getIsAsync()) {
082                    label += "(async)";
083                } else {
084                    label += "(sync)";
085                }
086                stat = new CallStat(label);
087                aSyncStats.put(name, stat);
088            }
089            stat.update(delta);
090        }
091    }
092
093    public static void logSyncExec(EventListenerDescriptor desc, long delta) {
094        if (!collectSyncHandlersExecTime) {
095            return;
096        }
097        String name = desc.getName();
098        syncStats.computeIfAbsent(name, k -> new CallStat(desc.asEventListener().getClass().getSimpleName()))
099                 .update(delta);
100    }
101
102    public static String getAsyncHandlersExecTime() {
103        return getStringSummary(aSyncStats);
104    }
105
106    /**
107     * @since 5.6
108     */
109    public static Map<String, CallStat> getAsyncHandlersCallStats() {
110        return Collections.unmodifiableMap(aSyncStats);
111    }
112
113    public static String getSyncHandlersExecTime() {
114        return getStringSummary(syncStats);
115    }
116
117    /**
118     * @since 5.6
119     */
120    public static Map<String, CallStat> getSyncHandlersCallStats() {
121        return Collections.unmodifiableMap(syncStats);
122    }
123
124    protected static String getStringSummary(Map<String, CallStat> stats) {
125        long totalTime = 0;
126        for (CallStat stat : stats.values()) {
127            totalTime += stat.getAccumulatedTime();
128        }
129        if (totalTime == 0) {
130            totalTime = 1;
131        }
132        StringBuilder sb = new StringBuilder();
133        for (Entry<String, CallStat> en : stats.entrySet()) {
134            String name = en.getKey();
135            CallStat stat = en.getValue();
136            sb.append(name);
137            sb.append(" - ");
138            sb.append(stat.getLabel());
139            sb.append(" - ");
140            sb.append(stat.getCallCount());
141            sb.append(" calls - ");
142            sb.append(stat.getAccumulatedTime());
143            sb.append("ms - ");
144            String pcent = String.format("%.2f", 100.0 * stat.getAccumulatedTime() / totalTime);
145            sb.append(pcent);
146            sb.append("%\n");
147        }
148        return sb.toString();
149    }
150
151    public static void resetHandlersExecTime() {
152        clearStats();
153    }
154
155}