/* * Copyright (C) 2006-2014 Gabriel Burca (gburca dash virtmus at ebixio dot com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package com.ebixio.virtmus.stats; import com.ebixio.util.Log; import com.ebixio.virtmus.Song; import com.ebixio.virtmus.Utils; import com.ebixio.virtmus.shapes.VmShape; import java.awt.Dimension; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; /** * Generates a LogRecord for some of the statistically meaningful properties. * * @author Gabriel Burca <gburca dash virtmus at ebixio dot com> */ public class StatsCollector implements PropertyChangeListener { private static StatsCollector instance = null; private final HashMap<Song, HashMap<String, Integer> > annotStats = new HashMap<>(); private final HashMap<String, Integer> pdfRenderer = new HashMap<>(); private StatsCollector() { } public static synchronized StatsCollector findInstance() { if (instance == null) { instance = new StatsCollector(); } return instance; } /** * Logs a bunch of configurations/settings every time VirtMus is started. * @param logger The logger to log to. */ public static void logStartup(Logger logger) { logger.log(getSystemConfig()); logger.log(getCpuInfo()); logger.log(getMemInfo()); logger.log(getScreenSizeInfo()); } public static void logAtExit(Logger logger) { StatsCollector collector = findInstance(); logger.log(collector.getPdfRenderersUsed()); } /** Creates a LogRecord with the JRE info. */ static LogRecord getSystemConfig() { LogRecord log = new LogRecord(Level.INFO, "System Config"); String os = System.getProperty("os.name", "unknown name") + ", " + System.getProperty("os.version", "unknown version") + ", " + System.getProperty("os.arch", "unknown arch"); String vm = System.getProperty("java.vm.name", "unknown VM name") + ", " + System.getProperty("java.vm.version", "unknown VM version") + ", " + System.getProperty("java.runtime.name", "unknown RT name") + ", " + System.getProperty("java.runtime.version", "unknown RT version"); Object[] params = new Object[]{"OS: " + os, "JVM: " + vm}; log.setParameters(params); return log; } /** Creates a LogRecord with the CPU info. */ static LogRecord getCpuInfo() { LogRecord log = new LogRecord(Level.INFO, "CPU Info"); OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean(); Object[] params = new Object[]{"Cores: " + os.getAvailableProcessors(), "Arch: " + os.getArch()}; log.setParameters(params); return log; } /** Creates a LogRecord with the amount of physical memory present. */ static LogRecord getMemInfo() { LogRecord log = new LogRecord(Level.INFO, "Memory"); try { OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); Method m = osBean.getClass().getMethod("getTotalPhysicalMemorySize"); m.setAccessible(true); long memSz = (Long) m.invoke(osBean); log.setParameters(new Object[]{memSz}); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { Log.log(ex); } return log; } /** Creates a LogRecord with the number of screens and the size of each. */ static LogRecord getScreenSizeInfo() { LogRecord log = new LogRecord(Level.INFO, "Screen Size"); List<Object> params = new ArrayList<>(); int screens = Utils.getNumberOfScreens(); params.add(screens); Dimension[] sizes = Utils.getScreenSizes(); for (Dimension d : sizes) { params.add(String.valueOf(d.width) + "x" + String.valueOf(d.height)); } log.setParameters(params.toArray()); return log; } public void usingRenderer(String renderer) { if (!pdfRenderer.containsKey(renderer)) { pdfRenderer.put(renderer, 1); } else { pdfRenderer.put(renderer, pdfRenderer.get(renderer) + 1); } } /** * Creates log record with the PDF renderer used. Also resets the counters. * @return The {@link java.util.logging.LogRecord} that was added. */ public LogRecord getPdfRenderersUsed() { LogRecord renderersLog = new LogRecord(Level.INFO, "PDF Renderers"); Object[] params = new Object[pdfRenderer.size()]; int idx = 0; for (String k: pdfRenderer.keySet()) { params[idx++] = k + ": " + pdfRenderer.get(k); } renderersLog.setParameters(params); pdfRenderer.clear(); return renderersLog; } /** * Keeps track of page annotations and writes a log record when the song * is saved. * * @param evt An event indicating annotation addition/removal or the song * being saved. */ @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getSource().getClass() == Song.class) { Song s = (Song)evt.getSource(); HashMap<String, Integer> songStats; if (Song.PROP_ANNOT.equals(evt.getPropertyName())) { if (!annotStats.containsKey(s)) { annotStats.put(s, new HashMap<String, Integer>()); } songStats = annotStats.get(s); VmShape shapeO = (VmShape)evt.getOldValue(); VmShape shapeN = (VmShape)evt.getNewValue(); if (shapeO == null && shapeN != null) { // shape added String sName = shapeN.getName(); if (songStats.containsKey(sName)) { songStats.put(sName, songStats.get(sName) + 1); } else { songStats.put(sName, 1); } } else if (shapeO != null && shapeN == null) { // shape removed String sName = shapeO.getName(); if (songStats.containsKey(sName)) { songStats.put(sName, songStats.get(sName) - 1); } else { songStats.put(sName, 0); } } } else if (Song.PROP_DIRTY.equals(evt.getPropertyName())) { // Use isDirty true->false as a proxy for isBeingSaved Boolean saved = (Boolean)evt.getOldValue(); // Record how many shapes were drawn on this song's pages if (saved && annotStats.containsKey(s) && !annotStats.get(s).isEmpty()) { songStats = annotStats.get(s); LogRecord shapesLog = new LogRecord(Level.INFO, "Page Annotations"); Object[] params = new Object[songStats.size()]; int idx = 0; // TODO: concurrency handling for (String k: songStats.keySet()) { params[idx++] = k + ": " + songStats.get(k); } shapesLog.setParameters(params); StatsLogger.getLogger().log(shapesLog); annotStats.remove(s); } } } } }