/* * 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 com.alipay.sofa.common.profile.diagnostic; import com.alipay.sofa.common.utils.ObjectUtil; import com.alipay.sofa.common.utils.StringUtil; import com.alipay.sofa.common.profile.enumeration.IntegerEnum; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author luoguimu123 * @version $Id: Profiler.java, v 0.1 2017年08月01日 上午11:30 luoguimu123 Exp $ */ public final class Profiler { private static final ThreadLocal entryStack = new ThreadLocal(); public Profiler() { } public static void start() { start((String) null); } public static void start(String message) { entryStack.set(new Profiler.Entry(message, null, null)); } public static void start(Profiler.Message message) { entryStack.set(new Profiler.Entry(message, null, null)); } public static void reset() { entryStack.set((Object) null); } public static void enter(String message) { Profiler.Entry currentEntry = getCurrentEntry(); if (currentEntry != null) { currentEntry.enterSubEntry(message); } } public static void enter(Profiler.Message message) { Profiler.Entry currentEntry = getCurrentEntry(); if (currentEntry != null) { currentEntry.enterSubEntry(message); } } public static void release() { Profiler.Entry currentEntry = getCurrentEntry(); if (currentEntry != null) { currentEntry.release(); } } public static long getDuration() { Profiler.Entry entry = (Profiler.Entry) entryStack.get(); return entry != null ? entry.getDuration() : -1L; } public static String dump() { return dump("", ""); } public static String dump(String prefix) { return dump(prefix, prefix); } public static String dump(String prefix1, String prefix2) { Profiler.Entry entry = (Profiler.Entry) entryStack.get(); return entry != null ? entry.toString(prefix1, prefix2) : ""; } public static Profiler.Entry getEntry() { return (Profiler.Entry) entryStack.get(); } private static Profiler.Entry getCurrentEntry() { Profiler.Entry subEntry = (Profiler.Entry) entryStack.get(); Profiler.Entry entry = null; if (subEntry != null) { do { entry = subEntry; subEntry = subEntry.getUnreleasedEntry(); } while (subEntry != null); } return entry; } public interface Message { Profiler.MessageLevel getMessageLevel(Profiler.Entry var1); String getBriefMessage(); String getDetailedMessage(); } public static final class MessageLevel extends IntegerEnum { private static final long serialVersionUID = 3257849896026388537L; public static final Profiler.MessageLevel NO_MESSAGE = (Profiler.MessageLevel) create(); public static final Profiler.MessageLevel BRIEF_MESSAGE = (Profiler.MessageLevel) create(); public static final Profiler.MessageLevel DETAILED_MESSAGE = (Profiler.MessageLevel) create(); public MessageLevel() { } } public static final class Entry { private final List subEntries; private final Object message; private final Profiler.Entry parentEntry; private final Profiler.Entry firstEntry; private final long baseTime; private final long startTime; private long endTime; private Entry(Object message, Profiler.Entry parentEntry, Profiler.Entry firstEntry) { this.subEntries = new ArrayList(4); this.message = message; this.startTime = System.currentTimeMillis(); this.parentEntry = parentEntry; this.firstEntry = (Profiler.Entry) ObjectUtil.defaultIfNull(firstEntry, this); this.baseTime = firstEntry == null ? 0L : firstEntry.startTime; } public String getMessage() { String messageString = null; if (this.message instanceof String) { messageString = (String) this.message; } else if (this.message instanceof Profiler.Message) { Profiler.Message messageObject = (Profiler.Message) this.message; Profiler.MessageLevel level = Profiler.MessageLevel.BRIEF_MESSAGE; if (this.isReleased()) { level = messageObject.getMessageLevel(this); } if (level == Profiler.MessageLevel.DETAILED_MESSAGE) { messageString = messageObject.getDetailedMessage(); } else { messageString = messageObject.getBriefMessage(); } } return StringUtil.defaultIfEmpty(messageString, (String) null); } public long getStartTime() { return this.baseTime > 0L ? this.startTime - this.baseTime : 0L; } public long getEndTime() { return this.endTime < this.baseTime ? -1L : this.endTime - this.baseTime; } public long getDuration() { return this.endTime < this.startTime ? -1L : this.endTime - this.startTime; } public long getDurationOfSelf() { long duration = this.getDuration(); if (duration < 0L) { return -1L; } else if (this.subEntries.isEmpty()) { return duration; } else { for (int i = 0; i < this.subEntries.size(); ++i) { Profiler.Entry subEntry = (Profiler.Entry) this.subEntries.get(i); duration -= subEntry.getDuration(); } return duration < 0L ? -1L : duration; } } public double getPecentage() { double parentDuration = 0.0D; double duration = (double) this.getDuration(); if (this.parentEntry != null && this.parentEntry.isReleased()) { parentDuration = (double) this.parentEntry.getDuration(); } return duration > 0.0D && parentDuration > 0.0D ? duration / parentDuration : 0.0D; } public double getPecentageOfAll() { double firstDuration = 0.0D; double duration = (double) this.getDuration(); if (this.firstEntry != null && this.firstEntry.isReleased()) { firstDuration = (double) this.firstEntry.getDuration(); } return duration > 0.0D && firstDuration > 0.0D ? duration / firstDuration : 0.0D; } public List getSubEntries() { return Collections.unmodifiableList(this.subEntries); } private void release() { this.endTime = System.currentTimeMillis(); } private boolean isReleased() { return this.endTime > 0L; } private void enterSubEntry(Object message) { Profiler.Entry subEntry = new Profiler.Entry(message, this, this.firstEntry); this.subEntries.add(subEntry); } private Profiler.Entry getUnreleasedEntry() { Profiler.Entry subEntry = null; if (!this.subEntries.isEmpty()) { subEntry = (Profiler.Entry) this.subEntries.get(this.subEntries.size() - 1); if (subEntry.isReleased()) { subEntry = null; } } return subEntry; } public String toString() { return this.toString("", ""); } private String toString(String prefix1, String prefix2) { StringBuffer buffer = new StringBuffer(); this.toString(buffer, prefix1, prefix2); return buffer.toString(); } private void toString(StringBuffer buffer, String prefix1, String prefix2) { buffer.append(prefix1); String message = this.getMessage(); long startTime = this.getStartTime(); long duration = this.getDuration(); long durationOfSelf = this.getDurationOfSelf(); double percent = this.getPecentage(); double percentOfAll = this.getPecentageOfAll(); Object[] params = new Object[] { message, new Long(startTime), new Long(duration), new Long(durationOfSelf), new Double(percent), new Double(percentOfAll) }; StringBuffer pattern = new StringBuffer("{1,number} "); if (this.isReleased()) { pattern.append("[{2,number}ms"); if (durationOfSelf > 0L && durationOfSelf != duration) { pattern.append(" ({3,number}ms)"); } if (percent > 0.0D) { pattern.append(", {4,number,##%}"); } if (percentOfAll > 0.0D) { pattern.append(", {5,number,##%}"); } pattern.append("]"); } else { pattern.append("[UNRELEASED]"); } if (message != null) { pattern.append(" - {0}"); } buffer.append(MessageFormat.format(pattern.toString(), params)); for (int i = 0; i < this.subEntries.size(); ++i) { Profiler.Entry subEntry = (Profiler.Entry) this.subEntries.get(i); buffer.append('\n'); if (i == this.subEntries.size() - 1) { subEntry.toString(buffer, prefix2 + "`---", prefix2 + " "); } else if (i == 0) { subEntry.toString(buffer, prefix2 + "+---", prefix2 + "| "); } else { subEntry.toString(buffer, prefix2 + "+---", prefix2 + "| "); } } } } }