/* * Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com). * * Licensed 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 och.util.stopwatch; import static java.util.Collections.*; import static och.util.Util.*; import java.util.ArrayList; import java.util.List; import och.util.model.Pair; public class ThreadStopwatch { private static class State { ThreadStopwatch root; ThreadStopwatch last; State(ThreadStopwatch root) { this.root = root; this.last = root; } } private static ThreadLocal<State> threadLocal = new ThreadLocal<>(); public static ThreadStopwatch startThreadStopwatch(String name){ return startThreadStopwatch(name, true); } public static ThreadStopwatch startThreadStopwatch(String name, boolean enable){ ThreadStopwatch sw = new ThreadStopwatch(name, enable); State state = threadLocal.get(); if(state == null){ state = new State(sw); threadLocal.set(state); return sw; } sw.setParent(state.last); state.last = sw; return sw; } public static void removeThreadStopwatch(ThreadStopwatch sw){ if(sw.removed) return; sw.removed = true; //root sw if(sw.parent == null) { threadLocal.remove(); return; } //child sw State state = threadLocal.get(); if(state == null) return; state.last = sw.parent; } public static void removeAllThreadStopwatchs(){ threadLocal.remove(); } public static ThreadStopwatch getRootThreadStopwatch(){ State state = threadLocal.get(); return state == null? null : state.root; } public final String name; private boolean enable; private long start; private long worktime = -1; private ArrayList<Pair<String, String>> params; //tree elem private ThreadStopwatch parent; private List<ThreadStopwatch> children; private boolean removed; private ThreadStopwatch(String name, boolean enable){ this.name = name; this.enable = enable; this.start = System.currentTimeMillis(); } private void setParent(ThreadStopwatch parent){ this.parent = parent; this.enable = parent.enable; parent.addChild(this); } private void addChild(ThreadStopwatch child) { if(children == null) children = new ArrayList<>(); children.add(child); } public void addExternalChild(ThreadStopwatch child){ child.enable = this.enable; addChild(child); } public List<ThreadStopwatch> getChildren(){ if(children == null) return emptyList(); return unmodifiableList(children); } public void addInfo(String key, Object ob) { if(!enable) return; if(params == null) params = new ArrayList<>(); params.add(new Pair<>(key, String.valueOf(ob))); } public String stopAndGetLog(){ stop(); return getLog(); } public void stop(){ if(worktime == -1) worktime = System.currentTimeMillis() - start; } public String getLog(){ return getLog(0); } public void remove(){ stop(); removeThreadStopwatch(this); } private String getLog(int level){ if(!enable) return "stopwatch not enabled"; StringBuilder sb = new StringBuilder(); if(level == 0)sb.append("stopwatch tree: \n"); sb.append(createLevelPrefix(level)); if(level> 0)sb.append(' '); sb.append(name); sb.append(" time=").append((worktime/1000.)).append("sec"); if( ! isEmpty(params)){ for (Pair<String, String> pair : params) { sb.append(", ").append(pair.first).append('=').append(pair.second); } } if(! isEmpty(children)){ int nextLevel = level+1; for (ThreadStopwatch child : children) { sb.append('\n').append(child.getLog(nextLevel)); } } return sb.toString(); } private String createLevelPrefix(int level) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < level; i++) { sb.append('-'); } return sb.toString(); } }