package com.github.games647.lagmonitor.task; import com.github.games647.lagmonitor.MethodMeasurement; import com.github.games647.lagmonitor.command.MonitorCommand; import com.google.common.net.UrlEscapers; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; /** * Based on the project https://github.com/sk89q/WarmRoast by sk89q */ public class MonitorTask extends TimerTask { private static final String PASTE_URL = "https://paste.enginehub.org/paste"; private static final int MAX_DEPTH = 25; private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); private final Logger logger; private final long threadId; private MethodMeasurement rootNode; private int samples; public MonitorTask(Logger logger, long threadId) { this.logger = logger; this.threadId = threadId; } public synchronized MethodMeasurement getRootSample() { return rootNode; } public synchronized int getSamples() { return samples; } @Override public void run() { ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, MAX_DEPTH); StackTraceElement[] stackTrace = threadInfo.getStackTrace(); if (stackTrace.length > 0) { StackTraceElement rootElement = stackTrace[stackTrace.length - 1]; synchronized (this) { samples++; if (rootNode == null) { String rootClass = rootElement.getClassName(); String rootMethod = rootElement.getMethodName(); String id = rootClass + '.' + rootMethod; rootNode = new MethodMeasurement(id, rootClass, rootMethod); } rootNode.onMeasurement(stackTrace, 0, MonitorCommand.SAMPLE_INTERVAL); } } } public String paste() { try { HttpURLConnection httpConnection = (HttpURLConnection) new URL(PASTE_URL).openConnection(); httpConnection.setRequestMethod("POST"); httpConnection.setDoOutput(true); httpConnection.setDoInput(true); try (BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(httpConnection.getOutputStream(), StandardCharsets.UTF_8)) ) { writer.write("content=" + UrlEscapers.urlPathSegmentEscaper().escape(toString())); writer.write("&from=" + logger.getName()); } JsonObject object; try (Reader reader = new BufferedReader( new InputStreamReader(httpConnection.getInputStream(), StandardCharsets.UTF_8)) ) { object = new Gson().fromJson(reader, JsonObject.class); } if (object.has("url")) { return object.get("url").getAsString(); } logger.log(Level.INFO, "Failed to parse url from {0}", object); } catch (IOException ex) { logger.log(Level.SEVERE, "Failed to upload monitoring data", ex); } return null; } @Override public String toString() { ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, MAX_DEPTH); StringBuilder builder = new StringBuilder(); builder.append(threadInfo.getThreadName()); builder.append(' '); synchronized (this) { builder.append(rootNode.getTotalTime()).append("ms"); builder.append('\n'); rootNode.writeString(builder, 1); } return builder.toString(); } }