├── .gitignore ├── COPYING.txt ├── LICENSE.txt ├── README.md ├── examples └── screenshots │ ├── examplePercentilePlot.png │ └── exampleTimelinePlot.png ├── pom.xml └── src └── main ├── assembly └── dist.xml ├── java └── org │ └── HdrHistogram │ └── HistogramLogAnalyzer │ ├── applicationlayer │ ├── AboutDialog.java │ ├── Application.java │ ├── Configuration.java │ ├── ConstantsHelper.java │ ├── DnDTabbedPane.java │ ├── ElapsedTimeAndValue.java │ ├── HLAChartType.java │ ├── HLAPanel.java │ ├── HLATabbedPane.java │ ├── PlotFilesDialog.java │ ├── PlotFilesMode.java │ ├── PrintStatistics.java │ ├── TabCloseComponent.java │ ├── TabsListener.java │ ├── Version.java.template │ └── ZFileChooser.java │ ├── charts │ ├── BucketsChartBuilder.java │ ├── ColorHelper.java │ ├── CommonSeries.java │ ├── CommonSeriesCollection.java │ ├── HLATimeSeries.java │ ├── HLATimeSeriesCollection.java │ ├── HLAXYSeries.java │ ├── HLAXYSeriesCollection.java │ ├── PercentileChartBuilder.java │ ├── TimelineChartBuilder.java │ └── TimelineDatasetBuilder.java │ ├── datalayer │ ├── BucketIterator.java │ ├── DBConnect.java │ ├── DBManager.java │ ├── HistogramModel.java │ ├── LogGeneratorType.java │ ├── MaxPercentileIterator.java │ ├── PercentileIterator.java │ ├── ResultSetIterator.java │ ├── TagsHelper.java │ └── TimelineIterator.java │ ├── dataobjectlayer │ ├── BucketObject.java │ ├── PercentileObject.java │ └── TimelineObject.java │ ├── panels │ ├── DatePanel.java │ ├── MWPPanel.java │ └── SLAPanel.java │ └── properties │ ├── AppProperties.java │ ├── DateProperties.java │ ├── HPLProperties.java │ ├── MWPProperties.java │ ├── SLAProperties.java │ ├── ScaleProperties.java │ ├── ViewProperties.java │ └── ZoomProperties.java └── resources └── org └── HdrHistogram └── HistogramLogAnalyzer ├── applicationlayer ├── License.html ├── SLAdetails.xml ├── azul_logo.png ├── icon_close.png ├── icon_close2.png ├── icon_maxrange.png ├── icon_open.png ├── icon_photo.png └── icon_sla.png └── panels ├── icon_add.png ├── icon_apply.png └── icon_delete.png /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | out 4 | gh-pages 5 | .classpath 6 | .project 7 | .settings 8 | release.properties 9 | pom.xml.releaseBackup 10 | src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/Version.java 11 | *.a 12 | *.iml 13 | *.o 14 | .DS_Store 15 | build 16 | dependency-reduced-pom.xml 17 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | * This code was Written by Azul Systems, and released to the 2 | * public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | For users of this code who wish to consume it under the "BSD" license 5 | rather than under the public domain or CC0 contribution text mentioned 6 | above, the code found under this directory is *also* provided under the 7 | following license (commonly referred to as the BSD 2-Clause License). This 8 | license does not detract from the above stated release of the code into 9 | the public domain, and simply represents an additional license granted by 10 | the Author. 11 | 12 | ----------------------------------------------------------------------------- 13 | ** Beginning of "BSD 2-Clause License" text. ** 14 | 15 | Copyright (c) 2015, 2016 Azul Systems 16 | All rights reserved. 17 | 18 | Redistribution and use in source and binary forms, with or without 19 | modification, are permitted provided that the following conditions are met: 20 | 21 | 1. Redistributions of source code must retain the above copyright notice, 22 | this list of conditions and the following disclaimer. 23 | 24 | 2. Redistributions in binary form must reproduce the above copyright notice, 25 | this list of conditions and the following disclaimer in the documentation 26 | and/or other materials provided with the distribution. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 32 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 38 | THE POSSIBILITY OF SUCH DAMAGE. 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HistogramLogAnalyzer 2 | ---------------------------------------------- 3 | 4 | HistogramLogAnalyzer is a User Interface (UI) tool that plots log files in a histogram log format produced by jHiccup, cassandra-stress, or other tools that support this format. 5 | 6 | How to Build 7 | ---------------------------------------------- 8 | 9 | Use Maven to build it from the source code 10 | 11 | % mvn clean package 12 | 13 | How to Run 14 | ---------------------------------------------- 15 | 16 | You can run it by using the command line: java -jar HistogramLogAnalyzer.jar or just by double-clicking the jar file. 17 | 18 | Select a log file to plot (by selecting the “File->Open” menu, using the command-line option “-f file”, or just dragging and dropping the log file in the main window). 19 | 20 | Timeline and Percentile Charts 21 | ---------------------------------------------- 22 | 23 | The tool produces two charts arranged vertically one above the other: 24 | 25 | - Timeline chart 26 | - Percentile chart 27 | 28 | The timeline chart plots the maximum latency duration observed in each time interval. There is a spike in every recorded sample. The height of each spike indicates the maximum pause time experienced during that interval. 29 | 30 | ![timeline example plot] 31 | 32 | The percentile chart plots the observed latency durations at varying percentiles. 33 | 34 | ![percentile example plot] 35 | 36 | The tool enables viewing SLA (service level agreement) data in the percentile chart. Use the “Master SLA” button in the toolbar to open new tab and configure SLA values (arbitrary number of percentile/latency pairs). Use the “Show SLA” button to display SLA in the percentile chart. 37 | 38 | The tool also enables plotting multiple percentiles and multiple moving window widths on the timeline chart. Use the “Master MWP” button in the toolbar to open a new tab and configure MWP (a multiple window percentile) values (percentile/“window length” pairs). Use the “Show MWP” button to display MWP in the timeline chart. 39 | 40 | Customize Charts 41 | ---------------------------------------------- 42 | 43 | The tool provides several options to customize the charts: 44 | 45 | - Chart properties 46 | 47 | Right-click a chart to open a popup menu and then select the “Properties…” menu item to open the "Chart properties" dialog. This dialog allows you to customize different chart settings (titles, fonts, colors, etc). 48 | 49 | - Zooming functionality 50 | 51 | To zoom in, left-click inside a chart and drag over an area in the chart
52 | To zoom out, left-click inside a chart and drag outside the chart
53 | When zooming in on a range in the timeline chart, the percentile chart changes to represent only the percentiles in that time range.
54 | 55 | Snapshot 56 | ---------------------------------------------- 57 | 58 | The tool also provides a way to make a snapshot image of charts by using the “Snapshot” button in the toolbar. 59 | 60 | Plotting Tags 61 | ---------------------------------------------- 62 | 63 | The tool supports log files with multiple tags. It plots multiple tags (with different colors) on the same chart. Clicking a tag item in the chart legend toggles visibility of this tag in the chart. 64 | 65 | [timeline example plot]:https://raw.github.com/HdrHistogram/HistogramLogAnalyzer/master/examples/screenshots/exampleTimelinePlot.png "Example timeline plot" 66 | [percentile example plot]:https://raw.github.com/HdrHistogram/HistogramLogAnalyzer/master/examples/screenshots/examplePercentilePlot.png "Example timeline plot" 67 | -------------------------------------------------------------------------------- /examples/screenshots/examplePercentilePlot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/examples/screenshots/examplePercentilePlot.png -------------------------------------------------------------------------------- /examples/screenshots/exampleTimelinePlot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/examples/screenshots/exampleTimelinePlot.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.hdrhistogram.histogramloganalyzer 6 | HistogramLogAnalyzer 7 | 1.0.4-SNAPSHOT 8 | 9 | HistogramLogAnalyzer 10 | 11 | 12 | 13 | 14 | * This code was Written by Azul Systems, and released to the 15 | * public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ 16 | 17 | Public Domain, per Creative Commons CC0 18 | http://creativecommons.org/publicdomain/zero/1.0/ 19 | 20 | 21 | 22 | 23 | scm:git:git://github.com/HdrHistogram/HistogramLogAnalyzer.git 24 | scm:git:git://github.com/HdrHistogram/HistogramLogAnalyzer.git 25 | scm:git:git@github.com:HdrHistogram/HistogramLogAnalyzer.git 26 | HistogramLogAnalyzer-1.0.1 27 | 28 | 29 | 30 | https://github.com/HdrHistogram/HistogramLogAnalyzer/issues 31 | GitHub Issues 32 | 33 | 34 | jar 35 | 36 | 37 | UTF-8 38 | src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/Version.java.template 39 | src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/Version.java 40 | org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.Application 41 | dd-MMM-yyyy HH:mm:ss z 42 | 43 | 44 | 45 | 46 | 47 | org.jfree 48 | jfreechart 49 | 1.0.17 50 | 51 | 52 | 53 | org.hdrhistogram 54 | HdrHistogram 55 | 2.1.11 56 | 57 | 58 | 59 | org.xerial 60 | sqlite-jdbc 61 | 3.41.2.2 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-compiler-plugin 72 | 2.3.2 73 | 74 | 1.7 75 | 1.7 76 | UTF-8 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-shade-plugin 83 | 2.4.3 84 | 85 | 86 | package 87 | 88 | shade 89 | 90 | 91 | 92 | 93 | 94 | ${main.class} 95 | ${project.name} 96 | ${project.version} 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-release-plugin 108 | 2.5 109 | 110 | 111 | 112 | com.google.code.maven-replacer-plugin 113 | maven-replacer-plugin 114 | 1.4.0 115 | 116 | 117 | process-sources 118 | 119 | replace 120 | 121 | 122 | 123 | 124 | ${version.template.file} 125 | ${version.file} 126 | 127 | 128 | \$BUILD_TIME\$ 129 | ${maven.build.timestamp} 130 | 131 | 132 | \$VERSION\$ 133 | ${project.version} 134 | 135 | 136 | 137 | 138 | 139 | 140 | maven-assembly-plugin 141 | 2.4 142 | 143 | 144 | src/main/assembly/dist.xml 145 | 146 | 147 | 148 | 149 | package 150 | 151 | single 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /src/main/assembly/dist.xml: -------------------------------------------------------------------------------- 1 | 3 | dist 4 | 5 | tar.gz 6 | zip 7 | 8 | 9 | 10 | ${project.basedir} 11 | / 12 | 13 | COPYING.txt 14 | LICENSE.txt 15 | README.md 16 | pom.xml 17 | 18 | 19 | 20 | ${project.build.directory} 21 | / 22 | 23 | HistogramLogAnalyzer*.jar 24 | 25 | 26 | 27 | ${project.basedir}/src 28 | /src 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/AboutDialog.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.InputStream; 10 | import java.io.InputStreamReader; 11 | 12 | import javax.swing.JComponent; 13 | import javax.swing.JDialog; 14 | import javax.swing.JEditorPane; 15 | import javax.swing.JFrame; 16 | import javax.swing.JScrollPane; 17 | 18 | 19 | public class AboutDialog extends JDialog { 20 | 21 | private static String _azulCopyrightString = "Copyright 2016, Azul Systems, Inc. All Rights Reserved."; 22 | private static String _licenseFilename = "/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/License.html"; 23 | 24 | /** 25 | * @param parentFrame 26 | */ 27 | public AboutDialog(JFrame parentFrame) { 28 | super(parentFrame); 29 | 30 | super.setSize(Math.max((int)(parentFrame.getWidth() * 0.6), 400), Math.max((int)(parentFrame.getHeight() * 0.6), 400)); 31 | super.setModal(true); 32 | super.setLocationRelativeTo(parentFrame); 33 | super.setTitle("Help About Histogram Log Analyzer"); 34 | super.getContentPane().add(getMainPane()); 35 | } 36 | 37 | /** 38 | * @return 39 | */ 40 | private JComponent getMainPane() { 41 | JEditorPane editorPane = new JEditorPane(); 42 | editorPane.setContentType("text/html"); 43 | editorPane.setEditable(false); 44 | editorPane.setText(getAboutText()); 45 | editorPane.setCaretPosition(0); 46 | JScrollPane pane = new JScrollPane(editorPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 47 | return pane; 48 | } 49 | 50 | private String getAboutText() { 51 | StringBuilder sb = new StringBuilder(1000); 52 | sb.append(""); 53 | sb.append("

Histogram Log Analyzer

"); 54 | sb.append("Version: ").append(Version.version).append("
"); 55 | // sb.append("Release version: ").append(Version.RELEASE_VERSION).append("
"); 56 | sb.append("Build time: ").append(Version.build_time).append("

"); 57 | sb.append(_azulCopyrightString).append("
"); 58 | try { 59 | String licenseFileLine = ""; 60 | InputStream licenseStream = getClass().getResourceAsStream(_licenseFilename); 61 | BufferedReader br = new BufferedReader(new InputStreamReader(licenseStream, "US-ASCII")); 62 | while ((licenseFileLine = br.readLine()) != null) { 63 | sb.append(licenseFileLine); 64 | } 65 | } catch (Exception ex) { 66 | System.err.println("Unable to find the license file or there is an IO error: " + _licenseFilename); 67 | System.err.println(ex.getMessage()); 68 | } 69 | sb.append(""); 70 | return sb.toString(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/Configuration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | /* 9 | * The class contains various CLI parameters, it's not thread safe. 10 | */ 11 | public class Configuration { 12 | 13 | private static Configuration instance; 14 | 15 | private boolean helpMode = false; 16 | private String inputFileName; 17 | 18 | // options related to console mode 19 | private boolean ppMode = false; 20 | private int nlhValue = 15; 21 | private boolean nlhSpecified = false; 22 | private boolean eapMode = false; 23 | 24 | private Configuration() { 25 | } 26 | 27 | public static Configuration getInstance() { 28 | if (instance == null) { 29 | instance = new Configuration(); 30 | } 31 | return instance; 32 | } 33 | 34 | String getInputFileName() { 35 | return inputFileName; 36 | } 37 | 38 | boolean isPPmode() { 39 | return ppMode; 40 | } 41 | 42 | boolean isEAPmode() { 43 | return eapMode; 44 | } 45 | 46 | int getNlhValue() { 47 | return nlhValue; 48 | } 49 | 50 | void parseArgs(String[] args) { 51 | 52 | for (int i = 0; i < args.length; ++i) { 53 | if (args[i].equals("-help") || args[i].equals("-h")) { 54 | helpMode = true; 55 | } else if (args[i].equals("-f")) { 56 | inputFileName = args[++i]; 57 | } else if (args[i].equals("-pp")) { 58 | ppMode = true; 59 | } else if (args[i].equals("-nlh")) { 60 | nlhSpecified = true; 61 | nlhValue = Integer.parseInt(args[++i]); 62 | } else if (args[i].equals("-eap")) { 63 | eapMode = true; 64 | } else { 65 | System.err.println("Invalid args: " + args[i] + 66 | "\n"); 67 | printHelpMessage(); 68 | System.exit(1); 69 | } 70 | } 71 | 72 | if (helpMode) { 73 | printHelpMessage(); 74 | System.exit(1); 75 | } 76 | 77 | if (ppMode && inputFileName == null) { 78 | throw new RuntimeException("-pp option requires input file"); 79 | } 80 | 81 | if ((nlhSpecified || eapMode) && !ppMode) { 82 | throw new RuntimeException("-nlh/-eap options require specifying -pp option"); 83 | } 84 | } 85 | 86 | private void printHelpMessage() { 87 | System.err.println( 88 | "HistogramLogAnalyzer, version " + Version.version + "\n" + 89 | "Usage: java -jar HistogramLogAnalyzer.jar " + 90 | "[-h|-help] [-pp [-nlh NUMBER]] [-eap] [-f FILE]\n" + 91 | "\n" + 92 | "Options:\n" + 93 | " -h|-help prints this message\n" + 94 | " -f FILE file name of histogram log file to process\n" + 95 | " -pp print percentile and max data to stdout\n" + 96 | " -nlh NUMBER print N top latency values to stdout\n" + 97 | " the default is to print " + nlhValue + " top latency values\n" + 98 | " -eap exit after parsing file\n"); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/ConstantsHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | public class ConstantsHelper { 9 | 10 | public static String getLatencyName() { 11 | return "Latency"; 12 | } 13 | 14 | public static String getChartTitle(HLAChartType chartType) { 15 | switch (chartType) { 16 | case PERCENTILE: 17 | return getLatencyName() + " By Percentile Distribution"; 18 | case TIMELINE_ELAPSED_TIME: 19 | case TIMELINE_DATE: 20 | return "Maximum " + getLatencyName() + " In Time Interval"; 21 | case BUCKETS: 22 | return "Raw " + getLatencyName() + " Duration Bucketed Values"; 23 | } 24 | throw new IllegalArgumentException(); 25 | } 26 | 27 | public static String getXAxisLabel(HLAChartType chartType) { 28 | return getXAxisLabel(chartType, null); 29 | } 30 | 31 | public static String getXAxisLabel(HLAChartType chartType, String timezone) { 32 | switch (chartType) { 33 | case PERCENTILE: 34 | return "Percentile"; 35 | case TIMELINE_ELAPSED_TIME: 36 | return "Elapsed Time (sec)"; 37 | case TIMELINE_DATE: 38 | return timezone + " time"; 39 | case BUCKETS: 40 | return "Bucketed Raw " + getLatencyName() + " Duration (msec)"; 41 | } 42 | throw new IllegalArgumentException(); 43 | } 44 | 45 | public static String getYAxisLabel(HLAChartType chartType) { 46 | switch (chartType) { 47 | case PERCENTILE: 48 | return getLatencyName() + " Duration (msec)"; 49 | case TIMELINE_ELAPSED_TIME: 50 | case TIMELINE_DATE: 51 | return getLatencyName() + " Duration (msec)"; 52 | case BUCKETS: 53 | return "Number in buckets"; 54 | } 55 | throw new IllegalArgumentException(); 56 | } 57 | 58 | public static String getLogAxisLabel(HLAChartType chartType) { 59 | switch (chartType) { 60 | case PERCENTILE: 61 | return getLatencyName() + " by Percentile"; 62 | } 63 | throw new IllegalArgumentException(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/DnDTabbedPane.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | import java.awt.*; 9 | import java.awt.event.*; 10 | import java.awt.image.BufferedImage; 11 | 12 | import javax.swing.*; 13 | import javax.swing.plaf.basic.BasicTabbedPaneUI; 14 | 15 | class DnDTabbedPane extends JTabbedPane { 16 | 17 | private BufferedImage dragImage = null; 18 | private MouseDragTracker mouseTracker = new MouseDragTracker(); 19 | 20 | DnDTabbedPane() { 21 | super(); 22 | installListeners(); 23 | 24 | setUI(new BasicTabbedPaneUI() { 25 | @Override 26 | protected void installDefaults() { 27 | super.installDefaults(); 28 | } 29 | }); 30 | } 31 | 32 | private void installListeners() { 33 | addMouseMotionListener(new MouseMotionAdapter() { 34 | @Override 35 | public void mouseDragged(MouseEvent e) { 36 | mouseTracker.mouseDraggedHandler(e); 37 | repaint(); 38 | super.mouseDragged(e); 39 | } 40 | }); 41 | 42 | addMouseListener(new MouseAdapter() { 43 | @Override 44 | public void mouseReleased(MouseEvent e) { 45 | mouseTracker.mouseReleasedHandler(e); 46 | repaint(); 47 | super.mouseReleased(e); 48 | } 49 | }); 50 | } 51 | 52 | @Override 53 | protected void paintComponent(Graphics g) { 54 | super.paintComponent(g); 55 | if(mouseTracker.isDragging()) { 56 | int x = mouseTracker.tabStartPoint.x + mouseTracker.currentPoint.x - mouseTracker.startPoint.x; 57 | int y = mouseTracker.tabStartPoint.y + mouseTracker.currentPoint.y - mouseTracker.startPoint.y; 58 | g.drawImage(dragImage, x, y, this); 59 | } 60 | } 61 | 62 | /* 63 | * create transparent button with dashed border as drag image 64 | */ 65 | private void createDragImage(Rectangle bounds) { 66 | JButton b = new JButton(); 67 | b.setOpaque(false); 68 | b.setContentAreaFilled(false); 69 | b.setBorder(BorderFactory.createDashedBorder(Color.BLACK)); 70 | b.setBounds(0, 0, bounds.width, bounds.height); 71 | dragImage = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB); 72 | b.print(dragImage.createGraphics()); 73 | } 74 | 75 | private void switchTabs(int fromIndex, int toIndex) { 76 | if (fromIndex == toIndex) { 77 | return; 78 | } 79 | Component comp = getComponentAt(fromIndex); 80 | TabsListener tabsListener = ((TabCloseComponent)getTabComponentAt(fromIndex)).getTabsListener(); 81 | String title = getTitleAt(fromIndex); 82 | String tip = getToolTipTextAt(fromIndex); 83 | removeTabAt(fromIndex); 84 | 85 | insertTab(title, null, comp, tip, toIndex); 86 | setTabComponentAt(toIndex, new TabCloseComponent(title, DnDTabbedPane.this, tabsListener)); 87 | 88 | setSelectedIndex(toIndex); 89 | repaint(); 90 | } 91 | 92 | private class MouseDragTracker { 93 | 94 | private boolean dragging = false; 95 | private int draggedTabIndex = 0; 96 | 97 | private Point tabStartPoint; 98 | private Point startPoint; 99 | private Point currentPoint; 100 | 101 | private boolean isDragging() { 102 | return dragging; 103 | } 104 | 105 | private void startDragging(MouseEvent event) { 106 | dragging = true; 107 | } 108 | 109 | private void stopDragging() { 110 | dragging = false; 111 | } 112 | 113 | private void mouseReleasedHandler(MouseEvent e) { 114 | stopDragging(); 115 | } 116 | 117 | private void mouseDraggedHandler(MouseEvent e) { 118 | int tabIndex = DnDTabbedPane.this.getUI().tabForCoordinate(DnDTabbedPane.this, e.getX(), e.getY()); 119 | if (tabIndex == -1) { 120 | return; 121 | } 122 | 123 | if(!dragging) { 124 | draggedTabIndex = tabIndex; 125 | Rectangle bounds = getUI().getTabBounds(DnDTabbedPane.this, tabIndex); 126 | createDragImage(bounds); 127 | 128 | tabStartPoint = new Point(bounds.x, bounds.y); 129 | startPoint = e.getPoint(); 130 | currentPoint = (Point)startPoint.clone(); 131 | startDragging(e); 132 | } else { 133 | currentPoint = e.getPoint(); 134 | checkSwitchTabs(e, tabIndex); 135 | } 136 | } 137 | 138 | /* 139 | * Switch tabs when mouse overcomes third part of right/left tab on X axis 140 | */ 141 | private void checkSwitchTabs(MouseEvent e, int tabIndex) { 142 | Rectangle tabBounds = getUI().getTabBounds(DnDTabbedPane.this, tabIndex); 143 | boolean isNextRight = (tabIndex > draggedTabIndex); 144 | 145 | if (isNextRight) { 146 | int switchX = tabBounds.x + (tabBounds.width / 3); 147 | if (e.getX() > switchX) { 148 | switchTabs(draggedTabIndex, tabIndex); 149 | draggedTabIndex = tabIndex; 150 | } 151 | } else { 152 | int switchX = tabBounds.x + (2 * tabBounds.width / 3); 153 | if (e.getX() < switchX) { 154 | switchTabs(draggedTabIndex, tabIndex); 155 | draggedTabIndex = tabIndex; 156 | } 157 | } 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/ElapsedTimeAndValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | public class ElapsedTimeAndValue implements Comparable { 9 | 10 | private double elapsedTime = 0.0F; 11 | private double value = 0.0F; 12 | private static boolean sortValuesDescending = false; 13 | private static boolean sortElapsedTimesAscending = false; 14 | 15 | public ElapsedTimeAndValue(double elapsedTimeIn, double valueIn) { 16 | super(); 17 | elapsedTime = elapsedTimeIn; 18 | value = valueIn; 19 | } 20 | 21 | public double getElapsedTime() { 22 | return elapsedTime; 23 | } 24 | 25 | public double getValue() { 26 | return value; 27 | } 28 | 29 | public static synchronized void setToSortValuesDescending() { 30 | sortValuesDescending = true; 31 | sortElapsedTimesAscending = false; 32 | } 33 | 34 | public static synchronized void setToSortElapsedTimesAscending() { 35 | sortValuesDescending = false; 36 | sortElapsedTimesAscending = true; 37 | } 38 | 39 | public int compareTo(ElapsedTimeAndValue elapsedTimeAndValueIn) { 40 | double diff = 0.0F; 41 | if (sortValuesDescending) { 42 | diff = elapsedTimeAndValueIn.getValue() - value; 43 | } else if (sortElapsedTimesAscending) { 44 | diff = elapsedTime - elapsedTimeAndValueIn.getElapsedTime(); 45 | } 46 | if (diff > 0.0F) { 47 | return 1; 48 | } 49 | else if (diff < 0.0F) { 50 | return -1; 51 | } 52 | return 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/HLAChartType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | public enum HLAChartType { 9 | TIMELINE_ELAPSED_TIME, 10 | TIMELINE_DATE, 11 | PERCENTILE, 12 | BUCKETS 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/HLAPanel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.charts.BucketsChartBuilder; 9 | import org.HdrHistogram.HistogramLogAnalyzer.charts.PercentileChartBuilder; 10 | import org.HdrHistogram.HistogramLogAnalyzer.charts.TimelineChartBuilder; 11 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.HistogramModel; 12 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.LogGeneratorType; 13 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.MaxPercentileIterator; 14 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 15 | import org.HdrHistogram.HistogramLogAnalyzer.properties.*; 16 | import org.jfree.chart.ChartPanel; 17 | import org.jfree.data.Range; 18 | 19 | import javax.swing.*; 20 | import java.awt.*; 21 | import java.beans.PropertyChangeEvent; 22 | import java.beans.PropertyChangeListener; 23 | import java.io.IOException; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | /* 28 | * Custom JPanel that contains top (timeline) and bottom (percentile) charts 29 | */ 30 | class HLAPanel extends JPanel 31 | { 32 | private static TimelineChartBuilder timelineChartBuilder = new TimelineChartBuilder(); 33 | private static PercentileChartBuilder percentileChartBuilder = new PercentileChartBuilder(); 34 | private static BucketsChartBuilder bucketsChartBuilder = new BucketsChartBuilder(); 35 | 36 | // top "timeline" charts 37 | private Container topChart = null; 38 | private JPanel elapsedTimeChart = null; 39 | private JPanel dateChart = null; 40 | 41 | private Container bottomChart = null; 42 | private JPanel percentileChart = null; 43 | private JPanel bucketsChart = null; 44 | 45 | private List< HistogramModel> histogramModels = new ArrayList<>(); 46 | private ScaleProperties scaleProperties = null; 47 | private ScaleProperties.ScaleEntry scaleEntry = null; 48 | 49 | private String tabTitle; 50 | private String[] tooltipTexts; 51 | 52 | HLAPanel(String inputFileName, AppProperties appProperties) 53 | throws IOException 54 | { 55 | this(new String[] {inputFileName}, appProperties); 56 | } 57 | 58 | HLAPanel(String[] inputFileNames, final AppProperties appProperties) throws IOException { 59 | final ZoomProperties zoomProperty = new ZoomProperties(); 60 | scaleProperties = new ScaleProperties(); 61 | 62 | histogramModels = new ArrayList<>(); 63 | for (String inputFileName : inputFileNames) { 64 | HistogramModel histogramModel = 65 | new HistogramModel(inputFileName, null, null, appProperties.getMwpProperties()); 66 | // print statistics to console when requested 67 | if (Configuration.getInstance().isPPmode()) { 68 | PrintStatistics.print(histogramModel); 69 | } 70 | histogramModels.add(histogramModel); 71 | } 72 | 73 | initUISettings(histogramModels); 74 | 75 | setLayout(new GridLayout(2, 1)); 76 | setBorder(BorderFactory.createLineBorder(Color.BLACK)); 77 | 78 | topChart = new Container(); 79 | bottomChart = new Container(); 80 | topChart.setLayout(new GridLayout(1, 1)); 81 | bottomChart.setLayout(new GridLayout(1, 1)); 82 | 83 | add(topChart); 84 | add(bottomChart); 85 | 86 | createTopChart(appProperties, zoomProperty); 87 | createBottomChart(histogramModels, appProperties); 88 | 89 | updateTopChart(appProperties.getDateProperties()); 90 | updateBottomChart(appProperties.getViewProperties()); 91 | 92 | appProperties.getViewProperties().addPropertyChangeListener(new PropertyChangeListener() { 93 | @Override 94 | public void propertyChange(PropertyChangeEvent evt) { 95 | if (evt.getPropertyName().equals("bottomChartTypeChanged")) { 96 | updateBottomChart(appProperties.getViewProperties()); 97 | } 98 | } 99 | }); 100 | 101 | appProperties.getDateProperties().addPropertyChangeListener(new PropertyChangeListener() { 102 | @Override 103 | public void propertyChange(PropertyChangeEvent evt) { 104 | if (evt.getPropertyName().equals("setDatesVisible")) { 105 | updateTopChart(appProperties.getDateProperties()); 106 | } 107 | } 108 | }); 109 | 110 | // re-create top charts when MWP enabled 111 | // 112 | // tool doesn't support MWP for charts with multiple files 113 | // tool doesn't support MWP for files with multiple tags 114 | boolean multipleFiles = histogramModels.size() > 1; 115 | if (!multipleFiles) { 116 | final HistogramModel model = histogramModels.get(0); 117 | boolean multipleTags = model.getTags().size() > 1; 118 | if (!multipleTags) { 119 | appProperties.getMwpProperties().addPropertyChangeListener(new PropertyChangeListener() { 120 | @Override 121 | public void propertyChange(PropertyChangeEvent evt) { 122 | if (evt.getPropertyName().equals("applyMWP")) { 123 | 124 | histogramModels.clear(); 125 | String inputFileName = model.getInputFileName(); 126 | MWPProperties mwpProperties = model.getMwpProperties(); 127 | try { 128 | histogramModels.add(new HistogramModel(inputFileName, null, null, mwpProperties)); 129 | } catch (IOException e) { 130 | e.printStackTrace(); 131 | } 132 | 133 | SwingUtilities.invokeLater(new Runnable() { 134 | @Override 135 | public void run() { 136 | createTopChart(appProperties, zoomProperty); 137 | updateTopChart(appProperties.getDateProperties()); 138 | } 139 | }); 140 | } 141 | } 142 | }); 143 | } 144 | } 145 | 146 | // zooming on timeline chart updates percentile chart (listener part) 147 | zoomProperty.addPropertyChangeListener(new PropertyChangeListener() { 148 | @Override 149 | public void propertyChange(PropertyChangeEvent evt) { 150 | ZoomProperties.ZoomValue v = (ZoomProperties.ZoomValue) evt.getNewValue(); 151 | // ignore range changes in vertical axis 152 | if (v.getAxisType() == ZoomProperties.AxisType.RANGE) { 153 | return; 154 | } 155 | 156 | final List percetileHistModels = new ArrayList<>(); 157 | for (HistogramModel model : histogramModels) { 158 | String inputFileName = model.getInputFileName(); 159 | Range range = v.getRange(); 160 | if (appProperties.getDateProperties().isDatesVisible()) { 161 | range = DateProperties.dateRangeToRange(model.getStartTimeSec(), v.getRange()); 162 | } 163 | try { 164 | HistogramModel newModel = new HistogramModel(inputFileName, 165 | range.getLowerBound(), range.getUpperBound(), appProperties.getMwpProperties()); 166 | percetileHistModels.add(newModel); 167 | } catch (IOException e) { 168 | e.printStackTrace(); 169 | } 170 | } 171 | 172 | SwingUtilities.invokeLater(new Runnable() { 173 | @Override 174 | public void run() { 175 | createBottomChart(percetileHistModels, appProperties); 176 | updateBottomChart(appProperties.getViewProperties()); 177 | } 178 | }); 179 | } 180 | }); 181 | 182 | // FIXME: uninstall listeners 183 | } 184 | 185 | public ChartPanel getTopChart() { 186 | return (ChartPanel) ((Container)getComponent(0)).getComponent(0); 187 | } 188 | 189 | public ChartPanel getBottomChart() { 190 | return (ChartPanel) ((Container)getComponent(1)).getComponent(0); 191 | } 192 | 193 | private void createTopChart(AppProperties appProperties, ZoomProperties zoomProperty) { 194 | elapsedTimeChart = 195 | timelineChartBuilder.createTimelineChart( 196 | histogramModels, appProperties, zoomProperty, scaleProperties, HLAChartType.TIMELINE_ELAPSED_TIME); 197 | 198 | dateChart = 199 | timelineChartBuilder.createTimelineChart( 200 | histogramModels, appProperties, zoomProperty, scaleProperties, HLAChartType.TIMELINE_DATE); 201 | } 202 | 203 | private void updateTopChart(DateProperties dateProperties) { 204 | topChart.removeAll(); 205 | topChart.add(dateProperties.isDatesVisible() ? dateChart : elapsedTimeChart); 206 | topChart.revalidate(); 207 | } 208 | 209 | // use models where "percentile"-range might be enabled 210 | private void createBottomChart(List percentileHistModels, AppProperties appProperties) { 211 | percentileChart = 212 | percentileChartBuilder.createPercentileChart(percentileHistModels, appProperties, scaleProperties); 213 | 214 | bucketsChart = 215 | bucketsChartBuilder.createBucketsChart(percentileHistModels, appProperties); 216 | } 217 | 218 | private void updateBottomChart(ViewProperties viewProperties) { 219 | bottomChart.removeAll(); 220 | boolean usePercentileChart = viewProperties.getBottomChartType().equals(HLAChartType.PERCENTILE); 221 | bottomChart.add(usePercentileChart ? percentileChart : bucketsChart); 222 | bottomChart.revalidate(); 223 | } 224 | 225 | 226 | private void initUISettings(List histogramModels) { 227 | // use first model for setting tab title 228 | HistogramModel histogramModel = histogramModels.get(0); 229 | LogGeneratorType logGeneratorType = histogramModel.getLogGeneratorType(); 230 | 231 | String prefix = ""; 232 | if (!logGeneratorType.equals(LogGeneratorType.UNKNOWN)) { 233 | prefix = logGeneratorType.getDescription() + " - "; 234 | } 235 | 236 | tabTitle = prefix + histogramModel.getShortFileName(); 237 | 238 | // use all models for setting (multi-line) tooltip text 239 | tooltipTexts = new String[histogramModels.size()]; 240 | for (int i = 0; i < histogramModels.size(); i++) { 241 | histogramModel = histogramModels.get(i); 242 | prefix = ""; 243 | if (!logGeneratorType.equals(LogGeneratorType.UNKNOWN)) { 244 | prefix = logGeneratorType.getDescription() + " - "; 245 | } 246 | tooltipTexts[i] = prefix + histogramModel.getInputFileName(); 247 | } 248 | } 249 | 250 | String getTabTitle() { 251 | return tabTitle; 252 | } 253 | 254 | String[] getTooltipTexts() { 255 | return tooltipTexts; 256 | } 257 | 258 | /* 259 | * returns max values on X/Y axis for this percentile chart 260 | */ 261 | ScaleProperties.ScaleEntry getScaleEntry() { 262 | if (scaleEntry == null) { 263 | double maxLatencyAxisValue = 0.0; 264 | double maxPercentileAxisValue = 0.0; 265 | for (HistogramModel histogramModel : histogramModels) { 266 | for (String tag : histogramModel.getTags()) { 267 | MaxPercentileIterator mpi = histogramModel.listMaxPercentileObjects(tag); 268 | PercentileObject mpo; 269 | while (mpi.hasNext()) { 270 | mpo = mpi.next(); 271 | maxLatencyAxisValue = Math.max(maxLatencyAxisValue, mpo.getLatencyAxisValue()); 272 | maxPercentileAxisValue = Math.max(maxPercentileAxisValue, mpo.getPercentileAxisValue()); 273 | } 274 | } 275 | } 276 | scaleEntry = new ScaleProperties.ScaleEntry(maxPercentileAxisValue, maxLatencyAxisValue); 277 | } 278 | return scaleEntry; 279 | } 280 | 281 | void scale() { 282 | scaleProperties.applyScale(getScaleEntry()); 283 | } 284 | 285 | void scale(ScaleProperties.ScaleEntry scaleEntry) { 286 | scaleProperties.applyScale(scaleEntry); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/HLATabbedPane.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.TagsHelper; 9 | import org.HdrHistogram.HistogramLogAnalyzer.panels.MWPPanel; 10 | import org.HdrHistogram.HistogramLogAnalyzer.panels.SLAPanel; 11 | import org.HdrHistogram.HistogramLogAnalyzer.panels.DatePanel; 12 | 13 | import javax.swing.*; 14 | import java.awt.*; 15 | import java.io.FileNotFoundException; 16 | import java.io.IOException; 17 | import java.util.Set; 18 | 19 | public class HLATabbedPane extends DnDTabbedPane { 20 | 21 | private TabsListener tabsListener = null; 22 | 23 | HLATabbedPane(TabsListener tabsListener) { 24 | super(); 25 | this.tabsListener = tabsListener; 26 | } 27 | 28 | void plotInputFiles(String[] inputFileNames, Application app) throws IOException { 29 | boolean multipleFiles = inputFileNames.length > 1; 30 | boolean firstFile = getTabCount() == 0; 31 | boolean masterTab = isMasterTabCurrent(); 32 | boolean needNewTab = firstFile || masterTab; 33 | 34 | PlotFilesMode mode; 35 | if (!multipleFiles) { 36 | HLAPanel latencyPanel = new HLAPanel(inputFileNames, app.getAppProperties()); 37 | if (needNewTab) { 38 | add_to_newtab(latencyPanel); 39 | } else { 40 | mode = PlotFilesDialog.showDialog(app.getMainFrame(), false); 41 | if (mode == PlotFilesMode.SAME_TAB) { 42 | add_to_currenttab(latencyPanel); 43 | } else if (mode == PlotFilesMode.MULTIPLE_TABS) { 44 | add_to_newtab(latencyPanel); 45 | } 46 | } 47 | } else { 48 | // tool supports plotting multiple (only untagged) files in the same chart 49 | if (!containsTaggedFile(inputFileNames)) { 50 | mode = PlotFilesDialog.showDialog(app.getMainFrame(), true); 51 | } else { 52 | mode = PlotFilesDialog.showDialog(app.getMainFrame(), true, true); 53 | } 54 | 55 | if (mode == PlotFilesMode.SAME_CHART) { 56 | HLAPanel latencyPanel = new HLAPanel(inputFileNames, app.getAppProperties()); 57 | if (needNewTab) { 58 | add_to_newtab(latencyPanel); 59 | } else { 60 | add_to_currenttab(latencyPanel); 61 | } 62 | } else { 63 | for (String inputFileName : inputFileNames) { 64 | HLAPanel latencyPanel = new HLAPanel(inputFileName, app.getAppProperties()); 65 | if (mode == PlotFilesMode.SAME_TAB && !needNewTab) { 66 | add_to_currenttab(latencyPanel); 67 | } else { 68 | add_to_newtab(latencyPanel); 69 | needNewTab = false; 70 | } 71 | } 72 | 73 | } 74 | } 75 | } 76 | 77 | private boolean containsTaggedFile(String[] inputFileNames) throws FileNotFoundException { 78 | for (String inputFileName : inputFileNames) { 79 | Set tags = TagsHelper.listTags(inputFileName); 80 | if (tags.size() > 1) { 81 | return true; 82 | } 83 | } 84 | return false; 85 | } 86 | 87 | private void checkFirstTabOpened() { 88 | if (getTabCount() == 1) { 89 | tabsListener.firstTabOpened(); 90 | } 91 | } 92 | 93 | private void add_to_newtab(HLAPanel latencyPanel) { 94 | String tabTitle = latencyPanel.getTabTitle(); 95 | insertTab(tabTitle, null, tab_builder(latencyPanel), 96 | getMultiLineTooltipText(latencyPanel.getTooltipTexts()), getTabCount()); 97 | setTabComponentAt(getTabCount() - 1, new TabCloseComponent(tabTitle, this, tabsListener)); 98 | checkFirstTabOpened(); 99 | setSelectedIndex(getTabCount() - 1); 100 | } 101 | 102 | private String updateMultilineTooltipText(String oldTooltipText, String[] tooltipTexts) { 103 | String tooltipText = ""; 104 | for (String tt : tooltipTexts) { 105 | tooltipText += "
" + tt; 106 | } 107 | return oldTooltipText.replaceAll("", tooltipText +""); 108 | } 109 | 110 | private JPanel tab_builder(HLAPanel latencyPanel) { 111 | JPanel coverPanel = new JPanel(new GridLayout(1,1)); 112 | coverPanel.add(latencyPanel); 113 | return coverPanel; 114 | } 115 | 116 | private void add_to_currenttab(HLAPanel latencyPanel) { 117 | JPanel p1 = (JPanel) getSelectedComponent(); 118 | p1.add(latencyPanel); 119 | p1.setLayout(new GridLayout(1,2)); 120 | 121 | // update tooltip of the current tab 122 | String newTooltipText = 123 | updateMultilineTooltipText(getToolTipTextAt(getSelectedIndex()), latencyPanel.getTooltipTexts()); 124 | setToolTipTextAt(getSelectedIndex(), newTooltipText); 125 | } 126 | 127 | // multi-line tooltip needs to be wrapped in html tags 128 | private String getMultiLineTooltipText(String[] tooltipTexts) { 129 | String ret = ""; 130 | for (int i = 0; i < tooltipTexts.length; i++) { 131 | if (i != 0) { 132 | ret += "
"; 133 | } 134 | ret += tooltipTexts[i]; 135 | } 136 | ret += ""; 137 | return ret; 138 | } 139 | 140 | /* 141 | * SLA/MWP-related methods 142 | */ 143 | boolean isSLAMasterTabOpen() { 144 | if (getTabCount() == 0) { 145 | return false; 146 | } 147 | for (int i = 0; i < getTabCount(); i++) { 148 | if (getTitleAt(i).contains(Application.SLA_MASTER_TABNAME)) { 149 | return true; 150 | } 151 | } 152 | return false; 153 | } 154 | 155 | boolean isMWPMasterTabOpen() { 156 | if (getTabCount() == 0) { 157 | return false; 158 | } 159 | for (int i = 0; i < getTabCount(); i++) { 160 | if (getTitleAt(i).contains(Application.MWP_MASTER_TABNAME)) { 161 | return true; 162 | } 163 | } 164 | return false; 165 | } 166 | 167 | boolean isTimelineMasterTabOpen() { 168 | if (getTabCount() == 0) { 169 | return false; 170 | } 171 | for (int i = 0; i < getTabCount(); i++) { 172 | if (getTitleAt(i).contains(Application.TIME_OPTIONS_MASTER_TABNAME)) { 173 | return true; 174 | } 175 | } 176 | return false; 177 | } 178 | 179 | void openSLAMasterTab(Application app) { 180 | SLAPanel slaPanel = new SLAPanel(app.getMainFrame(), app.getAppProperties().getSlaProperties()); 181 | addTab(Application.SLA_MASTER_TABNAME, slaPanel); 182 | setTabComponentAt(getTabCount() - 1, new TabCloseComponent(Application.SLA_MASTER_TABNAME, this, tabsListener)); 183 | setSelectedIndex(getTabCount() - 1); 184 | checkFirstTabOpened(); 185 | } 186 | 187 | void openMWPMasterTab(Application app) { 188 | MWPPanel mwpPanel = new MWPPanel(app.getMainFrame(), app.getAppProperties().getMwpProperties()); 189 | addTab(Application.MWP_MASTER_TABNAME, mwpPanel); 190 | setTabComponentAt(getTabCount() - 1, new TabCloseComponent(Application.MWP_MASTER_TABNAME, this, tabsListener)); 191 | setSelectedIndex(getTabCount() - 1); 192 | checkFirstTabOpened(); 193 | } 194 | 195 | void openTimeOptionsMasterTab(Application app) { 196 | DatePanel timeOptionsPanel = new DatePanel(app.getMainFrame(), app.getAppProperties().getDateProperties()); 197 | addTab(Application.TIME_OPTIONS_MASTER_TABNAME, timeOptionsPanel); 198 | setTabComponentAt(getTabCount() - 1, new TabCloseComponent(Application.TIME_OPTIONS_MASTER_TABNAME, this, tabsListener)); 199 | setSelectedIndex(getTabCount() - 1); 200 | checkFirstTabOpened(); 201 | } 202 | 203 | boolean isMasterTabCurrent() { 204 | if (getTabCount() != 0) { 205 | if (getTitleAt(getSelectedIndex()).contains(Application.SLA_MASTER_TABNAME) || 206 | getTitleAt(getSelectedIndex()).contains(Application.MWP_MASTER_TABNAME)) 207 | { 208 | return true; 209 | } 210 | } 211 | return false; 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/PlotFilesDialog.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | 11 | class PlotFilesDialog { 12 | 13 | private static String getLabelText(boolean multipleFilesMode, boolean disableSameChart) { 14 | if (multipleFilesMode) { 15 | if (!disableSameChart) { 16 | return "" + 17 | "New files can either be plotted in the same chart, the same tab or multiple tabs.
" + 18 | "How would you like to plot files?" + 19 | ""; 20 | } else { 21 | return "" + 22 | "New files can either be plotted in the same tab or multiple tabs.
" + 23 | "How would you like to plot files?" + 24 | ""; 25 | } 26 | } else { 27 | return "" + 28 | "New file can either be plotted in the current tab or new tab.
" + 29 | "How would you like to plot file?" + 30 | ""; 31 | } 32 | } 33 | 34 | static PlotFilesMode showDialog(Window owner, boolean multipleFilesMode) { 35 | return showDialog(owner, multipleFilesMode, false); 36 | } 37 | 38 | /* 39 | * Shows a dialog that asks user how to plot new files (in the same chart, the same tab or multiple tabs). 40 | * 41 | * multipleFilesMode true if user selected multiple files and the same chart option is needed 42 | * 43 | * disableSameChart true if one of selected files contains multiple tags and the same chart option isn't supported 44 | */ 45 | static PlotFilesMode showDialog(Window owner, boolean multipleFilesMode, boolean disableSameChart) { 46 | Object[] message = new Object[1]; 47 | JLabel label = new JLabel(getLabelText(multipleFilesMode, disableSameChart)); 48 | message[0] = label; 49 | 50 | String[] options; 51 | if (multipleFilesMode) { 52 | if (!disableSameChart) { 53 | options = new String[]{ 54 | "Multiple tabs", 55 | "Same tab", 56 | "Same chart" 57 | }; 58 | } else { 59 | options = new String[]{ 60 | "Multiple tabs", 61 | "Same tab" 62 | }; 63 | } 64 | } else { 65 | options = new String[]{ 66 | "New tab", 67 | "Current tab" 68 | }; 69 | } 70 | 71 | String title = (multipleFilesMode ? "Plot Files" : "Plot File"); 72 | int result = JOptionPane.showOptionDialog( 73 | owner, 74 | message, 75 | title, 76 | JOptionPane.DEFAULT_OPTION, 77 | JOptionPane.QUESTION_MESSAGE, 78 | null, 79 | options, 80 | options[options.length - 1] 81 | ); 82 | 83 | switch(result) { 84 | case 0: 85 | return PlotFilesMode.MULTIPLE_TABS; 86 | case 1: 87 | return PlotFilesMode.SAME_TAB; 88 | case 2: 89 | return PlotFilesMode.SAME_CHART; 90 | default: 91 | break; 92 | 93 | } 94 | 95 | // User pressed Close button 96 | return PlotFilesMode.SAME_CHART; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/PlotFilesMode.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | enum PlotFilesMode { 9 | SAME_CHART, 10 | SAME_TAB, 11 | MULTIPLE_TABS 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/PrintStatistics.java: -------------------------------------------------------------------------------- 1 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 2 | 3 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.HistogramModel; 4 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.MaxPercentileIterator; 5 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.TimelineIterator; 6 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 7 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.TimelineObject; 8 | import org.HdrHistogram.HistogramLogAnalyzer.properties.MWPProperties; 9 | 10 | import java.io.IOException; 11 | import java.util.*; 12 | 13 | class PrintStatistics { 14 | 15 | // prints statistics to console 16 | static void print(HistogramModel histogramModel) throws IOException { 17 | for (String tag : histogramModel.getTags()) { 18 | print(histogramModel, tag); 19 | } 20 | } 21 | 22 | static void print(HistogramModel histogramModel, String tag) throws IOException { 23 | String tagString = (tag != null) ? "[tag=" + tag + "]" : ""; 24 | System.out.println("LogFile"+ tagString + ": " + histogramModel.getInputFileName()); 25 | 26 | double startTime = histogramModel.getStartTimeSec(); 27 | System.out.format(Locale.US, "StartTime: %.3f (seconds since epoch), %s\n", 28 | startTime, (new Date((long) (startTime * 1000))).toString()); 29 | 30 | // 99.99'ile value 31 | double valueFor9999 = 0; 32 | 33 | Iterator pi = histogramModel.listHPLPercentileObjects(tag); 34 | while (pi.hasNext()) { 35 | PercentileObject po = pi.next(); 36 | String percentileString = String.valueOf(po.getPercentileValue() * 100); 37 | if ("99.99".equals(percentileString)) { 38 | valueFor9999 = po.getLatencyAxisValue(); 39 | } 40 | percentileString = !percentileString.contains(".") ? 41 | percentileString : percentileString.replaceAll("0*$", "").replaceAll("\\.$", ""); 42 | percentileString = percentileString + "th:"; 43 | System.out.format("%-8s" + " %7.3f%n", percentileString, po.getLatencyAxisValue()); 44 | } 45 | 46 | // number of latency points greater than 99.99'ile 47 | int counter = 0; 48 | MWPProperties.MWPEntry mwpEntry = MWPProperties.getDefaultMWPEntry(); 49 | TimelineIterator ti = histogramModel.listTimelineObjects(false, tag, mwpEntry); 50 | while (ti.hasNext()) { 51 | TimelineObject to = ti.next(); 52 | if (to.getLatencyAxisValue() > valueFor9999) { 53 | counter++; 54 | } 55 | } 56 | System.out.format("NumberGreaterThan99.99: %d%n", counter); 57 | 58 | // maximum latency value 59 | Double maxLatencyAxisValue = 0.0; 60 | MaxPercentileIterator mpi = histogramModel.listMaxPercentileObjects(tag); 61 | PercentileObject mpo; 62 | while (mpi.hasNext()) { 63 | mpo = mpi.next(); 64 | maxLatencyAxisValue = Math.max(maxLatencyAxisValue, mpo.getLatencyAxisValue()); 65 | } 66 | System.out.format("Maximum: %7.3f%n", maxLatencyAxisValue); 67 | 68 | // top N (10 by default) latency points on timeline chart (sort by timeline) 69 | List topLatencyObjects = new ArrayList<>(); 70 | ti = histogramModel.listTimelineObjects(false, tag, mwpEntry, 71 | Configuration.getInstance().getNlhValue()); 72 | while (ti.hasNext()) { 73 | topLatencyObjects.add(ti.next()); 74 | } 75 | 76 | Collections.sort(topLatencyObjects, new Comparator() { 77 | @Override 78 | public int compare(TimelineObject o1, TimelineObject o2) { 79 | return Double.compare(o1.getTimelineAxisValue(), o2.getTimelineAxisValue()); 80 | } 81 | }); 82 | counter = 0; 83 | for (TimelineObject to : topLatencyObjects) { 84 | System.out.format("%d %7.3f %7.3f%n", counter, to.getTimelineAxisValue(), to.getLatencyAxisValue()); 85 | counter++; 86 | } 87 | System.out.println(); 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/TabCloseComponent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | import java.awt.Component; 9 | import java.awt.Dimension; 10 | import java.awt.FlowLayout; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | import java.awt.event.MouseAdapter; 14 | import java.awt.event.MouseEvent; 15 | import java.awt.event.MouseListener; 16 | 17 | import javax.swing.*; 18 | import javax.swing.plaf.basic.BasicButtonUI; 19 | 20 | @SuppressWarnings("serial") 21 | public class TabCloseComponent extends JPanel { 22 | private final JTabbedPane pane; 23 | private final JLabel label; 24 | private final JButton button = new TabButton(); 25 | private final TabsListener tabsListener; 26 | 27 | public TabCloseComponent(String tabTitle, JTabbedPane pane, TabsListener tabsListener) { 28 | super(new FlowLayout(FlowLayout.LEFT, 0, 0)); 29 | this.pane = pane; 30 | this.tabsListener = tabsListener; 31 | setOpaque(false); 32 | label = new JLabel(tabTitle); 33 | add(label); 34 | label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); 35 | add(button); 36 | setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0)); 37 | } 38 | 39 | TabsListener getTabsListener() { 40 | return tabsListener; 41 | } 42 | 43 | private class TabButton extends JButton implements ActionListener { 44 | public TabButton() { 45 | int size = 17; 46 | setIcon((new ImageIcon(getClass().getResource("icon_close2.png")))); 47 | setPreferredSize(new Dimension(size, size)); 48 | setToolTipText("close this tab"); 49 | setUI(new BasicButtonUI()); 50 | 51 | setContentAreaFilled(false); 52 | setFocusable(false); 53 | setBorder(BorderFactory.createEtchedBorder()); 54 | setBorderPainted(false); 55 | addMouseListener(buttonMouseListener); 56 | setRolloverEnabled(true); 57 | addActionListener(this); 58 | } 59 | 60 | @Override 61 | public void actionPerformed(ActionEvent e) { 62 | int index = pane.indexOfTabComponent(TabCloseComponent.this); 63 | if(JOptionPane.showConfirmDialog(pane.getParent().getParent(), 64 | "Are you sure you want to close the tab?",pane.getTitleAt(index),JOptionPane.YES_NO_OPTION)==0) 65 | { 66 | if (index != -1) { 67 | pane.remove(index); 68 | if (pane.getTabCount() == 0) { 69 | tabsListener.lastTabClosed(); 70 | } 71 | } 72 | } 73 | } 74 | 75 | @Override 76 | public void updateUI() { 77 | } 78 | } 79 | 80 | private final static MouseListener buttonMouseListener = new MouseAdapter() { 81 | 82 | @Override 83 | public void mouseEntered(MouseEvent e) { 84 | Component component = e.getComponent(); 85 | if (component instanceof AbstractButton) { 86 | AbstractButton button = (AbstractButton) component; 87 | button.setBorderPainted(true); 88 | } 89 | } 90 | 91 | @Override 92 | public void mouseExited(MouseEvent e) { 93 | Component component = e.getComponent(); 94 | if (component instanceof AbstractButton) { 95 | AbstractButton button = (AbstractButton) component; 96 | button.setBorderPainted(false); 97 | } 98 | } 99 | }; 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/TabsListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | public interface TabsListener { 9 | 10 | public void firstTabOpened(); 11 | 12 | public void lastTabClosed(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/Version.java.template: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | public final class Version { 9 | public static final String version="$VERSION$"; 10 | public static final String build_time="$BUILD_TIME$"; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/ZFileChooser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.applicationlayer; 7 | 8 | import java.awt.Dimension; 9 | import java.awt.FileDialog; 10 | import java.io.File; 11 | import java.io.FilenameFilter; 12 | 13 | import javax.swing.JFileChooser; 14 | import javax.swing.JFrame; 15 | import javax.swing.filechooser.FileFilter; 16 | 17 | /** 18 | * ZFileChooser.java - ZFileChooser.java 19 | * 20 | * @since : 1.0 (Aug 18, 2012) 21 | */ 22 | public final class ZFileChooser { 23 | 24 | private FileDialog fileDialog; 25 | private JFileChooser fileChooser; 26 | private JFrame mainFrame; 27 | private File defaultDir; 28 | 29 | /** 30 | * @param mainFrame 31 | */ 32 | public ZFileChooser(JFrame mainFrame) { 33 | this.mainFrame = mainFrame; 34 | } 35 | 36 | /** 37 | * @param mainFrame 38 | */ 39 | public ZFileChooser(JFrame mainFrame, File defaultDir) { 40 | this.mainFrame = mainFrame; 41 | this.defaultDir = defaultDir; 42 | } 43 | 44 | /** 45 | * @return option selected by the user 46 | * mac: CANCEL_OPTION, APPROVE_OPTION 47 | * all other: CANCEL_OPTION, APPROVE_OPTION, ... 48 | */ 49 | public int showSaveDialog() { 50 | if (isMac()) { 51 | getFileDialog().setMode(FileDialog.SAVE); 52 | getFileDialog().setVisible(true); 53 | String file = getFileDialog().getFile(); 54 | if (file == null) { 55 | return JFileChooser.CANCEL_OPTION; 56 | } 57 | return JFileChooser.APPROVE_OPTION; 58 | } else { 59 | return getFileChooser().showSaveDialog(mainFrame); 60 | } 61 | } 62 | 63 | /** 64 | * @return option selected by the user 65 | * mac: CANCEL_OPTION, APPROVE_OPTION 66 | * all other: CANCEL_OPTION, APPROVE_OPTION, ... 67 | */ 68 | public int showOpenDialog() { 69 | if (isMac()) { 70 | getFileDialog().setMode(FileDialog.LOAD); 71 | getFileDialog().setVisible(true); 72 | String file = getFileDialog().getFile(); 73 | if (file == null) { 74 | return JFileChooser.CANCEL_OPTION; 75 | } 76 | return JFileChooser.APPROVE_OPTION; 77 | } else { 78 | return getFileChooser().showOpenDialog(mainFrame); 79 | } 80 | } 81 | 82 | /** 83 | * @return File object that contains information about the file selected in 84 | * the dialog 85 | */ 86 | public File getSelectedFile() { 87 | if (isMac()) { 88 | String dir = getFileDialog().getDirectory(); 89 | String file = getFileDialog().getFile(); 90 | return new File(dir, file); 91 | } else { 92 | return getFileChooser().getSelectedFile(); 93 | } 94 | } 95 | 96 | File[] getSelectedFiles() { 97 | if (isMac()) { 98 | return getFileDialog().getFiles(); 99 | } else { 100 | return getFileChooser().getSelectedFiles(); 101 | } 102 | } 103 | 104 | /** 105 | * @param fil is the File object that contains the information to which to set 106 | * the file field 107 | */ 108 | public void setSelectedFile(File fil) { 109 | if (isMac()) { 110 | // TODO: getFileDialog().setFile(fil); 111 | } else { 112 | getFileChooser().setSelectedFile(fil); 113 | } 114 | } 115 | 116 | /** 117 | * @return File object that contains information about the directory displayed 118 | * in the dialog 119 | */ 120 | public File getCurrentDirectory() { 121 | if (isMac()) { 122 | return new File(getFileDialog().getDirectory()); 123 | } else { 124 | return getFileChooser().getCurrentDirectory(); 125 | } 126 | } 127 | 128 | /** 129 | * @param dir is the File object that contains the information to which to set 130 | * the directory field 131 | */ 132 | public void setCurrentDirectory(File dir) { 133 | if (isMac()) { 134 | // TODO: getFileDialog().setDirectory(dir.something()); 135 | } else { 136 | getFileChooser().setCurrentDirectory(dir); 137 | } 138 | } 139 | 140 | /** 141 | * @param dialogTitle is the title to display in the dialog 142 | */ 143 | public void setDialogTitle(String dialogTitle) { 144 | if (isMac()) { 145 | // TODO: getFileDialog().setDialogTitle(dialogTitle); 146 | } else { 147 | getFileChooser().setDialogTitle(dialogTitle); 148 | } 149 | } 150 | 151 | /** 152 | * @return FileDialog 153 | */ 154 | private FileDialog getFileDialog() { 155 | if (fileDialog == null) { 156 | fileDialog = new FileDialog(mainFrame, "Histogram Log Analyzer"); 157 | udateDefaultDir(defaultDir); 158 | } 159 | return fileDialog; 160 | } 161 | 162 | private JFileChooser getFileChooser() { 163 | if (fileChooser == null) { 164 | fileChooser = new JFileChooser(defaultDir); 165 | } 166 | return fileChooser; 167 | } 168 | 169 | /** 170 | * @param aFilter 171 | */ 172 | public void addChoosableFileFilter(final FileFilter aFilter) { 173 | if (isMac()) { 174 | getFileDialog().setFilenameFilter(new FilenameFilter() { 175 | @Override 176 | public boolean accept(File dir, String name) { 177 | return name != null ? aFilter.accept(new File(dir, name)) : aFilter.accept(dir); 178 | } 179 | }); 180 | 181 | } 182 | else { 183 | getFileChooser().addChoosableFileFilter(aFilter); 184 | } 185 | 186 | } 187 | 188 | private static boolean isMac() { 189 | String os = System.getProperty("os.name").toLowerCase(); 190 | return (os.indexOf("mac") >= 0); 191 | } 192 | 193 | /** 194 | * @param filesOnly 195 | */ 196 | public void setFileSelectionMode(int filesOnly) { 197 | if (isMac()) { 198 | 199 | } else { 200 | getFileChooser().setFileSelectionMode(filesOnly); 201 | } 202 | } 203 | 204 | /** 205 | * @param dimension 206 | */ 207 | public void setPreferredSize(Dimension dimension) { 208 | if (isMac()) { 209 | getFileDialog().setPreferredSize(dimension); 210 | } else { 211 | getFileChooser().setPreferredSize(dimension); 212 | } 213 | } 214 | 215 | /** 216 | * @param dDir 217 | */ 218 | private void udateDefaultDir(File dDir) { 219 | if (isMac()) { 220 | if (dDir != null) { 221 | if (dDir.isDirectory()) { 222 | getFileDialog().setDirectory(dDir.toString()); 223 | } else if (dDir.isFile()) { 224 | getFileDialog().setFile(dDir.toString()); 225 | } 226 | } 227 | } 228 | } 229 | 230 | void setMultipleMode(boolean enable) { 231 | if (isMac()) { 232 | getFileDialog().setMultipleMode(enable); 233 | } else { 234 | getFileChooser().setMultiSelectionEnabled(enable); 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/BucketsChartBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.*; 9 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.BucketIterator; 10 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.HistogramModel; 11 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.MaxPercentileIterator; 12 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.BucketObject; 13 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 14 | import org.HdrHistogram.HistogramLogAnalyzer.properties.AppProperties; 15 | import org.HdrHistogram.HistogramLogAnalyzer.properties.ZoomProperties; 16 | import org.jfree.chart.*; 17 | import org.jfree.chart.axis.LogAxis; 18 | import org.jfree.chart.axis.NumberAxis; 19 | import org.jfree.chart.entity.ChartEntity; 20 | import org.jfree.chart.entity.LegendItemEntity; 21 | import org.jfree.chart.labels.StandardXYToolTipGenerator; 22 | import org.jfree.chart.plot.PlotOrientation; 23 | import org.jfree.chart.plot.XYPlot; 24 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 25 | import org.jfree.data.xy.XYSeries; 26 | import org.jfree.data.xy.XYSeriesCollection; 27 | 28 | import javax.swing.*; 29 | import java.awt.*; 30 | import java.awt.event.MouseAdapter; 31 | import java.awt.event.MouseEvent; 32 | import java.beans.PropertyChangeEvent; 33 | import java.beans.PropertyChangeListener; 34 | import java.io.IOException; 35 | import java.text.DecimalFormat; 36 | import java.text.DecimalFormatSymbols; 37 | import java.util.*; 38 | import java.util.List; 39 | 40 | public class BucketsChartBuilder { 41 | 42 | public JPanel createBucketsChart(final List models, final AppProperties appProperties) { 43 | JFreeChart drawable = createBucketsDrawable(models, appProperties); 44 | 45 | final ChartPanel chartPanel = new ChartPanel(drawable, true); 46 | chartPanel.setPreferredSize(new java.awt.Dimension(800, 600)); 47 | chartPanel.setBackground(Color.gray); 48 | chartPanel.setBorder(BorderFactory.createCompoundBorder( 49 | BorderFactory.createEmptyBorder(4, 4, 4, 4), 50 | BorderFactory.createLineBorder(Color.black))); 51 | 52 | ToolTipManager.sharedInstance().registerComponent(chartPanel); 53 | chartPanel.addMouseListener(new MouseAdapter() { 54 | @Override 55 | public void mouseEntered(MouseEvent e) { 56 | ToolTipManager ttm = ToolTipManager.sharedInstance(); 57 | ttm.setDismissDelay(10000); 58 | ttm.setReshowDelay(0); 59 | ttm.setInitialDelay(0); 60 | } 61 | }); 62 | 63 | // clicking on legend item for a tag changes visibility of this tag on chart 64 | chartPanel.addChartMouseListener(new ChartMouseListener() { 65 | @Override 66 | public void chartMouseClicked(ChartMouseEvent chartMouseEvent) { 67 | ChartEntity chartEntity = chartMouseEvent.getEntity(); 68 | if (chartEntity instanceof LegendItemEntity) { 69 | LegendItemEntity legendItemEntity = (LegendItemEntity)chartEntity; 70 | XYPlot plot = (XYPlot) chartMouseEvent.getChart().getPlot(); 71 | XYLineAndShapeRenderer renderer = 72 | (XYLineAndShapeRenderer) plot.getRenderer(); 73 | 74 | XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); 75 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 76 | XYSeries series = dataset.getSeries(i); 77 | String key = (String) series.getKey(); 78 | Boolean flag = renderer.getSeriesLinesVisible(i); 79 | if (key.equals(legendItemEntity.getSeriesKey())) { 80 | renderer.setSeriesLinesVisible(i, !flag); 81 | } 82 | } 83 | } 84 | } 85 | @Override 86 | public void chartMouseMoved(ChartMouseEvent chartMouseEvent) { 87 | } 88 | }); 89 | 90 | // toggling HPL menu item changes visibility of HPL lines on chart 91 | appProperties.getHplProperties().addPropertyChangeListener(new PropertyChangeListener() { 92 | @Override 93 | public void propertyChange(PropertyChangeEvent evt) { 94 | if (evt.getPropertyName().equals("hplShow")) { 95 | Boolean b = (Boolean) evt.getNewValue(); 96 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 97 | XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); 98 | 99 | XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); 100 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 101 | XYSeries series = dataset.getSeries(i); 102 | String key = (String) series.getKey(); 103 | boolean hplKey = key.endsWith("%") || key.endsWith("Max"); 104 | if (hplKey) { 105 | renderer.setSeriesVisible(i, b); 106 | } 107 | } 108 | } 109 | } 110 | }); 111 | 112 | // FIXME: uninstall listeners 113 | 114 | return chartPanel; 115 | } 116 | 117 | private JFreeChart createBucketsDrawable(List histogramModels, AppProperties appProperties) 118 | { 119 | String chartTitle = ConstantsHelper.getChartTitle(HLAChartType.BUCKETS); 120 | String xAxisLabel = ConstantsHelper.getXAxisLabel(HLAChartType.BUCKETS); 121 | String yAxisLabel = ConstantsHelper.getYAxisLabel(HLAChartType.BUCKETS); 122 | 123 | XYSeriesCollection sc = createXYSeriesCollection(histogramModels); 124 | 125 | JFreeChart drawable = ChartFactory.createXYLineChart(chartTitle, 126 | xAxisLabel, 127 | yAxisLabel, 128 | sc, 129 | PlotOrientation.VERTICAL, 130 | true, 131 | true, 132 | false); 133 | 134 | drawable.getPlot().setBackgroundPaint(Color.white); 135 | drawable.getXYPlot().setRangeGridlinePaint(Color.gray); 136 | drawable.getXYPlot().setDomainGridlinePaint(Color.gray); 137 | 138 | XYPlot plot = (XYPlot) drawable.getPlot(); 139 | 140 | XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); 141 | XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); 142 | 143 | LogAxis logDomain = new LogAxis(xAxisLabel); 144 | logDomain.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); 145 | logDomain.setMinorTickMarksVisible(false); 146 | logDomain.setBase(10); 147 | DecimalFormat df = new DecimalFormat("0.###"); 148 | df.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.ENGLISH)); 149 | logDomain.setNumberFormatOverride(df); 150 | 151 | logDomain.setLabelFont(plot.getDomainAxis().getLabelFont()); 152 | plot.setDomainAxis(0, logDomain); 153 | 154 | LogAxis logRange = new LogAxis(yAxisLabel); 155 | logRange.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); 156 | logRange.setMinorTickMarksVisible(false); 157 | logRange.setBase(10); 158 | df = new DecimalFormat("#,###,###"); 159 | df.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.ENGLISH)); 160 | logRange.setNumberFormatOverride(df); 161 | 162 | logRange.setLabelFont(plot.getRangeAxis().getLabelFont()); 163 | plot.setRangeAxis(0, logRange); 164 | 165 | for (int i = 0; i < plot.getSeriesCount(); i++) { 166 | 167 | XYSeries series = dataset.getSeries(i); 168 | String key = (String) series.getKey(); 169 | 170 | renderer.setSeriesLinesVisible(i, true); 171 | if (key.endsWith("%") || key.equals("Max")) { 172 | renderer.setSeriesVisible(i, appProperties.getHplProperties().isHPLVisible()); 173 | renderer.setSeriesPaint(i, ColorHelper.getHPLColor(key)); 174 | renderer.setSeriesStroke(i, new BasicStroke(2.0f)); 175 | } else { 176 | renderer.setSeriesPaint(i, ColorHelper.getColor(i)); 177 | } 178 | 179 | boolean hplKey = key.endsWith("%") || key.endsWith("Max"); 180 | if (!hplKey) { 181 | renderer.setSeriesStroke(i, new BasicStroke(1.0f, java.awt.BasicStroke.CAP_SQUARE, java.awt.BasicStroke.JOIN_MITER)); 182 | renderer.setSeriesShapesVisible(i, false); 183 | } else { 184 | renderer.setSeriesStroke(i, new BasicStroke(2.0f)); 185 | renderer.setSeriesShapesVisible(i, false); 186 | renderer.setSeriesItemLabelsVisible(i, true); 187 | } 188 | } 189 | 190 | renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); 191 | 192 | plot.setDomainGridlinesVisible(false); 193 | plot.setDomainPannable(true); 194 | plot.setRangePannable(true); 195 | plot.setRenderer(renderer); 196 | 197 | return drawable; 198 | 199 | } 200 | 201 | private static final String DEFAULT_KEY = "Raw latency count"; 202 | 203 | private XYSeriesCollection createXYSeriesCollection(List histogramModels) { 204 | XYSeriesCollection ret = new XYSeriesCollection(); 205 | 206 | // tool doesn't support MWP/HPL for charts with multiple files 207 | // tool doesn't support MWP/HPL for files with multiple tags 208 | boolean multipleFiles = histogramModels.size() > 1; 209 | if (!multipleFiles) { 210 | HistogramModel histogramModel = histogramModels.get(0); 211 | Set tags = histogramModel.getTags(); 212 | 213 | boolean multipleTags = tags.size() > 1; 214 | if (!multipleTags) { 215 | String tag = null; 216 | if(!tags.isEmpty()) { 217 | tag = tags.iterator().next(); 218 | } 219 | XYSeries series = new XYSeries(DEFAULT_KEY); 220 | BucketIterator bi = histogramModel.listBucketObjects(tag); 221 | while (bi.hasNext()) { 222 | BucketObject bo = (BucketObject) bi.next(); 223 | double hiccupValue = bo.getLatencyValue(); 224 | double totalCount = bo.getCountAtValue(); 225 | if (totalCount == 0) { 226 | continue; 227 | } 228 | series.add(hiccupValue, totalCount); 229 | } 230 | ret.addSeries(series); 231 | 232 | // HPL lines (vertical lines actually but let's use HPL for consistency) 233 | Iterator pi = histogramModel.listHPLPercentileObjects(tag); 234 | while (pi.hasNext()) { 235 | PercentileObject po = pi.next(); 236 | String key = String.valueOf(po.getPercentileValue() * 100); 237 | key = !key.contains(".") ? key : key.replaceAll("0*$", "").replaceAll("\\.$", ""); 238 | series = new XYSeries(key +"%"); 239 | 240 | double latencyValue = po.getLatencyAxisValue(); 241 | 242 | series.add(latencyValue, ret.getRangeLowerBound(false)); 243 | series.add(latencyValue, ret.getRangeUpperBound(false)); 244 | ret.addSeries(series); 245 | } 246 | 247 | // Max line 248 | Double maxLatencyAxisValue = 0.0; 249 | MaxPercentileIterator mpi = histogramModel.listMaxPercentileObjects(tag); 250 | PercentileObject mpo; 251 | while (mpi.hasNext()) { 252 | mpo = mpi.next(); 253 | maxLatencyAxisValue = Math.max(maxLatencyAxisValue, mpo.getLatencyAxisValue()); 254 | } 255 | series = new XYSeries("Max"); 256 | series.add(ret.getDomainUpperBound(false), ret.getRangeLowerBound(false)); 257 | series.add(ret.getDomainUpperBound(false), ret.getRangeUpperBound(false)); 258 | ret.addSeries(series); 259 | 260 | return ret; 261 | } 262 | } 263 | 264 | for (HistogramModel histogramModel : histogramModels) { 265 | boolean multipleTags = histogramModel.getTags().size() > 1; 266 | for (String tag : histogramModel.getTags()) { 267 | String key; 268 | if (multipleFiles) { 269 | key = histogramModel.getShortFileName(); 270 | } else { 271 | if (multipleTags) { 272 | key = tag == null ? "No Tag" : tag; 273 | } else { 274 | key = DEFAULT_KEY; 275 | } 276 | } 277 | XYSeries series = new XYSeries(key); 278 | 279 | BucketIterator bi = histogramModel.listBucketObjects(tag); 280 | while (bi.hasNext()) { 281 | BucketObject bo = (BucketObject) bi.next(); 282 | double hiccupValue = bo.getLatencyValue(); 283 | double totalCount = bo.getCountAtValue(); 284 | if (totalCount == 0) { 285 | continue; 286 | } 287 | series.add(hiccupValue, totalCount); 288 | } 289 | ret.addSeries(series); 290 | } 291 | } 292 | 293 | 294 | return ret; 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/ColorHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import java.awt.Color; 9 | import java.util.ArrayList; 10 | import java.util.Random; 11 | 12 | class ColorHelper { 13 | 14 | private static ArrayList colors = new ArrayList(); 15 | 16 | static { 17 | colors.add(Color.RED); 18 | colors.add(Color.BLUE); 19 | colors.add(Color.GREEN); 20 | colors.add(Color.MAGENTA); 21 | colors.add(Color.CYAN); 22 | } 23 | 24 | private static void ensureSize(int size) { 25 | while (colors.size() < size) { 26 | Random r = new Random(); 27 | colors.add(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255))); 28 | } 29 | } 30 | 31 | static Color getColor(int i) { 32 | ensureSize(i + 1); 33 | return colors.get(i); 34 | } 35 | 36 | static Color getSLAColor() { 37 | return new Color(255, 204, 102); 38 | } 39 | 40 | static Color getHPLColor(String percentileString) { 41 | switch (percentileString) { 42 | case "99%": 43 | case "99.0%": 44 | return Color.GREEN; 45 | case "99.9%": 46 | return Color.BLUE; 47 | case "99.99%": 48 | return new Color(128, 0, 128); // purple 49 | case "Max": 50 | return Color.RED; 51 | } 52 | throw new RuntimeException("unexpected HPL: "+percentileString); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/CommonSeries.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 9 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.TimelineObject; 10 | import org.jfree.data.Range; 11 | import org.jfree.data.general.Series; 12 | 13 | interface CommonSeries { 14 | 15 | void add(TimelineObject timelineObject, double startTime); 16 | 17 | void add(double latencyAxisValue, Range domainBounds); 18 | 19 | Series get(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/CommonSeriesCollection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.jfree.data.Range; 9 | import org.jfree.data.general.Series; 10 | import org.jfree.data.xy.XYDataset; 11 | 12 | interface CommonSeriesCollection { 13 | 14 | void add(CommonSeries series); 15 | 16 | Range getDomainBounds(); 17 | 18 | XYDataset getDataset(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/HLATimeSeries.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 9 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.TimelineObject; 10 | import org.jfree.data.Range; 11 | import org.jfree.data.general.Series; 12 | import org.jfree.data.time.FixedMillisecond; 13 | import org.jfree.data.time.TimeSeries; 14 | 15 | import java.util.Date; 16 | 17 | class HLATimeSeries extends TimeSeries implements CommonSeries { 18 | 19 | HLATimeSeries(Comparable name) { 20 | super(name); 21 | } 22 | 23 | @Override 24 | public void add(TimelineObject timelineObject, double startTime) { 25 | double xValue = startTime + timelineObject.getTimelineAxisValue(); 26 | Date date = new Date((long) (xValue * 1000)); 27 | super.addOrUpdate(new FixedMillisecond(date), timelineObject.getLatencyAxisValue()); 28 | } 29 | 30 | @Override 31 | public void add(double latencyAxisValue, Range domainBounds) { 32 | double lowerValue = domainBounds.getLowerBound(); 33 | double upperValue = domainBounds.getUpperBound(); 34 | 35 | super.addOrUpdate(new FixedMillisecond(new Date((long) lowerValue)), latencyAxisValue); 36 | super.addOrUpdate(new FixedMillisecond(new Date((long) upperValue)), latencyAxisValue); 37 | } 38 | 39 | @Override 40 | public Series get() { 41 | return this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/HLATimeSeriesCollection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.jfree.data.Range; 9 | import org.jfree.data.time.TimeSeries; 10 | import org.jfree.data.time.TimeSeriesCollection; 11 | import org.jfree.data.xy.XYDataset; 12 | 13 | class HLATimeSeriesCollection extends TimeSeriesCollection 14 | implements CommonSeriesCollection { 15 | 16 | @Override 17 | public void add(CommonSeries series) { 18 | super.addSeries((TimeSeries) series); 19 | } 20 | 21 | @Override 22 | public Range getDomainBounds() { 23 | return super.getDomainBounds(false); 24 | } 25 | 26 | @Override 27 | public XYDataset getDataset() { 28 | return this; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/HLAXYSeries.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 9 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.TimelineObject; 10 | import org.jfree.data.Range; 11 | import org.jfree.data.general.Series; 12 | import org.jfree.data.xy.XYSeries; 13 | 14 | class HLAXYSeries extends XYSeries implements CommonSeries { 15 | 16 | HLAXYSeries(Comparable key) { 17 | super(key); 18 | } 19 | 20 | @Override 21 | public void add(TimelineObject timelineObject, double startTime) { 22 | super.add(timelineObject.getTimelineAxisValue(), timelineObject.getLatencyAxisValue()); 23 | } 24 | 25 | @Override 26 | public void add(double latencyAxisValue, Range domainBounds) { 27 | super.add(domainBounds.getLowerBound(), latencyAxisValue); 28 | super.add(domainBounds.getUpperBound(), latencyAxisValue); 29 | } 30 | 31 | @Override 32 | public Series get() { 33 | return this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/HLAXYSeriesCollection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.jfree.data.Range; 9 | import org.jfree.data.xy.XYDataset; 10 | import org.jfree.data.xy.XYSeries; 11 | import org.jfree.data.xy.XYSeriesCollection; 12 | 13 | class HLAXYSeriesCollection extends XYSeriesCollection 14 | implements CommonSeriesCollection { 15 | 16 | @Override 17 | public void add(CommonSeries series) { 18 | super.addSeries((XYSeries) series); 19 | } 20 | 21 | @Override 22 | public Range getDomainBounds() { 23 | return super.getDomainBounds(false); 24 | } 25 | 26 | @Override 27 | public XYDataset getDataset() { 28 | return this; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/PercentileChartBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.*; 9 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.HistogramModel; 10 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.MaxPercentileIterator; 11 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.PercentileIterator; 12 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 13 | import org.HdrHistogram.HistogramLogAnalyzer.properties.*; 14 | import org.jfree.chart.*; 15 | import org.jfree.chart.axis.LogAxis; 16 | import org.jfree.chart.axis.NumberAxis; 17 | import org.jfree.chart.entity.ChartEntity; 18 | import org.jfree.chart.entity.LegendItemEntity; 19 | import org.jfree.chart.labels.StandardXYToolTipGenerator; 20 | import org.jfree.chart.plot.PlotOrientation; 21 | import org.jfree.chart.plot.XYPlot; 22 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 23 | import org.jfree.data.xy.XYSeries; 24 | import org.jfree.data.xy.XYSeriesCollection; 25 | 26 | import javax.swing.*; 27 | import java.awt.*; 28 | import java.awt.event.MouseAdapter; 29 | import java.awt.event.MouseEvent; 30 | import java.beans.PropertyChangeEvent; 31 | import java.beans.PropertyChangeListener; 32 | import java.text.FieldPosition; 33 | import java.text.NumberFormat; 34 | import java.text.ParsePosition; 35 | import java.util.List; 36 | 37 | public class PercentileChartBuilder { 38 | 39 | private Double maxLatencyAxisValue = 0.0; 40 | private Double maxPercentileAxisValue = 0.0; 41 | 42 | public JPanel createPercentileChart(final List histogramModels, final AppProperties appProperties, 43 | final ScaleProperties scaleProperties) 44 | { 45 | JFreeChart drawable = createPercentileDrawable(histogramModels, appProperties); 46 | 47 | final ChartPanel chartPanel = new ChartPanel(drawable, true); 48 | chartPanel.setPreferredSize(new java.awt.Dimension(800, 600)); 49 | chartPanel.setBackground(Color.gray); 50 | chartPanel.setBorder(BorderFactory.createCompoundBorder( 51 | BorderFactory.createEmptyBorder(4, 4, 4, 4), 52 | BorderFactory.createLineBorder(Color.black))); 53 | 54 | ToolTipManager.sharedInstance().registerComponent(chartPanel); 55 | chartPanel.addMouseListener(new MouseAdapter() { 56 | @Override 57 | public void mouseEntered(MouseEvent e) { 58 | ToolTipManager ttm = ToolTipManager.sharedInstance(); 59 | ttm.setDismissDelay(10000); 60 | ttm.setReshowDelay(0); 61 | ttm.setInitialDelay(0); 62 | } 63 | }); 64 | 65 | // clicking on legend item for a tag changes visibility of this tag on chart 66 | chartPanel.addChartMouseListener(new ChartMouseListener() { 67 | @Override 68 | public void chartMouseClicked(ChartMouseEvent chartMouseEvent) { 69 | ChartEntity chartEntity = chartMouseEvent.getEntity(); 70 | if (chartEntity instanceof LegendItemEntity) { 71 | LegendItemEntity legendItemEntity = (LegendItemEntity)chartEntity; 72 | XYPlot plot = (XYPlot) chartMouseEvent.getChart().getPlot(); 73 | XYLineAndShapeRenderer renderer = 74 | (XYLineAndShapeRenderer) plot.getRenderer(); 75 | 76 | XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); 77 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 78 | XYSeries series = dataset.getSeries(i); 79 | String key = (String) series.getKey(); 80 | Boolean flag = renderer.getSeriesLinesVisible(i); 81 | if (key.equals(legendItemEntity.getSeriesKey())) { 82 | renderer.setSeriesLinesVisible(i, !flag); 83 | } 84 | } 85 | } 86 | } 87 | @Override 88 | public void chartMouseMoved(ChartMouseEvent chartMouseEvent) { 89 | } 90 | }); 91 | 92 | // enabling/disabling SLA checkbox changes visibility of this SLA line on chart 93 | appProperties.getSlaProperties().addPropertyChangeListener(new PropertyChangeListener() { 94 | @Override 95 | public void propertyChange(PropertyChangeEvent evt) { 96 | if (evt.getPropertyName().equals("slaShow")) { 97 | Boolean b = (Boolean) evt.getNewValue(); 98 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 99 | XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); 100 | 101 | XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); 102 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 103 | XYSeries series = dataset.getSeries(i); 104 | String key = (String) series.getKey(); 105 | if ("SLA".equals(key)) { 106 | if (b) { 107 | renderer.setSeriesVisible(i, true); 108 | } else { 109 | renderer.setSeriesVisible(i, false); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | }); 116 | 117 | // reseting SLA values updates percentile chart 118 | appProperties.getSlaProperties().addPropertyChangeListener(new PropertyChangeListener() { 119 | @Override 120 | public void propertyChange(PropertyChangeEvent evt) { 121 | if (evt.getPropertyName().equals("applySLA")) { 122 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 123 | XYSeries slaSeries = null; 124 | // get SLA series 125 | XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); 126 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 127 | XYSeries series = dataset.getSeries(i); 128 | String key = (String) series.getKey(); 129 | if ("SLA".equals(key)) { 130 | slaSeries = series; 131 | } 132 | } 133 | // update SLA series 134 | if (slaSeries != null) { 135 | slaSeries.clear(); 136 | List slaEntries = appProperties.getSlaProperties().getSLAEntries(); 137 | slaEntries.add(0, new SLAProperties.SLAEntry(0.0, 0.0)); 138 | int count = slaEntries.size(); 139 | for (int i = 0; i < count; i++) { 140 | SLAProperties.SLAEntry slaEntry = slaEntries.get(i); 141 | slaSeries.add(slaEntry.getPercentileAxis(), slaEntry.getLatency()); 142 | if (i + 1 < count) { 143 | SLAProperties.SLAEntry nextSLAEntry = slaEntries.get(i + 1); 144 | slaSeries.add(slaEntry.getPercentileAxis(), nextSLAEntry.getLatency()); 145 | } 146 | } 147 | slaEntries.remove(0); 148 | } 149 | } 150 | } 151 | }); 152 | 153 | // toggling HPL menu item changes visibility of HPL lines on chart 154 | appProperties.getHplProperties().addPropertyChangeListener(new PropertyChangeListener() { 155 | @Override 156 | public void propertyChange(PropertyChangeEvent evt) { 157 | if (evt.getPropertyName().equals("hplShow")) { 158 | Boolean b = (Boolean) evt.getNewValue(); 159 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 160 | XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); 161 | 162 | XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); 163 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 164 | XYSeries series = dataset.getSeries(i); 165 | String key = (String) series.getKey(); 166 | if (key.endsWith("%") || key.equals("Max")) { 167 | renderer.setSeriesVisible(i, b); 168 | } 169 | } 170 | } 171 | } 172 | }); 173 | 174 | scaleProperties.addPropertyChangeListener(new PropertyChangeListener() { 175 | @Override 176 | public void propertyChange(PropertyChangeEvent evt) { 177 | if (evt.getPropertyName().equals("applyScale")) { 178 | ScaleProperties.ScaleEntry se = (ScaleProperties.ScaleEntry) evt.getNewValue(); 179 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 180 | plot.getRangeAxis(0).setRange(0.0, se.getMaxYValue() + se.getMaxYValue() * 0.1); 181 | } 182 | } 183 | }); 184 | 185 | // FIXME: uninstall listeners 186 | 187 | return chartPanel; 188 | } 189 | 190 | private JFreeChart createPercentileDrawable(List histogramModels, AppProperties appProperties) { 191 | String chartTitle = ConstantsHelper.getChartTitle(HLAChartType.PERCENTILE); 192 | String xAxisLabel = ConstantsHelper.getXAxisLabel(HLAChartType.PERCENTILE); 193 | String yAxisLabel = ConstantsHelper.getYAxisLabel(HLAChartType.PERCENTILE); 194 | String logAxis = ConstantsHelper.getLogAxisLabel(HLAChartType.PERCENTILE); 195 | XYSeriesCollection sc = createXYSeriesCollection(histogramModels, appProperties.getSlaProperties()); 196 | 197 | JFreeChart drawable = ChartFactory.createXYLineChart(chartTitle, 198 | xAxisLabel, 199 | yAxisLabel, 200 | sc, 201 | PlotOrientation.VERTICAL, 202 | true, 203 | true, 204 | false); 205 | 206 | drawable.getPlot().setBackgroundPaint(Color.white); 207 | drawable.getXYPlot().setRangeGridlinePaint(Color.gray); 208 | drawable.getXYPlot().setDomainGridlinePaint(Color.gray); 209 | 210 | XYPlot plot = (XYPlot) drawable.getPlot(); 211 | 212 | LogAxis ll = new LogAxis(logAxis); 213 | ll.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); 214 | ll.setMinorTickMarksVisible(false); 215 | ll.setBase(10); 216 | 217 | ll.setNumberFormatOverride(new NumberFormat() { 218 | private static final long serialVersionUID = 6737184345504901251L; 219 | 220 | @Override 221 | public Number parse(String source, ParsePosition parsePosition) { 222 | return null; 223 | } 224 | 225 | @Override 226 | public StringBuffer format(long number, StringBuffer toAppendTo, 227 | FieldPosition pos) { 228 | return toAppendTo; 229 | } 230 | 231 | @Override 232 | public StringBuffer format(double number, StringBuffer toAppendTo, 233 | FieldPosition pos) { 234 | if (number == 1) { 235 | return new StringBuffer("0.0%"); 236 | } 237 | if (number == 10) { 238 | return new StringBuffer("90.0%"); 239 | } 240 | if (number == 100) { 241 | return new StringBuffer("99.0%"); 242 | } 243 | if (number == 1000) { 244 | return new StringBuffer("99.9%"); 245 | } 246 | if (number == 10000) { 247 | return new StringBuffer("99.99%"); 248 | } 249 | if (number == 100000) { 250 | return new StringBuffer("99.999%"); 251 | } 252 | return toAppendTo; 253 | } 254 | }); 255 | 256 | ll.setLabelFont(plot.getDomainAxis().getLabelFont()); 257 | plot.setDomainAxis(0, ll); 258 | plot.getDomainAxis(0).setUpperBound(maxPercentileAxisValue + (maxPercentileAxisValue * 0.4)); 259 | 260 | plot.getRangeAxis(0).setRange(0.0, maxLatencyAxisValue + maxLatencyAxisValue * 0.1); 261 | plot.getRangeAxis().setAutoRange(false); 262 | 263 | // SLA/HPL visibility, line settings etc 264 | final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); 265 | XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); 266 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 267 | renderer.setSeriesShapesVisible(i, false); 268 | renderer.setSeriesLinesVisible(i, true); 269 | 270 | XYSeries series = dataset.getSeries(i); 271 | String key = (String) series.getKey(); 272 | if (key.equals("SLA")) { 273 | renderer.setSeriesVisible(i, appProperties.getSlaProperties().isSLAVisible()); 274 | renderer.setSeriesPaint(i, ColorHelper.getSLAColor()); 275 | renderer.setSeriesShapesVisible(i, false); 276 | renderer.setSeriesStroke(i, new BasicStroke(2.0f)); 277 | } else if (key.endsWith("%") || key.equals("Max")) { 278 | renderer.setSeriesVisible(i, appProperties.getHplProperties().isHPLVisible()); 279 | renderer.setSeriesPaint(i, ColorHelper.getHPLColor(key)); 280 | renderer.setSeriesStroke(i, new BasicStroke(2.0f)); 281 | } else { 282 | renderer.setSeriesPaint(i, ColorHelper.getColor(i)); 283 | } 284 | } 285 | 286 | plot.setDomainGridlinesVisible(false); 287 | renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); 288 | 289 | plot.setDomainPannable(true); 290 | plot.setRangePannable(true); 291 | plot.setRenderer(renderer); 292 | 293 | return drawable; 294 | } 295 | 296 | private XYSeriesCollection createXYSeriesCollection(List histogramModels, SLAProperties slaProperties) { 297 | XYSeriesCollection ret = new XYSeriesCollection(); 298 | 299 | maxLatencyAxisValue = 0.0; 300 | maxPercentileAxisValue = 0.0; 301 | boolean multipleFiles = histogramModels.size() > 1; 302 | boolean multipleTagsEncountered = false; 303 | String defaultTagKey = ConstantsHelper.getLatencyName() + " by Percentile"; 304 | for (HistogramModel histogramModel : histogramModels) { 305 | boolean multipleTags = histogramModel.getTags().size() > 1; 306 | if (multipleTags) { 307 | multipleTagsEncountered = true; 308 | } 309 | for (String tag : histogramModel.getTags()) { 310 | String key; 311 | if (multipleFiles) { 312 | key = histogramModel.getShortFileName(); 313 | } else { 314 | if (multipleTags) { 315 | key = tag == null ? "No Tag" : tag; 316 | } else { 317 | key = defaultTagKey; 318 | } 319 | } 320 | XYSeries series = new XYSeries(key); 321 | 322 | MaxPercentileIterator mpi = histogramModel.listMaxPercentileObjects(tag); 323 | PercentileObject mpo = null; 324 | while (mpi.hasNext()) { 325 | mpo = mpi.next(); 326 | maxLatencyAxisValue = Math.max(maxLatencyAxisValue, mpo.getLatencyAxisValue()); 327 | maxPercentileAxisValue = Math.max(maxPercentileAxisValue, mpo.getPercentileAxisValue()); 328 | } 329 | 330 | PercentileIterator pi = histogramModel.listPercentileObjects(tag, mpo); 331 | while (pi.hasNext()) { 332 | PercentileObject to = pi.next(); 333 | series.add(to.getPercentileAxisValue(), to.getLatencyAxisValue()); 334 | } 335 | 336 | ret.addSeries(series); 337 | } 338 | } 339 | 340 | // tool doesn't support SLA/HPL for charts with multiple files 341 | // tool doesn't support SLA/HPL for files with multiple tags 342 | if (!multipleFiles && !multipleTagsEncountered) { 343 | XYSeries series = new XYSeries("Max"); 344 | series.add(ret.getDomainLowerBound(false), maxLatencyAxisValue); 345 | series.add(ret.getDomainUpperBound(false), maxLatencyAxisValue); 346 | ret.addSeries(series); 347 | } 348 | 349 | XYSeries series = new XYSeries("SLA"); 350 | List slaEntries = slaProperties.getSLAEntries(); 351 | slaEntries.add(0, new SLAProperties.SLAEntry(0.0, 0.0)); 352 | int count = slaEntries.size(); 353 | for (int i = 0; i < count; i++) { 354 | SLAProperties.SLAEntry slaEntry = slaEntries.get(i); 355 | // limit SLA line 356 | Double percentileAxis = Math.min(slaEntry.getPercentileAxis(), ret.getDomainUpperBound(false)); 357 | series.add(percentileAxis, slaEntry.getLatency()); 358 | if (i + 1 < count) { 359 | SLAProperties.SLAEntry nextSLAEntry = slaEntries.get(i + 1); 360 | series.add(slaEntry.getPercentileAxis(), nextSLAEntry.getLatency()); 361 | } 362 | } 363 | slaEntries.remove(0); 364 | ret.addSeries(series); 365 | 366 | return ret; 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/TimelineChartBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.*; 9 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.HistogramModel; 10 | import org.HdrHistogram.HistogramLogAnalyzer.properties.*; 11 | 12 | import org.jfree.chart.*; 13 | import org.jfree.chart.axis.DateAxis; 14 | import org.jfree.chart.axis.NumberAxis; 15 | import org.jfree.chart.axis.ValueAxis; 16 | import org.jfree.chart.entity.ChartEntity; 17 | import org.jfree.chart.entity.LegendItemEntity; 18 | import org.jfree.chart.event.AxisChangeEvent; 19 | import org.jfree.chart.event.AxisChangeListener; 20 | import org.jfree.chart.labels.StandardXYToolTipGenerator; 21 | import org.jfree.chart.plot.PlotOrientation; 22 | import org.jfree.chart.plot.XYPlot; 23 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 24 | import org.jfree.chart.title.LegendTitle; 25 | 26 | import org.jfree.data.Range; 27 | import org.jfree.data.xy.*; 28 | import org.jfree.ui.RectangleEdge; 29 | 30 | import javax.swing.*; 31 | import java.awt.*; 32 | import java.awt.event.MouseAdapter; 33 | import java.awt.event.MouseEvent; 34 | import java.beans.PropertyChangeEvent; 35 | import java.beans.PropertyChangeListener; 36 | import java.text.DateFormat; 37 | import java.util.Date; 38 | import java.util.List; 39 | import java.util.TimeZone; 40 | 41 | public class TimelineChartBuilder { 42 | 43 | public JPanel createTimelineChart(final List models, final AppProperties appProperties, 44 | final ZoomProperties zoomProperty, final ScaleProperties scaleProperties, 45 | final HLAChartType chartType) 46 | { 47 | JFreeChart drawable = createTimelineDrawable(models, zoomProperty, appProperties, chartType); 48 | 49 | final DateProperties dateProperties = appProperties.getDateProperties(); 50 | 51 | final ChartPanel chartPanel = new ChartPanel(drawable, true); 52 | chartPanel.setPreferredSize(new java.awt.Dimension(800, 600)); 53 | chartPanel.setBackground(Color.gray); 54 | chartPanel.setBorder(BorderFactory.createCompoundBorder( 55 | BorderFactory.createEmptyBorder(4, 4, 4, 4), 56 | BorderFactory.createLineBorder(Color.black))); 57 | 58 | ToolTipManager.sharedInstance().registerComponent(chartPanel); 59 | chartPanel.addMouseListener(new MouseAdapter() { 60 | @Override 61 | public void mouseEntered(MouseEvent e) { 62 | ToolTipManager ttm = ToolTipManager.sharedInstance(); 63 | ttm.setDismissDelay(10000); 64 | ttm.setReshowDelay(0); 65 | ttm.setInitialDelay(0); 66 | } 67 | }); 68 | 69 | // clicking on legend item for a tag changes visibility of this tag on chart 70 | chartPanel.addChartMouseListener(new ChartMouseListener() { 71 | @Override 72 | public void chartMouseClicked(ChartMouseEvent chartMouseEvent) { 73 | ChartEntity chartEntity = chartMouseEvent.getEntity(); 74 | if (chartEntity instanceof LegendItemEntity) { 75 | LegendItemEntity legendItemEntity = (LegendItemEntity)chartEntity; 76 | XYPlot plot = (XYPlot) chartMouseEvent.getChart().getPlot(); 77 | XYLineAndShapeRenderer renderer = 78 | (XYLineAndShapeRenderer) plot.getRenderer(); 79 | 80 | XYDataset dataset = plot.getDataset(); 81 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 82 | String key = (String)dataset.getSeriesKey(i); 83 | Boolean flag = renderer.getSeriesLinesVisible(i); 84 | if (key.equals(legendItemEntity.getSeriesKey())) { 85 | renderer.setSeriesLinesVisible(i, !flag); 86 | } 87 | } 88 | } 89 | } 90 | @Override 91 | public void chartMouseMoved(ChartMouseEvent chartMouseEvent) { 92 | } 93 | }); 94 | 95 | // enabling/disabling MWP checkbox changes visibility of this MWP lines on chart 96 | appProperties.getMwpProperties().addPropertyChangeListener(new PropertyChangeListener() { 97 | @Override 98 | public void propertyChange(PropertyChangeEvent evt) { 99 | if (evt.getPropertyName().equals("mwpShow")) { 100 | Boolean b = (Boolean) evt.getNewValue(); 101 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 102 | XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); 103 | 104 | XYDataset dataset = plot.getDataset(); 105 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 106 | String key = (String)dataset.getSeriesKey(i); 107 | if (key.contains("%'ile")) { 108 | renderer.setSeriesVisible(i, b); 109 | } 110 | } 111 | } 112 | } 113 | }); 114 | 115 | // toggling HPL menu item changes visibility of HPL lines on chart 116 | appProperties.getHplProperties().addPropertyChangeListener(new PropertyChangeListener() { 117 | @Override 118 | public void propertyChange(PropertyChangeEvent evt) { 119 | if (evt.getPropertyName().equals("hplShow")) { 120 | Boolean b = (Boolean) evt.getNewValue(); 121 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 122 | XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); 123 | 124 | XYDataset dataset = plot.getDataset(); 125 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 126 | String key = (String)dataset.getSeriesKey(i); 127 | if (key.endsWith("%") || key.equals("Max")) { 128 | renderer.setSeriesVisible(i, b); 129 | } 130 | } 131 | } 132 | } 133 | }); 134 | 135 | scaleProperties.addPropertyChangeListener(new PropertyChangeListener() { 136 | @Override 137 | public void propertyChange(PropertyChangeEvent evt) { 138 | if (evt.getPropertyName().equals("applyScale")) { 139 | ScaleProperties.ScaleEntry se = (ScaleProperties.ScaleEntry) evt.getNewValue(); 140 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 141 | plot.getRangeAxis(0).setRange(0.0, se.getMaxYValue() + se.getMaxYValue() * 0.1); 142 | } 143 | } 144 | }); 145 | 146 | zoomProperty.addPropertyChangeListener(new PropertyChangeListener() { 147 | @Override 148 | public void propertyChange(PropertyChangeEvent evt) { 149 | ZoomProperties.ZoomValue v = (ZoomProperties.ZoomValue) evt.getNewValue(); 150 | // ignore notifications from same chart 151 | if (v.getChartType() == chartType) { 152 | return; 153 | } 154 | 155 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 156 | if (v.getAxisType() == ZoomProperties.AxisType.RANGE) { 157 | plot.getRangeAxis().setRange(v.getRange()); 158 | return; 159 | } 160 | 161 | Range newDomainRange; 162 | // match date chart with first model 163 | double startTime = models.get(0).getStartTimeSec(); 164 | if (v.getChartType() == HLAChartType.TIMELINE_ELAPSED_TIME) { 165 | newDomainRange = DateProperties.rangeToDateRange(startTime, v.getRange()); 166 | } else { 167 | newDomainRange = DateProperties.dateRangeToRange(startTime, v.getRange()); 168 | } 169 | plot.getDomainAxis().setRange(newDomainRange); 170 | } 171 | }); 172 | 173 | if (chartType == HLAChartType.TIMELINE_DATE) { 174 | dateProperties.addPropertyChangeListener(new PropertyChangeListener() { 175 | @Override 176 | public void propertyChange(PropertyChangeEvent evt) { 177 | if (evt.getPropertyName().equals("setShowDate") || 178 | evt.getPropertyName().equals("setUserTimezone") || 179 | evt.getPropertyName().equals("setShowSeconds") || 180 | evt.getPropertyName().equals("setShowMilliseconds")) 181 | { 182 | XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); 183 | DateAxis axis = (DateAxis) plot.getDomainAxis(); 184 | axis.setDateFormatOverride(dateProperties.getDateFormat(models)); 185 | 186 | TimeZone timezone = appProperties.getDateProperties().getTimeZone(models); 187 | Date startTime = models.get(0).getStartTime(); 188 | String xAxisLabel = ConstantsHelper.getXAxisLabel(chartType, 189 | DateProperties.getShortTimezoneString(timezone, startTime)); 190 | chartPanel.getChart().getXYPlot().getDomainAxis().setLabel(xAxisLabel); 191 | } 192 | } 193 | }); 194 | } 195 | 196 | // FIXME: uninstall listeners 197 | 198 | return chartPanel; 199 | } 200 | 201 | /* 202 | * defaults for TimeSeries and XYLine charts are slightly different 203 | * use these tweaks to align these charts so that switching 204 | * between these charts doesn't change plots. 205 | */ 206 | private void adjustDrawable(JFreeChart drawable, HLAChartType chartType) { 207 | if (chartType == HLAChartType.TIMELINE_DATE) { 208 | ((NumberAxis)drawable.getXYPlot().getRangeAxis()).setAutoRangeIncludesZero(true); 209 | } else { 210 | ((NumberAxis)drawable.getXYPlot().getDomainAxis()).setAutoRangeStickyZero(false); 211 | } 212 | 213 | final XYPlot plot = drawable.getXYPlot(); 214 | ValueAxis v = plot.getDomainAxis(); 215 | v.setLowerMargin(0.05D); 216 | v.setUpperMargin(0.05D); 217 | } 218 | 219 | private JFreeChart createTimelineDrawable(final List models, 220 | final ZoomProperties zoomProperty, 221 | final AppProperties appProperties, final HLAChartType chartType) 222 | { 223 | TimeZone timeZone = appProperties.getDateProperties().getTimeZone(models); 224 | 225 | String chartTitle = ConstantsHelper.getChartTitle(chartType); 226 | Date startTime = models.get(0).getStartTime(); 227 | String xAxisLabel = ConstantsHelper.getXAxisLabel(chartType, DateProperties.getShortTimezoneString(timeZone, startTime)); 228 | String yAxisLabel = ConstantsHelper.getYAxisLabel(chartType); 229 | 230 | CommonSeriesCollection seriesCollection = new TimelineDatasetBuilder().build(models, chartType); 231 | XYDataset dataset = seriesCollection.getDataset(); 232 | 233 | JFreeChart drawable; 234 | if (chartType == HLAChartType.TIMELINE_DATE) { 235 | drawable = ChartFactory.createTimeSeriesChart(chartTitle, 236 | xAxisLabel, 237 | yAxisLabel, 238 | dataset, 239 | true, 240 | true, 241 | false); 242 | } else { 243 | drawable = ChartFactory.createXYLineChart(chartTitle, 244 | xAxisLabel, 245 | yAxisLabel, 246 | dataset, 247 | PlotOrientation.VERTICAL, 248 | true, 249 | true, 250 | false); 251 | } 252 | adjustDrawable(drawable, chartType); 253 | 254 | final XYPlot plot = drawable.getXYPlot(); 255 | drawable.getPlot().setBackgroundPaint(Color.white); 256 | drawable.getXYPlot().setRangeGridlinePaint(Color.gray); 257 | drawable.getXYPlot().setDomainGridlinePaint(Color.gray); 258 | 259 | // zooming on timeline chart updates percentile chart (fire part) 260 | // also updates another timeline chart 261 | plot.getDomainAxis().addChangeListener(new AxisChangeListener() { 262 | @Override 263 | public void axisChanged(AxisChangeEvent axisChangeEvent) { 264 | if (appProperties.getDateProperties().isChartActive(chartType)) { 265 | Range range = plot.getDomainAxis().getRange(); 266 | zoomProperty.zoom(new ZoomProperties.ZoomValue(ZoomProperties.AxisType.DOMAIN, range, chartType)); 267 | } 268 | } 269 | }); 270 | plot.getRangeAxis().addChangeListener(new AxisChangeListener() { 271 | @Override 272 | public void axisChanged(AxisChangeEvent axisChangeEvent) { 273 | if (appProperties.getDateProperties().isChartActive(chartType)) { 274 | Range range = plot.getRangeAxis().getRange(); 275 | zoomProperty.zoom(new ZoomProperties.ZoomValue(ZoomProperties.AxisType.RANGE, range, chartType)); 276 | } 277 | } 278 | }); 279 | 280 | if (chartType == HLAChartType.TIMELINE_DATE) { 281 | DateAxis axis = (DateAxis) plot.getDomainAxis(); 282 | DateFormat df = appProperties.getDateProperties().getDateFormat(models); 283 | axis.setDateFormatOverride(df); 284 | axis.setTimeZone(df.getTimeZone()); 285 | } 286 | 287 | // MWP/HPL visibility, line settings 288 | final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); 289 | for (int i = 0; i < dataset.getSeriesCount(); i++) { 290 | renderer.setSeriesShapesVisible(i, false); 291 | renderer.setSeriesLinesVisible(i, true); 292 | 293 | String key = (String)dataset.getSeriesKey(i); 294 | if (key.contains("%'ile")) { 295 | renderer.setSeriesVisible(i, appProperties.getMwpProperties().isMWPVisible()); 296 | renderer.setSeriesPaint(i, ColorHelper.getColor(i)); 297 | } else if (key.endsWith("%") || key.equals("Max")) { 298 | renderer.setSeriesVisible(i, appProperties.getHplProperties().isHPLVisible()); 299 | renderer.setSeriesPaint(i, ColorHelper.getHPLColor(key)); 300 | renderer.setSeriesStroke(i, new BasicStroke(2.0f)); 301 | } else { 302 | renderer.setSeriesPaint(i, ColorHelper.getColor(i)); 303 | } 304 | } 305 | 306 | LegendTitle legend = drawable.getLegend(); 307 | legend.setPosition(RectangleEdge.TOP); 308 | plot.setDomainGridlinesVisible(false); 309 | renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); 310 | plot.setDomainPannable(true); 311 | plot.setRangePannable(true); 312 | plot.setRenderer(renderer); 313 | 314 | return drawable; 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/charts/TimelineDatasetBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.charts; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.HLAChartType; 9 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.HistogramModel; 10 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.MaxPercentileIterator; 11 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.TimelineIterator; 12 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 13 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.TimelineObject; 14 | import org.HdrHistogram.HistogramLogAnalyzer.properties.MWPProperties; 15 | 16 | import java.util.Iterator; 17 | import java.util.List; 18 | import java.util.Set; 19 | 20 | class TimelineDatasetBuilder { 21 | 22 | private CommonSeries createSeries(String name, HLAChartType chartType) { 23 | if (chartType == HLAChartType.TIMELINE_DATE) { 24 | return new HLATimeSeries(name); 25 | } else { 26 | return new HLAXYSeries(name); 27 | } 28 | } 29 | 30 | private CommonSeriesCollection createSeriesCollection(HLAChartType chartType) { 31 | if (chartType == HLAChartType.TIMELINE_DATE) { 32 | return new HLATimeSeriesCollection(); 33 | } else { 34 | return new HLAXYSeriesCollection(); 35 | } 36 | } 37 | 38 | private static final String DEFAULT_KEY = "Max per interval"; 39 | 40 | CommonSeriesCollection build(List histogramModels, HLAChartType chartType) { 41 | 42 | CommonSeriesCollection ret = createSeriesCollection(chartType); 43 | 44 | // tool doesn't support MWP/HPL for charts with multiple files 45 | // tool doesn't support MWP/HPL for files with multiple tags 46 | boolean multipleFiles = histogramModels.size() > 1; 47 | if (!multipleFiles) { 48 | HistogramModel histogramModel = histogramModels.get(0); 49 | Set tags = histogramModel.getTags(); 50 | 51 | boolean multipleTags = tags.size() > 1; 52 | if (!multipleTags) { 53 | String tag = null; 54 | if(!tags.isEmpty()) { 55 | tag = tags.iterator().next(); 56 | } 57 | MWPProperties mwpProperties = histogramModel.getMwpProperties(); 58 | List mwpEntries = mwpProperties.getMWPEntries(); 59 | for (MWPProperties.MWPEntry mwpEntry : mwpEntries) { 60 | String key; 61 | if (mwpEntry.isDefaultEntry()) { 62 | key = DEFAULT_KEY; 63 | } else { 64 | key = mwpEntry.toString(); 65 | } 66 | 67 | double startTime = histogramModel.getStartTimeSec(); 68 | CommonSeries series = createSeries(key, chartType); 69 | TimelineIterator ti = histogramModel.listTimelineObjects(false, tag, mwpEntry); 70 | while (ti.hasNext()) { 71 | TimelineObject to = ti.next(); 72 | series.add(to, startTime); 73 | } 74 | ret.add(series); 75 | } 76 | 77 | // HPL lines 78 | Iterator pi = histogramModel.listHPLPercentileObjects(tag); 79 | while (pi.hasNext()) { 80 | PercentileObject po = pi.next(); 81 | String key = String.valueOf(po.getPercentileValue() * 100); 82 | key = !key.contains(".") ? key : key.replaceAll("0*$", "").replaceAll("\\.$", ""); 83 | 84 | CommonSeries series = createSeries(key + "%", chartType); 85 | series.add(po.getLatencyAxisValue(), ret.getDomainBounds()); 86 | ret.add(series); 87 | } 88 | 89 | // Max line 90 | Double maxLatencyAxisValue = 0.0; 91 | MaxPercentileIterator mpi = histogramModel.listMaxPercentileObjects(null); 92 | PercentileObject mpo; 93 | while (mpi.hasNext()) { 94 | mpo = mpi.next(); 95 | maxLatencyAxisValue = Math.max(maxLatencyAxisValue, mpo.getLatencyAxisValue()); 96 | } 97 | 98 | CommonSeries series = createSeries("Max", chartType); 99 | series.add(maxLatencyAxisValue, ret.getDomainBounds()); 100 | ret.add(series); 101 | 102 | return ret; 103 | } 104 | } 105 | 106 | for (HistogramModel histogramModel : histogramModels) { 107 | boolean multipleTags = histogramModel.getTags().size() > 1; 108 | for (String tag : histogramModel.getTags()) { 109 | String key; 110 | if (multipleFiles) { 111 | key = histogramModel.getShortFileName(); 112 | } else { 113 | if (multipleTags) { 114 | key = tag == null ? "No Tag" : tag; 115 | } else { 116 | key = DEFAULT_KEY; 117 | } 118 | } 119 | 120 | CommonSeries series = createSeries(key, chartType); 121 | TimelineIterator ti = histogramModel.listTimelineObjects(true, tag, null); 122 | while (ti.hasNext()) { 123 | TimelineObject to = ti.next(); 124 | series.add(to, histogramModel.getStartTimeSec()); 125 | } 126 | ret.add(series); 127 | } 128 | } 129 | return ret; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/BucketIterator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.Configuration; 9 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.BucketObject; 10 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 11 | 12 | import java.util.Iterator; 13 | 14 | public class BucketIterator implements Iterator { 15 | 16 | private PercentileIterator pi; 17 | 18 | private double maxCountAtValue = 0.0D; 19 | 20 | BucketIterator(PercentileIterator pi) { 21 | this.pi = pi; 22 | } 23 | 24 | @Override 25 | public boolean hasNext() { 26 | return pi.hasNext(); 27 | } 28 | 29 | @Override 30 | public Object next() { 31 | PercentileObject po = pi.next(); 32 | 33 | double latencyValue = po.getLatencyAxisValue(); 34 | double countAtValue = po.getCountAtValue(); 35 | 36 | if (countAtValue > maxCountAtValue) { 37 | maxCountAtValue = countAtValue; 38 | } 39 | 40 | return new BucketObject(latencyValue, countAtValue); 41 | } 42 | 43 | public double getMaxCountAtValue() { 44 | return maxCountAtValue; 45 | } 46 | 47 | @Override 48 | public void remove() { 49 | throw new UnsupportedOperationException(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/DBConnect.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import java.sql.Connection; 9 | import java.sql.DriverManager; 10 | import java.sql.ResultSet; 11 | import java.sql.Statement; 12 | 13 | class DBConnect{ 14 | private Connection connection = null; 15 | private ResultSet resultSet = null; 16 | Statement statement = null; 17 | private String filename=null; 18 | 19 | DBConnect(String db_filename) { 20 | try { 21 | this.filename = db_filename; 22 | Class.forName("org.sqlite.JDBC"); 23 | connection = DriverManager.getConnection("jdbc:sqlite::memory:"); 24 | // connection = DriverManager.getConnection("jdbc:sqlite:"+filename); 25 | statement = connection.createStatement(); 26 | } catch (Exception ignored) { 27 | } 28 | } 29 | void close_db() { 30 | try { 31 | if(resultSet!=null) { 32 | if(!resultSet.isClosed()) { 33 | resultSet.close(); 34 | } 35 | statement.close(); 36 | connection.close(); 37 | } 38 | } catch (Exception e) { 39 | System.out.println("DDD ERROR"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/DBManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.properties.MWPProperties; 9 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.*; 10 | 11 | import java.sql.ResultSet; 12 | import java.sql.SQLException; 13 | 14 | class DBManager { 15 | 16 | private DBConnect db = null; 17 | 18 | DBManager() { 19 | try { 20 | this.db = new DBConnect(((Long) System.currentTimeMillis()).toString()); 21 | create_base_tables(); 22 | this.db.close_db(); 23 | } catch (Exception e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | 28 | private void create_base_tables() { 29 | try { 30 | String db_create_j_hst = "CREATE TABLE j_hst("+ 31 | "latencyAxisValue REAL, " + 32 | "percentileAxisValue REAL, " + 33 | "percentileValue REAL, " + 34 | "countAtValue REAL, " + 35 | "tag REAL);"; 36 | db.statement.execute(db_create_j_hst); 37 | 38 | String db_create_j_percentile = "CREATE TABLE j_percentile("+ 39 | "timelineAxisValue REAL," + 40 | "latencyAxisValue REAL," + 41 | "tag REAL," + 42 | "mwp_percentile REAL," + 43 | "mwp_windowLength REAL);"; 44 | db.statement.execute(db_create_j_percentile); 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | 50 | 51 | /* 52 | * Timeline related statements 53 | */ 54 | void insertTimelineObject(TimelineObject to) { 55 | try { 56 | String db_insert_j_percentile = "INSERT INTO j_percentile VALUES(\"" + 57 | String.valueOf(to.getTimelineAxisValue()) + "\",\"" + 58 | String.valueOf(to.getLatencyAxisValue()) + "\",\"" + 59 | to.getTag() + "\",\"" + 60 | to.getMwpPercentile() + "\",\"" + 61 | to.getMwpWindowLength() + "\");"; 62 | db.statement.execute(db_insert_j_percentile); 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | 68 | TimelineIterator listTimelineObjects(boolean multipleTags, String tag, 69 | MWPProperties.MWPEntry mwpEntry, int topLatencies) { 70 | String queryString = createTimelineQueryString(multipleTags, tag, mwpEntry, topLatencies); 71 | ResultSet rs = null; 72 | try { 73 | rs = db.statement.executeQuery(queryString); 74 | } catch (SQLException e) { 75 | e.printStackTrace(); 76 | } 77 | return new TimelineIterator(rs); 78 | } 79 | 80 | private String createTimelineQueryString(boolean multipleTags, String tag, 81 | MWPProperties.MWPEntry mwpEntry, int topLatencies) 82 | { 83 | String topLatenciesString = 84 | (topLatencies > 0) ? " order by latencyAxisValue desc limit " + topLatencies : ""; 85 | 86 | if (multipleTags) { 87 | MWPProperties.MWPEntry defaultMWPEntry = MWPProperties.getDefaultMWPEntry(); 88 | return "select timelineAxisValue,latencyAxisValue from j_percentile" + 89 | " where tag='"+ tag + "'" + 90 | " and mwp_percentile='" + defaultMWPEntry.getPercentile() + "'" + 91 | " and mwp_windowLength='" + defaultMWPEntry.getWindowLength() + "'" + 92 | topLatenciesString + ";"; 93 | } else { 94 | return "select timelineAxisValue,latencyAxisValue from j_percentile" + 95 | " where mwp_percentile='" + mwpEntry.getPercentile() + "'" + 96 | " and mwp_windowLength='" + mwpEntry.getWindowLength() + "'" + 97 | topLatenciesString + ";"; 98 | } 99 | } 100 | 101 | /* 102 | * Percentile related statements 103 | */ 104 | 105 | void insertPercentileObject(PercentileObject po) { 106 | try { 107 | String db_insert_j_hst = "INSERT INTO j_hst VALUES (\"" + 108 | String.valueOf(po.getLatencyAxisValue()) + "\", \"" + 109 | String.valueOf(po.getPercentileAxisValue()) + "\", \"" + 110 | String.valueOf(po.getPercentileValue()) + "\", \"" + 111 | String.valueOf(po.getCountAtValue()) + "\", \"" + 112 | po.getTag() + "\")"; 113 | 114 | db.statement.execute(db_insert_j_hst); 115 | } catch (Exception except) { 116 | except.printStackTrace(); 117 | } 118 | } 119 | 120 | PercentileIterator listPercentileObjects(String tag, PercentileObject limitObject) { 121 | String queryString = createPercentileQueryString(tag, limitObject); 122 | ResultSet rs = null; 123 | try { 124 | rs = db.statement.executeQuery(queryString); 125 | } catch (SQLException e) { 126 | e.printStackTrace(); 127 | } 128 | return new PercentileIterator(rs); 129 | } 130 | 131 | private String createPercentileQueryString(String tag, PercentileObject limitObject) { 132 | String queryString = "select * from j_hst where tag='" + tag + "'"; 133 | 134 | if (limitObject != null) { 135 | queryString += " and latencyAxisValue < " + String.valueOf(limitObject.getLatencyAxisValue()); 136 | } 137 | 138 | queryString += ";"; 139 | 140 | return queryString; 141 | } 142 | 143 | MaxPercentileIterator listMaxPercentileObjects(String tag) { 144 | String queryString = createMaxValuesQueryString(tag); 145 | ResultSet rs = null; 146 | try { 147 | rs = db.statement.executeQuery(queryString); 148 | } catch (SQLException e) { 149 | e.printStackTrace(); 150 | } 151 | 152 | return new MaxPercentileIterator(rs); 153 | } 154 | 155 | private String createMaxValuesQueryString(String tag) { 156 | return "select max( cast(latencyAxisValue as float) ) as MaxLatencyAxisValue, "+ 157 | "max(cast(PercentileAxisValue as float) ) as MaxPercentileAxisValue, "+ 158 | "max(cast(PercentileValue as float) ) as MaxPercentileValue from j_hst"+ 159 | " where tag='"+ tag + "';"; 160 | } 161 | 162 | BucketIterator listBucketObjects(String tag) { 163 | PercentileIterator pi = listPercentileObjects(tag, null); 164 | 165 | return new BucketIterator(pi); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/HistogramModel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import org.HdrHistogram.*; 9 | import org.HdrHistogram.HistogramLogAnalyzer.properties.DateProperties; 10 | import org.HdrHistogram.HistogramLogAnalyzer.properties.HPLProperties; 11 | import org.HdrHistogram.HistogramLogAnalyzer.properties.MWPProperties; 12 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.*; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.File; 16 | import java.io.FileReader; 17 | import java.io.IOException; 18 | import java.util.*; 19 | 20 | public class HistogramModel { 21 | private String inputFileName; 22 | 23 | // range to process 24 | private Double intervalStart; 25 | private Double intervalEnd; 26 | 27 | // retrieved from log 28 | private double startTimeSec; 29 | private Date startTime; 30 | private String startTimeString; // string from log as is 31 | 32 | // timezone detected based on info in log 33 | private TimeZone timezone; 34 | 35 | private MWPProperties mwpProperties; 36 | private Set tags; 37 | 38 | private DBManager dbManager; 39 | private LogGeneratorType logGeneratorType; 40 | 41 | public HistogramModel(String inputFileName, Double intervalStart, Double intervalEnd, MWPProperties mwpProperties) 42 | throws IOException 43 | { 44 | this.inputFileName = inputFileName; 45 | this.intervalStart = intervalStart; 46 | this.intervalEnd = intervalEnd; 47 | this.mwpProperties = mwpProperties; 48 | this.tags = TagsHelper.listTags(inputFileName); 49 | this.dbManager = new DBManager(); 50 | 51 | parseLogHeader(); 52 | init(); 53 | } 54 | 55 | private static LogGeneratorType detectLogGeneratorType(String line) { 56 | LogGeneratorType logGeneratorType = LogGeneratorType.UNKNOWN; 57 | if (line.contains(LogGeneratorType.CASSANDRA_STRESS.getDescription())) { 58 | logGeneratorType = LogGeneratorType.CASSANDRA_STRESS; 59 | } else if (line.contains(LogGeneratorType.JHICCUP.getDescription())) { 60 | logGeneratorType = LogGeneratorType.JHICCUP; 61 | } 62 | return logGeneratorType; 63 | } 64 | 65 | /* 66 | * traverse all available timezones and for each timezone 67 | * convert start time to string that represents start time in this timezone 68 | * 69 | * if any of these strings is the same as string in log, 70 | * then use this timezone for this model 71 | */ 72 | private static TimeZone detectTimeZone(String startTimeString, Date startTime) { 73 | if (startTimeString == null) { 74 | return null; 75 | } 76 | 77 | TimeZone timezone = null; 78 | TimeZone defaultTimezone = TimeZone.getDefault(); 79 | List allTimezones = DateProperties.getAllTimeZones(); 80 | for (String item : allTimezones) { 81 | TimeZone candidateTimeZone = TimeZone.getTimeZone(item); 82 | TimeZone.setDefault(candidateTimeZone); 83 | String timeStringForCandidate = startTime.toString(); 84 | if (startTimeString.equals(timeStringForCandidate)) { 85 | timezone = candidateTimeZone; 86 | break; 87 | } 88 | } 89 | TimeZone.setDefault(defaultTimezone); 90 | return timezone; 91 | } 92 | 93 | private void parseLogHeader() throws IOException { 94 | BufferedReader reader = null; 95 | try { 96 | reader = new BufferedReader(new FileReader(new File(inputFileName))); 97 | String line = reader.readLine(); 98 | boolean firstLine = true; 99 | while (line != null) { 100 | if (firstLine) { 101 | firstLine = false; 102 | logGeneratorType = detectLogGeneratorType(line); 103 | } else { 104 | if (line.contains("#[StartTime: ")) { 105 | startTimeString = line.substring(line.indexOf(",") + 2, line.indexOf("]")); 106 | } 107 | } 108 | line = reader.readLine(); 109 | } 110 | } finally { 111 | if (reader != null) { 112 | reader.close(); 113 | } 114 | } 115 | } 116 | 117 | public String getInputFileName() { 118 | return inputFileName; 119 | } 120 | 121 | public double getStartTimeSec() { 122 | return startTimeSec; 123 | } 124 | 125 | public Date getStartTime() { 126 | return startTime; 127 | } 128 | 129 | public TimeZone getTimeZone() { 130 | return timezone; 131 | } 132 | 133 | public LogGeneratorType getLogGeneratorType() { 134 | return logGeneratorType; 135 | } 136 | 137 | public String getShortFileName() { 138 | return new File(inputFileName).getName(); 139 | } 140 | 141 | public Set getTags() { 142 | return tags; 143 | } 144 | 145 | public MWPProperties getMwpProperties() { 146 | return mwpProperties; 147 | } 148 | 149 | private boolean compareTags(String tag, String hTag) { 150 | return (tag == null ? hTag == null : tag.equals(hTag)); 151 | } 152 | 153 | private EncodableHistogram getIntervalHistogram(HistogramLogReader reader, String tag) { 154 | EncodableHistogram histogram; 155 | histogram = getIntervalHistogram(reader); 156 | while (histogram != null && !compareTags(tag, histogram.getTag())) { 157 | histogram = getIntervalHistogram(reader); 158 | } 159 | return histogram; 160 | } 161 | 162 | private EncodableHistogram getIntervalHistogram(HistogramLogReader reader) { 163 | EncodableHistogram histogram; 164 | if (intervalStart != null && intervalEnd != null) { 165 | histogram = reader.nextIntervalHistogram(intervalStart, intervalEnd); 166 | } else { 167 | histogram = reader.nextIntervalHistogram(); 168 | } 169 | return histogram; 170 | } 171 | 172 | /* 173 | * fill database with data 174 | */ 175 | private void init() throws IOException { 176 | for (String tag : tags) { 177 | List mwpEntries = mwpProperties.getMWPEntries(); 178 | hplValues.put(tag, new ArrayList()); 179 | 180 | for (MWPProperties.MWPEntry mwpEntry : mwpEntries) { 181 | init(tag, mwpEntry); 182 | } 183 | // always build data for non-"moving window" (default MWP) entry, needed for percentile chart 184 | if (!mwpEntries.contains(MWPProperties.getDefaultMWPEntry())) { 185 | init(tag, MWPProperties.getDefaultMWPEntry()); 186 | } 187 | } 188 | } 189 | 190 | private void init(String tag, MWPProperties.MWPEntry mwpEntry) 191 | throws IOException 192 | { 193 | HistogramLogReader reader = new HistogramLogReader(new File(inputFileName)); 194 | EncodableHistogram intervalHistogram = getIntervalHistogram(reader, tag); 195 | 196 | startTimeSec = reader.getStartTimeSec(); 197 | startTime = new Date((long) startTimeSec * 1000); 198 | timezone = detectTimeZone(startTimeString, startTime); 199 | 200 | if (intervalHistogram == null) { 201 | return; // no interval found 202 | } 203 | 204 | boolean isMovingWindow = !MWPProperties.getDefaultMWPEntry().equals(mwpEntry); 205 | Double movingWindowPercentile = mwpEntry.getPercentile(); 206 | long movingWindowLengthInMsec = mwpEntry.getWindowLength(); 207 | 208 | Histogram accumulatedRegularHistogram = null; 209 | DoubleHistogram accumulatedDoubleHistogram = null; 210 | 211 | // EncodableHistogram[] movingWindow = new EncodableHistogram[mwpEntry.getWindowLength()]; 212 | EncodableHistogram movingWindowSumHistogram; 213 | Queue movingWindowQueue = new LinkedList<>(); 214 | 215 | if (intervalHistogram instanceof DoubleHistogram) { 216 | accumulatedDoubleHistogram = ((DoubleHistogram) intervalHistogram).copy(); 217 | accumulatedDoubleHistogram.reset(); 218 | accumulatedDoubleHistogram.setAutoResize(true); 219 | movingWindowSumHistogram = new DoubleHistogram(3); 220 | } else { 221 | accumulatedRegularHistogram = ((Histogram ) intervalHistogram).copy(); 222 | accumulatedRegularHistogram.reset(); 223 | accumulatedRegularHistogram.setAutoResize(true); 224 | movingWindowSumHistogram = new Histogram(3); 225 | } 226 | 227 | Double outputValueUnitRatio = 1000000.0; 228 | while (intervalHistogram != null) { 229 | 230 | if (intervalHistogram instanceof DoubleHistogram) { 231 | if (accumulatedDoubleHistogram == null) { 232 | throw new IllegalStateException("Encountered a DoubleHistogram line in a log of Histograms."); 233 | } 234 | accumulatedDoubleHistogram.add((DoubleHistogram) intervalHistogram); 235 | } else { 236 | if (accumulatedRegularHistogram == null) { 237 | throw new IllegalStateException("Encountered a Histogram line in a log of DoubleHistograms."); 238 | } 239 | accumulatedRegularHistogram.add((Histogram) intervalHistogram); 240 | } 241 | 242 | long windowCutOffTimeStamp = intervalHistogram.getEndTimeStamp() - movingWindowLengthInMsec; 243 | if (isMovingWindow) { 244 | // Add the current interval histogram to the moving winow sums: 245 | if (movingWindowSumHistogram instanceof DoubleHistogram) { 246 | ((DoubleHistogram) movingWindowSumHistogram).add((DoubleHistogram) intervalHistogram); 247 | } else { 248 | ((Histogram) movingWindowSumHistogram).add((Histogram) intervalHistogram); 249 | } 250 | // Remove previous, now-out-of-window interval histograms from moving window: 251 | while ((movingWindowQueue.peek() != null) && 252 | (movingWindowQueue.peek().getEndTimeStamp() <= windowCutOffTimeStamp)) { 253 | EncodableHistogram prevHist = movingWindowQueue.remove(); 254 | if (movingWindowSumHistogram instanceof DoubleHistogram) { 255 | if (prevHist != null) { 256 | ((DoubleHistogram) movingWindowSumHistogram).subtract((DoubleHistogram) prevHist); 257 | } 258 | } else { 259 | if (prevHist != null) { 260 | ((Histogram) movingWindowSumHistogram).subtract((Histogram) prevHist); 261 | } 262 | } 263 | } 264 | // add interval histogram to moving window previous intervals memory: 265 | movingWindowQueue.add(intervalHistogram); 266 | } 267 | 268 | double timelineAxisValue = (intervalHistogram.getEndTimeStamp()/1000.0) - reader.getStartTimeSec(); 269 | double latencyAxisValue; 270 | 271 | if (isMovingWindow) { 272 | if (intervalHistogram instanceof DoubleHistogram) { 273 | latencyAxisValue = ((DoubleHistogram) movingWindowSumHistogram).getValueAtPercentile(movingWindowPercentile) / outputValueUnitRatio; 274 | } else { 275 | latencyAxisValue = ((Histogram) movingWindowSumHistogram).getValueAtPercentile(movingWindowPercentile) / outputValueUnitRatio; 276 | } 277 | } else { 278 | latencyAxisValue = intervalHistogram.getMaxValueAsDouble()/ outputValueUnitRatio; 279 | } 280 | 281 | String moving_window_percentile = String.valueOf(movingWindowPercentile); 282 | String moving_window_length = String.valueOf(movingWindowLengthInMsec); 283 | 284 | dbManager.insertTimelineObject( 285 | new TimelineObject(timelineAxisValue, latencyAxisValue, 286 | tag, moving_window_percentile, moving_window_length)); 287 | 288 | intervalHistogram = getIntervalHistogram(reader, tag); 289 | } 290 | 291 | int percentilesOutputTicksPerHalf = 5; 292 | 293 | // build percentile chart for default MWP entry (non-moving window) only 294 | if (!isMovingWindow) { 295 | if (accumulatedRegularHistogram != null) { 296 | for (HistogramIterationValue iterationValue : accumulatedRegularHistogram.percentiles(percentilesOutputTicksPerHalf)) { 297 | double value = iterationValue.getValueIteratedTo() / outputValueUnitRatio; 298 | double percentile = iterationValue.getPercentileLevelIteratedTo() / 100.0D; 299 | double countAtValue = iterationValue.getCountAddedInThisIterationStep(); 300 | 301 | dbManager.insertPercentileObject( 302 | new PercentileObject(value, percentile, countAtValue, tag) 303 | ); 304 | } 305 | // needed for horizontal lines 306 | for (Double percentile : HPLProperties.getPercentiles()) { 307 | double value = accumulatedRegularHistogram.getValueAtPercentile(percentile * 100) / outputValueUnitRatio; 308 | hplValues.get(tag).add(new PercentileObject(value, percentile, 0.0, tag)); 309 | } 310 | } else { 311 | for (DoubleHistogramIterationValue iterationValue : accumulatedDoubleHistogram.percentiles(percentilesOutputTicksPerHalf)) { 312 | double value = iterationValue.getValueIteratedTo() / outputValueUnitRatio; 313 | double percentile = iterationValue.getPercentileLevelIteratedTo() / 100.0D; 314 | double countAtValue = iterationValue.getCountAddedInThisIterationStep(); 315 | 316 | dbManager.insertPercentileObject( 317 | new PercentileObject(value, percentile, countAtValue, tag) 318 | ); 319 | } 320 | // needed for horizontal lines 321 | for (Double percentile : HPLProperties.getPercentiles()) { 322 | double value = accumulatedDoubleHistogram.getValueAtPercentile(percentile * 100) / outputValueUnitRatio; 323 | hplValues.get(tag).add(new PercentileObject(value, percentile, 0.0, tag)); 324 | } 325 | } 326 | } 327 | } 328 | 329 | /* 330 | * queries 331 | */ 332 | public TimelineIterator listTimelineObjects(boolean multipleTags, String tag, MWPProperties.MWPEntry mwpEntry) { 333 | return listTimelineObjects(multipleTags, tag, mwpEntry, 0); 334 | } 335 | 336 | public TimelineIterator listTimelineObjects(boolean multipleTags, String tag, MWPProperties.MWPEntry mwpEntry, 337 | int topLatencies) { 338 | return dbManager.listTimelineObjects(multipleTags, tag, mwpEntry, topLatencies); 339 | } 340 | 341 | public PercentileIterator listPercentileObjects(String tag, PercentileObject limitObject) { 342 | return dbManager.listPercentileObjects(tag, limitObject); 343 | } 344 | 345 | public MaxPercentileIterator listMaxPercentileObjects(String tag) { 346 | return dbManager.listMaxPercentileObjects(tag); 347 | } 348 | 349 | // "HPL" values per tag 350 | private Map> hplValues = new HashMap<>(); 351 | 352 | public Iterator listHPLPercentileObjects(String tag) { 353 | return hplValues.get(tag).iterator(); 354 | } 355 | 356 | public BucketIterator listBucketObjects(String tag) { 357 | return dbManager.listBucketObjects(tag); 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/LogGeneratorType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | public enum LogGeneratorType { 9 | 10 | CASSANDRA_STRESS("Cassandra Stress"), 11 | JHICCUP("jHiccup"), 12 | UNKNOWN("Unknown"); 13 | 14 | private String description; 15 | 16 | LogGeneratorType(String description) { 17 | this.description = description; 18 | } 19 | 20 | public String getDescription() { 21 | return description; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/MaxPercentileIterator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 9 | 10 | import java.sql.ResultSet; 11 | import java.sql.SQLException; 12 | 13 | public class MaxPercentileIterator extends ResultSetIterator { 14 | 15 | MaxPercentileIterator(ResultSet resultSet) { 16 | super(resultSet); 17 | } 18 | 19 | @Override 20 | public PercentileObject nextObject() { 21 | double latencyAxisValue = 0; 22 | double percentileAxisValue = 0; 23 | double percentileValue = 0; 24 | double countAtValue = 0; 25 | try { 26 | latencyAxisValue = resultSet.getDouble("MaxLatencyAxisValue"); 27 | percentileAxisValue = resultSet.getDouble("MaxPercentileAxisValue"); 28 | percentileValue = resultSet.getDouble("MaxPercentileValue"); 29 | } catch (SQLException e) { 30 | e.printStackTrace(); 31 | } 32 | return new PercentileObject(latencyAxisValue, percentileAxisValue, percentileValue, countAtValue); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/PercentileIterator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.PercentileObject; 9 | 10 | import java.sql.ResultSet; 11 | import java.sql.SQLException; 12 | 13 | public class PercentileIterator extends ResultSetIterator { 14 | 15 | PercentileIterator(ResultSet resultSet) { 16 | super(resultSet); 17 | } 18 | 19 | @Override 20 | public PercentileObject nextObject() { 21 | double percentileAxisValue = 0; 22 | double percentileValue = 0; 23 | double latencyAxisValue = 0; 24 | double countAtValue = 0; 25 | try { 26 | percentileAxisValue = resultSet.getDouble("percentileAxisValue"); 27 | percentileValue = resultSet.getDouble("percentileValue"); 28 | latencyAxisValue = resultSet.getDouble("latencyAxisValue"); 29 | countAtValue = resultSet.getDouble("countAtValue"); 30 | } catch (SQLException e) { 31 | e.printStackTrace(); 32 | } 33 | return new PercentileObject(latencyAxisValue, percentileAxisValue, 34 | percentileValue, countAtValue); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/ResultSetIterator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.util.Iterator; 11 | 12 | abstract class ResultSetIterator implements Iterator { 13 | 14 | ResultSet resultSet; 15 | private boolean didNext = false; 16 | private boolean hasNext = false; 17 | 18 | ResultSetIterator(ResultSet resultSet) { 19 | this.resultSet = resultSet; 20 | } 21 | 22 | @Override 23 | public T next() { 24 | if (!didNext) { 25 | try { 26 | resultSet.next(); 27 | } catch (SQLException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | didNext = false; 32 | 33 | return nextObject(); 34 | } 35 | 36 | abstract T nextObject(); 37 | 38 | @Override 39 | public boolean hasNext() { 40 | if (!didNext) { 41 | try { 42 | hasNext = resultSet.next(); 43 | } catch (SQLException e) { 44 | e.printStackTrace(); 45 | } 46 | didNext = true; 47 | } 48 | return hasNext; 49 | } 50 | 51 | @Override 52 | public void remove() { 53 | throw new UnsupportedOperationException(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/TagsHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import org.HdrHistogram.HistogramLogProcessor; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.FileNotFoundException; 12 | import java.io.PrintStream; 13 | import java.util.*; 14 | 15 | public class TagsHelper { 16 | 17 | /* 18 | * returns list of tags or null if no tags found (default tag) 19 | */ 20 | public static Set listTags(String inputFileName) 21 | throws FileNotFoundException 22 | { 23 | TagsExtractor te = new TagsExtractor(); 24 | PrintStream origOut = System.out; 25 | System.setOut(te); 26 | 27 | String[] args = new String[]{"-i", inputFileName, "-listtags"}; 28 | HistogramLogProcessor hlp = new HistogramLogProcessor(args); 29 | hlp.run(); 30 | 31 | System.setOut(origOut); 32 | return te.getTags(); 33 | } 34 | 35 | private static class TagsExtractor extends PrintStream { 36 | Set tags = null; 37 | boolean inited = false; 38 | 39 | private final static String NO_TAG_STRING = "[NO TAG (default)]"; 40 | 41 | TagsExtractor() { super(new ByteArrayOutputStream());} 42 | 43 | Set getTags() { 44 | if (!inited) { 45 | String content = new String(((ByteArrayOutputStream)out).toByteArray()); 46 | String[] lines = content.split(System.getProperty("line.separator")); 47 | for (int i = 0; i < lines.length; i++) { 48 | if (lines[i].equals(NO_TAG_STRING)) { 49 | lines[i] = null; // treat default tag as null 50 | } 51 | } 52 | tags = new LinkedHashSet<>(); 53 | // ignore first line 54 | tags.addAll(Arrays.asList(lines).subList(1, lines.length)); 55 | inited = true; 56 | } 57 | return tags; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/datalayer/TimelineIterator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.datalayer; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer.TimelineObject; 9 | 10 | import java.sql.ResultSet; 11 | import java.sql.SQLException; 12 | 13 | public class TimelineIterator extends ResultSetIterator { 14 | 15 | TimelineIterator(ResultSet resultSet) { 16 | super(resultSet); 17 | } 18 | 19 | @Override 20 | public TimelineObject nextObject() { 21 | double percentileElapsedTime = 0; 22 | double percentileIntervalMax = 0; 23 | try { 24 | percentileElapsedTime = resultSet.getDouble("timelineAxisValue"); 25 | percentileIntervalMax = resultSet.getDouble("latencyAxisValue"); 26 | } catch (SQLException e) { 27 | e.printStackTrace(); 28 | } 29 | return new TimelineObject(percentileElapsedTime, percentileIntervalMax); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/dataobjectlayer/BucketObject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer; 7 | 8 | public class BucketObject { 9 | 10 | private double latencyValue; 11 | private double countAtValue; 12 | 13 | public BucketObject(double hiccupValue, double countAtValue) { 14 | this.latencyValue = hiccupValue; 15 | this.countAtValue = countAtValue; 16 | } 17 | 18 | public double getLatencyValue() { 19 | return latencyValue; 20 | } 21 | 22 | public double getCountAtValue() { 23 | return countAtValue; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/dataobjectlayer/PercentileObject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer; 7 | 8 | public class PercentileObject { 9 | 10 | private double latencyAxisValue; 11 | // 1/(1-percentile) 12 | private double percentileAxisValue; 13 | private double percentileValue; 14 | 15 | private double countAtValue; 16 | 17 | private String tag; 18 | 19 | // "insert" object 20 | public PercentileObject(double latencyAxisValue, double percentileValue, 21 | double countAtValue, String tag) 22 | { 23 | this.latencyAxisValue = latencyAxisValue; 24 | 25 | Double cvalue = 1.0d/ (1.0-percentileValue); 26 | if(cvalue.isInfinite()) { 27 | cvalue = (double) 0; 28 | } 29 | this.percentileAxisValue = cvalue; 30 | this.percentileValue = percentileValue; 31 | 32 | this.countAtValue = countAtValue; 33 | 34 | this.tag = tag; 35 | } 36 | 37 | // "query" object 38 | public PercentileObject(double latencyAxisValue, double percentileAxisValue, 39 | double percentileValue, double countAtValue) 40 | { 41 | this.latencyAxisValue = latencyAxisValue; 42 | this.percentileAxisValue = percentileAxisValue; 43 | this.percentileValue = percentileValue; 44 | this.countAtValue = countAtValue; 45 | 46 | } 47 | 48 | public double getLatencyAxisValue() { 49 | return latencyAxisValue; 50 | } 51 | 52 | public double getPercentileAxisValue() { 53 | return percentileAxisValue; 54 | } 55 | 56 | public double getPercentileValue() { 57 | return percentileValue; 58 | } 59 | 60 | public double getCountAtValue() { 61 | return countAtValue; 62 | } 63 | 64 | public String getTag() { 65 | return tag; 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/dataobjectlayer/TimelineObject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.dataobjectlayer; 7 | 8 | public class TimelineObject { 9 | 10 | private double timelineAxisValue; 11 | private double latencyAxisValue; 12 | 13 | private String tag; 14 | 15 | private String mwp_percentile; 16 | private String mwp_windowLength; 17 | 18 | // "query" object 19 | public TimelineObject(double timelineAxisValue, double latencyAxisValue) { 20 | this.timelineAxisValue = timelineAxisValue; 21 | this.latencyAxisValue = latencyAxisValue; 22 | } 23 | 24 | // "insert" object 25 | public TimelineObject(double timelineAxisValue, double latencyAxisValue, 26 | String tag, String mwp_percentile, String mwp_windowLength) 27 | { 28 | this.timelineAxisValue = timelineAxisValue; 29 | this.latencyAxisValue = latencyAxisValue; 30 | 31 | this.tag = tag; 32 | 33 | this.mwp_percentile = mwp_percentile; 34 | this.mwp_windowLength = mwp_windowLength; 35 | } 36 | 37 | public double getTimelineAxisValue() { 38 | return timelineAxisValue; 39 | } 40 | 41 | public double getLatencyAxisValue() { 42 | return latencyAxisValue; 43 | } 44 | 45 | public String getTag() { 46 | return tag; 47 | } 48 | 49 | public String getMwpPercentile() { 50 | return mwp_percentile; 51 | } 52 | 53 | public String getMwpWindowLength() { 54 | return mwp_windowLength; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/panels/DatePanel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.panels; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.properties.DateProperties; 9 | 10 | import javax.swing.*; 11 | import java.awt.*; 12 | import java.awt.event.ItemEvent; 13 | import java.awt.event.ItemListener; 14 | 15 | public class DatePanel extends JPanel { 16 | 17 | public DatePanel(JFrame mainframe, final DateProperties dateProperties) { 18 | setBorder(BorderFactory.createLineBorder(Color.BLACK)); 19 | JPanel panel = new JPanel(); 20 | panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 21 | 22 | final JCheckBox secondsCheckBox = new JCheckBox("Display time with seconds"); 23 | final JCheckBox millisecondsCheckBox = new JCheckBox("Display time with milliseconds"); 24 | final JCheckBox dateCheckBox = new JCheckBox("Show date"); 25 | final JCheckBox timezoneCheckbox = new JCheckBox("Set time zone"); 26 | final JComboBox timezoneCombobox = new JComboBox(); 27 | 28 | secondsCheckBox.setSelected(dateProperties.isShowSeconds()); 29 | secondsCheckBox.addItemListener(new ItemListener() { 30 | @Override 31 | public void itemStateChanged(ItemEvent e) { 32 | dateProperties.setShowSeconds(secondsCheckBox.isSelected()); 33 | millisecondsCheckBox.setEnabled(secondsCheckBox.isSelected()); 34 | } 35 | }); 36 | 37 | millisecondsCheckBox.setEnabled(secondsCheckBox.isSelected()); 38 | millisecondsCheckBox.setSelected(dateProperties.isShowMilliseconds()); 39 | millisecondsCheckBox.addItemListener(new ItemListener() { 40 | @Override 41 | public void itemStateChanged(ItemEvent e) { 42 | dateProperties.setShowMilliseconds(millisecondsCheckBox.isSelected()); 43 | } 44 | }); 45 | 46 | dateCheckBox.setSelected(dateProperties.isShowDate()); 47 | dateCheckBox.addItemListener(new ItemListener() { 48 | @Override 49 | public void itemStateChanged(ItemEvent e) { 50 | dateProperties.setShowDate(dateCheckBox.isSelected()); 51 | } 52 | }); 53 | 54 | timezoneCheckbox.setSelected(dateProperties.isOverrideTimezone()); 55 | timezoneCheckbox.addItemListener(new ItemListener() { 56 | @Override 57 | public void itemStateChanged(ItemEvent e) { 58 | timezoneCombobox.setEnabled(timezoneCheckbox.isSelected()); 59 | dateProperties.setOverrideTimezone(timezoneCheckbox.isSelected()); 60 | dateProperties.setUserTimezone((String)timezoneCombobox.getSelectedItem()); 61 | } 62 | }); 63 | 64 | for (String tz : dateProperties.getAllTimeZones()) { 65 | timezoneCombobox.addItem(tz); 66 | } 67 | String localtz = dateProperties.getLocalTimeZone(); 68 | timezoneCombobox.setEnabled(timezoneCheckbox.isSelected()); 69 | timezoneCombobox.setSelectedItem(localtz); 70 | timezoneCombobox.addItemListener(new ItemListener() { 71 | @Override 72 | public void itemStateChanged(ItemEvent e) { 73 | dateProperties.setUserTimezone((String)timezoneCombobox.getSelectedItem()); 74 | } 75 | }); 76 | 77 | secondsCheckBox.setAlignmentX(LEFT_ALIGNMENT); 78 | millisecondsCheckBox.setAlignmentX(LEFT_ALIGNMENT); 79 | timezoneCheckbox.setAlignmentX(LEFT_ALIGNMENT); 80 | dateCheckBox.setAlignmentX(LEFT_ALIGNMENT); 81 | timezoneCombobox.setAlignmentX(LEFT_ALIGNMENT); 82 | 83 | panel.add(secondsCheckBox); 84 | panel.add(millisecondsCheckBox); 85 | panel.add(dateCheckBox); 86 | panel.add(timezoneCheckbox); 87 | panel.add(timezoneCombobox); 88 | 89 | add(panel); 90 | } 91 | } -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/panels/MWPPanel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.panels; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.properties.MWPProperties; 9 | 10 | import javax.swing.*; 11 | import java.awt.*; 12 | import java.awt.event.ActionEvent; 13 | import java.awt.event.ActionListener; 14 | import java.awt.event.KeyEvent; 15 | import java.util.List; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | public class MWPPanel extends JPanel { 19 | 20 | private JFrame mainframe; 21 | private JPanel panel; 22 | private org.HdrHistogram.HistogramLogAnalyzer.properties.MWPProperties MWPProperties; 23 | 24 | private static final int TIMELINE_COLUMNS_NUMBER = 3; 25 | 26 | 27 | public MWPPanel(JFrame mainframe, MWPProperties MWPProperties) { 28 | this.mainframe = mainframe; 29 | this.MWPProperties = MWPProperties; 30 | List mwpEntries = 31 | MWPProperties.getAccessibleMWPEntry(); 32 | 33 | setBorder(BorderFactory.createLineBorder(Color.BLACK)); 34 | 35 | int rowcount = mwpEntries.size(); 36 | panel = new JPanel(new GridLayout(rowcount + 1, TIMELINE_COLUMNS_NUMBER)); 37 | panel.setBorder(BorderFactory.createLineBorder(Color.BLACK)); 38 | panel.add(new JLabel("Row")); 39 | panel.add(new JLabel("Percentile(%)")); 40 | panel.add(new JLabel("Window length(seconds)")); 41 | 42 | for (int i = 0; i < rowcount; i++) { 43 | panel.add(new JLabel(((Integer) (i + 1)).toString())); 44 | panel.add(new JTextField(mwpEntries.get(i).getPercentile().toString())); 45 | panel.add(new JTextField(Long.toString( 46 | TimeUnit.MILLISECONDS.toSeconds(mwpEntries.get(i).getWindowLength())))); 47 | } 48 | 49 | JPanel ButtonSlab = new JPanel(new GridLayout(3, 2)); 50 | JButton bttsla_add = new JButton("Add"); 51 | bttsla_add.setMnemonic(KeyEvent.VK_A); 52 | bttsla_add.setIcon(new ImageIcon(getClass().getResource("icon_add.png"))); 53 | bttsla_add.setToolTipText("Add an SLA Percentile"); 54 | bttsla_add.addActionListener(new ActionListener() { 55 | @Override 56 | public void actionPerformed(ActionEvent e) { 57 | sla_addrows(); 58 | } 59 | }); 60 | 61 | JButton bttsla_delete = new JButton("Delete"); 62 | bttsla_delete.setMnemonic(KeyEvent.VK_D); 63 | bttsla_delete.setIcon(new ImageIcon(getClass().getResource("icon_delete.png"))); 64 | bttsla_delete.setToolTipText("Delete an SLA Percentile"); 65 | bttsla_delete.addActionListener(new ActionListener() { 66 | @Override 67 | public void actionPerformed(ActionEvent e) { 68 | sla_delete(); 69 | } 70 | }); 71 | 72 | JButton bttsla_apply = new JButton("Apply"); 73 | bttsla_apply.setIcon(new ImageIcon(getClass().getResource("icon_apply.png"))); 74 | bttsla_apply.setMnemonic(KeyEvent.VK_P); 75 | bttsla_apply.setToolTipText("Apply to all graphs"); 76 | bttsla_apply.addActionListener(new ActionListener() { 77 | @Override 78 | public void actionPerformed(ActionEvent e) { 79 | sla_apply(); 80 | } 81 | }); 82 | 83 | ButtonSlab.add(bttsla_add); 84 | ButtonSlab.add(bttsla_delete); 85 | ButtonSlab.add(bttsla_apply); 86 | 87 | add(panel); 88 | add(ButtonSlab); 89 | 90 | // init MWP tab with empty entry 91 | sla_addrows(); 92 | } 93 | 94 | private void sla_apply() { 95 | if (sla_isvalid() != 0) { 96 | if (sla_fillvalues()) { 97 | MWPProperties.applyMWP(); 98 | } 99 | } 100 | } 101 | 102 | private void sla_addrows() { 103 | int rows = panel.getComponentCount() / TIMELINE_COLUMNS_NUMBER; 104 | sla_addat(rows); 105 | } 106 | 107 | private void sla_delete() { 108 | int rows = panel.getComponentCount() / TIMELINE_COLUMNS_NUMBER - 1; 109 | if (rows == 1) { 110 | JOptionPane.showMessageDialog(mainframe, "Last row, unable to delete"); 111 | return; 112 | } 113 | 114 | String rtarget = JOptionPane.showInputDialog(null, "Which row to delete? (1-" + rows + "): "); 115 | if (rtarget != null) { 116 | int userrownumber = Integer.parseInt(rtarget); 117 | if (!(userrownumber > 0 && userrownumber <= rows)) { 118 | JOptionPane.showMessageDialog(mainframe, "Incorrect choice. Try again!" + userrownumber); 119 | return; 120 | } 121 | sla_deleteat(userrownumber); 122 | } 123 | } 124 | 125 | private int sla_isvalid() { 126 | int rowcount = panel.getComponentCount() / TIMELINE_COLUMNS_NUMBER; 127 | for (int i = TIMELINE_COLUMNS_NUMBER; i < rowcount * TIMELINE_COLUMNS_NUMBER; i = i + TIMELINE_COLUMNS_NUMBER) { 128 | JTextField ptxt = (JTextField) panel.getComponent(i + 1); 129 | JTextField hicptxt = (JTextField) panel.getComponent(i + 2); 130 | try { 131 | Double.parseDouble(ptxt.getText()); 132 | } catch (Exception ee) { 133 | JOptionPane.showMessageDialog(mainframe, "Only numeric values allowed as percentile values. Try again!"); 134 | ptxt.grabFocus(); 135 | return 0; 136 | } 137 | try { 138 | Double.parseDouble(hicptxt.getText()); 139 | } catch (Exception ee) { 140 | JOptionPane.showMessageDialog(mainframe, "Only numeric values allowed as window length. Try again!"); 141 | hicptxt.grabFocus(); 142 | return 0; 143 | } 144 | } 145 | return 1; 146 | } 147 | 148 | private void sla_deleteat(int at) { 149 | int index = (at + 1) * TIMELINE_COLUMNS_NUMBER; 150 | panel.remove(--index); 151 | panel.remove(--index); 152 | panel.remove(--index); 153 | 154 | sla_regain(); 155 | } 156 | 157 | private void sla_addat(int at) { 158 | int index = (at) * TIMELINE_COLUMNS_NUMBER; 159 | panel.add(new JLabel("Sno"), index++); 160 | panel.add(new JTextField("*"), index++); 161 | panel.add(new JTextField("*"), index); 162 | 163 | sla_regain(); 164 | } 165 | 166 | private void sla_regain() { 167 | int rowcount = panel.getComponentCount() / TIMELINE_COLUMNS_NUMBER; 168 | for (int i = 3; i < rowcount * TIMELINE_COLUMNS_NUMBER; i = i + TIMELINE_COLUMNS_NUMBER) { 169 | JLabel lbl = (JLabel) panel.getComponent(i); 170 | lbl.setText(((Integer) (i / TIMELINE_COLUMNS_NUMBER)).toString()); 171 | } 172 | panel.setLayout(new GridLayout(rowcount, 3)); 173 | this.repaint(); 174 | } 175 | 176 | private boolean sla_fillvalues() { 177 | MWPProperties.reset(); 178 | int rowcount = panel.getComponentCount() / 3; 179 | for (int i = TIMELINE_COLUMNS_NUMBER; i < rowcount * TIMELINE_COLUMNS_NUMBER; i = i + TIMELINE_COLUMNS_NUMBER) { 180 | JTextField ptxt = (JTextField) panel.getComponent(i + 1); 181 | JTextField htxt = (JTextField) panel.getComponent(i + 2); 182 | double windowLengthInSeconds = Double.parseDouble(htxt.getText()); 183 | MWPProperties.MWPEntry MWPEntry = 184 | new MWPProperties.MWPEntry(Double.parseDouble(ptxt.getText()), 185 | (long) (windowLengthInSeconds * 1000)); 186 | MWPProperties.addMWPEntry(MWPEntry); 187 | } 188 | return true; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/panels/SLAPanel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.panels; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.properties.SLAProperties; 9 | import org.HdrHistogram.HistogramLogAnalyzer.properties.SLAProperties.SLAEntry; 10 | 11 | import javax.swing.*; 12 | import java.awt.*; 13 | import java.awt.event.ActionEvent; 14 | import java.awt.event.ActionListener; 15 | import java.awt.event.KeyEvent; 16 | import java.util.List; 17 | 18 | public class SLAPanel extends JPanel { 19 | 20 | private JFrame mainframe; 21 | private JPanel panel; 22 | private SLAProperties slaProperties; 23 | 24 | private static final int SLA_COLUMNS_NUMBER = 3; 25 | 26 | public SLAPanel(JFrame mainframe, SLAProperties slaProperties) 27 | { 28 | this.mainframe = mainframe; 29 | this.slaProperties = slaProperties; 30 | List slaEntries = slaProperties.getSLAEntries(); 31 | 32 | setBorder(BorderFactory.createLineBorder(Color.BLACK)); 33 | 34 | int rowcount = slaEntries.size(); 35 | panel = new JPanel(new GridLayout(rowcount + 1, SLA_COLUMNS_NUMBER)); 36 | panel.setBorder(BorderFactory.createLineBorder(Color.BLACK)); 37 | panel.add(new JLabel("Row")); 38 | panel.add(new JLabel("Percentile(%)")); 39 | panel.add(new JLabel("Hiccup(msec)")); 40 | 41 | for (int i = 0; i < rowcount; i++) { 42 | panel.add(new JLabel(((Integer) (i + 1)).toString())); 43 | panel.add(new JTextField(slaEntries.get(i).getPercentile().toString())); 44 | panel.add(new JTextField(slaEntries.get(i).getLatency().toString())); 45 | } 46 | 47 | JPanel ButtonSlab = new JPanel(new GridLayout(3, 2)); 48 | JButton bttsla_add = new JButton("Add"); 49 | bttsla_add.setMnemonic(KeyEvent.VK_A); 50 | bttsla_add.setIcon(new ImageIcon(getClass().getResource("icon_add.png"))); 51 | bttsla_add.setToolTipText("Add an SLA Percentile"); 52 | bttsla_add.addActionListener(new ActionListener() { 53 | @Override 54 | public void actionPerformed(ActionEvent e) { 55 | sla_addrows(); 56 | } 57 | }); 58 | 59 | JButton bttsla_delete = new JButton("Delete"); 60 | bttsla_delete.setMnemonic(KeyEvent.VK_D); 61 | bttsla_delete.setIcon(new ImageIcon(getClass().getResource("icon_delete.png"))); 62 | bttsla_delete.setToolTipText("Delete an SLA Percentile"); 63 | bttsla_delete.addActionListener(new ActionListener() { 64 | @Override 65 | public void actionPerformed(ActionEvent e) { 66 | sla_delete(); 67 | } 68 | }); 69 | 70 | JButton bttsla_apply = new JButton("Apply"); 71 | bttsla_apply.setIcon(new ImageIcon(getClass().getResource("icon_apply.png"))); 72 | bttsla_apply.setMnemonic(KeyEvent.VK_P); 73 | bttsla_apply.setToolTipText("Apply to all graphs"); 74 | bttsla_apply.addActionListener(new ActionListener() { 75 | @Override 76 | public void actionPerformed(ActionEvent e) { 77 | sla_apply(); 78 | } 79 | }); 80 | 81 | ButtonSlab.add(bttsla_add); 82 | ButtonSlab.add(bttsla_delete); 83 | ButtonSlab.add(bttsla_apply); 84 | 85 | add(panel); 86 | add(ButtonSlab); 87 | } 88 | 89 | private void sla_apply() { 90 | if (sla_isvalid() != 0) { 91 | if (sla_fillvalues()) { 92 | slaProperties.applySLA(); 93 | JOptionPane.showMessageDialog(mainframe, "Updated!"); 94 | } 95 | } 96 | } 97 | 98 | private void sla_addrows() { 99 | int rows = panel.getComponentCount() / SLA_COLUMNS_NUMBER - 1; 100 | String rtarget = JOptionPane.showInputDialog(null, "Add after which row number? (0-" + (rows - 1) + "): ", "jHiccup", 1); 101 | if (rtarget != null) { 102 | int userrownumber = Integer.parseInt(rtarget); 103 | if (!(userrownumber >= 0 && userrownumber < rows)) { 104 | JOptionPane.showMessageDialog(mainframe, "Incorrect choice. Try again! " + userrownumber, "jHiccup", 1); 105 | return; 106 | } 107 | sla_addat(userrownumber + 1); 108 | } 109 | } 110 | 111 | private void sla_delete() { 112 | int rows = panel.getComponentCount() / SLA_COLUMNS_NUMBER - 1; 113 | String rtarget = JOptionPane.showInputDialog(null, "Which row to delete? (1-" + (rows-1) + "): ", "jHiccup", 1); 114 | if (rtarget != null) { 115 | int userrownumber = Integer.parseInt(rtarget); 116 | if (!(userrownumber > 0 && userrownumber < rows)) { 117 | JOptionPane.showMessageDialog(mainframe, "Incorrect choice. Try again!" + userrownumber, "jHiccup", 1); 118 | return; 119 | } 120 | sla_deleteat(userrownumber); 121 | } 122 | } 123 | 124 | private int sla_isvalid() { 125 | int rowcount = panel.getComponentCount() / SLA_COLUMNS_NUMBER; 126 | for (int i = SLA_COLUMNS_NUMBER; i < rowcount * SLA_COLUMNS_NUMBER; i = i + SLA_COLUMNS_NUMBER) { 127 | JTextField ptxt = (JTextField) panel.getComponent(i + 1); 128 | JTextField hicptxt = (JTextField) panel.getComponent(i + 2); 129 | try { 130 | Double.parseDouble(ptxt.getText()); 131 | } catch (Exception ee) { 132 | JOptionPane.showMessageDialog(mainframe, "Only numeric values allowed as percentile values. Try again!"); 133 | ptxt.grabFocus(); 134 | return 0; 135 | } 136 | try { 137 | Double.parseDouble(hicptxt.getText()); 138 | } catch (Exception ee) { 139 | JOptionPane.showMessageDialog(mainframe, "Only numeric values allowed as latency pause times. Try again!"); 140 | hicptxt.grabFocus(); 141 | return 0; 142 | } 143 | } 144 | return 1; 145 | } 146 | 147 | private void sla_deleteat(int at) { 148 | int index = (at + 1) * SLA_COLUMNS_NUMBER; 149 | panel.remove(index--); 150 | panel.remove(index--); 151 | panel.remove(index); 152 | 153 | sla_regain(); 154 | } 155 | 156 | private void sla_addat(int at) { 157 | int index = (at) * SLA_COLUMNS_NUMBER; 158 | panel.add(new JLabel("Sno"), index++); 159 | panel.add(new JTextField("*"), index++); 160 | panel.add(new JTextField("*"), index); 161 | 162 | sla_regain(); 163 | } 164 | 165 | private void sla_regain() { 166 | int rowcount = panel.getComponentCount() / SLA_COLUMNS_NUMBER; 167 | for (int i = 3; i < rowcount * SLA_COLUMNS_NUMBER; i = i + SLA_COLUMNS_NUMBER) { 168 | JLabel lbl = (JLabel) panel.getComponent(i); 169 | lbl.setText(((Integer) (i / SLA_COLUMNS_NUMBER)).toString()); 170 | } 171 | panel.setLayout(new GridLayout(rowcount, 3)); 172 | } 173 | 174 | private boolean sla_fillvalues() { 175 | slaProperties.clear(); 176 | int rowcount = panel.getComponentCount() / 3; 177 | for (int i = SLA_COLUMNS_NUMBER; i < rowcount * SLA_COLUMNS_NUMBER; i = i + SLA_COLUMNS_NUMBER) { 178 | JTextField ptxt = (JTextField) panel.getComponent(i + 1); 179 | JTextField htxt = (JTextField) panel.getComponent(i + 2); 180 | SLAEntry slaEntry = new SLAEntry(Double.parseDouble(ptxt.getText()), Double.parseDouble(htxt.getText())); 181 | slaProperties.addSLAEntry(slaEntry); 182 | } 183 | return true; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/properties/AppProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.properties; 7 | 8 | public class AppProperties { 9 | 10 | private SLAProperties slaProperties = new SLAProperties(); 11 | private MWPProperties mwpProperties = new MWPProperties(); 12 | private HPLProperties hplProperties = new HPLProperties(); 13 | private ViewProperties viewProperties = new ViewProperties(); 14 | private DateProperties dateProperties = new DateProperties(); 15 | 16 | public SLAProperties getSlaProperties() { 17 | return slaProperties; 18 | } 19 | 20 | public MWPProperties getMwpProperties() { 21 | return mwpProperties; 22 | } 23 | 24 | public HPLProperties getHplProperties() { 25 | return hplProperties; 26 | } 27 | 28 | public ViewProperties getViewProperties() { 29 | return viewProperties; 30 | } 31 | 32 | public DateProperties getDateProperties() { 33 | return dateProperties; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/properties/DateProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.properties; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.HLAChartType; 9 | import org.HdrHistogram.HistogramLogAnalyzer.datalayer.HistogramModel; 10 | import org.jfree.data.Range; 11 | import org.jfree.data.time.DateRange; 12 | import sun.util.calendar.ZoneInfo; 13 | 14 | import java.beans.PropertyChangeListener; 15 | import java.beans.PropertyChangeSupport; 16 | import java.text.DateFormat; 17 | import java.text.SimpleDateFormat; 18 | import java.util.ArrayList; 19 | import java.util.Date; 20 | import java.util.List; 21 | import java.util.TimeZone; 22 | 23 | public class DateProperties { 24 | 25 | private boolean isDatesVisible = false; 26 | 27 | // format-related fields 28 | private boolean showDate = false; 29 | private boolean showSeconds = false; 30 | private boolean showMilliseconds = false; 31 | private boolean overrideTimezone = false; 32 | private TimeZone userTimezone = null; 33 | 34 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 35 | 36 | public void addPropertyChangeListener(PropertyChangeListener listener) { 37 | pcs.addPropertyChangeListener(listener); 38 | } 39 | 40 | public void removePropertyChangeListener(PropertyChangeListener listener) { 41 | pcs.removePropertyChangeListener(listener); 42 | } 43 | 44 | public boolean isDatesVisible() { 45 | return isDatesVisible; 46 | } 47 | 48 | public boolean isChartActive(HLAChartType chartType) { 49 | return (isDatesVisible ? chartType == HLAChartType.TIMELINE_DATE : 50 | chartType == HLAChartType.TIMELINE_ELAPSED_TIME); 51 | } 52 | 53 | public static DateRange rangeToDateRange(double startTime, Range range) { 54 | double newLowerBound = startTime + range.getLowerBound(); 55 | double newUpperBound = startTime + range.getUpperBound(); 56 | Date lowerBoundDate = new Date((long) (newLowerBound * 1000)); 57 | Date upperBoundDate = new Date((long) (newUpperBound * 1000)); 58 | return new DateRange(lowerBoundDate, upperBoundDate); 59 | } 60 | 61 | public static Range dateRangeToRange(double startTime, Range dateRange) { 62 | double newLowerBound = dateRange.getLowerBound() / 1000.0d; 63 | double newUpperBounds = dateRange.getUpperBound() / 1000.0d; 64 | return new Range(newLowerBound - startTime, newUpperBounds - startTime); 65 | } 66 | 67 | 68 | public void setDatesVisible(boolean newValue) { 69 | isDatesVisible = newValue; 70 | pcs.firePropertyChange("setDatesVisible", !newValue, newValue); 71 | } 72 | 73 | public boolean isShowDate() { 74 | return showDate; 75 | } 76 | 77 | public void setShowDate(boolean newValue) { 78 | showDate = newValue; 79 | pcs.firePropertyChange("setShowDate", !newValue, newValue); 80 | } 81 | 82 | public boolean isShowSeconds() { 83 | return showSeconds; 84 | } 85 | 86 | public void setShowSeconds(boolean newValue) { 87 | showSeconds = newValue; 88 | pcs.firePropertyChange("setShowSeconds", !newValue, newValue); 89 | } 90 | 91 | public boolean isShowMilliseconds() { 92 | return showMilliseconds; 93 | } 94 | 95 | public void setShowMilliseconds(boolean newValue) { 96 | showMilliseconds = newValue; 97 | pcs.firePropertyChange("setShowMilliseconds", !newValue, newValue); 98 | } 99 | 100 | public boolean isOverrideTimezone() { 101 | return overrideTimezone; 102 | } 103 | 104 | public void setOverrideTimezone(boolean overrideTimezone) { 105 | this.overrideTimezone = overrideTimezone; 106 | } 107 | 108 | public void setUserTimezone(String userTimezone) { 109 | this.userTimezone = TimeZone.getTimeZone(userTimezone); 110 | 111 | pcs.firePropertyChange("setUserTimezone", null, userTimezone); 112 | } 113 | 114 | public TimeZone getTimeZone(List models) { 115 | if (overrideTimezone) { 116 | return userTimezone; 117 | } 118 | 119 | // try to get timezone of first model 120 | TimeZone timezone = models.get(0).getTimeZone(); 121 | if (timezone != null) { 122 | return timezone; 123 | } 124 | 125 | return ZoneInfo.getDefault(); 126 | } 127 | 128 | public static String getShortTimezoneString(TimeZone timeZone, Date date) { 129 | return timeZone.getDisplayName(timeZone.inDaylightTime(date), TimeZone.SHORT); 130 | } 131 | 132 | public DateFormat getDateFormat(List models) { 133 | String dateFormat; 134 | if (isShowDate()) { 135 | dateFormat = "yyyy/MM/dd HH:mm"; 136 | } else { 137 | dateFormat = "HH:mm"; 138 | } 139 | if (isShowSeconds()) { 140 | dateFormat = dateFormat.concat(":ss"); 141 | if (isShowMilliseconds()) { 142 | dateFormat = dateFormat.concat(":SSS"); 143 | } 144 | } 145 | 146 | SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); 147 | sdf.setTimeZone(getTimeZone(models)); 148 | 149 | return sdf; 150 | } 151 | 152 | public static List getAllTimeZones() { 153 | List ret = new ArrayList<>(); 154 | String[] tzIds = TimeZone.getAvailableIDs(); 155 | for (String tzId : tzIds) { 156 | if (tzId.length() > 3 && !tzId.startsWith("Etc")) { 157 | String tzName = TimeZone.getTimeZone(tzId).getDisplayName(); 158 | if (!tzName.startsWith("GMT-") && !tzName.startsWith("GMT+")) { 159 | ret.add(tzId); 160 | } 161 | } 162 | } 163 | return ret; 164 | } 165 | 166 | public String getLocalTimeZone() { 167 | return TimeZone.getDefault().getID(); 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/properties/HPLProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.properties; 7 | 8 | import java.beans.PropertyChangeListener; 9 | import java.beans.PropertyChangeSupport; 10 | 11 | /* 12 | * HPL stands for horizontal percentile lines 13 | */ 14 | public class HPLProperties { 15 | 16 | private boolean isHPLVisible = false; 17 | 18 | public static double[] getPercentiles() { 19 | return new double[] { 0.99D, 0.999D, 0.9999D }; 20 | } 21 | 22 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 23 | 24 | public void addPropertyChangeListener(PropertyChangeListener listener) { 25 | pcs.addPropertyChangeListener(listener); 26 | } 27 | 28 | public void removePropertyChangeListener(PropertyChangeListener listener) { 29 | pcs.removePropertyChangeListener(listener); 30 | } 31 | 32 | public boolean isHPLVisible() { 33 | return isHPLVisible; 34 | } 35 | 36 | public void toggleHPLVisibility(boolean newValue) { 37 | isHPLVisible = newValue; 38 | pcs.firePropertyChange("hplShow", !newValue, newValue); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/properties/MWPProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.properties; 7 | 8 | import java.beans.PropertyChangeListener; 9 | import java.beans.PropertyChangeSupport; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class MWPProperties { 14 | 15 | private List mwpEntries = new ArrayList(); 16 | 17 | private boolean isMWPVisible = false; 18 | 19 | public static class MWPEntry { 20 | 21 | private Double percentile; 22 | private long windowLength; // in msec 23 | 24 | public MWPEntry(Double percentile, long windowLength) { 25 | this.percentile = percentile; 26 | this.windowLength = windowLength; 27 | } 28 | 29 | public Double getPercentile() { 30 | return percentile; 31 | } 32 | 33 | public long getWindowLength() { 34 | return windowLength; 35 | } 36 | 37 | public boolean isDefaultEntry() { 38 | return DEFAULT_MWP_ENTRY.equals(this); 39 | } 40 | 41 | @Override 42 | public boolean equals(Object anObject) { 43 | if (this == anObject) { 44 | return true; 45 | } 46 | 47 | if (anObject instanceof MWPEntry) { 48 | MWPEntry anEntry = (MWPEntry)anObject; 49 | return getPercentile().equals(anEntry.getPercentile()) && 50 | getWindowLength() == anEntry.getWindowLength(); 51 | } 52 | return false; 53 | } 54 | 55 | public String toString() { 56 | double windowLengthInSeconds = (double) getWindowLength() / 1000; 57 | String secondsWording = windowLengthInSeconds == 1 ? " second" : " seconds"; 58 | 59 | if (windowLengthInSeconds == (long) windowLengthInSeconds) { 60 | return " " + getPercentile() + "%'ile, " + 61 | String.format("%d", (long) windowLengthInSeconds) + secondsWording; 62 | } else { 63 | return " " + getPercentile() + "%'ile, " + 64 | String.format("%s", windowLengthInSeconds) + secondsWording; 65 | } 66 | } 67 | } 68 | 69 | public MWPProperties() { 70 | mwpEntries.add(DEFAULT_MWP_ENTRY); 71 | } 72 | 73 | // FIXME: default entry still contains interval count (but not used) 74 | private static final MWPEntry DEFAULT_MWP_ENTRY = new MWPEntry(100.0, 1); 75 | 76 | public static MWPEntry getDefaultMWPEntry() { 77 | return DEFAULT_MWP_ENTRY; 78 | } 79 | 80 | public List getMWPEntries() { 81 | return mwpEntries; 82 | } 83 | 84 | /* 85 | * entries accessible via MWP master tab (default entry not accessible) 86 | */ 87 | public List getAccessibleMWPEntry() { 88 | List ret = new ArrayList<>(); 89 | for (MWPEntry mwpEntry : getMWPEntries()) { 90 | if (!DEFAULT_MWP_ENTRY.equals(mwpEntry)) { 91 | ret.add(mwpEntry); 92 | } 93 | } 94 | return ret; 95 | } 96 | 97 | public void addMWPEntry(MWPEntry MWPEntry) { 98 | mwpEntries.add(MWPEntry); 99 | } 100 | 101 | public void reset() { 102 | mwpEntries.clear(); 103 | mwpEntries.add(DEFAULT_MWP_ENTRY); 104 | } 105 | 106 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 107 | 108 | public void addPropertyChangeListener(PropertyChangeListener listener) { 109 | pcs.addPropertyChangeListener(listener); 110 | } 111 | 112 | public void removePropertyChangeListener(PropertyChangeListener listener) { 113 | pcs.removePropertyChangeListener(listener); 114 | } 115 | 116 | public boolean isMWPVisible() { 117 | return isMWPVisible; 118 | } 119 | 120 | public void toggleMWPVisibility(boolean newValue) { 121 | isMWPVisible = newValue; 122 | pcs.firePropertyChange("mwpShow", !newValue, newValue); 123 | } 124 | 125 | public void applyMWP() { 126 | pcs.firePropertyChange("applyMWP", false, true); 127 | } 128 | 129 | public boolean isShowMWPUnlocked() { 130 | return mwpEntries.size() > 1; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/properties/SLAProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.properties; 7 | 8 | import org.w3c.dom.Document; 9 | import org.w3c.dom.Element; 10 | import org.w3c.dom.NodeList; 11 | import org.xml.sax.SAXException; 12 | 13 | import javax.xml.parsers.DocumentBuilder; 14 | import javax.xml.parsers.DocumentBuilderFactory; 15 | import javax.xml.parsers.ParserConfigurationException; 16 | import java.beans.PropertyChangeListener; 17 | import java.beans.PropertyChangeSupport; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | public class SLAProperties { 24 | 25 | private List slaEntries = new ArrayList(); 26 | 27 | private boolean isSLAVisible = false; 28 | 29 | public static class SLAEntry { 30 | private Double percentile; 31 | private Double latency; 32 | 33 | public SLAEntry(Double percentile, Double latency) { 34 | this.percentile = percentile; 35 | this.latency = latency; 36 | } 37 | 38 | public Double getPercentile() { 39 | return percentile; 40 | } 41 | 42 | public Double getLatency() { 43 | return latency; 44 | } 45 | 46 | // 1/1-percentile 47 | public Double getPercentileAxis() { 48 | Double cvalue = 1 / (1 - getPercentile() / 100); 49 | if (cvalue.isInfinite()) { 50 | cvalue = 1000 / (1 - (99.999) / 100); 51 | } 52 | cvalue = (double) Math.round(cvalue); 53 | return cvalue; 54 | } 55 | } 56 | 57 | // private String SLA_FILENAME = "SLAdetails.xml"; 58 | private String SLA_FILENAME = "/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/SLAdetails.xml"; 59 | 60 | SLAProperties() { 61 | try { 62 | readPropertiesFromFile(); 63 | } catch (ParserConfigurationException e) { 64 | e.printStackTrace(); 65 | } catch (IOException e) { 66 | e.printStackTrace(); 67 | } catch (SAXException e) { 68 | e.printStackTrace(); 69 | } 70 | } 71 | 72 | private void readPropertiesFromFile() throws ParserConfigurationException, IOException, SAXException { 73 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 74 | DocumentBuilder db1 = dbf.newDocumentBuilder(); 75 | InputStream is = getClass().getResourceAsStream(SLA_FILENAME); 76 | Document dom = db1.parse(is); 77 | NodeList nlist = dom.getElementsByTagName("SLAPercentile"); 78 | for (int temp = 0; temp < nlist.getLength(); temp++) { 79 | Element e = (Element) nlist.item(temp); 80 | Double percentile = Double.parseDouble(nlist.item(temp).getFirstChild().getNodeValue()); 81 | Double latency = Double.parseDouble(e.getAttribute("acceptablehicupp_msec")); 82 | slaEntries.add(new SLAEntry(percentile, latency)); 83 | } 84 | } 85 | 86 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 87 | 88 | public void addPropertyChangeListener(PropertyChangeListener listener) { 89 | pcs.addPropertyChangeListener(listener); 90 | } 91 | 92 | public void removePropertyChangeListener(PropertyChangeListener listener) { 93 | pcs.removePropertyChangeListener(listener); 94 | } 95 | 96 | public boolean isSLAVisible() { 97 | return isSLAVisible; 98 | } 99 | 100 | public void toggleSLAVisibility(boolean newValue) { 101 | isSLAVisible = newValue; 102 | pcs.firePropertyChange("slaShow", !newValue, newValue); 103 | } 104 | 105 | public void applySLA() { 106 | pcs.firePropertyChange("applySLA", false, true); 107 | } 108 | 109 | public List getSLAEntries() { 110 | return slaEntries; 111 | } 112 | 113 | public void clear() { 114 | slaEntries.clear(); 115 | } 116 | 117 | public void addSLAEntry(SLAEntry slaEntry) { 118 | slaEntries.add(slaEntry); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/properties/ScaleProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.properties; 7 | 8 | import java.beans.PropertyChangeListener; 9 | import java.beans.PropertyChangeSupport; 10 | 11 | public class ScaleProperties { 12 | 13 | public static class ScaleEntry { 14 | private Double maxXValue; 15 | private Double maxYValue; 16 | 17 | public ScaleEntry(double maxXValue, double maxYValue) { 18 | this.maxXValue = maxXValue; 19 | this.maxYValue = maxYValue; 20 | } 21 | 22 | public double getMaxXValue() { 23 | return maxXValue; 24 | } 25 | 26 | public double getMaxYValue() { 27 | return maxYValue; 28 | } 29 | } 30 | 31 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 32 | 33 | public void addPropertyChangeListener(PropertyChangeListener listener) { 34 | pcs.addPropertyChangeListener(listener); 35 | } 36 | 37 | public void removePropertyChangeListener(PropertyChangeListener listener) { 38 | pcs.removePropertyChangeListener(listener); 39 | } 40 | 41 | public void applyScale(ScaleEntry se) { 42 | pcs.firePropertyChange("applyScale", null, se); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/properties/ViewProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.properties; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.HLAChartType; 9 | 10 | import java.beans.PropertyChangeListener; 11 | import java.beans.PropertyChangeSupport; 12 | 13 | public class ViewProperties { 14 | 15 | // Percentile by default but can be changed to Buckets via "View" settings 16 | private HLAChartType bottomChartType = HLAChartType.PERCENTILE; 17 | 18 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 19 | 20 | public void addPropertyChangeListener(PropertyChangeListener listener) { 21 | pcs.addPropertyChangeListener(listener); 22 | } 23 | 24 | public void removePropertyChangeListener(PropertyChangeListener listener) { 25 | pcs.removePropertyChangeListener(listener); 26 | } 27 | 28 | public HLAChartType getBottomChartType() { 29 | return bottomChartType; 30 | } 31 | 32 | public void toogleBottomChartType(HLAChartType newBottomChartType) { 33 | if (bottomChartType != newBottomChartType) { 34 | HLAChartType oldBottomChartType = bottomChartType; 35 | bottomChartType = newBottomChartType; 36 | pcs.firePropertyChange("bottomChartTypeChanged", oldBottomChartType, bottomChartType); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/HdrHistogram/HistogramLogAnalyzer/properties/ZoomProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Written by Azul Systems, and released to the public domain, 3 | * as explained at http://creativecommons.org/publicdomain/zero/1.0/ 4 | */ 5 | 6 | package org.HdrHistogram.HistogramLogAnalyzer.properties; 7 | 8 | import org.HdrHistogram.HistogramLogAnalyzer.applicationlayer.HLAChartType; 9 | import org.jfree.data.Range; 10 | 11 | import java.beans.PropertyChangeListener; 12 | import java.beans.PropertyChangeSupport; 13 | 14 | public class ZoomProperties { 15 | 16 | public enum AxisType { DOMAIN, RANGE } 17 | 18 | 19 | public static class ZoomValue { 20 | 21 | private Range range; 22 | private AxisType axisType; 23 | 24 | // it's either date or elapsed-time bounds 25 | private HLAChartType chartType; 26 | 27 | public ZoomValue(AxisType axisType, Range range, HLAChartType chartType) { 28 | this.axisType = axisType; 29 | this.range = range; 30 | this.chartType = chartType; 31 | } 32 | 33 | public AxisType getAxisType() { 34 | return axisType; 35 | } 36 | 37 | public Range getRange() { 38 | return range; 39 | } 40 | 41 | public HLAChartType getChartType() { 42 | return chartType; 43 | } 44 | } 45 | 46 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 47 | 48 | public void addPropertyChangeListener(PropertyChangeListener listener) { 49 | pcs.addPropertyChangeListener(listener); 50 | } 51 | 52 | public void removePropertyChangeListener(PropertyChangeListener listener) { 53 | pcs.removePropertyChangeListener(listener); 54 | } 55 | 56 | public void zoom(ZoomValue value) { 57 | pcs.firePropertyChange("zoom", null, value); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/SLAdetails.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 90.0 4 | 99.0 5 | 99.90 6 | 99.99 7 | 99.999 8 | 99.9999 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/azul_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/azul_logo.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_close.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_close2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_close2.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_maxrange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_maxrange.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_open.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_photo.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_sla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/applicationlayer/icon_sla.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/panels/icon_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/panels/icon_add.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/panels/icon_apply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/panels/icon_apply.png -------------------------------------------------------------------------------- /src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/panels/icon_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HdrHistogram/HistogramLogAnalyzer/df5c6009e477e72afcfad60f84a72b253f4ebc80/src/main/resources/org/HdrHistogram/HistogramLogAnalyzer/panels/icon_delete.png --------------------------------------------------------------------------------