├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── pom.xml ├── src ├── main │ ├── java │ │ └── io │ │ │ └── omnition │ │ │ └── loadgenerator │ │ │ ├── App.java │ │ │ ├── LoadGeneratorParams.java │ │ │ ├── model │ │ │ ├── topology │ │ │ │ ├── OperationModel.java │ │ │ │ ├── ServiceRoute.java │ │ │ │ ├── ServiceTier.java │ │ │ │ ├── TagGenerator.java │ │ │ │ ├── TagNameGenerator.java │ │ │ │ ├── TagSet.java │ │ │ │ └── Topology.java │ │ │ └── trace │ │ │ │ ├── KeyValue.java │ │ │ │ ├── Reference.java │ │ │ │ ├── Service.java │ │ │ │ ├── Span.java │ │ │ │ └── Trace.java │ │ │ └── util │ │ │ ├── ITraceEmitter.java │ │ │ ├── JaegerTraceEmitter.java │ │ │ ├── LogLevel.java │ │ │ ├── OpenTracingTraceConverter.java │ │ │ ├── ScheduledTraceGenerator.java │ │ │ ├── SpanConventions.java │ │ │ ├── SummaryLogger.java │ │ │ ├── TraceGenerator.java │ │ │ ├── TraceTraversal.java │ │ │ └── ZipkinTraceEmitter.java │ └── resources │ │ ├── adjectives.txt │ │ ├── log4j.properties │ │ ├── natures.txt │ │ └── pokemon.txt └── test │ └── java │ └── io │ └── omnition │ └── loadgenerator │ └── AppTest.java ├── start.sh └── topologies ├── 100_000_spans_per_second.json └── hipster-shop.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # Eclipse Files 26 | .settings/* 27 | .classpath 28 | .project 29 | target/* 30 | /target/ 31 | 32 | # IntelliJ files 33 | .idea/ 34 | *.iml 35 | 36 | # VS Code files 37 | .vscode 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Pull base image. 2 | FROM openjdk:8-jre-alpine 3 | 4 | # Define working directory. 5 | RUN mkdir -p /opt/omnition/topologies 6 | COPY target/SyntheticLoadGenerator-1.0-SNAPSHOT-jar-with-dependencies.jar /opt/omnition/synthetic-load-generator.jar 7 | COPY topologies/* /opt/omnition/topologies/ 8 | COPY start.sh /opt/omnition/ 9 | RUN chmod +x /opt/omnition/start.sh 10 | WORKDIR /opt/omnition/ 11 | 12 | # Define default command 13 | CMD ["./start.sh"] 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAVEN=mvn 2 | DOCKER_IMAGE?=omnition/synthetic-load-generator 3 | BUILD_NUMBER?=latest 4 | 5 | .PHONY: all build publish 6 | all: build publish 7 | build: java-jars docker-build 8 | 9 | .PHONY: java-jars 10 | java-jars: # Create jars without running tests. 11 | @echo "\n===== $@ ======" 12 | # Ensure required dependencies are met 13 | @$(call --check-dependencies,${MAVEN}) 14 | $(MAVEN) package -DskipTests 15 | 16 | .PHONY: docker-build 17 | docker-build: 18 | @echo "\n===== $@ ======" 19 | envsubst < Dockerfile | docker build --pull -t ${DOCKER_IMAGE}:${BUILD_NUMBER} -f - . 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *NOTE: This repository is archived and is no longer recommended for use.* 2 | 3 | # Synthetic Load Generator 4 | 5 | The synthetic load generator is a utility to generate synthetic operational 6 | data (traces, metrics, logs, events) for a simulated microservice-based application. 7 | The application is modeled through its topology and operation models for each 8 | of the components within the topology. 9 | 10 | ## Building the Docker image 11 | 12 | Invoke the Makefile: 13 | ``` 14 | make build 15 | ``` 16 | 17 | ## Configuration 18 | 19 | Configuration is done by JSON file. The json file should describe a complete topology by describing various services, 20 | their routes, and the downstreamCalls that those routes make. `rootRoutes` indicate the root spans or ingress points 21 | into the topology, and can be configured with the number of traces per hour to send. 22 | 23 | Different tags can also be added to each service or to each route. Tags added to a service will apply to all routes for 24 | that service, whereas tags for a route will be added for that route only. The tags for a route supersede tags set for 25 | a service. Services and routes can set tags probabilistically based on integer weights (default is 1) where the 26 | probability of selection is divided by . Additionally, services/routes 27 | can inherit tags from their direct caller by specifying the keys that should be inherited, which can be 28 | useful for modeling region-locked flows. TagGenerators can also be added to add many tags with many values of varying 29 | length. 30 | 31 | Simple Example JSON: 32 | ```json 33 | { 34 | "topology" : { 35 | "services" : [ 36 | { 37 | "serviceName" : "poke-mart", 38 | "instances" : [ "viridian-d847fdcf5-j6s2f", "pallet-79d8c8d6c8-9sbff" ], 39 | "tagSets" : [ 40 | { "weight": 2, "tags": { "generation" : "v1", "region" : "kanto" }, "tagGenerators": [{"numTags": 32, "numVals": 152, "valLength": 64}] }, 41 | { "tags": { "generation" : "v2", "region" : "johto" }} 42 | ], 43 | "routes" : [ 44 | { 45 | "route" : "/product", 46 | "downstreamCalls" : { "pokemon-center" : "/Recover", "brock" : "/GetRecommendations" }, 47 | "maxLatencyMillis": 200, 48 | "tagSets": [ 49 | { "weight": 1, "tags": { "starter" : "charmander"}}, 50 | { "weight": 1, "tags": { "starter" : "squirtle"}}, 51 | { "weight": 1, "tags": { "starter" : "bulbasaur"}} 52 | ] 53 | } 54 | ] 55 | }, 56 | { 57 | "serviceName" : "brock", 58 | "instances" : [ "pewter-a347fe1ce-g4sl1"], 59 | "tagSets" : [ 60 | { "tags": { "uselss": true }, "inherit": ["region", "starter"]} 61 | ], 62 | "routes" : [ 63 | { 64 | "route" : "/GetRecommendations", 65 | "downstreamCalls" : { }, 66 | "maxLatencyMillis": 1000, 67 | "tagSets": [ 68 | { "tags": { "loves" : "jenny"}}, 69 | { "tags": { "loves" : "joy"}} 70 | ] 71 | } 72 | ] 73 | }, 74 | { 75 | "serviceName" : "pokemon-center", 76 | "instances" : [ "cerulean-23kn9aajk-lk12d"], 77 | "tagSets" : [ 78 | { "tags": { "generation" : "v1", "region" : "kanto"}, "inherit": ["starter"] } 79 | ], 80 | "routes" : [ 81 | { 82 | "route" : "/Recover", 83 | "downstreamCalls" : { }, 84 | "maxLatencyMillis": 300 85 | } 86 | ] 87 | } 88 | ] 89 | }, 90 | "rootRoutes" : [ 91 | { 92 | "service" : "poke-mart", 93 | "route" : "/product", 94 | "tracesPerHour" : 18000 95 | } 96 | ] 97 | } 98 | ``` 99 | 100 | ## Building and running locally 101 | 102 | Building the JAR: 103 | ``` 104 | mvn package 105 | ``` 106 | 107 | Running locally with sample topology: 108 | ``` 109 | java -jar ./target/SyntheticLoadGenerator-1.0-SNAPSHOT-jar-with-dependencies.jar --paramsFile ./topologies/hipster-shop.json --jaegerCollectorUrl http://localhost:14268 110 | ``` 111 | 112 | This assumes that you have the `jaeger-collector` component running and listening 113 | on port 14268 for thrift/HTTP protocol/transport. 114 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | io.omnition.loadgenerator 8 | SyntheticLoadGenerator 9 | 1.0-SNAPSHOT 10 | 11 | SyntheticLoadGenerator 12 | https://omnition.io/ 13 | 14 | 15 | UTF-8 16 | 1.8 17 | 1.8 18 | 0.28.0 19 | 0.33.9 20 | 5.6.0 21 | 2.7.13 22 | 2.11.12 23 | 1.2.17 24 | 1.7.5 25 | 3.7 26 | 1.72 27 | 28 | 29 | 30 | 31 | 32 | com.beust 33 | jcommander 34 | ${version.jcommander} 35 | 36 | 37 | 38 | io.jaegertracing 39 | jaeger-core 40 | ${version.io.jaegertracing} 41 | 42 | 43 | 44 | io.opentracing.brave 45 | brave-opentracing 46 | ${version.io.opentracing.brave} 47 | 48 | 49 | 50 | io.zipkin.brave 51 | brave-instrumentation-okhttp3 52 | ${version.io.zipkin.brave} 53 | 54 | 55 | io.zipkin.zipkin2 56 | zipkin 57 | ${version.io.zipkin} 58 | 59 | 60 | 61 | io.zipkin.reporter2 62 | zipkin-sender-okhttp3 63 | ${version.io.zipkin.reporter2} 64 | 65 | 66 | 67 | log4j 68 | log4j 69 | ${version.log4j} 70 | 71 | 72 | 73 | org.apache.commons 74 | commons-lang3 75 | ${version.apache.commons} 76 | 77 | 78 | 79 | org.slf4j 80 | slf4j-api 81 | ${version.slf4j} 82 | 83 | 84 | 85 | org.slf4j 86 | slf4j-log4j12 87 | ${version.slf4j} 88 | 89 | 90 | 91 | junit 92 | junit 93 | 4.13.1 94 | test 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | maven-assembly-plugin 103 | 3.1.0 104 | 105 | 106 | jar-with-dependencies 107 | 108 | 109 | 110 | io.omnition.loadgenerator.App 111 | 112 | 113 | 114 | 115 | 116 | make-assembly 117 | package 118 | 119 | single 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/App.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.nio.file.Files; 6 | import java.nio.file.Paths; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.concurrent.CountDownLatch; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import io.omnition.loadgenerator.util.ITraceEmitter; 13 | import io.omnition.loadgenerator.util.ZipkinTraceEmitter; 14 | import org.apache.log4j.Logger; 15 | 16 | import com.beust.jcommander.JCommander; 17 | import com.beust.jcommander.Parameter; 18 | import com.google.gson.Gson; 19 | 20 | import io.omnition.loadgenerator.LoadGeneratorParams.RootServiceRoute; 21 | import io.omnition.loadgenerator.util.JaegerTraceEmitter; 22 | import io.omnition.loadgenerator.util.LogLevel; 23 | import io.omnition.loadgenerator.util.ScheduledTraceGenerator; 24 | import io.omnition.loadgenerator.util.SummaryLogger; 25 | 26 | import zipkin2.codec.SpanBytesEncoder; 27 | 28 | public class App { 29 | private final static Logger logger = Logger.getLogger(App.class); 30 | 31 | @Parameter(names = "--paramsFile", description = "Name of the file containing the topology params", required = true) 32 | private String topologyFile; 33 | 34 | @Parameter(names = "--logLevel", description = "Level of log verbosity (0..2). 0=Silent, 1=Minimum, 2=Verbose. If unspecified defaults to 2.", required = false) 35 | private Integer logLevelParam = 2; 36 | 37 | @Parameter(names = "--jaegerCollectorUrl", description = "URL of the jaeger collector", required = false) 38 | private String jaegerCollectorUrl = null; 39 | 40 | @Parameter(names = "--zipkinV1JsonUrl", description = "URL of the zipkinV1 json collector", required = false) 41 | private String zipkinV1JsonUrl = null; 42 | 43 | @Parameter(names = "--zipkinV1ThriftUrl", description = "URL of the zipkinV1 Thrift collector", required = false) 44 | private String zipkinV1ThriftUrl = null; 45 | 46 | @Parameter(names = "--zipkinV2JsonUrl", description = "URL of the zipkinV2 json collector", required = false) 47 | private String zipkinV2JsonUrl = null; 48 | 49 | @Parameter(names = "--zipkinV2Proto3Url", description = "URL of the zipkinV2 proto3 collector", required = false) 50 | private String zipkinV2Proto3Url = null; 51 | 52 | @Parameter(names = "--flushIntervalMillis", description = "How often to flush traces", required = false) 53 | private long flushIntervalMillis = TimeUnit.SECONDS.toMillis(5); 54 | 55 | @Parameter(names = { "--help", "-h" }, help = true) 56 | private boolean help; 57 | 58 | private List scheduledTraceGenerators = new ArrayList<>(); 59 | private final CountDownLatch latch = new CountDownLatch(1); 60 | 61 | private final SummaryLogger summaryLogger = new SummaryLogger(App.logger); 62 | private LogLevel logLevel; 63 | 64 | private List emitters; 65 | 66 | public static void main(String[] args) { 67 | App app = new App(); 68 | JCommander.newBuilder().addObject(app).build().parse(args); 69 | try { 70 | Runtime.getRuntime().addShutdownHook(new Thread() { 71 | public void run() { 72 | try { 73 | app.shutdown(); 74 | } catch (Exception e) { 75 | logger.error("Error shutting down: " + e, e); 76 | } 77 | } 78 | }); 79 | app.init(); 80 | app.start(); 81 | } catch (Exception e) { 82 | logger.error("Error running load generator: " + e, e); 83 | System.exit(1); 84 | } 85 | } 86 | 87 | public void init() throws Exception { 88 | if (this.logLevelParam < 0 || this.logLevelParam > 2) { 89 | logger.warn("Invalid logLevel specified, using logLevel=2"); 90 | this.logLevelParam = 2; 91 | } 92 | 93 | this.logLevel = LogLevel.values()[this.logLevelParam]; 94 | 95 | File f = new File(this.topologyFile); 96 | if(!f.exists() || f.isDirectory()) { 97 | logger.error("Invalid topology file specified: " + this.topologyFile); 98 | throw new FileNotFoundException(this.topologyFile); 99 | } 100 | 101 | String json = new String(Files.readAllBytes(Paths.get(this.topologyFile)), "UTF-8"); 102 | Gson gson = new Gson(); 103 | LoadGeneratorParams params = gson.fromJson(json, LoadGeneratorParams.class); 104 | logger.info("Params: " + gson.toJson(params)); 105 | this.emitters = getTraceEmitters(); 106 | for (ITraceEmitter emitter : this.emitters) { 107 | for (RootServiceRoute route : params.rootRoutes) { 108 | this.scheduledTraceGenerators.add(new ScheduledTraceGenerator( 109 | params.topology, route.service, route.route, 110 | route.tracesPerHour, emitter, logLevel, summaryLogger)); 111 | } 112 | } 113 | } 114 | 115 | public void start() throws Exception { 116 | for (ScheduledTraceGenerator gen : this.scheduledTraceGenerators) { 117 | gen.start(); 118 | } 119 | latch.await(); 120 | } 121 | 122 | public void shutdown() throws Exception { 123 | this.scheduledTraceGenerators.forEach(ScheduledTraceGenerator::shutdown); 124 | for (ScheduledTraceGenerator gen : this.scheduledTraceGenerators) { 125 | gen.awaitTermination(); 126 | } 127 | latch.countDown(); 128 | this.emitters.forEach(ITraceEmitter::close); 129 | } 130 | 131 | private List getTraceEmitters() { 132 | List emitters = new ArrayList<>(3); 133 | if (jaegerCollectorUrl != null) { 134 | emitters.add(new JaegerTraceEmitter(jaegerCollectorUrl, (int) flushIntervalMillis)); 135 | } 136 | if (zipkinV1JsonUrl != null) { 137 | emitters.add(new ZipkinTraceEmitter(zipkinV1JsonUrl, SpanBytesEncoder.JSON_V1)); 138 | } 139 | if (zipkinV1ThriftUrl != null) { 140 | emitters.add(new ZipkinTraceEmitter(zipkinV1ThriftUrl, SpanBytesEncoder.THRIFT)); 141 | } 142 | if (zipkinV2JsonUrl != null) { 143 | emitters.add(new ZipkinTraceEmitter(zipkinV2JsonUrl, SpanBytesEncoder.JSON_V2)); 144 | } 145 | if (zipkinV2Proto3Url != null) { 146 | emitters.add(new ZipkinTraceEmitter(zipkinV2Proto3Url, SpanBytesEncoder.PROTO3)); 147 | } 148 | if (emitters.size() == 0) { 149 | logger.error("No emitters specified."); 150 | throw new IllegalArgumentException("No emitters specified"); 151 | } 152 | 153 | return emitters; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/LoadGeneratorParams.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import io.omnition.loadgenerator.model.topology.Topology; 7 | 8 | public class LoadGeneratorParams { 9 | public Topology topology; 10 | public List rootRoutes = new ArrayList<>(); 11 | 12 | public class RootServiceRoute { 13 | public String service; 14 | public String route; 15 | public int tracesPerHour; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/topology/OperationModel.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.topology; 2 | 3 | public class OperationModel { 4 | public int errorPercent = 0; 5 | public int errorCode = 503; 6 | public int maxLatencyMillis = 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/topology/ServiceRoute.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.topology; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class ServiceRoute { 9 | public String route; 10 | public Map downstreamCalls = new HashMap<>(); 11 | public List tagSets = new ArrayList<>(); 12 | public int maxLatencyMillis; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/topology/ServiceTier.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.topology; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.TreeMap; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | import java.util.concurrent.ConcurrentMap; 10 | 11 | public class ServiceTier { 12 | public String serviceName; 13 | public List tagSets = new ArrayList<>(); 14 | public List routes = new ArrayList<>(); 15 | public List instances = new ArrayList<>(); 16 | 17 | private ConcurrentMap> mergedTagSets = new ConcurrentHashMap<>(); 18 | private Random random = new Random(); 19 | 20 | public ServiceRoute getRoute(String routeName) { 21 | return this.routes.stream() 22 | .filter(r -> r.route.equalsIgnoreCase(routeName)) 23 | .findFirst().get(); 24 | } 25 | 26 | public TagSet getTagSet(String routeName) { 27 | mergedTagSets.computeIfAbsent(routeName, this::generateMergedTagSets); 28 | TreeMap routeSets = mergedTagSets.get(routeName); 29 | return routeSets.higherEntry(random.nextInt(routeSets.lastKey())).getValue(); 30 | } 31 | 32 | private TreeMap generateMergedTagSets(String routeName) { 33 | TreeMap treeMap = new TreeMap<>(); 34 | int total = 0; 35 | ServiceRoute route = routes.stream().filter((r) -> r.route.equals(routeName)).findFirst().get(); 36 | 37 | // If we have to merge, merge, otherwise just set the service set. 38 | if (route.tagSets != null && route.tagSets.size() > 0) { 39 | for (TagSet routeSet : route.tagSets) { 40 | if (tagSets.isEmpty()) { 41 | total += routeSet.getWeight(); 42 | treeMap.put(total, routeSet); 43 | } else { 44 | for (TagSet serviceSet : tagSets) { 45 | TagSet mergedSet = new TagSet(); 46 | mergedSet.tags = new HashMap<>(serviceSet.tags); 47 | mergedSet.inherit = new ArrayList<>(serviceSet.inherit); 48 | mergedSet.tagGenerators = new ArrayList<>(serviceSet.tagGenerators); 49 | 50 | mergedSet.tags.putAll(routeSet.tags); 51 | mergedSet.inherit.addAll(routeSet.inherit); 52 | mergedSet.tagGenerators.addAll(routeSet.tagGenerators); 53 | mergedSet.setWeight(routeSet.getWeight() * serviceSet.getWeight()); 54 | total += mergedSet.getWeight(); 55 | treeMap.put(total, mergedSet); 56 | } 57 | } 58 | } 59 | } else { 60 | for (TagSet serviceSet : tagSets) { 61 | total += serviceSet.getWeight(); 62 | treeMap.put(total, serviceSet); 63 | } 64 | } 65 | return treeMap; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/topology/TagGenerator.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.topology; 2 | 3 | import org.apache.commons.lang3.RandomStringUtils; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Random; 8 | 9 | public class TagGenerator { 10 | 11 | private Random rand = new Random(); 12 | private TagNameGenerator tagGen = new TagNameGenerator(); 13 | 14 | public int valLength = 10; 15 | public int numTags = 0; 16 | public int numVals = 0; 17 | 18 | public Map generateTags() { 19 | Map retVal = new HashMap<>(); 20 | for (int genIndex = 0; genIndex < numTags; genIndex++) { 21 | String val; 22 | val = RandomStringUtils.random(valLength, 0, 0, true, true, null, new Random(rand.nextInt(numVals))); 23 | retVal.put(tagGen.getForIndex(genIndex), val); 24 | } 25 | 26 | return retVal; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/topology/TagNameGenerator.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.topology; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | // Can generate about 16M different unique combinations 9 | class TagNameGenerator { 10 | 11 | private static List pokemon = new BufferedReader( 12 | new InputStreamReader(TagNameGenerator.class.getResourceAsStream("/pokemon.txt")) 13 | ).lines().collect(Collectors.toList()); 14 | 15 | private static List natures = new BufferedReader( 16 | new InputStreamReader(TagNameGenerator.class.getResourceAsStream("/natures.txt")) 17 | ).lines().collect(Collectors.toList()); 18 | 19 | private static List adjectives = new BufferedReader( 20 | new InputStreamReader(TagNameGenerator.class.getResourceAsStream("/adjectives.txt")) 21 | ).lines().collect(Collectors.toList()); 22 | 23 | String getForIndex(int index) { 24 | return adjectives.get((adjectives.size()-1) % (index+1) ) 25 | + "-" + natures.get((natures.size()-1) % (index+1)) 26 | + "-" + pokemon.get((pokemon.size()-1) % (index+1)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/topology/TagSet.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.topology; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class TagSet { 9 | private Integer weight; 10 | public Map tags = new HashMap<>(); 11 | public List tagGenerators = new ArrayList<>(); 12 | public List inherit = new ArrayList<>(); 13 | 14 | public void setWeight(int weight) { 15 | if (weight <= 0) { 16 | throw new IllegalArgumentException("Weight must be greater than 0"); 17 | } 18 | this.weight = weight; 19 | } 20 | 21 | public int getWeight() { 22 | if (weight == null) { 23 | return 1; 24 | } 25 | return weight; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/topology/Topology.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.topology; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Topology { 7 | public List services = new ArrayList<>(); 8 | 9 | public ServiceTier getServiceTier(String serviceName) { 10 | return this.services.stream().filter(s -> s.serviceName.equalsIgnoreCase(serviceName)) 11 | .findFirst().get(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/trace/KeyValue.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.trace; 2 | 3 | public class KeyValue { 4 | public static final String STRING_VALUE_TYPE = "string"; 5 | public static final String LONG_VALUE_TYPE = "long"; 6 | public static final String BOOLEAN_VALUE_TYPE = "bool"; 7 | public String key; 8 | public String valueType; 9 | 10 | // TODO there are more types: double, binary, not needed at the moment 11 | public String valueString; 12 | public Boolean valueBool; 13 | public Long valueLong; 14 | 15 | public static KeyValue ofStringType(String key, String value) { 16 | KeyValue kv = new KeyValue(); 17 | kv.key = key; 18 | kv.valueType = STRING_VALUE_TYPE; 19 | kv.valueString = value; 20 | return kv; 21 | } 22 | 23 | public static KeyValue ofLongType(String key, Long value) { 24 | KeyValue kv = new KeyValue(); 25 | kv.key = key; 26 | kv.valueType = LONG_VALUE_TYPE; 27 | kv.valueLong = value; 28 | return kv; 29 | } 30 | 31 | public static KeyValue ofBooleanType(String key, Boolean value) { 32 | KeyValue kv = new KeyValue(); 33 | kv.key = key; 34 | kv.valueType = BOOLEAN_VALUE_TYPE; 35 | kv.valueBool = value; 36 | return kv; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/trace/Reference.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.trace; 2 | 3 | import java.util.UUID; 4 | 5 | public class Reference { 6 | public enum RefType { 7 | CHILD_OF, FOLLOWS_FROM 8 | } 9 | 10 | public RefType refType; 11 | public UUID fromSpanId; 12 | public UUID toSpanId; 13 | 14 | public Reference(RefType refType, UUID fromSpanId, UUID toSpanId) { 15 | this.refType = refType; 16 | this.fromSpanId = fromSpanId; 17 | this.toSpanId = toSpanId; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/trace/Service.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.trace; 2 | 3 | import java.util.List; 4 | 5 | public class Service { 6 | public String serviceName; 7 | public String instanceName; 8 | public List tags; 9 | 10 | public Service(String serviceName, String instanceName, List tags) { 11 | this.serviceName = serviceName; 12 | this.instanceName = instanceName; 13 | this.tags = tags; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/trace/Span.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.trace; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.UUID; 6 | 7 | import io.omnition.loadgenerator.util.SpanConventions; 8 | 9 | public class Span { 10 | public final UUID id = UUID.randomUUID(); 11 | public Service service; 12 | public Long startTimeMicros; 13 | public Long endTimeMicros; 14 | public String operationName; 15 | public List tags = new ArrayList<>(); 16 | public List refs = new ArrayList<>(); 17 | 18 | public void markError() { 19 | this.tags.add(KeyValue.ofBooleanType(SpanConventions.IS_ERROR_KEY, true)); 20 | } 21 | 22 | public void markRootCauseError() { 23 | this.tags.add(KeyValue.ofBooleanType(SpanConventions.IS_ROOT_CAUSE_ERROR_KEY, true)); 24 | } 25 | 26 | public boolean isErrorSpan() { 27 | return tags.stream() 28 | .anyMatch(kv -> (kv.key.equalsIgnoreCase(SpanConventions.HTTP_STATUS_CODE_KEY) && kv.valueLong != 200) 29 | || (kv.key.equalsIgnoreCase(SpanConventions.IS_ERROR_KEY) && kv.valueBool)); 30 | } 31 | 32 | public void setHttpCode(int code) { 33 | this.tags.add(KeyValue.ofLongType(SpanConventions.HTTP_STATUS_CODE_KEY, (long)code)); 34 | } 35 | 36 | public Integer getHttpCode() { 37 | return tags.stream() 38 | .filter(kv -> kv.key.equalsIgnoreCase(SpanConventions.HTTP_STATUS_CODE_KEY)) 39 | .map(kv -> kv.valueLong.intValue()) 40 | .findFirst().orElse(null); 41 | } 42 | 43 | public void setHttpUrlTag(String url) { 44 | this.tags.add(KeyValue.ofStringType(SpanConventions.HTTP_URL_KEY, url)); 45 | } 46 | 47 | public void setHttpMethodTag(String method) { 48 | this.tags.add(KeyValue.ofStringType(SpanConventions.HTTP_METHOD_KEY, method)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/model/trace/Trace.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.model.trace; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.UUID; 8 | 9 | public class Trace { 10 | public Span rootSpan; 11 | public List spans = new ArrayList<>(); 12 | public Map spanIdToSpan = new HashMap<>(); 13 | public Map> spanIdToOutgoingRefs = new HashMap<>(); 14 | 15 | public void addSpan(Span span) { 16 | this.spans.add(span); 17 | this.spanIdToSpan.put(span.id, span); 18 | } 19 | 20 | public void addRefs() { 21 | for (Span span : spans) { 22 | for (Reference ref : span.refs) { 23 | this.spanIdToOutgoingRefs.computeIfAbsent(ref.fromSpanId, id -> new ArrayList<>()) 24 | .add(ref); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/ITraceEmitter.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | import io.omnition.loadgenerator.model.trace.Trace; 4 | 5 | public interface ITraceEmitter { 6 | 7 | String emit(Trace trace); 8 | 9 | void close(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/JaegerTraceEmitter.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLDecoder; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.UUID; 8 | import java.util.function.Consumer; 9 | 10 | import io.jaegertracing.reporters.Reporter; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.apache.commons.lang3.mutable.MutableObject; 13 | import org.apache.log4j.Logger; 14 | 15 | import io.jaegertracing.Tracer; 16 | import io.jaegertracing.Tracer.Builder; 17 | import io.jaegertracing.reporters.RemoteReporter; 18 | import io.jaegertracing.samplers.ConstSampler; 19 | import io.jaegertracing.senders.HttpSender; 20 | import io.omnition.loadgenerator.model.trace.KeyValue; 21 | import io.omnition.loadgenerator.model.trace.Service; 22 | import io.omnition.loadgenerator.model.trace.Span; 23 | import io.omnition.loadgenerator.model.trace.Trace; 24 | import io.opentracing.propagation.Format; 25 | import io.opentracing.propagation.TextMapInjectAdapter; 26 | 27 | public class JaegerTraceEmitter implements ITraceEmitter { 28 | 29 | private final static Logger logger = Logger.getLogger(JaegerTraceEmitter.class); 30 | 31 | private final Map serviceNameToTracer = new HashMap<>(); 32 | private final String collectorUrl; 33 | private final int flushIntervalMillis; 34 | 35 | public JaegerTraceEmitter(String collectorUrl, int flushIntervalMillis) { 36 | this.collectorUrl = collectorUrl; 37 | this.flushIntervalMillis = flushIntervalMillis; 38 | } 39 | 40 | @Override 41 | public void close() { 42 | serviceNameToTracer.forEach((name, tracer) -> tracer.close()); 43 | } 44 | 45 | public String emit(Trace trace) { 46 | final MutableObject traceId = new MutableObject<>(null); 47 | final Map convertedSpans = new HashMap<>(); 48 | Consumer createOtSpan = span -> { 49 | boolean extract = StringUtils.isEmpty(traceId.getValue()); 50 | Tracer tracer = getTracer(span.service); 51 | io.opentracing.Span otSpan = OpenTracingTraceConverter.createOTSpan( 52 | tracer, span, convertedSpans 53 | ); 54 | convertedSpans.put(span.id, otSpan); 55 | if (extract) { 56 | traceId.setValue(extractTraceId(tracer, otSpan)); 57 | } 58 | }; 59 | Consumer closeOtSpan = span -> { 60 | // mark span as closed 61 | convertedSpans.get(span.id).finish(span.endTimeMicros); 62 | }; 63 | TraceTraversal.prePostOrder(trace, createOtSpan, closeOtSpan); 64 | return traceId.getValue(); 65 | } 66 | 67 | /** 68 | * This extracts the jaeger-header traceID from an opentracing span. 69 | * 70 | * @param tracer tracer to use to extract the jaeger header trace id 71 | * @param otSpan span from which to extract the traceID 72 | * @return string traceID or null if could not decode 73 | */ 74 | private String extractTraceId(Tracer tracer, io.opentracing.Span otSpan) { 75 | HashMap baggage = new HashMap<>(); 76 | TextMapInjectAdapter map = new TextMapInjectAdapter(baggage); 77 | tracer.inject(otSpan.context(), Format.Builtin.HTTP_HEADERS, map); 78 | try { 79 | String encodedTraceId = URLDecoder.decode(baggage.get("uber-trace-id"), "UTF-8"); 80 | return encodedTraceId.split(":")[0]; 81 | } catch (UnsupportedEncodingException e) { 82 | logger.error(e); 83 | return null; 84 | } 85 | } 86 | 87 | private Tracer getTracer(Service service) { 88 | return this.serviceNameToTracer.computeIfAbsent(service.serviceName, 89 | s -> createJaegerTracer(collectorUrl, service, flushIntervalMillis)); 90 | } 91 | 92 | private static Tracer createJaegerTracer(String collectorUrl, Service svc, int flushIntervalMillis) { 93 | HttpSender sender = new HttpSender.Builder(collectorUrl + "/api/traces").build(); 94 | Reporter reporter = new RemoteReporter.Builder().withSender(sender) 95 | .withMaxQueueSize(100000) 96 | .withFlushInterval(flushIntervalMillis) 97 | .build(); 98 | Builder bld = new Builder(svc.serviceName).withReporter(reporter) 99 | .withSampler(new ConstSampler(true)); 100 | for (KeyValue kv : svc.tags) { 101 | bld.withTag(kv.key, kv.valueString); 102 | } 103 | return bld.build(); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/LogLevel.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | public enum LogLevel { 4 | Silent, Minimum, Verbose 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/OpenTracingTraceConverter.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | import io.omnition.loadgenerator.model.trace.KeyValue; 4 | import io.omnition.loadgenerator.model.trace.Span; 5 | import io.opentracing.Tracer; 6 | 7 | import java.util.Map; 8 | import java.util.UUID; 9 | 10 | public final class OpenTracingTraceConverter { 11 | 12 | private OpenTracingTraceConverter() {} 13 | 14 | public static io.opentracing.Span createOTSpan( 15 | Tracer tracer, Span span, Map parentSpans 16 | ) { 17 | Tracer.SpanBuilder otSpanBuilder = tracer.buildSpan(span.operationName) 18 | .withStartTimestamp(span.startTimeMicros); 19 | for (KeyValue tag : span.tags) { 20 | otSpanBuilder = addModelTag(tag, otSpanBuilder); 21 | } 22 | final Tracer.SpanBuilder finalSpanBuilder = otSpanBuilder; 23 | span.refs.forEach(ref -> { 24 | switch (ref.refType) { 25 | case CHILD_OF: 26 | finalSpanBuilder.addReference( 27 | io.opentracing.References.CHILD_OF, 28 | parentSpans.get(ref.fromSpanId).context() 29 | ); 30 | break; 31 | case FOLLOWS_FROM: 32 | finalSpanBuilder.addReference( 33 | io.opentracing.References.FOLLOWS_FROM, 34 | parentSpans.get(ref.fromSpanId).context() 35 | ); 36 | break; 37 | default: 38 | break; 39 | } 40 | }); 41 | return finalSpanBuilder.start(); 42 | } 43 | 44 | private static Tracer.SpanBuilder addModelTag(KeyValue tag, Tracer.SpanBuilder otSpanBld) { 45 | if (tag.valueType.equalsIgnoreCase(KeyValue.STRING_VALUE_TYPE)) { 46 | otSpanBld = otSpanBld.withTag(tag.key, tag.valueString); 47 | } else if (tag.valueType.equalsIgnoreCase(KeyValue.BOOLEAN_VALUE_TYPE)) { 48 | otSpanBld = otSpanBld.withTag(tag.key, tag.valueBool); 49 | } else if (tag.valueType.equalsIgnoreCase(KeyValue.LONG_VALUE_TYPE)) { 50 | otSpanBld = otSpanBld.withTag(tag.key, tag.valueLong); 51 | } // other types are ignored for now 52 | return otSpanBld; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/ScheduledTraceGenerator.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.ScheduledExecutorService; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import org.apache.log4j.Logger; 9 | 10 | import io.omnition.loadgenerator.model.topology.Topology; 11 | import io.omnition.loadgenerator.model.trace.Trace; 12 | 13 | public class ScheduledTraceGenerator { 14 | private static final Logger logger = Logger.getLogger(ScheduledTraceGenerator.class); 15 | private static final long GRACEFUL_SHUTDOWN_TIME_SEC = 5; 16 | private static final int NUM_THREADS = 5; 17 | private final ScheduledExecutorService scheduler; 18 | 19 | private final int tracesPerHour; 20 | private final Topology topology; 21 | private final String service; 22 | private final String route; 23 | private final ITraceEmitter emitter; 24 | 25 | private final LogLevel logLevel; 26 | private final SummaryLogger summaryLogger; 27 | 28 | public ScheduledTraceGenerator( 29 | Topology topology, 30 | String service, 31 | String route, 32 | int tracesPerHour, 33 | ITraceEmitter emitter, 34 | LogLevel logLevel, 35 | SummaryLogger summaryLogger 36 | ) { 37 | this.scheduler = Executors.newScheduledThreadPool(NUM_THREADS); 38 | this.tracesPerHour = tracesPerHour; 39 | this.topology = topology; 40 | this.service = service; 41 | this.route = route; 42 | this.emitter = emitter; 43 | this.logLevel = logLevel; 44 | this.summaryLogger = summaryLogger; 45 | } 46 | 47 | public void start() { 48 | logger.info(String.format("Starting trace generation for service %s, route %s, %d traces/hr", 49 | this.service, this.route, this.tracesPerHour)); 50 | scheduler.scheduleAtFixedRate(() -> emitOneTrace(), TimeUnit.SECONDS.toMillis(1), 51 | TimeUnit.HOURS.toMillis(1) / this.tracesPerHour, TimeUnit.MILLISECONDS); 52 | } 53 | 54 | public void shutdown() { 55 | logger.info("Shutting down..."); 56 | scheduler.shutdown(); 57 | this.summaryLogger.flush(); 58 | try { 59 | if (!scheduler.awaitTermination(GRACEFUL_SHUTDOWN_TIME_SEC, TimeUnit.SECONDS)) { 60 | logger.error("Executor did not terminate in the specified time."); 61 | List droppedTasks = scheduler.shutdownNow(); // optional ** 62 | logger.error( 63 | "Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); 64 | } else { 65 | logger.info("Graceful shutdown completed"); 66 | } 67 | } catch (InterruptedException e) { 68 | logger.error("Interrupted while waiting for termination", e); 69 | } 70 | } 71 | 72 | public void awaitTermination() throws InterruptedException { 73 | scheduler.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); 74 | } 75 | 76 | private void emitOneTrace() { 77 | try { 78 | long now = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); 79 | Trace trace = TraceGenerator.generate(this.topology, this.service, this.route, now); 80 | String traceId = this.emitter.emit(trace); 81 | 82 | if (this.logLevel == LogLevel.Verbose) { 83 | logger.info(String.format("Emitted traceId %s for service %s route %s", 84 | traceId, this.service, this.route)); 85 | } 86 | if (this.logLevel != LogLevel.Silent) { 87 | this.summaryLogger.logEmit(1, trace.spans.size()); 88 | } 89 | 90 | } catch (Exception e) { 91 | logger.error( 92 | String.format("Error emit trace for service %s route %s, reason: %s", this.service, this.route, e), 93 | e); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/SpanConventions.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | public class SpanConventions { 4 | // Span tags as suggested by OpenTracing semantic conventions here: 5 | // https://github.com/opentracing/specification/blob/master/semantic_conventions.md 6 | public static String HTTP_METHOD_KEY = "http.method"; 7 | public static String HTTP_STATUS_CODE_KEY = "http.status_code"; 8 | public static String HTTP_URL_KEY = "http.url"; 9 | public static String IS_ERROR_KEY = "error"; 10 | 11 | // Omnition conventions 12 | public static String IS_ROOT_CAUSE_ERROR_KEY = "root_cause_error"; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/SummaryLogger.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.apache.log4j.Logger; 6 | 7 | /** 8 | * SummaryLogger maintains global count of emitted traces and periodically 9 | * outputs emitted counters and speed. Intended to be used as a single instance 10 | * per app. 11 | */ 12 | public class SummaryLogger { 13 | // How often to print summary log. 1 second. 14 | private static final long SUM_LOG_PERIOD_NANOSEC = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS); 15 | 16 | // Last time summary log was printed, in monotinic nanoseconds 17 | private long lastSumLogTimestampNano; 18 | 19 | // Helper class to store emit counters (last since logged and total). 20 | class EmitCounters { 21 | public long emitsSinceLastSumLog; 22 | public long totalEmits; 23 | 24 | public void add(long counter) { 25 | emitsSinceLastSumLog += counter; 26 | totalEmits += counter; 27 | } 28 | 29 | public void copyFrom(EmitCounters from) { 30 | emitsSinceLastSumLog = from.emitsSinceLastSumLog; 31 | totalEmits = from.totalEmits; 32 | } 33 | 34 | public void resetLast() { 35 | emitsSinceLastSumLog = 0; 36 | } 37 | 38 | public double calcRate(double elapsedSec) { 39 | return elapsedSec > 0 ? (double) emitsSinceLastSumLog / elapsedSec : 0; 40 | } 41 | 42 | public String getPrintable(double elapsedSec) { 43 | return String.format("%d total, %d new, %.2f per second", totalEmits, emitsSinceLastSumLog, 44 | calcRate(elapsedSec)); 45 | } 46 | } 47 | 48 | // Count of traces emited since summary log last printed 49 | private EmitCounters traceCounters = new EmitCounters(); 50 | private EmitCounters spanCounters = new EmitCounters(); 51 | 52 | private final Logger logger; 53 | 54 | public SummaryLogger(Logger logger) { 55 | this.logger = logger; 56 | } 57 | 58 | public void flush() { 59 | logEmit(0, 0, true); 60 | } 61 | 62 | /** 63 | * Count emitted traces and output summary if enough time elapsed since last 64 | * output. This function is thread safe and can be called from multipler trace 65 | * emitters concurrently. 66 | */ 67 | public void logEmit(long emittedTraces, long emittedSpans) { 68 | logEmit(emittedTraces, emittedSpans, false); 69 | } 70 | 71 | public void logEmit(long emittedTraces, long emittedSpans, boolean flushLog) { 72 | // Keep data that will be logged in local variables that will live after the 73 | // synchornized block. 74 | boolean shouldEmitLog = false; 75 | double elapsedSec = 0; 76 | EmitCounters traceCounters = new EmitCounters(); 77 | EmitCounters spanCounters = new EmitCounters(); 78 | 79 | // Check if logging is needed and obtain data to log. 80 | synchronized (this) { 81 | // Count trace and spans emitted. 82 | this.traceCounters.add(emittedTraces); 83 | this.spanCounters.add(emittedSpans); 84 | 85 | boolean forceLog = false; 86 | if (flushLog && (this.traceCounters.emitsSinceLastSumLog!=0 || this.spanCounters.emitsSinceLastSumLog!=0)) { 87 | // We were requested to flush the log. We must do it if any of the counters are changed since last log. 88 | forceLog = true; 89 | } 90 | 91 | // Copy counters to local variables to be used outside synchronized block. 92 | traceCounters.copyFrom(this.traceCounters); 93 | spanCounters.copyFrom(this.spanCounters); 94 | 95 | // Check if it is time to output summary. Use nanoTime() as monotonic clock. 96 | long curNanoTime = System.nanoTime(); 97 | long elapsedNano = curNanoTime - lastSumLogTimestampNano; 98 | 99 | if (elapsedNano >= SummaryLogger.SUM_LOG_PERIOD_NANOSEC || forceLog) { 100 | // Calculate elapsed time in seconds since last output. 101 | // Using ugly division since TimeUnit does not support floating point 102 | // operations. 103 | elapsedSec = (double) elapsedNano / 1_000_000_000; 104 | 105 | // Reset counters to start measuring next cycle. 106 | lastSumLogTimestampNano = curNanoTime; 107 | this.traceCounters.resetLast(); 108 | this.spanCounters.resetLast(); 109 | 110 | shouldEmitLog = true; 111 | } 112 | } 113 | 114 | // Emit the log outsize synchronized block to avoid contention of other callers 115 | // on logging. 116 | if (shouldEmitLog) { 117 | logger.info(String.format("Emitted Traces: " + traceCounters.getPrintable(elapsedSec) + ", " + "Spans: " 118 | + spanCounters.getPrintable(elapsedSec))); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/TraceGenerator.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Objects; 7 | import java.util.Random; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicLong; 10 | import java.util.stream.Collectors; 11 | 12 | import io.omnition.loadgenerator.model.topology.ServiceRoute; 13 | import io.omnition.loadgenerator.model.topology.ServiceTier; 14 | import io.omnition.loadgenerator.model.topology.TagGenerator; 15 | import io.omnition.loadgenerator.model.topology.TagSet; 16 | import io.omnition.loadgenerator.model.topology.Topology; 17 | import io.omnition.loadgenerator.model.trace.KeyValue; 18 | import io.omnition.loadgenerator.model.trace.Reference; 19 | import io.omnition.loadgenerator.model.trace.Reference.RefType; 20 | import io.omnition.loadgenerator.model.trace.Service; 21 | import io.omnition.loadgenerator.model.trace.Span; 22 | import io.omnition.loadgenerator.model.trace.Trace; 23 | 24 | public class TraceGenerator { 25 | private final Random random = new Random(); 26 | private final Trace trace = new Trace(); 27 | private Topology topology; 28 | 29 | private static final AtomicLong sequenceNumber = new AtomicLong(1); 30 | 31 | public static Trace generate(Topology topology, String rootServiceName, String rootRouteName, long startTimeMicros) { 32 | TraceGenerator gen = new TraceGenerator(topology); 33 | ServiceTier rootService = gen.topology.getServiceTier(rootServiceName); 34 | Span rootSpan = gen.createSpanForServiceRouteCall(null, rootService, rootRouteName, startTimeMicros); 35 | gen.trace.rootSpan = rootSpan; 36 | gen.trace.addRefs(); 37 | return gen.trace; 38 | } 39 | 40 | private TraceGenerator(Topology topology) { 41 | this.topology = topology; 42 | } 43 | 44 | private Span createSpanForServiceRouteCall(TagSet parentTagSet, ServiceTier serviceTier, String routeName, long startTimeMicros) { 45 | String instanceName = serviceTier.instances.get( 46 | random.nextInt(serviceTier.instances.size())); 47 | ServiceRoute route = serviceTier.getRoute(routeName); 48 | 49 | // send tags of serviceTier and serviceTier instance 50 | Service service = new Service(serviceTier.serviceName, instanceName, new ArrayList<>()); 51 | Span span = new Span(); 52 | span.startTimeMicros = startTimeMicros; 53 | span.operationName = route.route; 54 | span.service = service; 55 | span.tags.add(KeyValue.ofLongType("load_generator.seq_num", sequenceNumber.getAndIncrement())); 56 | 57 | // Setup base tags 58 | span.setHttpMethodTag("GET"); 59 | span.setHttpUrlTag("http://" + serviceTier.serviceName + routeName); 60 | // Get additional tags for this route, and update with any inherited tags 61 | TagSet routeTags = serviceTier.getTagSet(routeName); 62 | HashMap tagsToSet = new HashMap<>(routeTags.tags); 63 | for (TagGenerator tagGenerator : routeTags.tagGenerators) { 64 | tagsToSet.putAll(tagGenerator.generateTags()); 65 | } 66 | if (parentTagSet != null && routeTags.inherit != null) { 67 | for (String inheritTagKey : routeTags.inherit) { 68 | Object value = parentTagSet.tags.get(inheritTagKey); 69 | if (value != null) { 70 | tagsToSet.put(inheritTagKey, value); 71 | } 72 | } 73 | } 74 | 75 | // Set the additional tags on the span 76 | List spanTags = tagsToSet.entrySet().stream() 77 | .map(t -> { 78 | Object val = t.getValue(); 79 | if (val instanceof String) { 80 | return KeyValue.ofStringType(t.getKey(), (String) val); 81 | } 82 | if (val instanceof Double) { 83 | return KeyValue.ofLongType(t.getKey(), ((Double) val).longValue()); 84 | } 85 | if (val instanceof Boolean) { 86 | return KeyValue.ofBooleanType(t.getKey(), (Boolean) val); 87 | } 88 | return null; 89 | }) 90 | .filter(Objects::nonNull) 91 | .collect(Collectors.toList()); 92 | span.tags.addAll(spanTags); 93 | 94 | final AtomicLong maxEndTime = new AtomicLong(startTimeMicros); 95 | if (span.isErrorSpan()) { 96 | // inject root cause error and terminate trace there 97 | span.markRootCauseError(); 98 | } else { 99 | // no error, make downstream calls 100 | route.downstreamCalls.forEach((s, r) -> { 101 | long childStartTimeMicros = startTimeMicros + TimeUnit.MILLISECONDS.toMicros(random.nextInt(route.maxLatencyMillis)); 102 | ServiceTier childSvc = this.topology.getServiceTier(s); 103 | Span childSpan = createSpanForServiceRouteCall(routeTags, childSvc, r, childStartTimeMicros); 104 | Reference ref = new Reference(RefType.CHILD_OF, span.id, childSpan.id); 105 | childSpan.refs.add(ref); 106 | maxEndTime.set(Math.max(maxEndTime.get(), childSpan.endTimeMicros)); 107 | if (childSpan.isErrorSpan()) { 108 | Integer httpCode = childSpan.getHttpCode(); 109 | if (httpCode != null) { 110 | span.setHttpCode(httpCode); 111 | } 112 | span.markError(); 113 | } 114 | }); 115 | } 116 | long ownDuration = TimeUnit.MILLISECONDS.toMicros((long)this.random.nextInt(route.maxLatencyMillis)); 117 | span.endTimeMicros = maxEndTime.get() + ownDuration; 118 | trace.addSpan(span); 119 | return span; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/TraceTraversal.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | import java.util.List; 4 | import java.util.function.Consumer; 5 | 6 | import io.omnition.loadgenerator.model.trace.Reference; 7 | import io.omnition.loadgenerator.model.trace.Span; 8 | import io.omnition.loadgenerator.model.trace.Trace; 9 | 10 | public class TraceTraversal { 11 | public static void prePostOrder(Trace trace, Consumer preVisitConsumer, Consumer postVisitConsumer) { 12 | prePostOrder(trace, trace.rootSpan, preVisitConsumer, postVisitConsumer); 13 | } 14 | 15 | private static void prePostOrder( 16 | Trace trace, 17 | Span span, 18 | Consumer preVisitConsumer, 19 | Consumer postVisitConsumer 20 | ) { 21 | preVisitConsumer.accept(span); 22 | List outgoing = trace.spanIdToOutgoingRefs.get(span.id); 23 | if (outgoing != null) { 24 | outgoing.stream() 25 | .map(ref -> trace.spanIdToSpan.get(ref.toSpanId)) 26 | .forEach(descendant -> prePostOrder(trace, descendant, preVisitConsumer, postVisitConsumer)); 27 | } 28 | postVisitConsumer.accept(span); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/omnition/loadgenerator/util/ZipkinTraceEmitter.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator.util; 2 | 3 | import brave.Tracing; 4 | import brave.opentracing.BraveTracer; 5 | import brave.propagation.B3Propagation; 6 | import brave.propagation.Propagation; 7 | import brave.sampler.Sampler; 8 | import io.omnition.loadgenerator.model.trace.Service; 9 | import io.omnition.loadgenerator.model.trace.Trace; 10 | import io.opentracing.Span; 11 | import io.opentracing.Tracer; 12 | import io.opentracing.propagation.Format; 13 | import io.opentracing.propagation.TextMapInjectAdapter; 14 | import org.apache.commons.lang3.StringUtils; 15 | import org.apache.commons.lang3.mutable.MutableObject; 16 | import org.apache.log4j.Logger; 17 | 18 | import zipkin2.codec.Encoding; 19 | import zipkin2.codec.SpanBytesEncoder; 20 | import zipkin2.reporter.AsyncReporter; 21 | import zipkin2.reporter.Reporter; 22 | import zipkin2.reporter.Sender; 23 | import zipkin2.reporter.okhttp3.OkHttpSender; 24 | 25 | import java.io.UnsupportedEncodingException; 26 | import java.net.URLDecoder; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | import java.util.UUID; 30 | import java.util.function.Consumer; 31 | 32 | public class ZipkinTraceEmitter implements ITraceEmitter { 33 | 34 | private static final Logger logger = Logger.getLogger(ZipkinTraceEmitter.class); 35 | private static final String V2_API = "/api/v2/spans"; 36 | private static final String V1_API = "/api/v1/spans"; 37 | 38 | private final Map serviceNameToTracer = new HashMap<>(); 39 | 40 | private String collectorURL; 41 | private SpanBytesEncoder spanBytesEncoder; 42 | 43 | public ZipkinTraceEmitter(String collectorURL, SpanBytesEncoder spanBytesEncoder) { 44 | this.collectorURL = collectorURL; 45 | this.spanBytesEncoder = spanBytesEncoder; 46 | } 47 | 48 | @Override 49 | public String emit(Trace trace) { 50 | final MutableObject traceId = new MutableObject<>(null); 51 | final Map convertedSpans = new HashMap<>(); 52 | Consumer createOtSpan = span -> { 53 | boolean extract = StringUtils.isEmpty(traceId.getValue()); 54 | BraveTracer tracer = getTracer(span.service); 55 | io.opentracing.Span otSpan = OpenTracingTraceConverter.createOTSpan( 56 | tracer, span, convertedSpans 57 | ); 58 | convertedSpans.put(span.id, otSpan); 59 | if (extract) { 60 | traceId.setValue(extractTraceID(tracer, otSpan)); 61 | } 62 | }; 63 | Consumer closeOtSpan = span -> { 64 | // mark span as closed 65 | convertedSpans.get(span.id).finish(span.endTimeMicros); 66 | }; 67 | TraceTraversal.prePostOrder(trace, createOtSpan, closeOtSpan); 68 | return traceId.getValue(); 69 | } 70 | 71 | @Override 72 | public void close() { 73 | // No close available. 74 | } 75 | 76 | private String extractTraceID(Tracer tracer, Span otSpan) { 77 | HashMap baggage = new HashMap<>(); 78 | TextMapInjectAdapter map = new TextMapInjectAdapter(baggage); 79 | tracer.inject(otSpan.context(), Format.Builtin.HTTP_HEADERS, map); 80 | try { 81 | String encodedTraceId = URLDecoder.decode(baggage.get("X-B3-TraceId"), "UTF-8"); 82 | return encodedTraceId.split(":")[0]; 83 | } catch (UnsupportedEncodingException e) { 84 | logger.error(e); 85 | return null; 86 | } 87 | } 88 | 89 | private BraveTracer getTracer(Service service) { 90 | return this.serviceNameToTracer.computeIfAbsent(service.serviceName, 91 | s -> createBraveTracer(collectorURL, service)); 92 | } 93 | 94 | private BraveTracer createBraveTracer(String collectorUrl, Service svc) { 95 | Encoding encoding = Encoding.JSON; 96 | String queryPath = V2_API; 97 | switch (this.spanBytesEncoder) { 98 | case JSON_V1: 99 | queryPath = V1_API; 100 | break; 101 | case THRIFT: 102 | encoding = Encoding.THRIFT; 103 | queryPath = V1_API; 104 | break; 105 | case JSON_V2: 106 | break; 107 | case PROTO3: 108 | encoding = Encoding.PROTO3; 109 | break; 110 | } 111 | 112 | Sender sender = OkHttpSender.newBuilder().encoding(encoding).endpoint(collectorUrl + queryPath).build(); 113 | Reporter spanReporter = AsyncReporter.builder(sender).build(this.spanBytesEncoder); 114 | Propagation.Factory propagationFactory = B3Propagation.FACTORY; 115 | Tracing.Builder braveTracingB = Tracing.newBuilder() 116 | .localServiceName(svc.serviceName) 117 | .propagationFactory(propagationFactory) 118 | .spanReporter(spanReporter) 119 | .sampler(Sampler.ALWAYS_SAMPLE); 120 | Tracing braveTracing = braveTracingB.build(); 121 | return BraveTracer.create(braveTracing); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/resources/adjectives.txt: -------------------------------------------------------------------------------- 1 | abandoned 2 | able 3 | absolute 4 | adorable 5 | adventurous 6 | academic 7 | acceptable 8 | acclaimed 9 | accomplished 10 | accurate 11 | aching 12 | acidic 13 | acrobatic 14 | active 15 | actual 16 | adept 17 | admirable 18 | admired 19 | adolescent 20 | adorable 21 | adored 22 | advanced 23 | afraid 24 | affectionate 25 | aged 26 | aggravating 27 | aggressive 28 | agile 29 | agitated 30 | agonizing 31 | agreeable 32 | ajar 33 | alarmed 34 | alarming 35 | alert 36 | alienated 37 | alive 38 | all 39 | altruistic 40 | amazing 41 | ambitious 42 | ample 43 | amused 44 | amusing 45 | anchored 46 | ancient 47 | angelic 48 | angry 49 | anguished 50 | animated 51 | annual 52 | another 53 | antique 54 | anxious 55 | any 56 | apprehensive 57 | appropriate 58 | apt 59 | arctic 60 | arid 61 | aromatic 62 | artistic 63 | ashamed 64 | assured 65 | astonishing 66 | athletic 67 | attached 68 | attentive 69 | attractive 70 | austere 71 | authentic 72 | authorized 73 | automatic 74 | avaricious 75 | average 76 | aware 77 | awesome 78 | awful 79 | awkward 80 | babyish 81 | bad 82 | back 83 | baggy 84 | bare 85 | barren 86 | basic 87 | beautiful 88 | belated 89 | beloved 90 | beneficial 91 | better 92 | best 93 | bewitched 94 | big 95 | big-hearted 96 | biodegradable 97 | bite-sized 98 | bitter 99 | black 100 | black-and-white 101 | bland 102 | blank 103 | blaring 104 | bleak 105 | blind 106 | blissful 107 | blond 108 | blue 109 | blushing 110 | bogus 111 | boiling 112 | bold 113 | bony 114 | boring 115 | bossy 116 | both 117 | bouncy 118 | bountiful 119 | bowed 120 | brave 121 | breakable 122 | brief 123 | bright 124 | brilliant 125 | brisk 126 | broken 127 | bronze 128 | brown 129 | bruised 130 | bubbly 131 | bulky 132 | bumpy 133 | buoyant 134 | burdensome 135 | burly 136 | bustling 137 | busy 138 | buttery 139 | buzzing 140 | calculating 141 | calm 142 | candid 143 | canine 144 | capital 145 | carefree 146 | careful 147 | careless 148 | caring 149 | cautious 150 | cavernous 151 | celebrated 152 | charming 153 | cheap 154 | cheerful 155 | cheery 156 | chief 157 | chilly 158 | chubby 159 | circular 160 | classic 161 | clean 162 | clear 163 | clear-cut 164 | clever 165 | close 166 | closed 167 | cloudy 168 | clueless 169 | clumsy 170 | cluttered 171 | coarse 172 | cold 173 | colorful 174 | colorless 175 | colossal 176 | comfortable 177 | common 178 | compassionate 179 | competent 180 | complete 181 | complex 182 | complicated 183 | composed 184 | concerned 185 | concrete 186 | confused 187 | conscious 188 | considerate 189 | constant 190 | content 191 | conventional 192 | cooked 193 | cool 194 | cooperative 195 | coordinated 196 | corny 197 | corrupt 198 | costly 199 | courageous 200 | courteous 201 | crafty 202 | crazy 203 | creamy 204 | creative 205 | creepy 206 | criminal 207 | crisp 208 | critical 209 | crooked 210 | crowded 211 | cruel 212 | crushing 213 | cuddly 214 | cultivated 215 | cultured 216 | cumbersome 217 | curly 218 | curvy 219 | cute 220 | cylindrical 221 | damaged 222 | damp 223 | dangerous 224 | dapper 225 | daring 226 | darling 227 | dark 228 | dazzling 229 | dead 230 | deadly 231 | deafening 232 | dear 233 | dearest 234 | decent 235 | decimal 236 | decisive 237 | deep 238 | defenseless 239 | defensive 240 | defiant 241 | deficient 242 | definite 243 | definitive 244 | delayed 245 | delectable 246 | delicious 247 | delightful 248 | delirious 249 | demanding 250 | dense 251 | dental 252 | dependable 253 | dependent 254 | descriptive 255 | deserted 256 | detailed 257 | determined 258 | devoted 259 | different 260 | difficult 261 | digital 262 | diligent 263 | dim 264 | dimpled 265 | dimwitted 266 | direct 267 | disastrous 268 | discrete 269 | disfigured 270 | disgusting 271 | disloyal 272 | dismal 273 | distant 274 | downright 275 | dreary 276 | dirty 277 | disguised 278 | dishonest 279 | dismal 280 | distant 281 | distinct 282 | distorted 283 | dizzy 284 | dopey 285 | doting 286 | double 287 | downright 288 | drab 289 | drafty 290 | dramatic 291 | dreary 292 | droopy 293 | dry 294 | dual 295 | dull 296 | dutiful 297 | each 298 | eager 299 | earnest 300 | early 301 | easy 302 | easy-going 303 | ecstatic 304 | edible 305 | educated 306 | elaborate 307 | elastic 308 | elated 309 | elderly 310 | electric 311 | elegant 312 | elementary 313 | elliptical 314 | embarrassed 315 | embellished 316 | eminent 317 | emotional 318 | empty 319 | enchanted 320 | enchanting 321 | energetic 322 | enlightened 323 | enormous 324 | enraged 325 | entire 326 | envious 327 | equal 328 | equatorial 329 | essential 330 | esteemed 331 | ethical 332 | euphoric 333 | even 334 | evergreen 335 | everlasting 336 | every 337 | evil 338 | exalted 339 | excellent 340 | exemplary 341 | exhausted 342 | excitable 343 | excited 344 | exciting 345 | exotic 346 | expensive 347 | experienced 348 | expert 349 | extraneous 350 | extroverted 351 | extra-large 352 | extra-small 353 | fabulous 354 | failing 355 | faint 356 | fair 357 | faithful 358 | fake 359 | false 360 | familiar 361 | famous 362 | fancy 363 | fantastic 364 | far 365 | faraway 366 | far-flung 367 | far-off 368 | fast 369 | fat 370 | fatal 371 | fatherly 372 | favorable 373 | favorite 374 | fearful 375 | fearless 376 | feisty 377 | feline 378 | female 379 | feminine 380 | few 381 | fickle 382 | filthy 383 | fine 384 | finished 385 | firm 386 | first 387 | firsthand 388 | fitting 389 | fixed 390 | flaky 391 | flamboyant 392 | flashy 393 | flat 394 | flawed 395 | flawless 396 | flickering 397 | flimsy 398 | flippant 399 | flowery 400 | fluffy 401 | fluid 402 | flustered 403 | focused 404 | fond 405 | foolhardy 406 | foolish 407 | forceful 408 | forked 409 | formal 410 | forsaken 411 | forthright 412 | fortunate 413 | fragrant 414 | frail 415 | frank 416 | frayed 417 | free 418 | French 419 | fresh 420 | frequent 421 | friendly 422 | frightened 423 | frightening 424 | frigid 425 | frilly 426 | frizzy 427 | frivolous 428 | front 429 | frosty 430 | frozen 431 | frugal 432 | fruitful 433 | full 434 | fumbling 435 | functional 436 | funny 437 | fussy 438 | fuzzy 439 | gargantuan 440 | gaseous 441 | general 442 | generous 443 | gentle 444 | genuine 445 | giant 446 | giddy 447 | gigantic 448 | gifted 449 | giving 450 | glamorous 451 | glaring 452 | glass 453 | gleaming 454 | gleeful 455 | glistening 456 | glittering 457 | gloomy 458 | glorious 459 | glossy 460 | glum 461 | golden 462 | good 463 | good-natured 464 | gorgeous 465 | graceful 466 | gracious 467 | grand 468 | grandiose 469 | granular 470 | grateful 471 | grave 472 | gray 473 | great 474 | greedy 475 | green 476 | gregarious 477 | grim 478 | grimy 479 | gripping 480 | grizzled 481 | gross 482 | grotesque 483 | grouchy 484 | grounded 485 | growing 486 | growling 487 | grown 488 | grubby 489 | gruesome 490 | grumpy 491 | guilty 492 | gullible 493 | gummy 494 | hairy 495 | half 496 | handmade 497 | handsome 498 | handy 499 | happy 500 | happy-go-lucky 501 | hard 502 | hard-to-find 503 | harmful 504 | harmless 505 | harmonious 506 | harsh 507 | hasty 508 | hateful 509 | haunting 510 | healthy 511 | heartfelt 512 | hearty 513 | heavenly 514 | heavy 515 | hefty 516 | helpful 517 | helpless 518 | hidden 519 | hideous 520 | high 521 | high-level 522 | hilarious 523 | hoarse 524 | hollow 525 | homely 526 | honest 527 | honorable 528 | honored 529 | hopeful 530 | horrible 531 | hospitable 532 | hot 533 | huge 534 | humble 535 | humiliating 536 | humming 537 | humongous 538 | hungry 539 | hurtful 540 | husky 541 | icky 542 | icy 543 | ideal 544 | idealistic 545 | identical 546 | idle 547 | idiotic 548 | idolized 549 | ignorant 550 | ill 551 | illegal 552 | ill-fated 553 | ill-informed 554 | illiterate 555 | illustrious 556 | imaginary 557 | imaginative 558 | immaculate 559 | immaterial 560 | immediate 561 | immense 562 | impassioned 563 | impeccable 564 | impartial 565 | imperfect 566 | imperturbable 567 | impish 568 | impolite 569 | important 570 | impossible 571 | impractical 572 | impressionable 573 | impressive 574 | improbable 575 | impure 576 | inborn 577 | incomparable 578 | incompatible 579 | incomplete 580 | inconsequential 581 | incredible 582 | indelible 583 | inexperienced 584 | indolent 585 | infamous 586 | infantile 587 | infatuated 588 | inferior 589 | infinite 590 | informal 591 | innocent 592 | insecure 593 | insidious 594 | insignificant 595 | insistent 596 | instructive 597 | insubstantial 598 | intelligent 599 | intent 600 | intentional 601 | interesting 602 | internal 603 | international 604 | intrepid 605 | ironclad 606 | irresponsible 607 | irritating 608 | itchy 609 | jaded 610 | jagged 611 | jam-packed 612 | jaunty 613 | jealous 614 | jittery 615 | joint 616 | jolly 617 | jovial 618 | joyful 619 | joyous 620 | jubilant 621 | judicious 622 | juicy 623 | jumbo 624 | junior 625 | jumpy 626 | juvenile 627 | kaleidoscopic 628 | keen 629 | key 630 | kind 631 | kindhearted 632 | kindly 633 | klutzy 634 | knobby 635 | knotty 636 | knowledgeable 637 | knowing 638 | known 639 | kooky 640 | kosher 641 | lame 642 | lanky 643 | large 644 | last 645 | lasting 646 | late 647 | lavish 648 | lawful 649 | lazy 650 | leading 651 | lean 652 | leafy 653 | left 654 | legal 655 | legitimate 656 | light 657 | lighthearted 658 | likable 659 | likely 660 | limited 661 | limp 662 | limping 663 | linear 664 | lined 665 | liquid 666 | little 667 | live 668 | lively 669 | livid 670 | loathsome 671 | lone 672 | lonely 673 | long 674 | long-term 675 | loose 676 | lopsided 677 | lost 678 | loud 679 | lovable 680 | lovely 681 | loving 682 | low 683 | loyal 684 | lucky 685 | lumbering 686 | luminous 687 | lumpy 688 | lustrous 689 | luxurious 690 | mad 691 | made-up 692 | magnificent 693 | majestic 694 | major 695 | male 696 | mammoth 697 | married 698 | marvelous 699 | masculine 700 | massive 701 | mature 702 | meager 703 | mealy 704 | mean 705 | measly 706 | meaty 707 | medical 708 | mediocre 709 | medium 710 | meek 711 | mellow 712 | melodic 713 | memorable 714 | menacing 715 | merry 716 | messy 717 | metallic 718 | mild 719 | milky 720 | mindless 721 | miniature 722 | minor 723 | minty 724 | miserable 725 | miserly 726 | misguided 727 | misty 728 | mixed 729 | modern 730 | modest 731 | moist 732 | monstrous 733 | monthly 734 | monumental 735 | moral 736 | mortified 737 | motherly 738 | motionless 739 | mountainous 740 | muddy 741 | muffled 742 | multicolored 743 | mundane 744 | murky 745 | mushy 746 | musty 747 | muted 748 | mysterious 749 | naive 750 | narrow 751 | nasty 752 | natural 753 | naughty 754 | nautical 755 | near 756 | neat 757 | necessary 758 | needy 759 | negative 760 | neglected 761 | negligible 762 | neighboring 763 | nervous 764 | new 765 | next 766 | nice 767 | nifty 768 | nimble 769 | nippy 770 | nocturnal 771 | noisy 772 | nonstop 773 | normal 774 | notable 775 | noted 776 | noteworthy 777 | novel 778 | noxious 779 | numb 780 | nutritious 781 | nutty 782 | obedient 783 | obese 784 | oblong 785 | oily 786 | oblong 787 | obvious 788 | occasional 789 | odd 790 | oddball 791 | offbeat 792 | offensive 793 | official 794 | old 795 | old-fashioned 796 | only 797 | open 798 | optimal 799 | optimistic 800 | opulent 801 | orange 802 | orderly 803 | organic 804 | ornate 805 | ornery 806 | ordinary 807 | original 808 | other 809 | our 810 | outlying 811 | outgoing 812 | outlandish 813 | outrageous 814 | outstanding 815 | oval 816 | overcooked 817 | overdue 818 | overjoyed 819 | overlooked 820 | palatable 821 | pale 822 | paltry 823 | parallel 824 | parched 825 | partial 826 | passionate 827 | past 828 | pastel 829 | peaceful 830 | peppery 831 | perfect 832 | perfumed 833 | periodic 834 | perky 835 | personal 836 | pertinent 837 | pesky 838 | pessimistic 839 | petty 840 | phony 841 | physical 842 | piercing 843 | pink 844 | pitiful 845 | plain 846 | plaintive 847 | plastic 848 | playful 849 | pleasant 850 | pleased 851 | pleasing 852 | plump 853 | plush 854 | polished 855 | polite 856 | political 857 | pointed 858 | pointless 859 | poised 860 | poor 861 | popular 862 | portly 863 | posh 864 | positive 865 | possible 866 | potable 867 | powerful 868 | powerless 869 | practical 870 | precious 871 | present 872 | prestigious 873 | pretty 874 | precious 875 | previous 876 | pricey 877 | prickly 878 | primary 879 | prime 880 | pristine 881 | private 882 | prize 883 | probable 884 | productive 885 | profitable 886 | profuse 887 | proper 888 | proud 889 | prudent 890 | punctual 891 | pungent 892 | puny 893 | pure 894 | purple 895 | pushy 896 | putrid 897 | puzzled 898 | puzzling 899 | quaint 900 | qualified 901 | quarrelsome 902 | quarterly 903 | queasy 904 | querulous 905 | questionable 906 | quick 907 | quick-witted 908 | quiet 909 | quintessential 910 | quirky 911 | quixotic 912 | quizzical 913 | radiant 914 | ragged 915 | rapid 916 | rare 917 | rash 918 | raw 919 | recent 920 | reckless 921 | rectangular 922 | ready 923 | real 924 | realistic 925 | reasonable 926 | red 927 | reflecting 928 | regal 929 | regular 930 | reliable 931 | relieved 932 | remarkable 933 | remorseful 934 | remote 935 | repentant 936 | required 937 | respectful 938 | responsible 939 | repulsive 940 | revolving 941 | rewarding 942 | rich 943 | rigid 944 | right 945 | ringed 946 | ripe 947 | roasted 948 | robust 949 | rosy 950 | rotating 951 | rotten 952 | rough 953 | round 954 | rowdy 955 | royal 956 | rubbery 957 | rundown 958 | ruddy 959 | rude 960 | runny 961 | rural 962 | rusty 963 | sad 964 | safe 965 | salty 966 | same 967 | sandy 968 | sane 969 | sarcastic 970 | sardonic 971 | satisfied 972 | scaly 973 | scarce 974 | scared 975 | scary 976 | scented 977 | scholarly 978 | scientific 979 | scornful 980 | scratchy 981 | scrawny 982 | second 983 | secondary 984 | second-hand 985 | secret 986 | self-assured 987 | self-reliant 988 | selfish 989 | sentimental 990 | separate 991 | serene 992 | serious 993 | serpentine 994 | several 995 | severe 996 | shabby 997 | shadowy 998 | shady 999 | shallow 1000 | shameful 1001 | shameless 1002 | sharp 1003 | shimmering 1004 | shiny 1005 | shocked 1006 | shocking 1007 | shoddy 1008 | short 1009 | short-term 1010 | showy 1011 | shrill 1012 | shy 1013 | sick 1014 | silent 1015 | silky 1016 | silly 1017 | silver 1018 | similar 1019 | simple 1020 | simplistic 1021 | sinful 1022 | single 1023 | sizzling 1024 | skeletal 1025 | skinny 1026 | sleepy 1027 | slight 1028 | slim 1029 | slimy 1030 | slippery 1031 | slow 1032 | slushy 1033 | small 1034 | smart 1035 | smoggy 1036 | smooth 1037 | smug 1038 | snappy 1039 | snarling 1040 | sneaky 1041 | sniveling 1042 | snoopy 1043 | sociable 1044 | soft 1045 | soggy 1046 | solid 1047 | somber 1048 | some 1049 | spherical 1050 | sophisticated 1051 | sore 1052 | sorrowful 1053 | soulful 1054 | soupy 1055 | sour 1056 | Spanish 1057 | sparkling 1058 | sparse 1059 | specific 1060 | spectacular 1061 | speedy 1062 | spicy 1063 | spiffy 1064 | spirited 1065 | spiteful 1066 | splendid 1067 | spotless 1068 | spotted 1069 | spry 1070 | square 1071 | squeaky 1072 | squiggly 1073 | stable 1074 | staid 1075 | stained 1076 | stale 1077 | standard 1078 | starchy 1079 | stark 1080 | starry 1081 | steep 1082 | sticky 1083 | stiff 1084 | stimulating 1085 | stingy 1086 | stormy 1087 | straight 1088 | strange 1089 | steel 1090 | strict 1091 | strident 1092 | striking 1093 | striped 1094 | strong 1095 | studious 1096 | stunning 1097 | stupendous 1098 | stupid 1099 | sturdy 1100 | stylish 1101 | subdued 1102 | submissive 1103 | substantial 1104 | subtle 1105 | suburban 1106 | sudden 1107 | sugary 1108 | sunny 1109 | super 1110 | superb 1111 | superficial 1112 | superior 1113 | supportive 1114 | sure-footed 1115 | surprised 1116 | suspicious 1117 | svelte 1118 | sweaty 1119 | sweet 1120 | sweltering 1121 | swift 1122 | sympathetic 1123 | tall 1124 | talkative 1125 | tame 1126 | tan 1127 | tangible 1128 | tart 1129 | tasty 1130 | tattered 1131 | taut 1132 | tedious 1133 | teeming 1134 | tempting 1135 | tender 1136 | tense 1137 | tepid 1138 | terrible 1139 | terrific 1140 | testy 1141 | thankful 1142 | that 1143 | these 1144 | thick 1145 | thin 1146 | third 1147 | thirsty 1148 | this 1149 | thorough 1150 | thorny 1151 | those 1152 | thoughtful 1153 | threadbare 1154 | thrifty 1155 | thunderous 1156 | tidy 1157 | tight 1158 | timely 1159 | tinted 1160 | tiny 1161 | tired 1162 | torn 1163 | total 1164 | tough 1165 | traumatic 1166 | treasured 1167 | tremendous 1168 | tragic 1169 | trained 1170 | tremendous 1171 | triangular 1172 | tricky 1173 | trifling 1174 | trim 1175 | trivial 1176 | troubled 1177 | true 1178 | trusting 1179 | trustworthy 1180 | trusty 1181 | truthful 1182 | tubby 1183 | turbulent 1184 | twin 1185 | ugly 1186 | ultimate 1187 | unacceptable 1188 | unaware 1189 | uncomfortable 1190 | uncommon 1191 | unconscious 1192 | understated 1193 | unequaled 1194 | uneven 1195 | unfinished 1196 | unfit 1197 | unfolded 1198 | unfortunate 1199 | unhappy 1200 | unhealthy 1201 | uniform 1202 | unimportant 1203 | unique 1204 | united 1205 | unkempt 1206 | unknown 1207 | unlawful 1208 | unlined 1209 | unlucky 1210 | unnatural 1211 | unpleasant 1212 | unrealistic 1213 | unripe 1214 | unruly 1215 | unselfish 1216 | unsightly 1217 | unsteady 1218 | unsung 1219 | untidy 1220 | untimely 1221 | untried 1222 | untrue 1223 | unused 1224 | unusual 1225 | unwelcome 1226 | unwieldy 1227 | unwilling 1228 | unwitting 1229 | unwritten 1230 | upbeat 1231 | upright 1232 | upset 1233 | urban 1234 | usable 1235 | used 1236 | useful 1237 | useless 1238 | utilized 1239 | utter 1240 | vacant 1241 | vague 1242 | vain 1243 | valid 1244 | valuable 1245 | vapid 1246 | variable 1247 | vast 1248 | velvety 1249 | venerated 1250 | vengeful 1251 | verifiable 1252 | vibrant 1253 | vicious 1254 | victorious 1255 | vigilant 1256 | vigorous 1257 | villainous 1258 | violet 1259 | violent 1260 | virtual 1261 | virtuous 1262 | visible 1263 | vital 1264 | vivacious 1265 | vivid 1266 | voluminous 1267 | wan 1268 | warlike 1269 | warm 1270 | warmhearted 1271 | warped 1272 | wary 1273 | wasteful 1274 | watchful 1275 | waterlogged 1276 | watery 1277 | wavy 1278 | wealthy 1279 | weak 1280 | weary 1281 | webbed 1282 | wee 1283 | weekly 1284 | weepy 1285 | weighty 1286 | weird 1287 | welcome 1288 | well-documented 1289 | well-groomed 1290 | well-informed 1291 | well-lit 1292 | well-made 1293 | well-off 1294 | well-to-do 1295 | well-worn 1296 | wet 1297 | which 1298 | whimsical 1299 | whirlwind 1300 | whispered 1301 | white 1302 | whole 1303 | whopping 1304 | wicked 1305 | wide 1306 | wide-eyed 1307 | wiggly 1308 | wild 1309 | willing 1310 | wilted 1311 | winding 1312 | windy 1313 | winged 1314 | wiry 1315 | wise 1316 | witty 1317 | wobbly 1318 | woeful 1319 | wonderful 1320 | wooden 1321 | woozy 1322 | wordy 1323 | worldly 1324 | worn 1325 | worried 1326 | worrisome 1327 | worse 1328 | worst 1329 | worthless 1330 | worthwhile 1331 | worthy 1332 | wrathful 1333 | wretched 1334 | writhing 1335 | wrong 1336 | wry 1337 | yawning 1338 | yearly 1339 | yellow 1340 | yellowish 1341 | young 1342 | youthful 1343 | yummy 1344 | zany 1345 | zealous 1346 | zesty 1347 | zigzag -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Set everything to be logged to the console 2 | log4j.rootCategory=WARN, console 3 | log4j.appender.console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.console.target=System.err 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n 7 | 8 | log4j.logger.io.omnition=INFO 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/natures.txt: -------------------------------------------------------------------------------- 1 | calm 2 | careful 3 | modest 4 | brave 5 | hardy 6 | bashful 7 | lax 8 | gentle 9 | adamant 10 | hasty 11 | impish 12 | bold 13 | docile 14 | quiet 15 | naughty 16 | jolly 17 | naive 18 | mild 19 | lonely 20 | quirky 21 | rash 22 | relaxed 23 | timid 24 | serious 25 | sassy -------------------------------------------------------------------------------- /src/main/resources/pokemon.txt: -------------------------------------------------------------------------------- 1 | abomasnow 2 | abra 3 | absol 4 | accelgor 5 | aegislash 6 | aerodactyl 7 | aggron 8 | aipom 9 | alakazam 10 | alomomola 11 | altaria 12 | amaura 13 | ambipom 14 | amoonguss 15 | ampharos 16 | anorith 17 | arbok 18 | arcanine 19 | arceus 20 | archen 21 | archeops 22 | ariados 23 | armaldo 24 | aromatisse 25 | aron 26 | articuno 27 | audino 28 | aurorus 29 | avalugg 30 | axew 31 | azelf 32 | azumarill 33 | azurill 34 | bagon 35 | baltoy 36 | banette 37 | barbaracle 38 | barboach 39 | basculin 40 | bastiodon 41 | bayleef 42 | beartic 43 | beautifly 44 | beedrill 45 | beheeyem 46 | beldum 47 | bellossom 48 | bellsprout 49 | bergmite 50 | bibarel 51 | bidoof 52 | binacle 53 | bisharp 54 | blastoise 55 | blaziken 56 | blissey 57 | blitzle 58 | boldore 59 | bonsly 60 | bouffalant 61 | braixen 62 | braviary 63 | breloom 64 | bronzong 65 | bronzor 66 | budew 67 | buizel 68 | bulbasaur 69 | buneary 70 | bunnelby 71 | burmy 72 | butterfree 73 | cacnea 74 | cacturne 75 | camerupt 76 | carbink 77 | carnivine 78 | carracosta 79 | carvanha 80 | cascoon 81 | castform 82 | caterpie 83 | celebi 84 | chandelure 85 | chansey 86 | charizard 87 | charmander 88 | charmeleon 89 | chatot 90 | cherrim 91 | cherubi 92 | chesnaught 93 | chespin 94 | chikorita 95 | chimchar 96 | chimecho 97 | chinchou 98 | chingling 99 | cinccino 100 | clamperl 101 | clauncher 102 | clawitzer 103 | claydol 104 | clefable 105 | clefairy 106 | cleffa 107 | cloyster 108 | cobalion 109 | cofagrigus 110 | combee 111 | combusken 112 | conkeldurr 113 | corphish 114 | corsola 115 | cottonee 116 | cradily 117 | cranidos 118 | crawdaunt 119 | cresselia 120 | croagunk 121 | crobat 122 | croconaw 123 | crustle 124 | cryogonal 125 | cubchoo 126 | cubone 127 | cyndaquil 128 | darkrai 129 | darmanitan 130 | darumaka 131 | dedenne 132 | deerling 133 | deino 134 | delcatty 135 | delibird 136 | delphox 137 | deoxys 138 | dewgong 139 | dewott 140 | dialga 141 | diggersby 142 | diglett 143 | ditto 144 | dodrio 145 | doduo 146 | donphan 147 | doublade 148 | dragalge 149 | dragonair 150 | dragonite 151 | drapion 152 | dratini 153 | drifblim 154 | drifloon 155 | drilbur 156 | drowzee 157 | druddigon 158 | ducklett 159 | dugtrio 160 | dunsparce 161 | duosion 162 | durant 163 | dusclops 164 | dusknoir 165 | duskull 166 | dustox 167 | dwebble 168 | eelektrik 169 | eelektross 170 | eevee 171 | ekans 172 | electabuzz 173 | electivire 174 | electrike 175 | electrode 176 | elekid 177 | elgyem 178 | emboar 179 | emolga 180 | empoleon 181 | entei 182 | escavalier 183 | espeon 184 | espurr 185 | excadrill 186 | exeggcute 187 | exeggutor 188 | exploud 189 | farfetch'd 190 | fearow 191 | feebas 192 | fennekin 193 | feraligatr 194 | ferroseed 195 | ferrothorn 196 | finneon 197 | flaaffy 198 | flabébé 199 | flareon 200 | fletchinder 201 | fletchling 202 | floatzel 203 | floette 204 | florges 205 | flygon 206 | foongus 207 | forretress 208 | fraxure 209 | frillish 210 | froakie 211 | frogadier 212 | froslass 213 | furfrou 214 | furret 215 | gabite 216 | gallade 217 | galvantula 218 | garbodor 219 | garchomp 220 | gardevoir 221 | gastly 222 | gastrodon 223 | genesect 224 | gengar 225 | geodude 226 | gible 227 | gigalith 228 | girafarig 229 | giratina 230 | glaceon 231 | glalie 232 | glameow 233 | gligar 234 | gliscor 235 | gloom 236 | gogoat 237 | golbat 238 | goldeen 239 | golduck 240 | golem 241 | golett 242 | golurk 243 | goodra 244 | goomy 245 | gorebyss 246 | gothita 247 | gothitelle 248 | gothorita 249 | gourgeist 250 | granbull 251 | graveler 252 | greninja 253 | grimer 254 | grotle 255 | groudon 256 | grovyle 257 | growlithe 258 | grumpig 259 | gulpin 260 | gurdurr 261 | gyarados 262 | happiny 263 | hariyama 264 | haunter 265 | hawlucha 266 | haxorus 267 | heatmor 268 | heatran 269 | heliolisk 270 | helioptile 271 | heracross 272 | herdier 273 | hippopotas 274 | hippowdon 275 | hitmonchan 276 | hitmonlee 277 | hitmontop 278 | ho-oh 279 | honchkrow 280 | honedge 281 | hoothoot 282 | hoppip 283 | horsea 284 | houndoom 285 | houndour 286 | huntail 287 | hydreigon 288 | hypno 289 | igglybuff 290 | illumise 291 | infernape 292 | inkay 293 | ivysaur 294 | jellicent 295 | jigglypuff 296 | jirachi 297 | jolteon 298 | joltik 299 | jumpluff 300 | jynx 301 | kabuto 302 | kabutops 303 | kadabra 304 | kakuna 305 | kangaskhan 306 | karrablast 307 | kecleon 308 | keldeo 309 | kingdra 310 | kingler 311 | kirlia 312 | klang 313 | klefki 314 | klink 315 | klinklang 316 | koffing 317 | krabby 318 | kricketot 319 | kricketune 320 | krokorok 321 | krookodile 322 | kyogre 323 | kyurem 324 | lairon 325 | lampent 326 | landorus 327 | lanturn 328 | lapras 329 | larvesta 330 | larvitar 331 | latias 332 | latios 333 | leafeon 334 | leavanny 335 | ledian 336 | ledyba 337 | lickilicky 338 | lickitung 339 | liepard 340 | lileep 341 | lilligant 342 | lillipup 343 | linoone 344 | litleo 345 | litwick 346 | lombre 347 | lopunny 348 | lotad 349 | loudred 350 | lucario 351 | ludicolo 352 | lugia 353 | lumineon 354 | lunatone 355 | luvdisc 356 | luxio 357 | luxray 358 | machamp 359 | machoke 360 | machop 361 | magby 362 | magcargo 363 | magikarp 364 | magmar 365 | magmortar 366 | magnemite 367 | magneton 368 | magnezone 369 | makuhita 370 | malamar 371 | mamoswine 372 | manaphy 373 | mandibuzz 374 | manectric 375 | mankey 376 | mantine 377 | mantyke 378 | maractus 379 | mareep 380 | marill 381 | marowak 382 | marshtomp 383 | masquerain 384 | mawile 385 | medicham 386 | meditite 387 | meganium 388 | meloetta 389 | meowstic 390 | meowth 391 | mesprit 392 | metagross 393 | metang 394 | metapod 395 | mew 396 | mewtwo 397 | mienfoo 398 | mienshao 399 | mightyena 400 | milotic 401 | miltank 402 | mime jr. 403 | minccino 404 | minun 405 | misdreavus 406 | mismagius 407 | moltres 408 | monferno 409 | mothim 410 | mr. mime 411 | mudkip 412 | muk 413 | munchlax 414 | munna 415 | murkrow 416 | musharna 417 | natu 418 | nidoking 419 | nidoqueen 420 | nidoran♀ 421 | nidoran♂ 422 | nidorina 423 | nidorino 424 | nincada 425 | ninetales 426 | ninjask 427 | noctowl 428 | noibat 429 | noivern 430 | nosepass 431 | numel 432 | nuzleaf 433 | octillery 434 | oddish 435 | omanyte 436 | omastar 437 | onix 438 | oshawott 439 | pachirisu 440 | palkia 441 | palpitoad 442 | pancham 443 | pangoro 444 | panpour 445 | pansage 446 | pansear 447 | paras 448 | parasect 449 | patrat 450 | pawniard 451 | pelipper 452 | persian 453 | petilil 454 | phanpy 455 | phantump 456 | phione 457 | pichu 458 | pidgeot 459 | pidgeotto 460 | pidgey 461 | pidove 462 | pignite 463 | pikachu 464 | piloswine 465 | pineco 466 | pinsir 467 | piplup 468 | plusle 469 | politoed 470 | poliwag 471 | poliwhirl 472 | poliwrath 473 | ponyta 474 | poochyena 475 | porygon 476 | porygon-z 477 | porygon2 478 | primeape 479 | prinplup 480 | probopass 481 | psyduck 482 | pumpkaboo 483 | pupitar 484 | purrloin 485 | purugly 486 | pyroar 487 | quagsire 488 | quilava 489 | quilladin 490 | qwilfish 491 | raichu 492 | raikou 493 | ralts 494 | rampardos 495 | rapidash 496 | raticate 497 | rattata 498 | rayquaza 499 | regice 500 | regigigas 501 | regirock 502 | registeel 503 | relicanth 504 | remoraid 505 | reshiram 506 | reuniclus 507 | rhydon 508 | rhyhorn 509 | rhyperior 510 | riolu 511 | roggenrola 512 | roselia 513 | roserade 514 | rotom 515 | rufflet 516 | sableye 517 | salamence 518 | samurott 519 | sandile 520 | sandshrew 521 | sandslash 522 | sawk 523 | sawsbuck 524 | scatterbug 525 | sceptile 526 | scizor 527 | scolipede 528 | scrafty 529 | scraggy 530 | scyther 531 | seadra 532 | seaking 533 | sealeo 534 | seedot 535 | seel 536 | seismitoad 537 | sentret 538 | serperior 539 | servine 540 | seviper 541 | sewaddle 542 | sharpedo 543 | shaymin 544 | shedinja 545 | shelgon 546 | shellder 547 | shellos 548 | shelmet 549 | shieldon 550 | shiftry 551 | shinx 552 | shroomish 553 | shuckle 554 | shuppet 555 | sigilyph 556 | silcoon 557 | simipour 558 | simisage 559 | simisear 560 | skarmory 561 | skiddo 562 | skiploom 563 | skitty 564 | skorupi 565 | skrelp 566 | skuntank 567 | slaking 568 | slakoth 569 | sliggoo 570 | slowbro 571 | slowking 572 | slowpoke 573 | slugma 574 | slurpuff 575 | smeargle 576 | smoochum 577 | sneasel 578 | snivy 579 | snorlax 580 | snorunt 581 | snover 582 | snubbull 583 | solosis 584 | solrock 585 | spearow 586 | spewpa 587 | spheal 588 | spinarak 589 | spinda 590 | spiritomb 591 | spoink 592 | spritzee 593 | squirtle 594 | stantler 595 | staraptor 596 | staravia 597 | starly 598 | starmie 599 | staryu 600 | steelix 601 | stoutland 602 | stunfisk 603 | stunky 604 | sudowoodo 605 | suicune 606 | sunflora 607 | sunkern 608 | surskit 609 | swablu 610 | swadloon 611 | swalot 612 | swampert 613 | swanna 614 | swellow 615 | swinub 616 | swirlix 617 | swoobat 618 | sylveon 619 | taillow 620 | talonflame 621 | tangela 622 | tangrowth 623 | tauros 624 | teddiursa 625 | tentacool 626 | tentacruel 627 | tepig 628 | terrakion 629 | throh 630 | thundurus 631 | timburr 632 | tirtouga 633 | togekiss 634 | togepi 635 | togetic 636 | torchic 637 | torkoal 638 | tornadus 639 | torterra 640 | totodile 641 | toxicroak 642 | tranquill 643 | trapinch 644 | treecko 645 | trevenant 646 | tropius 647 | trubbish 648 | turtwig 649 | tympole 650 | tynamo 651 | typhlosion 652 | tyranitar 653 | tyrantrum 654 | tyrogue 655 | tyrunt 656 | umbreon 657 | unfezant 658 | unown 659 | ursaring 660 | uxie 661 | vanillish 662 | vanillite 663 | vanilluxe 664 | vaporeon 665 | venipede 666 | venomoth 667 | venonat 668 | venusaur 669 | vespiquen 670 | vibrava 671 | victini 672 | victreebel 673 | vigoroth 674 | vileplume 675 | virizion 676 | vivillon 677 | volbeat 678 | volcarona 679 | voltorb 680 | vullaby 681 | vulpix 682 | wailmer 683 | wailord 684 | walrein 685 | wartortle 686 | watchog 687 | weavile 688 | weedle 689 | weepinbell 690 | weezing 691 | whimsicott 692 | whirlipede 693 | whiscash 694 | whismur 695 | wigglytuff 696 | wingull 697 | wobbuffet 698 | woobat 699 | wooper 700 | wormadam 701 | wurmple 702 | wynaut 703 | xatu 704 | xerneas 705 | yamask 706 | yanma 707 | yanmega 708 | yveltal 709 | zangoose 710 | zapdos 711 | zebstrika 712 | zekrom 713 | zigzagoon 714 | zoroark 715 | zorua 716 | zubat 717 | zweilous 718 | zygarde -------------------------------------------------------------------------------- /src/test/java/io/omnition/loadgenerator/AppTest.java: -------------------------------------------------------------------------------- 1 | package io.omnition.loadgenerator; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | { 12 | /** 13 | * Rigorous Test :-) 14 | */ 15 | @Test 16 | public void shouldAnswerWithTrue() 17 | { 18 | assertTrue( true ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -z "${JAEGER_COLLECTOR_URL}" ]; then JAEGER_COLLECTOR_URL=http://jaeger-collector:14268; fi 4 | if [ -z "${TOPOLOGY_FILE}" ]; then TOPOLOGY_FILE=./topologies/hipster-shop.json; fi 5 | 6 | if [ ! -z "${ZIPKINV1_JSON_URL}" ]; then 7 | PARAMS="$PARAMS --zipkinV1JsonUrl ${ZIPKINV1_JSON_URL}" 8 | fi 9 | 10 | if [ ! -z "${ZIPKINV2_JSON_URL}" ]; then 11 | PARAMS="$PARAMS --zipkinV2JsonUrl ${ZIPKINV2_JSON_URL}" 12 | fi 13 | 14 | if [ ! -z "${ZIPKINV1_THRIFT_URL}" ]; then 15 | PARAMS="$PARAMS --zipkinV1ThriftUrl ${ZIPKINV1_THRIFT_URL}" 16 | fi 17 | 18 | if [ ! -z "${ZIPKINV2_PROTO3_URL}" ]; then 19 | PARAMS="$PARAMS --zipkinV2Proto3Url ${ZIPKINV2_PROTO3_URL}" 20 | fi 21 | 22 | if [ -z "${PARAMS}" ]; then 23 | PARAMS="--jaegerCollectorUrl ${JAEGER_COLLECTOR_URL}" 24 | fi 25 | 26 | echo "using params: " ${PARAMS} 27 | 28 | java -jar synthetic-load-generator.jar \ 29 | --paramsFile ${TOPOLOGY_FILE} \ 30 | $PARAMS 31 | 32 | -------------------------------------------------------------------------------- /topologies/100_000_spans_per_second.json: -------------------------------------------------------------------------------- 1 | { 2 | "topology" : { 3 | "services" : [ 4 | { 5 | "serviceName" : "frontend", 6 | "instances" : [ "frontend-6b654dbf57-zq8dt", "frontend-d847fdcf5-j6s2f", "frontend-79d8c8d6c8-9sbff" ], 7 | "tagSets": [{"tags" : { "version" : "v125", "region" : "us-east-1" }}], 8 | "routes" : [ 9 | { 10 | "route" : "/product", 11 | "downstreamCalls" : { "productcatalogservice" : "/GetProducts", "recommendationservice" : "/GetRecommendations", "adservice" : "/AdRequest", "spanFiller0" : "/0", "spanFiller1" : "/1", "spanFiller2" : "/2", "spanFiller3" : "/3", "spanFiller4" : "/4", "spanFiller5" : "/5", "spanFiller6" : "/6", "spanFiller7" : "/7", "spanFiller8" : "/8", "spanFiller9" : "/9" }, 12 | "maxLatencyMillis" : 200 13 | }, 14 | { 15 | "route" : "/alt_product_0", 16 | "downstreamCalls" : { "productcatalogservice" : "/GetProducts", "recommendationservice" : "/GetRecommendations", "adservice" : "/AdRequest", "spanFiller0" : "/0", "spanFiller1" : "/1", "spanFiller2" : "/2", "spanFiller3" : "/3", "spanFiller4" : "/4", "spanFiller5" : "/5", "spanFiller6" : "/6", "spanFiller7" : "/7", "spanFiller8" : "/8", "spanFiller9" : "/9" }, 17 | "maxLatencyMillis" : 200 18 | }, 19 | { 20 | "route" : "/alt_product_1", 21 | "downstreamCalls" : { "productcatalogservice" : "/GetProducts", "recommendationservice" : "/GetRecommendations", "adservice" : "/AdRequest", "spanFiller0" : "/0", "spanFiller1" : "/1", "spanFiller2" : "/2", "spanFiller3" : "/3", "spanFiller4" : "/4", "spanFiller5" : "/5", "spanFiller6" : "/6", "spanFiller7" : "/7", "spanFiller8" : "/8", "spanFiller9" : "/9" }, 22 | "maxLatencyMillis" : 200 23 | }, 24 | { 25 | "route" : "/alt_product_2", 26 | "downstreamCalls" : { "productcatalogservice" : "/GetProducts", "recommendationservice" : "/GetRecommendations", "adservice" : "/AdRequest", "spanFiller0" : "/0", "spanFiller1" : "/1", "spanFiller2" : "/2", "spanFiller3" : "/3", "spanFiller4" : "/4", "spanFiller5" : "/5", "spanFiller6" : "/6", "spanFiller7" : "/7", "spanFiller8" : "/8", "spanFiller9" : "/9" }, 27 | "maxLatencyMillis" : 200 28 | } 29 | ] 30 | }, 31 | { 32 | "serviceName" : "productcatalogservice", 33 | "instances" : [ "productcatalogservice-6b654dbf57-zq8dt", "productcatalogservice-d847fdcf5-j6s2f" ], 34 | "tagSets": [{"tags" : { "version" : "v52", "region" : "us-east-1" }}], 35 | "routes" : [ 36 | { 37 | "route" : "/GetProducts", 38 | "downstreamCalls" : { "spanFiller0" : "/0", "spanFiller1" : "/1", "spanFiller2" : "/2", "spanFiller3" : "/3", "spanFiller4" : "/4", "spanFiller5" : "/5", "spanFiller6" : "/6", "spanFiller7" : "/7", "spanFiller8" : "/8", "spanFiller9" : "/9" }, 39 | "maxLatencyMillis" : 100 40 | } 41 | ] 42 | }, 43 | { 44 | "serviceName" : "recommendationservice", 45 | "instances" : [ "recommendationservice-6b654dbf57-zq8dt", "recommendationservice-d847fdcf5-j6s2f" ], 46 | "tagSets": [{"tags" : { "version" : "v234", "region" : "us-east-1" }}], 47 | "routes" : [ 48 | { 49 | "route" : "/GetRecommendations", 50 | "downstreamCalls" : { "productcatalogservice" : "/GetProducts", "spanFiller0" : "/0", "spanFiller1" : "/1", "spanFiller2" : "/2", "spanFiller3" : "/3", "spanFiller4" : "/4", "spanFiller5" : "/5", "spanFiller6" : "/6", "spanFiller7" : "/7", "spanFiller8" : "/8", "spanFiller9" : "/9" }, 51 | "maxLatencyMillis" : 200 52 | } 53 | ] 54 | }, 55 | { 56 | "serviceName" : "adservice", 57 | "instances" : [ "adservice-6b654dbf57-zq8dt", "adservice-d847fdcf5-j6s2f" ], 58 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 59 | "routes" : [ 60 | { 61 | "route" : "/AdRequest", 62 | "downstreamCalls" : { "spanFiller0" : "/0", "spanFiller1" : "/1", "spanFiller2" : "/2", "spanFiller3" : "/3", "spanFiller4" : "/4" }, 63 | "maxLatencyMillis" : 500 64 | }, 65 | { 66 | "route" : "/Ad", 67 | "downstreamCalls" : { "spanFiller0" : "/0", "spanFiller1" : "/1", "spanFiller2" : "/2", "spanFiller3" : "/3", "spanFiller4" : "/4" }, 68 | "maxLatencyMillis" : 500 69 | } 70 | ] 71 | }, 72 | { 73 | "serviceName" : "spanFiller0", 74 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 75 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 76 | "routes" : [ 77 | { 78 | "route" : "/0", 79 | "downstreamCalls" : { }, 80 | "maxLatencyMillis" : 1 81 | } 82 | ] 83 | }, 84 | { 85 | "serviceName" : "spanFiller1", 86 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 87 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 88 | "routes" : [ 89 | { 90 | "route" : "/1", 91 | "downstreamCalls" : { }, 92 | "maxLatencyMillis" : 1 93 | } 94 | ] 95 | }, 96 | { 97 | "serviceName" : "spanFiller2", 98 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 99 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 100 | "routes" : [ 101 | { 102 | "route" : "/2", 103 | "downstreamCalls" : { }, 104 | "maxLatencyMillis" : 1 105 | } 106 | ] 107 | }, 108 | { 109 | "serviceName" : "spanFiller3", 110 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 111 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 112 | "routes" : [ 113 | { 114 | "route" : "/3", 115 | "downstreamCalls" : { }, 116 | "maxLatencyMillis" : 1 117 | } 118 | ] 119 | }, 120 | { 121 | "serviceName" : "spanFiller4", 122 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 123 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 124 | "routes" : [ 125 | { 126 | "route" : "/4", 127 | "downstreamCalls" : { }, 128 | "maxLatencyMillis" : 1 129 | } 130 | ] 131 | }, 132 | { 133 | "serviceName" : "spanFiller5", 134 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 135 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 136 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 137 | "routes" : [ 138 | { 139 | "route" : "/5", 140 | "downstreamCalls" : { }, 141 | "maxLatencyMillis" : 1 142 | } 143 | ] 144 | }, 145 | { 146 | "serviceName" : "spanFiller6", 147 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 148 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 149 | "routes" : [ 150 | { 151 | "route" : "/6", 152 | "downstreamCalls" : { }, 153 | "maxLatencyMillis" : 1 154 | } 155 | ] 156 | }, 157 | { 158 | "serviceName" : "spanFiller7", 159 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 160 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 161 | "routes" : [ 162 | { 163 | "route" : "/7", 164 | "downstreamCalls" : { }, 165 | "maxLatencyMillis" : 1 166 | } 167 | ] 168 | }, 169 | { 170 | "serviceName" : "spanFiller8", 171 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 172 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 173 | "routes" : [ 174 | { 175 | "route" : "/8", 176 | "downstreamCalls" : { }, 177 | "maxLatencyMillis" : 1 178 | } 179 | ] 180 | }, 181 | { 182 | "serviceName" : "spanFiller9", 183 | "instances" : [ "spanfiller-6b654dbf57-00000" ], 184 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 185 | "routes" : [ 186 | { 187 | "route" : "/9", 188 | "downstreamCalls" : { }, 189 | "maxLatencyMillis" : 1 190 | } 191 | ] 192 | } 193 | ] 194 | }, 195 | "rootRoutes" : [ 196 | { 197 | "service" : "frontend", 198 | "route" : "/product", 199 | "tracesPerHour" : 1800000 200 | }, 201 | { 202 | "service" : "frontend", 203 | "route" : "/alt_product_0", 204 | "tracesPerHour" : 1800000 205 | }, 206 | { 207 | "service" : "frontend", 208 | "route" : "/alt_product_1", 209 | "tracesPerHour" : 1800000 210 | }, 211 | { 212 | "service" : "frontend", 213 | "route" : "/alt_product_2", 214 | "tracesPerHour" : 1800000 215 | } 216 | ] 217 | } 218 | -------------------------------------------------------------------------------- /topologies/hipster-shop.json: -------------------------------------------------------------------------------- 1 | { 2 | "topology" : { 3 | "services" : [ 4 | { 5 | "serviceName" : "frontend", 6 | "instances" : [ "frontend-6b654dbf57-zq8dt", "frontend-d847fdcf5-j6s2f", "frontend-79d8c8d6c8-9sbff" ], 7 | "tagSets" : [ 8 | { "weight": 1, "tags": { "version" : "v127", "region" : "us-east-1" }}, 9 | { "weight": 1, "tags": { "version" : "v125", "region" : "us-east-1" }}, 10 | { "weight": 2, "tags": { "version" : "v125", "region" : "us-west-1" }} 11 | ], 12 | "routes" : [ 13 | { 14 | "route" : "/product", 15 | "downstreamCalls" : { "productcatalogservice" : "/GetProducts", "recommendationservice" : "/GetRecommendations", "adservice" : "/AdRequest" }, 16 | "maxLatencyMillis": 200, 17 | "tagSets": [ 18 | { "weight": 1, "tags": { "starter" : "charmander"}, "tagGenerators": [{"numTags": 50, "numVals": 3000, "valLength": 16}]}, 19 | { "weight": 1, "tags": { "starter" : "squirtle"}}, 20 | { "weight": 1, "tags": { "starter" : "bulbasaur"}} 21 | ] 22 | }, 23 | { 24 | "route" : "/cart", 25 | "downstreamCalls" : { "cartservice" : "/GetCart", "recommendationservice" : "/GetRecommendations" }, 26 | "maxLatencyMillis" : 100 27 | }, 28 | { 29 | "route" : "/checkout", 30 | "downstreamCalls" : { "checkoutservice" : "/PlaceOrder" }, 31 | "maxLatencyMillis" : 800 32 | }, 33 | { 34 | "route" : "/shipping", 35 | "downstreamCalls" : { "shippingservice" : "/GetQuote" }, 36 | "maxLatencyMillis" : 50 37 | }, 38 | { 39 | "route" : "/currency", 40 | "downstreamCalls" : { "currencyservice" : "/GetConversion" }, 41 | "maxLatencyMillis" : 50 42 | } 43 | ] 44 | }, 45 | { 46 | "serviceName" : "productcatalogservice", 47 | "instances" : [ "productcatalogservice-6b654dbf57-zq8dt", "productcatalogservice-d847fdcf5-j6s2f" ], 48 | "tagSets" : [{"tags": { "version" : "v52"}, "inherit": ["region"]}], 49 | "routes" : [ 50 | { 51 | "route" : "/GetProducts", 52 | "downstreamCalls" : { }, 53 | "maxLatencyMillis" : 100, 54 | "tagSets": [ 55 | {"inherit": ["starter"]} 56 | ] 57 | }, 58 | { 59 | "route" : "/SearchProducts", 60 | "downstreamCalls" : { }, 61 | "tagSets" : [ 62 | {"weight": 15, "tags": { "error" : true, "http.status_code": 503}}, 63 | {"weight": 85, "tags": {}} 64 | ], 65 | "maxLatencyMillis" : 400 66 | } 67 | ] 68 | }, 69 | { 70 | "serviceName" : "recommendationservice", 71 | "instances" : [ "recommendationservice-6b654dbf57-zq8dt", "recommendationservice-d847fdcf5-j6s2f" ], 72 | "tagSets" : [{"tags" : { "version" : "v234", "region" : "us-east-1" }}], 73 | "routes" : [ 74 | { 75 | "route" : "/GetRecommendations", 76 | "downstreamCalls" : { "productcatalogservice" : "/GetProducts" }, 77 | "maxLatencyMillis" : 200 78 | } 79 | ] 80 | }, 81 | { 82 | "serviceName" : "cartservice", 83 | "instances" : [ "cartservice-6b654dbf57-zq8dt", "cartservice-d847fdcf5-j6s2f" ], 84 | "tagSets": [{"tags" : { "version" : "v5", "region" : "us-east-1" }}], 85 | "routes" : [ 86 | { 87 | "route" : "/GetCart", 88 | "downstreamCalls" : { }, 89 | "maxLatencyMillis" : 200 90 | } 91 | ] 92 | }, 93 | { 94 | "serviceName" : "checkoutservice", 95 | "instances" : [ "checkoutservice-6b654dbf57-zq8dt", "checkoutservice-d847fdcf5-j6s2f" ], 96 | "tagSets": [{"tags" : { "version" : "v37", "region" : "us-east-1" }}], 97 | "routes" : [ 98 | { 99 | "route" : "/PlaceOrder", 100 | "downstreamCalls" : { "paymentservice" : "/CreditCardInfo", "shippingservice" : "/Address", "currencyservice" : "/GetConversion", "cartservice" : "/GetCart", "emailservice" : "/SendOrderConfirmation" }, 101 | "tagSets" : [ 102 | {"weight": 25, "tags": { "error" : true, "http.status_code": 503}}, 103 | {"weight": 85, "tags": {}} 104 | ], 105 | "maxLatencyMillis" : 500 106 | } 107 | ] 108 | }, 109 | { 110 | "serviceName" : "paymentservice", 111 | "instances" : [ "paymentservice-6b654dbf57-zq8dt", "paymentservice-d847fdcf5-j6s2f" ], 112 | "tagSets": [{"tags" : { "version" : "v177", "region" : "us-east-1" }}], 113 | "routes" : [ 114 | { 115 | "route" : "/ChargeRequest", 116 | "downstreamCalls" : { "paymentservice" : "/CreditCardInfo" }, 117 | "maxLatencyMillis" : 700 118 | }, 119 | { 120 | "route" : "/CreditCardInfo", 121 | "downstreamCalls" : { }, 122 | "maxLatencyMillis" : 50 123 | } 124 | ] 125 | }, 126 | { 127 | "serviceName" : "shippingservice", 128 | "instances" : [ "shippingservice-6b654dbf57-zq8dt", "shippingservice-d847fdcf5-j6s2f" ], 129 | "tagSets": [{"tags" : { "version" : "v127", "region" : "us-east-1" }}], 130 | "routes" : [ 131 | { 132 | "route" : "/GetQuote", 133 | "downstreamCalls" : { "shippingservice" : "/Address" }, 134 | "maxLatencyMillis" : 250 135 | }, 136 | { 137 | "route" : "/ShipOrder", 138 | "downstreamCalls" : { "shippingservice" : "/Address"}, 139 | "maxLatencyMillis" : 500 140 | }, 141 | { 142 | "route" : "/Address", 143 | "downstreamCalls" : { }, 144 | "maxLatencyMillis" : 100 145 | } 146 | ] 147 | }, 148 | { 149 | "serviceName" : "emailservice", 150 | "instances" : [ "emailservice-6b654dbf57-zq8dt", "emailservice-d847fdcf5-j6s2f" ], 151 | "tagSets": [{"tags" : { "version" : "v27", "region" : "us-east-1" }}], 152 | "routes" : [ 153 | { 154 | "route" : "/SendOrderConfirmation", 155 | "downstreamCalls" : { "emailservice" : "/OrderResult" }, 156 | "tagSets" : [ 157 | {"weight": 15, "tags": { "error" : true, "http.status_code": 503}}, 158 | {"weight": 85, "tags": {}} 159 | ], 160 | "maxLatencyMillis" : 500 161 | }, 162 | { 163 | "route" : "/OrderResult", 164 | "downstreamCalls" : { }, 165 | "maxLatencyMillis" : 100 166 | } 167 | ] 168 | }, 169 | { 170 | "serviceName" : "currencyservice", 171 | "instances" : [ "currencyservice-6b654dbf57-zq8dt", "currencyservice-d847fdcf5-j6s2f" ], 172 | "tagSets": [{"tags" : { "version" : "v27", "region" : "us-east-1" }}], 173 | "routes" : [ 174 | { 175 | "route" : "/GetConversion", 176 | "downstreamCalls" : { "currencyservice" : "/Money" }, 177 | "maxLatencyMillis" : 100 178 | }, 179 | { 180 | "route" : "/Money", 181 | "downstreamCalls" : { }, 182 | "maxLatencyMillis" : 100 183 | } 184 | ] 185 | }, 186 | { 187 | "serviceName" : "adservice", 188 | "instances" : [ "adservice-6b654dbf57-zq8dt", "adservice-d847fdcf5-j6s2f" ], 189 | "tagSets" : [{ "version" : "v37", "region" : "us-east-1" }], 190 | "routes" : [ 191 | { 192 | "route" : "/AdRequest", 193 | "downstreamCalls" : { }, 194 | "maxLatencyMillis" : 500 195 | }, 196 | { 197 | "route" : "/Ad", 198 | "downstreamCalls" : { }, 199 | "maxLatencyMillis" : 500 200 | } 201 | ] 202 | } 203 | ] 204 | }, 205 | "rootRoutes" : [ 206 | { 207 | "service" : "frontend", 208 | "route" : "/product", 209 | "tracesPerHour" : 2880 210 | }, 211 | { 212 | "service" : "frontend", 213 | "route" : "/cart", 214 | "tracesPerHour" : 14400 215 | }, 216 | { 217 | "service" : "frontend", 218 | "route" : "/shipping", 219 | "tracesPerHour" : 480 220 | }, 221 | { 222 | "service" : "frontend", 223 | "route" : "/currency", 224 | "tracesPerHour" : 200 225 | }, 226 | { 227 | "service" : "frontend", 228 | "route" : "/checkout", 229 | "tracesPerHour" : 480 230 | } 231 | ] 232 | } 233 | --------------------------------------------------------------------------------