├── .bumpversion.cfg ├── .circleci └── config.yml ├── .github └── workflows │ └── send-pr-details-on-close.yml ├── .gitignore ├── LICENSE ├── README.md ├── agent ├── pom.xml └── src │ └── main │ └── java │ └── io │ └── lumigo │ └── agent │ └── Agent.java ├── findbugs └── findbugs-exclude.xml ├── java-sign-keys ├── private.asc └── public.asc ├── layers └── LAYERS.md ├── libs └── google-java-format-1.7-all-deps.jar ├── local-repository └── com │ └── sun │ └── tools │ └── 1.7.0.13 │ ├── _remote.repositories │ ├── tools-1.7.0.13.jar │ ├── tools-1.7.0.13.jar.lastUpdated │ ├── tools-1.7.0.13.jar.sha1 │ ├── tools-1.7.0.13.pom │ ├── tools-1.7.0.13.pom.lastUpdated │ └── tools-1.7.0.13.pom.sha1 ├── maven └── settings.xml ├── pom.xml ├── scripts ├── bd_to_prod.sh ├── checks.sh ├── ci_deploy.sh ├── deploy.sh └── prepare_layer_files.sh └── src ├── main ├── java │ └── io │ │ └── lumigo │ │ ├── core │ │ ├── SpansContainer.java │ │ ├── configuration │ │ │ └── Configuration.java │ │ ├── instrumentation │ │ │ ├── LumigoInstrumentationApi.java │ │ │ ├── agent │ │ │ │ ├── Installer.java │ │ │ │ └── Loader.java │ │ │ └── impl │ │ │ │ ├── AmazonHttpClientInstrumentation.java │ │ │ │ ├── AmazonHttpClientV2Instrumentation.java │ │ │ │ ├── ApacheHttpInstrumentation.java │ │ │ │ ├── ApacheKafkaConsumerInstrumentation.java │ │ │ │ ├── ApacheKafkaProducerInstrumentation.java │ │ │ │ └── AwsLambdaRequestHandlerInstrumentation.java │ │ ├── network │ │ │ └── Reporter.java │ │ ├── parsers │ │ │ ├── event │ │ │ │ ├── APIGWEvent.java │ │ │ │ ├── APIGWEventParser.java │ │ │ │ ├── EventParserFactory.java │ │ │ │ ├── IEventParser.java │ │ │ │ ├── SnsEvent.java │ │ │ │ ├── SnsEventParser.java │ │ │ │ ├── SqsEvent.java │ │ │ │ └── SqsEventParser.java │ │ │ ├── v1 │ │ │ │ ├── AwsSdkV1Parser.java │ │ │ │ ├── AwsSdkV1ParserFactory.java │ │ │ │ ├── DoNothingV1Parser.java │ │ │ │ ├── DynamoDBV1Parser.java │ │ │ │ ├── KinesisV1Parser.java │ │ │ │ ├── SnsV1Parser.java │ │ │ │ └── SqsV1Parser.java │ │ │ └── v2 │ │ │ │ ├── AwsSdkV2Parser.java │ │ │ │ ├── AwsSdkV2ParserFactory.java │ │ │ │ ├── DoNothingV2Parser.java │ │ │ │ ├── DynamoDBV2Parser.java │ │ │ │ ├── KinesisV2Parser.java │ │ │ │ ├── SnsV2Parser.java │ │ │ │ └── SqsV2Parser.java │ │ └── utils │ │ │ ├── AwsSdkV2Utils.java │ │ │ ├── AwsUtils.java │ │ │ ├── EnvUtil.java │ │ │ ├── JsonUtils.java │ │ │ ├── LRUCache.java │ │ │ ├── SecretScrubber.java │ │ │ ├── SecretScrubbingPatternProvider.java │ │ │ ├── StringUtils.java │ │ │ └── Strings.java │ │ ├── handlers │ │ ├── LumigoConfiguration.java │ │ ├── LumigoRequestExecutor.java │ │ ├── LumigoRequestHandler.java │ │ └── LumigoRequestStreamHandler.java │ │ └── models │ │ ├── BaseSpan.java │ │ ├── HttpSpan.java │ │ ├── KafkaSpan.java │ │ ├── KafkaSpanFactory.java │ │ ├── Reportable.java │ │ └── Span.java └── resources │ └── lumigo-version.txt └── test └── java ├── infa └── AwsLambdaEventGenerator.java └── io └── lumigo ├── core ├── SpansContainerTest.java ├── configuration │ └── ConfigurationTest.java ├── instrumentation │ └── impl │ │ ├── AmazonHttpClientInstrumentationTest.java │ │ └── ApacheHttpInstrumentationTest.java ├── network │ └── ReporterTest.java ├── parsers │ ├── event │ │ └── EventParserFactoryTest.java │ ├── v1 │ │ ├── AwsSdkV1ParserFactoryTest.java │ │ ├── DynamoDBV1ParserTest.java │ │ ├── KinesisV1ParserTest.java │ │ ├── SnsV1ParserTest.java │ │ └── SqsV1ParserTest.java │ └── v2 │ │ ├── AwsSdkV2ParserFactoryTest.java │ │ ├── DynamoDBV2ParserTest.java │ │ ├── KinesisV2ParserTest.java │ │ ├── SnsV2ParserTest.java │ │ └── SqsV2ParserTest.java └── utils │ ├── AwsSdkV2UtilsTests.java │ ├── AwsUtilsTest.java │ ├── EnvUtilTest.java │ ├── JsonUtilsTest.java │ ├── LRUCacheTest.java │ ├── SecretScrubberTest.java │ └── StringUtilsTest.java ├── handlers └── LumigoRequestHandlerTest.java ├── models ├── KafkaSpanFactoryTest.java └── KafkaSpanTest.java └── testUtils └── JsonTestUtils.java /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.0.49 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:README.md] 7 | 8 | [bumpversion:file:pom.xml] 9 | 10 | [bumpversion:file:agent/pom.xml] 11 | 12 | [bumpversion:file:src/main/java/io/lumigo/core/configuration/Configuration.java] 13 | 14 | [bumpversion:file:scripts/prepare_layer_files.sh] 15 | 16 | [bumpversion:file:src/main/resources/lumigo-version.txt] 17 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | lumigo-orb: &lumigo_orb_version lumigo/lumigo-orb@volatile 5 | 6 | defaults: &defaults 7 | working_directory: ~/java-tracer 8 | docker: 9 | - image: lumigo/ci:latest 10 | auth: 11 | username: lumigo 12 | password: $DOCKERHUB_PASSWORD 13 | 14 | workflows: 15 | test-deploy: 16 | jobs: 17 | - lumigo-orb/print_orb_versions: 18 | lumigo_orb_version: *lumigo_orb_version 19 | 20 | - test: 21 | context: 22 | - common 23 | - java 24 | filters: 25 | branches: 26 | ignore: master 27 | 28 | - lumigo-orb/is_environment_available: 29 | context: common 30 | filters: 31 | branches: 32 | ignore: master 33 | 34 | - lumigo-orb/be-deploy: 35 | context: common 36 | save_project_folder: false 37 | requires: 38 | - lumigo-orb/is_environment_available 39 | 40 | - lumigo-orb/prep-it-resources: 41 | context: common 42 | requires: 43 | - lumigo-orb/is_environment_available 44 | 45 | - lumigo-orb/prep-k8s-and-operator: 46 | context: common 47 | requires: 48 | - lumigo-orb/is_environment_available 49 | 50 | - lumigo-orb/integration-test-parallel: 51 | context: common 52 | run_test_cleanup: false 53 | requires: 54 | - lumigo-orb/be-deploy 55 | - lumigo-orb/prep-it-resources 56 | - lumigo-orb/prep-k8s-and-operator 57 | 58 | - lumigo-orb/e2e-test: 59 | context: common 60 | requires: 61 | - lumigo-orb/be-deploy 62 | - lumigo-orb/prep-it-resources 63 | - lumigo-orb/prep-k8s-and-operator 64 | 65 | - lumigo-orb/integration-test-cleanup: 66 | name: post-test-cleanup 67 | context: common 68 | requires: 69 | - lumigo-orb/integration-test-parallel 70 | - lumigo-orb/e2e-test 71 | 72 | - lumigo-orb/workflow-completed-successfully: 73 | context: common 74 | requires: 75 | - lumigo-orb/integration-test-parallel 76 | - test 77 | - lumigo-orb/e2e-test 78 | 79 | - deploy: 80 | context: 81 | - common 82 | - java 83 | filters: 84 | branches: 85 | only: master 86 | 87 | jobs: 88 | test: 89 | <<: *defaults 90 | steps: 91 | - lumigo-orb/checkout_utils 92 | - lumigo-orb/checkout_code 93 | - run: ./scripts/checks.sh 94 | - run: ../utils/common_bash/defaults/code_cov.sh 95 | 96 | deploy: 97 | <<: *defaults 98 | steps: 99 | - lumigo-orb/checkout_utils 100 | - lumigo-orb/checkout_code 101 | - run: mvn clean install 102 | - run: ../utils/common_bash/defaults/code_cov.sh 103 | - run: cd .. && git clone git@github.com:lumigo-io/larn.git 104 | - run: ./scripts/bd_to_prod.sh 105 | -------------------------------------------------------------------------------- /.github/workflows/send-pr-details-on-close.yml: -------------------------------------------------------------------------------- 1 | name: Send PR close event to the environment manager 2 | 3 | on: 4 | pull_request: 5 | types: [closed] 6 | 7 | jobs: 8 | send-pr-close-event-to-env-manager: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Send PR Close Event To Env Manager 12 | run: | 13 | full_repository_name="${{github.repository}}" 14 | # the repository name is everything after the slash "lumigo-io/" in the full repository name 15 | repository_name="${full_repository_name#lumigo-io/}" 16 | 17 | source_branch_name="${{github.head_ref}}" 18 | 19 | is_merged="${{github.event.pull_request.merged}}" 20 | if [ "$is_merged" = "true" ]; then 21 | event="PR_MERGED" 22 | else 23 | event="PR_CLOSED" 24 | fi 25 | 26 | request_type="POST" 27 | route="v1/github_actions_trigger" 28 | 29 | body="{" 30 | body+=" \"event\": \"${event}\"" 31 | body+=" , \"repository_name\": \"${repository_name}\"" 32 | body+=" , \"source_branch_name\": \"${source_branch_name}\"" 33 | body+="}" 34 | 35 | params=(\ 36 | -s \ 37 | --header "x-api-key: ${{secrets.ENV_MANAGER_API_KEY}}" \ 38 | --header "Content-Type: application/json" \ 39 | --compressed \ 40 | --request "$request_type" \ 41 | --data "$body") 42 | 43 | # ENV_MANAGER_API_ROOT="https://XXXX.execute-api.us-west-2.amazonaws.com/prod/env-manager/v1" 44 | params+=("${{secrets.ENV_MANAGER_API_ROOT}}/${route}") 45 | curl "${params[@]}" | tr -d '\r' 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # IPython 78 | profile_default/ 79 | ipython_config.py 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | .dmypy.json 112 | dmypy.json 113 | 114 | # Pyre type checker 115 | .pyre/ 116 | 117 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 118 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 119 | 120 | # User-specific stuff 121 | .idea/**/workspace.xml 122 | .idea/**/tasks.xml 123 | .idea/**/usage.statistics.xml 124 | .idea/**/dictionaries 125 | .idea/**/shelf 126 | 127 | # Generated files 128 | .idea/**/contentModel.xml 129 | 130 | # Sensitive or high-churn files 131 | .idea/**/dataSources/ 132 | .idea/**/dataSources.ids 133 | .idea/**/dataSources.local.xml 134 | .idea/**/sqlDataSources.xml 135 | .idea/**/dynamic.xml 136 | .idea/**/uiDesigner.xml 137 | .idea/**/dbnavigator.xml 138 | 139 | # Gradle 140 | .idea/**/gradle.xml 141 | .idea/**/libraries 142 | 143 | # Gradle and Maven with auto-import 144 | # When using Gradle or Maven with auto-import, you should exclude module files, 145 | # since they will be recreated, and may cause churn. Uncomment if using 146 | # auto-import. 147 | .idea/ 148 | *.iml 149 | 150 | # CMake 151 | cmake-build-*/ 152 | 153 | # Mongo Explorer plugin 154 | .idea/**/mongoSettings.xml 155 | 156 | # File-based project format 157 | *.iws 158 | 159 | # IntelliJ 160 | out/ 161 | 162 | # mpeltonen/sbt-idea plugin 163 | .idea_modules/ 164 | 165 | # JIRA plugin 166 | atlassian-ide-plugin.xml 167 | 168 | # Cursive Clojure plugin 169 | .idea/replstate.xml 170 | 171 | # Crashlytics plugin (for Android Studio and IntelliJ) 172 | com_crashlytics_export_strings.xml 173 | crashlytics.properties 174 | crashlytics-build.properties 175 | fabric.properties 176 | 177 | # Editor-based Rest Client 178 | .idea/httpRequests 179 | 180 | # Android studio 3.1+ serialized cache file 181 | .idea/caches/build_file_checksums.ser 182 | 183 | # Logs 184 | logs 185 | *.log 186 | npm-debug.log 187 | 188 | # Runtime data 189 | pids 190 | *.pid 191 | *.seed 192 | dist 193 | 194 | # Directory for instrumented libs generated by jscoverage/JSCover 195 | lib-cov 196 | 197 | # Coverage directory used by tools like istanbul 198 | coverage 199 | 200 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 201 | .grunt 202 | 203 | # node-waf configuration 204 | .lock-wscript 205 | 206 | # Compiled binary addons (http://nodejs.org/api/addons.html) 207 | build/Release 208 | 209 | # Dependency directory 210 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 211 | node_modules 212 | 213 | #OS STUFF 214 | .DS_Store 215 | .tmp 216 | 217 | #SERVERLESS STUFF 218 | admin.env 219 | .env 220 | _meta 221 | .serverless 222 | package-lock.json 223 | src/main/resources/lumigo-agent.jar 224 | .vscode/ 225 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Tracer 2 | 3 | [![CircleCI](https://dl.circleci.com/status-badge/img/gh/lumigo-io/java-tracer/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/lumigo-io/java-tracer/tree/master) 4 | ![Version](https://img.shields.io/badge/version-1.0.49-green.svg) 5 | [![codecov](https://codecov.io/gh/lumigo-io/java-tracer/branch/master/graph/badge.svg?token=D3IZ5hQwaQ)](https://codecov.io/gh/lumigo-io/java-tracer) 6 | 7 | Supported Runtimes: Java 8, Java 11, Java 17, Java 21 8 | 9 | ## Building With Lumigo 10 | Include lumigo java tracer dependency 11 | 12 | ### Maven 13 | For [Maven](https://maven.apache.org) projects, use: 14 | 15 | ```xml 16 | 17 | 18 | lumigo 19 | https://raw.githubusercontent.com/lumigo-io/java-tracer/master/local-repository/ 20 | 21 | 22 | ``` 23 | 24 | ```xml 25 | 26 | io.lumigo 27 | java-tracer 28 | 1.0.49 29 | 30 | 31 | 32 | io.lumigo 33 | lumigo-agent 34 | 1.0.49 35 | 36 | ``` 37 | 38 | ### Gradle 39 | For [Gradle](https://gradle.org) projects, use: 40 | 41 | ```groovy 42 | repositories { 43 | maven { 44 | url 'https://raw.githubusercontent.com/lumigo-io/java-tracer/master/local-repository/' 45 | } 46 | } 47 | ``` 48 | 49 | ```groovy 50 | dependencies { 51 | implementation 'io.lumigo:java-tracer:1.0.49' 52 | implementation 'io.lumigo:lumigo-agent:1.0.49' 53 | } 54 | ``` 55 | 56 | Find the latest version here (the format of the version will be n.n.n): 57 | 58 | ## Wrapping your Lambda 59 | 60 | * Wrap your lambda function by implementing a supplier which contains your code 61 | 62 | ```java 63 | class MyFunction implements RequestHandler { 64 | 65 | @Override 66 | public OUTPUT handleRequest(INPUT event, Context context) { 67 | Supplier supplier = () -> { 68 | //Your lambda code 69 | //return ; 70 | }; 71 | return LumigoRequestExecutor.execute(event, context, supplier); 72 | } 73 | } 74 | ``` 75 | 76 | * For handler return void use: 77 | 78 | ```java 79 | class MyFunction implements RequestHandler { 80 | 81 | @Override 82 | public Void handleRequest(INPUT event, Context context) { 83 | Supplier supplier = () -> { 84 | //Your lambda code 85 | return null; 86 | }; 87 | return LumigoRequestExecutor.execute(event, context, supplier); 88 | } 89 | } 90 | ``` 91 | 92 | ## Lambda Auto tracing with lambda layer 93 | 94 | * Add to your lambda a new layer with the arn from here 95 | * Add environment variable `JAVA_TOOL_OPTIONS` and set it to `-javaagent:/opt/lumigo-java/lumigo-agent.jar` (This is instead of the flag for more than java11 support) 96 | * Add the `LUMIGO_TRACER_TOKEN` env var. 97 | 98 | 99 | ### Configuration 100 | 101 | There are 2 way to pass configuration properties 102 | 103 | #### Environment variables 104 | 105 | Adding `LUMIGO_TRACER_TOKEN` environment variables 106 | 107 | #### Static code initiation 108 | 109 | ```java 110 | class MyFunction implements RequestHandler { 111 | 112 | static{ 113 | LumigoConfiguration.builder().token("xxx").build().init(); 114 | } 115 | 116 | @Override 117 | public String handleRequest(String event, Context context) { 118 | Supplier supplier = () -> { 119 | //Your lambda code 120 | return ""; 121 | }; 122 | return LumigoRequestExecutor.execute(event, context, supplier); 123 | } 124 | } 125 | ``` 126 | 127 | ### Support Java 11 and Above 128 | 129 | Add the environment variable `JAVA_TOOL_OPTIONS` to your Lambda functions and set it to 130 | `-Djdk.attach.allowAttachSelf=true` in addition to the manual code mentioned above (This is not needed for the auto trace with lambda layer). 131 | 132 | ### Supported Instrumentation Libraries 133 | 134 | - Aws SDK V1 135 | - Aws SDK V2 136 | - Apache HTTP Client 137 | - Apache Kafka 138 | 139 | ### Secret scrubbing 140 | 141 | The tracer will automatically scrub values for keys in payload objects such as HTTP request / response body, Lambda events, return value etc. that match (case-sensitively) the following regex patterns at any depth: 142 | - `.*pass.*` 143 | - `.*key.*` 144 | - `.*secret.*` 145 | - `.*credential.*` 146 | - `.*passphrase.*` 147 | - `SessionToken` 148 | - `x-amz-security-token` 149 | - `Signature` 150 | - `Authorization` 151 | 152 | This behavior can be overridden by setting the `LUMIGO_SECRET_MASKING_REGEX` environment variable to a JSON array of regex patterns to match, e.g.: `[".+top.secret.+", ".+pazzword.+"]`. 153 | 154 | #### Notes 155 | 1. providing a bad regex pattern (e.g., invalid JSON string) will result in an error and fallback to the default patterns. 156 | 2. Only values that are strings are redacted - objects, numbers etc. will stay intact even though their keys match the patterns. 157 | 158 | #### Escaping special characters 159 | When the patterns contain special characters such as double quotes (`"`) or backslashes (`\`), those should be escaped with a backslash (`\`). 160 | 161 | For example, the pattern for keys with whitespaces and quotes like `"key\s+spaced"` becomes `\"key\\\\s+spaced\"`. That's because each double quotes turns into `\"`, and the `\s+` expression requires the backslash character to be escaped both in the string context (`\s+` => `\\s+`) and again in a JSON string context (`\\s+` => `\\\\s+`). When placed into the env-var as an array-item, this becomes: 162 | ``` 163 | ["\\"key\\\\s+spaced\\""] 164 | ``` 165 | 166 | #### Examples 167 | 168 | `LUMIGO_SECRET_MASKING_REGEX` set to `[".*top\\\\s+secret.*", ".*password.*"]` for a payload object like: 169 | ```json 170 | { 171 | "top secret": { 172 | "password": "123456" 173 | }, 174 | "top secret object": { 175 | "this will not be scrubbed since the parent is an object": "123456" 176 | }, 177 | "password": "123456", 178 | "top secret:": "123456", 179 | "not so secret": "value", 180 | "ToP sEcReT": "is case sensitive" 181 | } 182 | ``` 183 | will result in the following payload shown in the Lumigo platform: 184 | ```json 185 | { 186 | "top secret": { 187 | "password": "****" 188 | }, 189 | "top secret object": { 190 | "this will not be scrubbed since the parent is an object": "123456" 191 | }, 192 | "password": "****", 193 | "top secret:": "****", 194 | "not so secret": "value", 195 | "ToP sEcReT": "is case sensitive" 196 | } 197 | ``` 198 | 199 | -------------------------------------------------------------------------------- /agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | 8 | 9 | 1.8 10 | 1.8 11 | UTF-8 12 | 13 | io.lumigo 14 | lumigo-agent 15 | 1.0.49 16 | 17 | Lumigo java tracer agent 18 | The Lumigo java tracer for serverless functions 19 | https://lumigo.io/ 20 | 21 | 22 | https://github.com/lumigo-io/java-tracer 23 | scm:git:https://github.com:lumigo-io/java-tracer.git 24 | scm:git:https://github.com:lumigo-io/java-tracer.git 25 | 1.0.49 26 | 27 | 28 | 29 | 30 | Lumigo Dev Team 31 | dev@lumigo.io 32 | Lumigo 33 | https://lumigo.io/ 34 | 35 | 36 | 37 | 38 | Lumigo 39 | https://lumigo.io/ 40 | 41 | 42 | 43 | 44 | Apache License, Version 2.0 45 | http://www.apache.org/licenses/LICENSE-2.0.txt 46 | repo 47 | 48 | 49 | 50 | 51 | 52 | ossrh 53 | https://oss.sonatype.org/content/repositories/snapshots 54 | 55 | 56 | ossrh 57 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-javadoc-plugin 67 | 3.1.0 68 | 69 | 1.8 70 | 71 | 72 | 73 | attach-javadocs 74 | 75 | jar 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-source-plugin 83 | 3.0.1 84 | 85 | 86 | attach-sources 87 | 88 | jar 89 | 90 | 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-jar-plugin 96 | 2.4 97 | 98 | 99 | 100 | io.lumigo.agent.Agent 101 | io.lumigo.agent.Agent 102 | io.lumigo.agent.Agent 103 | true 104 | true 105 | false 106 | Lumigo 107 | 108 | 109 | 110 | 111 | 112 | org.apache.maven.plugins 113 | maven-gpg-plugin 114 | 1.6 115 | 116 | 117 | sign-artifacts 118 | deploy 119 | 120 | sign 121 | 122 | 123 | 124 | --pinentry-mode 125 | loopback 126 | 127 | ${GPG_PASSPHRASE} 128 | 129 | 130 | 131 | 132 | 133 | org.sonatype.plugins 134 | nexus-staging-maven-plugin 135 | 1.6.8 136 | true 137 | 138 | ossrh 139 | https://oss.sonatype.org/ 140 | false 141 | 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-antrun-plugin 146 | 1.8 147 | 148 | 149 | package 150 | 151 | 152 | 154 | 155 | 156 | 157 | run 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /agent/src/main/java/io/lumigo/agent/Agent.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.agent; 2 | 3 | import java.io.File; 4 | import java.lang.instrument.Instrumentation; 5 | import java.lang.reflect.Method; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | import java.net.URLClassLoader; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | import java.util.jar.JarFile; 15 | import java.util.stream.Collectors; 16 | import java.util.stream.Stream; 17 | 18 | public class Agent { 19 | 20 | private static boolean initialized = false; 21 | 22 | public static String LUMIGO_JAVA_TRACER_PATH = "/opt/lumigo-java/lumigo-tracer.jar"; 23 | 24 | public static void premain(String agentArgs, Instrumentation inst) { 25 | if (!isKillSwitchOn()) { 26 | agentmain(agentArgs, inst); 27 | } 28 | } 29 | 30 | public static void agentmain(String agentArgs, Instrumentation inst) { 31 | if (initialized) { 32 | return; 33 | } 34 | initialized = true; 35 | try { 36 | URL[] urls; 37 | if ("lib".equalsIgnoreCase(agentArgs)) { 38 | urls = getUrls(); 39 | } else { 40 | urls = 41 | new URL[] { 42 | new File("/var/task/").toURI().toURL(), 43 | new File(LUMIGO_JAVA_TRACER_PATH).toURI().toURL() 44 | }; 45 | } 46 | installTracerJar(inst); 47 | URLClassLoader newClassLoader = new URLClassLoader(urls, null); 48 | Thread.currentThread().setContextClassLoader(newClassLoader); 49 | final Class loader = 50 | newClassLoader.loadClass("io.lumigo.core.instrumentation.agent.Loader"); 51 | final Method instrument = loader.getMethod("instrument", Instrumentation.class); 52 | instrument.invoke(null, inst); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | 58 | private static void installTracerJar(Instrumentation inst) { 59 | try (JarFile jar = new JarFile(new File(new File(LUMIGO_JAVA_TRACER_PATH).toURI()))) { 60 | inst.appendToSystemClassLoaderSearch(jar); 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | public static URL[] getUrls() { 67 | List jars = new LinkedList<>(); 68 | try (Stream paths = Files.walk(Paths.get("/var/task/lib"))) { 69 | jars.add(new File("/var/task/").toURI().toURL()); 70 | jars.addAll( 71 | paths.map( 72 | path -> { 73 | try { 74 | return path.toFile().toURI().toURL(); 75 | } catch (MalformedURLException e) { 76 | e.printStackTrace(); 77 | return null; 78 | } 79 | }) 80 | .filter(v -> v != null) 81 | .collect(Collectors.toList())); 82 | } catch (Throwable e) { 83 | e.printStackTrace(); 84 | } 85 | return jars.toArray(new URL[jars.size()]); 86 | } 87 | 88 | public static boolean isKillSwitchOn() { 89 | String value = System.getenv("LUMIGO_SWITCH_OFF"); 90 | return "true".equalsIgnoreCase(value); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /findbugs/findbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /java-sign-keys/private.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PRIVATE KEY BLOCK----- 2 | 3 | lIYEYUsoHxYJKwYBBAHaRw8BAQdAHJKNVXSghhFHizfXVlMfHKpiY3t7pG9M10dh 4 | /UVQjPP+BwMCGD6DLbX4JzL6Z+kjAwIPe5RwAsz/+EZnByd2yYSWwMWbsvWEggIg 5 | sCym3rhM+9yraiv6IBoy533Nlo3rVKtVM9zFZXlKwJZ3TIlGxJ6foLQbQXZpYWQg 6 | TW9yIDxhdmlhZEBsdW1pZ28uaW8+iJoEExYKAEICGwMFCwkIBwIDIgIBBhUKCQgL 7 | AgQWAgMBAh4HAheAFiEE0Kj6l7eMRJwp8k0qafyGjeJOpTMFAmY4t6sFCQ5TkQwA 8 | CgkQafyGjeJOpTM9aQEAtoa69rbVGLz5otBEvVwTRjcOeS5ZnsqQLbdSEqTjePcA 9 | /RHyK0STbNNB+MGt6Acf67q8StqOE4+04thmj89MXbILiJoEExYKAEIWIQTQqPqX 10 | t4xEnCnyTSpp/IaN4k6lMwUCYUsoHwIbAwUJA8JnAAULCQgHAgMiAgEGFQoJCAsC 11 | BBYCAwECHgcCF4AACgkQafyGjeJOpTNlVwD/Q0L+knrkUFCgztSQuCDk7E+ejmkh 12 | kGS0JCkQjFawg9kBAO8XCjtENpvZlsfrwBD3nX+GlJCPKmK7dTiTtYW3GrsPnIsE 13 | YUsoHxIKKwYBBAGXVQEFAQEHQIIQZa17fX3fr6autiqjsFsFVzS3zgarHuTiF2F2 14 | Z2t2AwEIB/4HAwJb5Bg/gIqzbfpUCvEN6h9l6ERaRdc3yQQ53LgNzAQOWozSSKdJ 15 | LxO2/smT5J3rI63IKYtEbdO39ixQc3MmTxONJity0sIwFdQO3KvyDqzbiH4EGBYK 16 | ACYWIQTQqPqXt4xEnCnyTSpp/IaN4k6lMwUCYUsoHwIbDAUJA8JnAAAKCRBp/IaN 17 | 4k6lM8jAAP0RPvEXNhlZOaJh1GHit1hQnf1ZNJg8UcNKU2Wh+VaRQwD/VwEw6Jto 18 | KxeQ48ye4eyDb9P84VpnD6Y4JwlZm2PjDg0= 19 | =9HX9 20 | -----END PGP PRIVATE KEY BLOCK----- 21 | -------------------------------------------------------------------------------- /java-sign-keys/public.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mDMEYUsoHxYJKwYBBAHaRw8BAQdAHJKNVXSghhFHizfXVlMfHKpiY3t7pG9M10dh 4 | /UVQjPO0G0F2aWFkIE1vciA8YXZpYWRAbHVtaWdvLmlvPoiaBBMWCgBCAhsDBQsJ 5 | CAcCAyICAQYVCgkICwIEFgIDAQIeBwIXgBYhBNCo+pe3jEScKfJNKmn8ho3iTqUz 6 | BQJmOLerBQkOU5EMAAoJEGn8ho3iTqUzPWkBALaGuva21Ri8+aLQRL1cE0Y3Dnku 7 | WZ7KkC23UhKk43j3AP0R8itEk2zTQfjBregHH+u6vErajhOPtOLYZo/PTF2yC4ia 8 | BBMWCgBCFiEE0Kj6l7eMRJwp8k0qafyGjeJOpTMFAmFLKB8CGwMFCQPCZwAFCwkI 9 | BwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEGn8ho3iTqUzZVcA/0NC/pJ65FBQ 10 | oM7UkLgg5OxPno5pIZBktCQpEIxWsIPZAQDvFwo7RDab2ZbH68AQ951/hpSQjypi 11 | u3U4k7WFtxq7D7g4BGFLKB8SCisGAQQBl1UBBQEBB0CCEGWte31936+mrrYqo7Bb 12 | BVc0t84Gqx7k4hdhdmdrdgMBCAeIfgQYFgoAJhYhBNCo+pe3jEScKfJNKmn8ho3i 13 | TqUzBQJhSygfAhsMBQkDwmcAAAoJEGn8ho3iTqUzyMAA/RE+8Rc2GVk5omHUYeK3 14 | WFCd/Vk0mDxRw0pTZaH5VpFDAP9XATDom2grF5DjzJ7h7INv0/zhWmcPpjgnCVmb 15 | Y+MODQ== 16 | =iIe4 17 | -----END PGP PUBLIC KEY BLOCK----- 18 | -------------------------------------------------------------------------------- /layers/LAYERS.md: -------------------------------------------------------------------------------- 1 | Layers runtime: java11 2 | ---- 3 | | Region | ARN | 4 | | --- | --- | 5 | |US East (N. Virginia) `us-east-1`|`arn:aws:lambda:us-east-1:114300393969:layer:lumigo-java-tracer:1`| 6 | |US East (Ohio) `us-east-2`|`arn:aws:lambda:us-east-2:114300393969:layer:lumigo-java-tracer:1`| 7 | |US West (N. California) `us-west-1`|`arn:aws:lambda:us-west-1:114300393969:layer:lumigo-java-tracer:1`| 8 | |US West (Oregon) `us-west-2`|`arn:aws:lambda:us-west-2:114300393969:layer:lumigo-java-tracer:1`| 9 | |Canada (Central) `ca-central-1`|`arn:aws:lambda:ca-central-1:114300393969:layer:lumigo-java-tracer:1`| 10 | |EU (Stockholm) `eu-north-1`|`arn:aws:lambda:eu-north-1:114300393969:layer:lumigo-java-tracer:1`| 11 | |EU (Ireland) `eu-west-1`|`arn:aws:lambda:eu-west-1:114300393969:layer:lumigo-java-tracer:1`| 12 | |EU (London) `eu-west-2`|`arn:aws:lambda:eu-west-2:114300393969:layer:lumigo-java-tracer:1`| 13 | |EU (Paris) `eu-west-3`|`arn:aws:lambda:eu-west-3:114300393969:layer:lumigo-java-tracer:1`| 14 | |EU (Frankfurt) `eu-central-1`|`arn:aws:lambda:eu-central-1:114300393969:layer:lumigo-java-tracer:1`| 15 | |Asia Pacific (Tokyo) `ap-northeast-1`|`arn:aws:lambda:ap-northeast-1:114300393969:layer:lumigo-java-tracer:1`| 16 | |Asia Pacific (Seoul) `ap-northeast-2`|`arn:aws:lambda:ap-northeast-2:114300393969:layer:lumigo-java-tracer:1`| 17 | |Asia Pacific (Singapore) `ap-southeast-1`|`arn:aws:lambda:ap-southeast-1:114300393969:layer:lumigo-java-tracer:1`| 18 | |Asia Pacific (Sydney) `ap-southeast-2`|`arn:aws:lambda:ap-southeast-2:114300393969:layer:lumigo-java-tracer:1`| 19 | |Asia Pacific (Hong Kong) `ap-east-1`|`arn:aws:lambda:ap-east-1:114300393969:layer:lumigo-java-tracer:1`| 20 | |Asia Pacific (Mumbai) `ap-south-1`|`arn:aws:lambda:ap-south-1:114300393969:layer:lumigo-java-tracer:1`| 21 | |South America (São Paulo) `sa-east-1`|`arn:aws:lambda:sa-east-1:114300393969:layer:lumigo-java-tracer:1`| 22 | |Middle East (Bahrain) `me-south-1`|`arn:aws:lambda:me-south-1:114300393969:layer:lumigo-java-tracer:1`| 23 | |Africa (Cape Town) `af-south-1`|`arn:aws:lambda:af-south-1:114300393969:layer:lumigo-java-tracer:1`| 24 | -------------------------------------------------------------------------------- /libs/google-java-format-1.7-all-deps.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumigo-io/java-tracer/fb3d173b68bee11be547186205c59bfd73a064d4/libs/google-java-format-1.7-all-deps.jar -------------------------------------------------------------------------------- /local-repository/com/sun/tools/1.7.0.13/_remote.repositories: -------------------------------------------------------------------------------- 1 | #NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. 2 | #Mon May 27 18:27:50 IDT 2019 3 | tools-1.7.0.13.jar>nuiton= 4 | tools-1.7.0.13.pom>nuiton= 5 | -------------------------------------------------------------------------------- /local-repository/com/sun/tools/1.7.0.13/tools-1.7.0.13.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumigo-io/java-tracer/fb3d173b68bee11be547186205c59bfd73a064d4/local-repository/com/sun/tools/1.7.0.13/tools-1.7.0.13.jar -------------------------------------------------------------------------------- /local-repository/com/sun/tools/1.7.0.13/tools-1.7.0.13.jar.lastUpdated: -------------------------------------------------------------------------------- 1 | #NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. 2 | #Sat Jun 06 21:03:45 IDT 2020 3 | http\://maven.nuiton.org/release/.lastUpdated=1558970870445 4 | https\://repo.maven.apache.org/maven2/.lastUpdated=1591466625833 5 | https\://repo.maven.apache.org/maven2/.error= 6 | -------------------------------------------------------------------------------- /local-repository/com/sun/tools/1.7.0.13/tools-1.7.0.13.jar.sha1: -------------------------------------------------------------------------------- 1 | b131b8c9b481c4860cc3e08ede96f2c545a369ad -------------------------------------------------------------------------------- /local-repository/com/sun/tools/1.7.0.13/tools-1.7.0.13.pom: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.sun 4 | tools 5 | 1.7.0.13 6 | 7 | 8 | 9 | GPLv2 10 | http://www.gnu.org/licenses/gpl-2.0.txt 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /local-repository/com/sun/tools/1.7.0.13/tools-1.7.0.13.pom.lastUpdated: -------------------------------------------------------------------------------- 1 | #NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. 2 | #Sat Jun 06 21:03:45 IDT 2020 3 | http\://maven.nuiton.org/release/.lastUpdated=1558970865273 4 | https\://repo.maven.apache.org/maven2/.lastUpdated=1591466625435 5 | https\://repo.maven.apache.org/maven2/.error= 6 | -------------------------------------------------------------------------------- /local-repository/com/sun/tools/1.7.0.13/tools-1.7.0.13.pom.sha1: -------------------------------------------------------------------------------- 1 | 4c8442dd709bba959299729a568bf97c202efe78 -------------------------------------------------------------------------------- /maven/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | /home/circleci/.m2/repository 4 | 5 | 6 | ossrh 7 | ${env.SONATYPE_USERNAME} 8 | ${env.SONATYPE_PASSWORD} 9 | 10 | 11 | 12 | 13 | 14 | true 15 | 16 | 17 | gpg 18 | ${env.GPG_PASSPHARSE} 19 | 20 | ossrh 21 | 22 | 23 | 24 | ossrh 25 | 26 | 27 | org.apache.maven.plugins 28 | org.codehaus.mojo 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /scripts/bd_to_prod.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeo pipefail 3 | 4 | setup_git() { 5 | git config --global user.email "no-reply@build.com" 6 | git config --global user.name "CircleCI" 7 | git checkout master 8 | # Avoid npm version failure 9 | git stash 10 | } 11 | 12 | push_tags() { 13 | git push origin master --tags 14 | } 15 | 16 | echo ".____ .__ .__ "; 17 | echo "| | __ __ _____ |__| ____ ____ |__| ____ "; 18 | echo "| | | | \/ \| |/ ___\ / _ \ | |/ _ \ "; 19 | echo "| |___| | / Y Y \ / /_/ > <_> ) | ( <_> )"; 20 | echo "|_______ \____/|__|_| /__\___ / \____/ /\ |__|\____/ "; 21 | echo " \/ \/ /_____/ \/ "; 22 | echo 23 | echo "Deploy lumigo-java-tracer to maven repository server" 24 | 25 | enc_location=../common-resources/encrypted_files/credentials_production.enc 26 | if [[ ! -f ${enc_location} ]] 27 | then 28 | echo "$enc_location not found" 29 | exit 1 30 | fi 31 | echo "Creating new credential files" 32 | mkdir -p ~/.aws 33 | echo ${KEY} | gpg --batch -d --passphrase-fd 0 ${enc_location} > ~/.aws/credentials 34 | 35 | setup_git 36 | echo "Getting latest changes from git" 37 | changes=$(git log $(git describe --tags --abbrev=0)..HEAD --oneline) 38 | 39 | pip install --upgrade bumpversion 40 | bumpversion patch --message "{current_version} → {new_version}. Changes: ${changes}" 41 | 42 | echo "Override maven settings" 43 | cp -rf maven/settings.xml /usr/share/maven/conf 44 | echo "Import gpg key" 45 | export GPG_TTY=$(tty) 46 | gpg --batch --passphrase "$GPG_PASSPHRASE" --import java-sign-keys/private.asc 47 | echo "Uploading lumigo java tracer to maven central repository" 48 | mvn -f agent/pom.xml clean deploy 49 | mvn -f agent/pom.xml nexus-staging:release 50 | mvn -Dmaven.test.skip=true -Dfindbugs.skip=true clean deploy 51 | mvn nexus-staging:release 52 | 53 | echo "Creating lumigo-java-tracer layer" 54 | ./scripts/prepare_layer_files.sh 55 | 56 | echo "Creating layer latest version arn table md file (LAYERS.md)" 57 | commit_version="$(git describe --abbrev=0 --tags)" 58 | ../utils/common_bash/create_layer.sh \ 59 | --layer-name lumigo-java-tracer \ 60 | --region ALL \ 61 | --package-folder lumigo-java \ 62 | --version "$commit_version" \ 63 | --runtimes "java11 java17 java21" 64 | 65 | cd ../larn && npm i -g 66 | larn -r java11 -n layers/LAYERS --filter lumigo-java-tracer -p ~/java-tracer 67 | cd ../java-tracer 68 | 69 | git add layers/LAYERS.md 70 | git commit -m "docs: update layers md [skip ci]" 71 | git push origin master 72 | 73 | echo "Create release tag" 74 | push_tags 75 | 76 | echo "Done" 77 | -------------------------------------------------------------------------------- /scripts/checks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | java -jar libs/google-java-format-1.7-all-deps.jar --set-exit-if-changed -i -a $(find . -type f -name "*.java" | grep ".*/src/.*java") 5 | mvn -Djava.security.manager=allow -f agent/pom.xml clean package 6 | mvn -Djava.security.manager=allow clean package -------------------------------------------------------------------------------- /scripts/ci_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | pushd "$(dirname "$0")" &> /dev/null 5 | # Go back one spot because we are on scripts dir. The other scripts assume you are in the root folder 6 | cd .. 7 | ../utils/common_bash/defaults/ci_deploy.sh java-tracer 8 | popd &> /dev/null -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | mvn -f agent/pom.xml clean install 5 | mvn -Dmaven.test.skip=true -Dfindbugs.skip=true clean install -------------------------------------------------------------------------------- /scripts/prepare_layer_files.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p lumigo-java 4 | 5 | MVN_DEFAULT_FLAGS="-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10" 6 | 7 | mvn "$MVN_DEFAULT_FLAGS" -Dmaven.test.skip=true -Dfindbugs.skip=true -DincludeShade=true clean package --quiet 8 | mvn "$MVN_DEFAULT_FLAGS" -f agent/pom.xml clean package --quiet 9 | 10 | cp target/java-tracer-1.0.49.jar lumigo-java/lumigo-tracer.jar 11 | cp agent/target/lumigo-agent-1.0.49.jar lumigo-java/lumigo-agent.jar 12 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/configuration/Configuration.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.configuration; 2 | 3 | import io.lumigo.core.instrumentation.agent.Installer; 4 | import io.lumigo.core.utils.EnvUtil; 5 | import io.lumigo.handlers.LumigoConfiguration; 6 | import java.time.Duration; 7 | import java.util.Locale; 8 | import lombok.Setter; 9 | import org.pmw.tinylog.Configurator; 10 | import org.pmw.tinylog.Level; 11 | import org.pmw.tinylog.Logger; 12 | import org.pmw.tinylog.writers.ConsoleWriter; 13 | 14 | public class Configuration { 15 | private static final String EDGE_PREFIX = "https://"; 16 | private static final String EDGE_DEFAULT_URL = "%s.lumigo-tracer-edge.golumigo.com"; 17 | private static final String EDGE_SUFFIX = "/api/spans"; 18 | 19 | public static final String TOKEN_KEY = "LUMIGO_TRACER_TOKEN"; 20 | public static final String TRACER_HOST_KEY = "LUMIGO_TRACER_HOST"; 21 | public static final String DEBUG_KEY = "LUMIGO_DEBUG"; 22 | public static final String REGION_KEY = "AWS_REGION"; 23 | public static final String LUMIGO_VERBOSE = "LUMIGO_VERBOSE"; 24 | public static final String REPORTER_TIMEOUT = "LUMIGO_REPORTER_TIMEOUT"; 25 | public static final String LUMIGO_KILL_SWITCH = "LUMIGO_SWITCH_OFF"; 26 | public static final String LUMIGO_MAX_ENTRY_SIZE = "LUMIGO_MAX_ENTRY_SIZE"; 27 | public static final String LUMIGO_MAX_RESPONSE_SIZE = "LUMIGO_MAX_RESPONSE_SIZE"; 28 | public static final String LUMIGO_MAX_SIZE_FOR_REQUEST = "LUMIGO_MAX_SIZE_FOR_REQUEST"; 29 | public static final String LUMIGO_INSTRUMENTATION = "LUMIGO_INSTRUMENTATION"; 30 | public static final String LUMIGO_SECRET_MASKING_REGEX = "LUMIGO_SECRET_MASKING_REGEX"; 31 | public static final String LUMIGO_MAX_BATCH_MESSAGE_IDS = "LUMIGO_MAX_BATCH_MESSAGE_IDS"; 32 | 33 | private static Configuration instance; 34 | private LumigoConfiguration inlineConf; 35 | 36 | @Setter private EnvUtil envUtil = new EnvUtil(); 37 | 38 | public static void init(LumigoConfiguration lumigoConfiguration) { 39 | if (lumigoConfiguration == null) { 40 | getInstance().inlineConf = LumigoConfiguration.builder().build(); 41 | } else { 42 | getInstance().inlineConf = lumigoConfiguration; 43 | } 44 | if (!getInstance().inlineConf.getLazyLoading()) { 45 | Logger.info("Lazy load was set as false, install agent now"); 46 | Installer.install(); 47 | } 48 | } 49 | 50 | private Configuration() { 51 | inlineConf = LumigoConfiguration.builder().build(); 52 | } 53 | 54 | public static synchronized Configuration getInstance() { 55 | if (instance == null) { 56 | instance = new Configuration(); 57 | Configurator.currentConfig() 58 | .writer(new ConsoleWriter()) 59 | .locale(Locale.US) 60 | .level(instance.getLogLevel()) 61 | .maxStackTraceElements(-1) 62 | .formatPattern( 63 | "{date:yyyy-MM-dd HH:mm:ss} {level} [thread-{thread}] {class}.{method}() - {message}") 64 | .activate(); 65 | } 66 | return instance; 67 | } 68 | 69 | public String getLumigoToken() { 70 | return inlineConf.getToken() != null ? inlineConf.getToken() : envUtil.getEnv(TOKEN_KEY); 71 | } 72 | 73 | public String getLumigoEdge() { 74 | String url = 75 | inlineConf.getEdgeHost() != null 76 | ? inlineConf.getEdgeHost() 77 | : envUtil.getEnv(TRACER_HOST_KEY); 78 | if (url == null) { 79 | url = String.format(EDGE_DEFAULT_URL, envUtil.getEnv(REGION_KEY)); 80 | } 81 | return EDGE_PREFIX + url + EDGE_SUFFIX; 82 | } 83 | 84 | public Level getLogLevel() { 85 | return envUtil.getBooleanEnv(DEBUG_KEY, false) ? Level.DEBUG : Level.OFF; 86 | } 87 | 88 | public String getLumigoTracerVersion() { 89 | return "1.0.49"; 90 | } 91 | 92 | public Duration getLumigoTimeout() { 93 | String timeout = envUtil.getEnv(REPORTER_TIMEOUT); 94 | if (timeout != null) { 95 | return Duration.ofMillis(Long.parseLong(timeout)); 96 | } 97 | return Duration.ofMillis(3000); 98 | } 99 | 100 | public int maxSpanFieldSize() { 101 | return envUtil.getIntegerEnv(LUMIGO_MAX_ENTRY_SIZE, 1024); 102 | } 103 | 104 | public int maxSpanFieldSizeWhenError() { 105 | return maxSpanFieldSize() * 10; 106 | } 107 | 108 | public boolean isAwsEnvironment() { 109 | return envUtil.getEnv("LAMBDA_RUNTIME_DIR") != null; 110 | } 111 | 112 | public boolean isLumigoVerboseMode() { 113 | return inlineConf.getVerbose() != null 114 | ? inlineConf.getVerbose() 115 | : envUtil.getBooleanEnv(LUMIGO_VERBOSE, true); 116 | } 117 | 118 | public boolean isKillingSwitchActivated() { 119 | return inlineConf.getKillSwitch() != null 120 | ? inlineConf.getKillSwitch() 121 | : envUtil.getBooleanEnv(LUMIGO_KILL_SWITCH, false); 122 | } 123 | 124 | public boolean isLumigoHost(String host) { 125 | return host.contains(getLumigoEdge().replace(EDGE_PREFIX, "").replace(EDGE_SUFFIX, "")); 126 | } 127 | 128 | public boolean isAwsHost(String host) { 129 | return host.endsWith("amazonaws.com"); 130 | } 131 | 132 | public boolean isInstrumentationEnabled() { 133 | return envUtil.getBooleanEnv(LUMIGO_INSTRUMENTATION, true); 134 | } 135 | 136 | public int maxRequestSize() { 137 | return envUtil.getIntegerEnv( 138 | LUMIGO_MAX_SIZE_FOR_REQUEST, 139 | envUtil.getIntegerEnv(LUMIGO_MAX_RESPONSE_SIZE, 1024 * 500)); 140 | } 141 | 142 | public int maxBatchMessageIds() { 143 | int value = envUtil.getIntegerEnv(LUMIGO_MAX_BATCH_MESSAGE_IDS, 20); 144 | if (value == 0) { 145 | value = 20; 146 | } 147 | return value; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/LumigoInstrumentationApi.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation; 2 | 3 | import net.bytebuddy.agent.builder.AgentBuilder; 4 | import net.bytebuddy.description.type.TypeDescription; 5 | import net.bytebuddy.matcher.ElementMatcher; 6 | 7 | public interface LumigoInstrumentationApi { 8 | 9 | ElementMatcher getTypeMatcher(); 10 | 11 | AgentBuilder.Transformer.ForAdvice getTransformer(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/agent/Installer.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.agent; 2 | 3 | import com.sun.tools.attach.VirtualMachine; 4 | import com.sun.tools.attach.VirtualMachineDescriptor; 5 | import io.lumigo.core.configuration.Configuration; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.List; 10 | import java.util.stream.Stream; 11 | import org.pmw.tinylog.Logger; 12 | 13 | public class Installer { 14 | private static boolean firstStart = true; 15 | 16 | public static synchronized void install() { 17 | if (Configuration.getInstance().isAwsEnvironment() 18 | && Configuration.getInstance().isInstrumentationEnabled() 19 | && firstStart) { 20 | firstStart = false; 21 | Logger.info("Agent installation start"); 22 | List vms = VirtualMachine.list(); 23 | String agentPath = findAgentPath(); 24 | Logger.info("Loading agent jar: {}", agentPath); 25 | for (VirtualMachineDescriptor vmd : vms) { 26 | try { 27 | VirtualMachine vm = VirtualMachine.attach(vmd.id()); 28 | try { 29 | if (agentPath.contains("lib")) { 30 | vm.loadAgent(agentPath, "lib"); 31 | } else { 32 | vm.loadAgent(agentPath); 33 | } 34 | } finally { 35 | vm.detach(); 36 | } 37 | } catch (Throwable e) { 38 | Logger.error(e, "Fail to attach agent, no instrumentation"); 39 | } 40 | } 41 | } else { 42 | Logger.info( 43 | "Agent installation is skipped, isAwsEnvironment: {}, isInstrumentationEnabled: {}, isFirstStart: {}", 44 | Configuration.getInstance().isAwsEnvironment(), 45 | Configuration.getInstance().isInstrumentationEnabled(), 46 | firstStart); 47 | } 48 | } 49 | 50 | public static String findAgentPath() { 51 | if (Files.notExists(Paths.get("/var/task/lumigo-agent.jar"))) { 52 | Logger.info( 53 | "Agent jar was not found under /var/task/lumigo-agent.jar, try to find it under /var/task/lib"); 54 | try (Stream paths = Files.walk(Paths.get("/var/task/lib"))) { 55 | Path lumigoAgentPath = 56 | paths.filter(p -> p.toFile().getAbsolutePath().contains("lumigo-agent")) 57 | .findFirst() 58 | .get(); 59 | return lumigoAgentPath.toFile().getAbsolutePath(); 60 | } catch (Exception e) { 61 | Logger.error(e); 62 | return "/var/task/lumigo-agent.jar"; 63 | } 64 | } else { 65 | return "/var/task/lumigo-agent.jar"; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/agent/Loader.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.agent; 2 | 3 | import io.lumigo.core.instrumentation.impl.*; 4 | import net.bytebuddy.agent.builder.AgentBuilder; 5 | import org.pmw.tinylog.Logger; 6 | 7 | @SuppressWarnings("unused") 8 | public class Loader { 9 | public static void instrument(java.lang.instrument.Instrumentation inst) { 10 | Logger.debug("Start Instrumentation"); 11 | ApacheHttpInstrumentation apacheHttpInstrumentation = new ApacheHttpInstrumentation(); 12 | AmazonHttpClientInstrumentation amazonHttpClientInstrumentation = 13 | new AmazonHttpClientInstrumentation(); 14 | AmazonHttpClientV2Instrumentation amazonHttpClientV2Instrumentation = 15 | new AmazonHttpClientV2Instrumentation(); 16 | ApacheKafkaProducerInstrumentation apacheKafkaInstrumentation = 17 | new ApacheKafkaProducerInstrumentation(); 18 | ApacheKafkaConsumerInstrumentation apacheKafkaConsumerInstrumentation = 19 | new ApacheKafkaConsumerInstrumentation(); 20 | AwsLambdaRequestHandlerInstrumentation awsLambdaRequestHandlerInstrumentation = 21 | new AwsLambdaRequestHandlerInstrumentation(); 22 | AgentBuilder builder = 23 | new AgentBuilder.Default() 24 | .disableClassFormatChanges() 25 | .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) 26 | .type(apacheHttpInstrumentation.getTypeMatcher()) 27 | .transform(apacheHttpInstrumentation.getTransformer()) 28 | .type(amazonHttpClientInstrumentation.getTypeMatcher()) 29 | .transform(amazonHttpClientInstrumentation.getTransformer()) 30 | .type(amazonHttpClientV2Instrumentation.getTypeMatcher()) 31 | .transform(amazonHttpClientV2Instrumentation.getTransformer()) 32 | .type(apacheKafkaInstrumentation.getTypeMatcher()) 33 | .transform(apacheKafkaInstrumentation.getTransformer()) 34 | .type(apacheKafkaConsumerInstrumentation.getTypeMatcher()) 35 | .transform(apacheKafkaConsumerInstrumentation.getTransformer()) 36 | .type(awsLambdaRequestHandlerInstrumentation.getTypeMatcher()) 37 | .transform(awsLambdaRequestHandlerInstrumentation.getTransformer()); 38 | 39 | builder.installOn(inst); 40 | Logger.debug("Finish Instrumentation"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientInstrumentation.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.impl; 2 | 3 | import static net.bytebuddy.matcher.ElementMatchers.isMethod; 4 | import static net.bytebuddy.matcher.ElementMatchers.named; 5 | 6 | import com.amazonaws.Request; 7 | import com.amazonaws.Response; 8 | import io.lumigo.core.SpansContainer; 9 | import io.lumigo.core.instrumentation.LumigoInstrumentationApi; 10 | import io.lumigo.core.instrumentation.agent.Loader; 11 | import io.lumigo.core.utils.LRUCache; 12 | import net.bytebuddy.agent.builder.AgentBuilder; 13 | import net.bytebuddy.asm.Advice; 14 | import net.bytebuddy.description.type.TypeDescription; 15 | import net.bytebuddy.matcher.ElementMatcher; 16 | import org.pmw.tinylog.Logger; 17 | 18 | public class AmazonHttpClientInstrumentation implements LumigoInstrumentationApi { 19 | 20 | @Override 21 | public ElementMatcher getTypeMatcher() { 22 | return named("com.amazonaws.http.AmazonHttpClient"); 23 | } 24 | 25 | @Override 26 | public AgentBuilder.Transformer.ForAdvice getTransformer() { 27 | return new AgentBuilder.Transformer.ForAdvice() 28 | .include(Loader.class.getClassLoader()) 29 | .advice(isMethod().and(named("execute")), AmazonHttpClientAdvice.class.getName()); 30 | } 31 | 32 | public static class AmazonHttpClientAdvice { 33 | 34 | public static final SpansContainer spansContainer = SpansContainer.getInstance(); 35 | 36 | public static final LRUCache handled = new LRUCache<>(1000); 37 | 38 | public static final LRUCache startTimeMap = new LRUCache<>(1000); 39 | 40 | @Advice.OnMethodEnter 41 | public static void methodEnter(@Advice.Argument(0) final Request request) { 42 | try { 43 | String patchedRoot = spansContainer.getPatchedRoot(); 44 | request.getHeaders().put("X-Amzn-Trace-Id", patchedRoot); 45 | startTimeMap.put(request.hashCode(), System.currentTimeMillis()); 46 | } catch (Throwable e) { 47 | Logger.error(e, "Failed to send data on http requests"); 48 | } 49 | } 50 | 51 | @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) 52 | public static void methodExit( 53 | @Advice.Argument(0) final Request request, 54 | @Advice.Return final Response response) { 55 | try { 56 | if (handled.get(request.hashCode()) == null) { 57 | Logger.info( 58 | "Handling request {} from host {}", 59 | request.hashCode(), 60 | request.getEndpoint()); 61 | spansContainer.addHttpSpan( 62 | startTimeMap.get(request.hashCode()), request, response); 63 | handled.put(request.hashCode(), true); 64 | } else { 65 | Logger.warn( 66 | "Already handle request {} for host {}", 67 | request.hashCode(), 68 | request.getEndpoint().getHost()); 69 | } 70 | } catch (Throwable e) { 71 | Logger.error(e, "Failed to send data on http response"); 72 | } finally { 73 | startTimeMap.remove(request.hashCode()); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientV2Instrumentation.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.impl; 2 | 3 | import static net.bytebuddy.matcher.ElementMatchers.*; 4 | 5 | import io.lumigo.core.SpansContainer; 6 | import io.lumigo.core.instrumentation.LumigoInstrumentationApi; 7 | import io.lumigo.core.instrumentation.agent.Loader; 8 | import io.lumigo.core.utils.LRUCache; 9 | import java.util.List; 10 | import net.bytebuddy.agent.builder.AgentBuilder; 11 | import net.bytebuddy.asm.Advice; 12 | import net.bytebuddy.description.type.TypeDescription; 13 | import net.bytebuddy.matcher.ElementMatcher; 14 | import org.pmw.tinylog.Logger; 15 | import software.amazon.awssdk.core.interceptor.Context; 16 | import software.amazon.awssdk.core.interceptor.ExecutionAttributes; 17 | import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; 18 | import software.amazon.awssdk.http.SdkHttpRequest; 19 | 20 | public class AmazonHttpClientV2Instrumentation implements LumigoInstrumentationApi { 21 | 22 | public static final String INSTRUMENTATION_PACKAGE_PREFIX = 23 | "software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder"; 24 | 25 | @Override 26 | public ElementMatcher getTypeMatcher() { 27 | return named(INSTRUMENTATION_PACKAGE_PREFIX); 28 | } 29 | 30 | @Override 31 | public AgentBuilder.Transformer.ForAdvice getTransformer() { 32 | return new AgentBuilder.Transformer.ForAdvice() 33 | .include(Loader.class.getClassLoader()) 34 | .advice( 35 | isMethod().and(named("resolveExecutionInterceptors")), 36 | AmazonHttpClientV2Advice.class.getName()); 37 | } 38 | 39 | @SuppressWarnings("unused") 40 | public static class AmazonHttpClientV2Advice { 41 | @Advice.OnMethodExit(suppress = Throwable.class) 42 | public static void methodExit( 43 | @Advice.Return final List interceptors) { 44 | Logger.debug("At AmazonHttpClientV2Instrumentation$AmazonHttpClientV2Advice"); 45 | for (ExecutionInterceptor interceptor : interceptors) { 46 | if (interceptor instanceof TracingExecutionInterceptor) { 47 | Logger.debug("Lumigo TracingExecutionInterceptor already exists, skipping..."); 48 | return; // list already has our interceptor, return to builder 49 | } 50 | } 51 | interceptors.add(new TracingExecutionInterceptor()); 52 | Logger.debug("Added Lumigo TracingExecutionInterceptor"); 53 | } 54 | 55 | public static class TracingExecutionInterceptor implements ExecutionInterceptor { 56 | public static final SpansContainer spansContainer = SpansContainer.getInstance(); 57 | public static final LRUCache handled = new LRUCache<>(1000); 58 | public static final LRUCache startTimeMap = new LRUCache<>(1000); 59 | 60 | @Override 61 | public void beforeExecution( 62 | final Context.BeforeExecution context, 63 | final ExecutionAttributes executionAttributes) { 64 | startTimeMap.put(context.request().hashCode(), System.currentTimeMillis()); 65 | } 66 | 67 | @Override 68 | public SdkHttpRequest modifyHttpRequest( 69 | Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { 70 | try { 71 | SdkHttpRequest.Builder requestBuilder = context.httpRequest().toBuilder(); 72 | requestBuilder.appendHeader("X-Amzn-Trace-Id", spansContainer.getPatchedRoot()); 73 | return requestBuilder.build(); 74 | } catch (Throwable e) { 75 | Logger.debug("Unable to inject trace header", e); 76 | } 77 | return context.httpRequest(); 78 | } 79 | 80 | @Override 81 | public void afterExecution( 82 | final Context.AfterExecution context, 83 | final ExecutionAttributes executionAttributes) { 84 | try { 85 | if (handled.get(context.request().hashCode()) == null) { 86 | Logger.info( 87 | "Handling request {} from host {}", context.request().hashCode()); 88 | spansContainer.addHttpSpan( 89 | startTimeMap.get(context.request().hashCode()), 90 | context, 91 | executionAttributes); 92 | handled.put(context.request().hashCode(), true); 93 | } else { 94 | Logger.warn( 95 | "Already handle request {} for host {}", 96 | context.request().hashCode(), 97 | context.httpRequest().host()); 98 | } 99 | } catch (Throwable e) { 100 | Logger.error(e, "Failed to send data on http response"); 101 | } finally { 102 | startTimeMap.remove(context.request().hashCode()); 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/impl/ApacheHttpInstrumentation.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.impl; 2 | 3 | import static net.bytebuddy.matcher.ElementMatchers.*; 4 | 5 | import io.lumigo.core.SpansContainer; 6 | import io.lumigo.core.configuration.Configuration; 7 | import io.lumigo.core.instrumentation.LumigoInstrumentationApi; 8 | import io.lumigo.core.instrumentation.agent.Loader; 9 | import io.lumigo.core.utils.LRUCache; 10 | import net.bytebuddy.agent.builder.AgentBuilder; 11 | import net.bytebuddy.asm.Advice; 12 | import net.bytebuddy.description.type.TypeDescription; 13 | import net.bytebuddy.matcher.ElementMatcher; 14 | import org.apache.http.HttpResponse; 15 | import org.apache.http.client.methods.*; 16 | import org.pmw.tinylog.Logger; 17 | 18 | public class ApacheHttpInstrumentation implements LumigoInstrumentationApi { 19 | 20 | @Override 21 | public ElementMatcher getTypeMatcher() { 22 | return hasSuperType(named("org.apache.http.client.HttpClient").and(isInterface())); 23 | } 24 | 25 | @Override 26 | public AgentBuilder.Transformer.ForAdvice getTransformer() { 27 | return new AgentBuilder.Transformer.ForAdvice() 28 | .include(Loader.class.getClassLoader()) 29 | .advice( 30 | isMethod() 31 | .and(named("execute")) 32 | .and( 33 | not(isAbstract()) 34 | .and( 35 | takesArgument( 36 | 0, 37 | named( 38 | "org.apache.http.client.methods.HttpUriRequest")))), 39 | ApacheHttpAdvice.class.getName()); 40 | } 41 | 42 | public static class ApacheHttpAdvice { 43 | 44 | public static final SpansContainer spansContainer = SpansContainer.getInstance(); 45 | 46 | public static final LRUCache handled = new LRUCache<>(1000); 47 | 48 | public static final LRUCache startTimeMap = new LRUCache<>(1000); 49 | 50 | @Advice.OnMethodEnter 51 | public static void methodEnter(@Advice.Argument(0) final HttpUriRequest request) { 52 | try { 53 | if (Configuration.getInstance().isLumigoHost(request.getURI().getHost())) { 54 | Logger.debug("Skip, internal lumigo reporter"); 55 | return; 56 | } 57 | if (Configuration.getInstance().isAwsHost(request.getURI().getHost())) { 58 | Logger.debug("Skip, aws api"); 59 | return; 60 | } 61 | startTimeMap.put(request.hashCode(), System.currentTimeMillis()); 62 | } catch (Throwable e) { 63 | Logger.error(e); 64 | } 65 | } 66 | 67 | @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) 68 | public static void methodExit( 69 | @Advice.Argument(0) HttpUriRequest request, @Advice.Return final Object result) { 70 | try { 71 | if (Configuration.getInstance().isLumigoHost(request.getURI().getHost())) { 72 | Logger.debug("Skip, internal lumigo reporter"); 73 | return; 74 | } 75 | if (Configuration.getInstance().isAwsHost(request.getURI().getHost())) { 76 | Logger.debug("Skip, aws api"); 77 | return; 78 | } 79 | if (handled.get(request.hashCode()) == null) { 80 | Logger.debug( 81 | "Handling request {} from host {}", 82 | request.hashCode(), 83 | request.getURI().getHost()); 84 | if (result instanceof HttpResponse) { 85 | spansContainer.addHttpSpan( 86 | startTimeMap.get(request.hashCode()), 87 | request, 88 | (HttpResponse) result); 89 | handled.put(request.hashCode(), true); 90 | } 91 | } else { 92 | Logger.warn( 93 | "Already handle request {} for host {}", 94 | request.hashCode(), 95 | request.getURI().getHost()); 96 | } 97 | } catch (Throwable e) { 98 | Logger.error(e, "Failed to send data on http requests"); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/impl/ApacheKafkaConsumerInstrumentation.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.impl; 2 | 3 | import static net.bytebuddy.matcher.ElementMatchers.*; 4 | 5 | import io.lumigo.core.SpansContainer; 6 | import io.lumigo.core.instrumentation.LumigoInstrumentationApi; 7 | import io.lumigo.core.instrumentation.agent.Loader; 8 | import io.lumigo.core.utils.LRUCache; 9 | import net.bytebuddy.agent.builder.AgentBuilder; 10 | import net.bytebuddy.asm.Advice; 11 | import net.bytebuddy.description.type.TypeDescription; 12 | import net.bytebuddy.matcher.ElementMatcher; 13 | import org.apache.kafka.clients.consumer.ConsumerRecords; 14 | import org.apache.kafka.clients.consumer.KafkaConsumer; 15 | import org.apache.kafka.clients.consumer.internals.ConsumerMetadata; 16 | import org.pmw.tinylog.Logger; 17 | 18 | public class ApacheKafkaConsumerInstrumentation implements LumigoInstrumentationApi { 19 | @Override 20 | public ElementMatcher getTypeMatcher() { 21 | return named("org.apache.kafka.clients.consumer.KafkaConsumer"); 22 | } 23 | 24 | @Override 25 | public AgentBuilder.Transformer.ForAdvice getTransformer() { 26 | return new AgentBuilder.Transformer.ForAdvice() 27 | .include(Loader.class.getClassLoader()) 28 | .advice( 29 | isMethod() 30 | .and(isPublic()) 31 | .and(named("poll")) 32 | .and(takesArguments(1)) 33 | .and( 34 | returns( 35 | named( 36 | "org.apache.kafka.clients.consumer.ConsumerRecords"))), 37 | ApacheKafkaConsumerAdvice.class.getName()); 38 | } 39 | 40 | public static class ApacheKafkaConsumerAdvice { 41 | public static final SpansContainer spansContainer = SpansContainer.getInstance(); 42 | public static final LRUCache startTimeMap = new LRUCache<>(1000); 43 | 44 | @Advice.OnMethodEnter(suppress = Throwable.class) 45 | public static void methodEnter(@Advice.FieldValue("clientId") String clientId) { 46 | try { 47 | startTimeMap.put(clientId, System.currentTimeMillis()); 48 | } catch (Throwable e) { 49 | Logger.error(e); 50 | } 51 | } 52 | 53 | @Advice.OnMethodExit(suppress = Throwable.class) 54 | public static void methodExit( 55 | @Advice.This KafkaConsumer consumer, 56 | @Advice.FieldValue("metadata") ConsumerMetadata metadata, 57 | @Advice.FieldValue("clientId") String clientId, 58 | @Advice.Return ConsumerRecords consumerRecords) { 59 | try { 60 | Logger.info("Handling kafka request {}", consumerRecords.hashCode()); 61 | spansContainer.addKafkaConsumeSpan( 62 | startTimeMap.get(clientId), consumer, metadata, consumerRecords); 63 | } catch (Throwable error) { 64 | Logger.error(error, "Failed to add kafka span"); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/impl/ApacheKafkaProducerInstrumentation.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.impl; 2 | 3 | import static net.bytebuddy.matcher.ElementMatchers.*; 4 | 5 | import io.lumigo.core.SpansContainer; 6 | import io.lumigo.core.instrumentation.LumigoInstrumentationApi; 7 | import io.lumigo.core.instrumentation.agent.Loader; 8 | import io.lumigo.models.KafkaSpan; 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.UUID; 11 | import lombok.AllArgsConstructor; 12 | import net.bytebuddy.agent.builder.AgentBuilder; 13 | import net.bytebuddy.asm.Advice; 14 | import net.bytebuddy.description.type.TypeDescription; 15 | import net.bytebuddy.matcher.ElementMatcher; 16 | import org.apache.kafka.clients.producer.Callback; 17 | import org.apache.kafka.clients.producer.ProducerRecord; 18 | import org.apache.kafka.clients.producer.RecordMetadata; 19 | import org.apache.kafka.clients.producer.internals.ProducerMetadata; 20 | import org.apache.kafka.common.serialization.Serializer; 21 | import org.pmw.tinylog.Logger; 22 | 23 | public class ApacheKafkaProducerInstrumentation implements LumigoInstrumentationApi { 24 | @Override 25 | public ElementMatcher getTypeMatcher() { 26 | return named("org.apache.kafka.clients.producer.KafkaProducer"); 27 | } 28 | 29 | @Override 30 | public AgentBuilder.Transformer.ForAdvice getTransformer() { 31 | return new AgentBuilder.Transformer.ForAdvice() 32 | .include(Loader.class.getClassLoader()) 33 | .advice( 34 | isMethod() 35 | .and(isPublic()) 36 | .and(named("send")) 37 | .and( 38 | takesArgument( 39 | 0, 40 | named( 41 | "org.apache.kafka.clients.producer.ProducerRecord")) 42 | .and( 43 | takesArgument( 44 | 1, 45 | named( 46 | "org.apache.kafka.clients.producer.Callback")))), 47 | ApacheKafkaProducerAdvice.class.getName()); 48 | } 49 | 50 | @SuppressWarnings("unused") 51 | public static class ApacheKafkaProducerAdvice { 52 | public static final SpansContainer spansContainer = SpansContainer.getInstance(); 53 | 54 | @Advice.OnMethodEnter 55 | public static void methodEnter( 56 | @Advice.FieldValue("metadata") ProducerMetadata metadata, 57 | @Advice.FieldValue("keySerializer") Serializer keySerializer, 58 | @Advice.FieldValue("valueSerializer") Serializer valueSerializer, 59 | @Advice.Argument(value = 0, readOnly = false) ProducerRecord record, 60 | @Advice.Argument(value = 1, readOnly = false) Callback callback) { 61 | try { 62 | callback = 63 | new KafkaProducerCallback<>( 64 | callback, 65 | keySerializer, 66 | valueSerializer, 67 | metadata, 68 | record, 69 | System.currentTimeMillis()); 70 | 71 | // Try to inject correlation id to the kafka record headers 72 | record.headers() 73 | .add( 74 | KafkaSpan.LUMIGO_MESSAGE_ID_KEY, 75 | UUID.randomUUID() 76 | .toString() 77 | .substring(0, 10) 78 | .getBytes(StandardCharsets.UTF_8)); 79 | } catch (Throwable e) { 80 | Logger.error(e); 81 | } 82 | } 83 | 84 | @AllArgsConstructor 85 | public static class KafkaProducerCallback implements Callback { 86 | private final Callback callback; 87 | private final Serializer keySerializer; 88 | private final Serializer valueSerializer; 89 | private final ProducerMetadata producerMetadata; 90 | private final ProducerRecord record; 91 | private final long startTime; 92 | 93 | @Override 94 | public void onCompletion(RecordMetadata recordMetadata, Exception exception) { 95 | try { 96 | if (callback != null) { 97 | callback.onCompletion(recordMetadata, exception); 98 | } 99 | Logger.info("Handling kafka request {}", record.hashCode()); 100 | spansContainer.addKafkaProduceSpan( 101 | startTime, 102 | keySerializer, 103 | valueSerializer, 104 | producerMetadata, 105 | record, 106 | recordMetadata, 107 | exception); 108 | } catch (Throwable error) { 109 | Logger.error(error, "Failed to add kafka span"); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/instrumentation/impl/AwsLambdaRequestHandlerInstrumentation.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.impl; 2 | 3 | import static net.bytebuddy.matcher.ElementMatchers.*; 4 | 5 | import com.amazonaws.services.lambda.runtime.Context; 6 | import io.lumigo.core.SpansContainer; 7 | import io.lumigo.core.instrumentation.LumigoInstrumentationApi; 8 | import io.lumigo.core.instrumentation.agent.Loader; 9 | import io.lumigo.core.network.Reporter; 10 | import io.lumigo.core.utils.EnvUtil; 11 | import net.bytebuddy.agent.builder.AgentBuilder; 12 | import net.bytebuddy.asm.Advice; 13 | import net.bytebuddy.description.type.TypeDescription; 14 | import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing; 15 | import net.bytebuddy.matcher.ElementMatcher; 16 | import org.pmw.tinylog.Logger; 17 | 18 | public class AwsLambdaRequestHandlerInstrumentation implements LumigoInstrumentationApi { 19 | @Override 20 | public ElementMatcher getTypeMatcher() { 21 | return hasSuperType(named("com.amazonaws.services.lambda.runtime.RequestHandler")) 22 | // we don't want to instrument handlers that implement our interfaces because they 23 | // are already instrumented 24 | .and( 25 | not(hasSuperType(named("io.lumigo.handlers.LumigoRequestHandler"))) 26 | .and( 27 | not( 28 | hasSuperType( 29 | named( 30 | "io.lumigo.handlers.LumigoRequestStreamHandler"))))); 31 | } 32 | 33 | @Override 34 | public AgentBuilder.Transformer.ForAdvice getTransformer() { 35 | return new AgentBuilder.Transformer.ForAdvice() 36 | .include(Loader.class.getClassLoader()) 37 | .advice( 38 | isMethod() 39 | .and(isPublic()) 40 | .and(named("handleRequest")) 41 | .and( 42 | takesArgument( 43 | 1, 44 | named( 45 | "com.amazonaws.services.lambda.runtime.Context"))), 46 | HandleRequestAdvice.class.getName()); 47 | } 48 | 49 | @SuppressWarnings("unused") 50 | public static class HandleRequestAdvice { 51 | public static final SpansContainer spansContainer = SpansContainer.getInstance(); 52 | 53 | @Advice.OnMethodEnter(suppress = Throwable.class) 54 | public static void methodEnter( 55 | @Advice.Argument(value = 0, typing = Typing.DYNAMIC) Object input, 56 | @Advice.Argument(1) Context context) { 57 | try { 58 | Logger.debug("Start AwsLambdaRequestHandlerInstrumentation$HandleRequestAdvice"); 59 | spansContainer.init(new EnvUtil().getEnv(), new Reporter(), context, input); 60 | spansContainer.start(); 61 | Logger.debug("Finish sending start message and instrumentation"); 62 | } catch (Throwable e) { 63 | Logger.error(e, "Failed to init span container"); 64 | } 65 | } 66 | 67 | @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) 68 | public static void methodExit( 69 | @Advice.Return(readOnly = false) Object returnValue, 70 | @Advice.Thrown Throwable throwable) { 71 | try { 72 | if (throwable != null) { 73 | spansContainer.endWithException(throwable); 74 | } else { 75 | spansContainer.end(returnValue); 76 | } 77 | } catch (Throwable e) { 78 | Logger.error(e, "Failed to create end span"); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/network/Reporter.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.network; 2 | 3 | import io.lumigo.core.configuration.Configuration; 4 | import io.lumigo.core.utils.JsonUtils; 5 | import io.lumigo.core.utils.StringUtils; 6 | import io.lumigo.models.BaseSpan; 7 | import java.io.IOException; 8 | import java.util.Collections; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | import okhttp3.*; 12 | import org.pmw.tinylog.Logger; 13 | 14 | public class Reporter { 15 | 16 | private final OkHttpClient client; 17 | 18 | public Reporter() { 19 | client = 20 | new OkHttpClient.Builder() 21 | .callTimeout(Configuration.getInstance().getLumigoTimeout()) 22 | .build(); 23 | } 24 | 25 | public long reportSpans(BaseSpan span, int maxSize) throws IOException { 26 | return reportSpans(Collections.singletonList(span), maxSize); 27 | } 28 | 29 | public long reportSpans(List spans, int maxSize) throws IOException { 30 | long time = System.currentTimeMillis(); 31 | List spansAsStringList = new LinkedList<>(); 32 | int sizeCount = 0; 33 | int handledSpans = 0; 34 | for (Object span : spans) { 35 | if (sizeCount >= maxSize) { 36 | Logger.debug("Dropped spans by request size: {}", spans.size() - handledSpans); 37 | break; 38 | } 39 | String spanAsString = JsonUtils.getObjectAsJsonString(span); 40 | if (spanAsString != null) { 41 | spansAsStringList.add(spanAsString); 42 | sizeCount += StringUtils.getBase64Size(spanAsString); 43 | } 44 | handledSpans++; 45 | } 46 | 47 | if (Configuration.getInstance().isAwsEnvironment() && !spansAsStringList.isEmpty()) { 48 | String spansAsString = "[" + String.join(",", spansAsStringList) + "]"; 49 | Logger.debug("Reporting the spans: {}", spansAsString); 50 | RequestBody body = 51 | RequestBody.create( 52 | MediaType.get("application/json; charset=utf-8"), spansAsString); 53 | Request request = 54 | new Request.Builder() 55 | .header("Accept", "application/json") 56 | .url(Configuration.getInstance().getLumigoEdge()) 57 | .post(body) 58 | .build(); 59 | Response response = client.newCall(request).execute(); 60 | if (response.body() != null) { 61 | response.body().close(); 62 | } 63 | return System.currentTimeMillis() - time; 64 | } 65 | return 0; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/event/APIGWEvent.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.event; 2 | 3 | import java.util.Map; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | 8 | @AllArgsConstructor 9 | @Builder(toBuilder = true) 10 | @Data(staticConstructor = "of") 11 | public class APIGWEvent { 12 | public String path; 13 | public String resource; 14 | public String httpMethod; 15 | public Map queryStringParameters; 16 | public Map pathParameters; 17 | public String body; 18 | public Map authorizer; 19 | public Map headers; 20 | public Map stageVariables; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/event/APIGWEventParser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.event; 2 | 3 | import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; 4 | import io.lumigo.core.utils.EnvUtil; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.stream.Stream; 8 | import lombok.Setter; 9 | 10 | public class APIGWEventParser implements IEventParser { 11 | 12 | private static final String LUMIGO_API_GW_PREFIX_KEYS_HEADERS_DELETE_KEYS = 13 | "LUMIGO_API_GW_PREFIX_KEYS_HEADERS_DELETE_KEYS"; 14 | 15 | @Setter private EnvUtil envUtil = new EnvUtil(); 16 | 17 | private String[] HEADERS_REMOVE_KEYS_DEFUALT = { 18 | "cookie", "x-amz", "accept", "cloudfront", "via", "x-forwarded", "sec-" 19 | }; 20 | private String[] HEADERS_REMOVE_KEYS = 21 | envUtil.getStringArrayEnv( 22 | LUMIGO_API_GW_PREFIX_KEYS_HEADERS_DELETE_KEYS, HEADERS_REMOVE_KEYS_DEFUALT); 23 | 24 | @Override 25 | public Object parse(APIGatewayProxyRequestEvent event) { 26 | return APIGWEvent.builder() 27 | .path(event.getPath()) 28 | .body(event.getBody()) 29 | .resource(event.getResource()) 30 | .httpMethod(event.getHttpMethod()) 31 | .stageVariables(event.getStageVariables()) 32 | .pathParameters(event.getPathParameters()) 33 | .queryStringParameters(event.getQueryStringParameters()) 34 | .authorizer(event.getRequestContext().getAuthorizer()) 35 | .headers(removeHeadersKeys(event.getHeaders())) 36 | .build(); 37 | } 38 | 39 | private Map removeHeadersKeys(Map headers) { 40 | 41 | Map res = new HashMap<>(); 42 | for (Map.Entry e : headers.entrySet()) { 43 | if (Stream.of(HEADERS_REMOVE_KEYS) 44 | .noneMatch(s -> e.getKey().toLowerCase().startsWith(s))) { 45 | res.put(e.getKey(), e.getValue()); 46 | } 47 | } 48 | return res; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/event/EventParserFactory.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.event; 2 | 3 | import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; 4 | import com.amazonaws.services.lambda.runtime.events.SNSEvent; 5 | import com.amazonaws.services.lambda.runtime.events.SQSEvent; 6 | import org.pmw.tinylog.Logger; 7 | 8 | public interface EventParserFactory { 9 | static Object parseEvent(Object event) { 10 | try { 11 | if (event instanceof APIGatewayProxyRequestEvent) { 12 | return new APIGWEventParser().parse((APIGatewayProxyRequestEvent) event); 13 | } else if (event instanceof SNSEvent) { 14 | return new SnsEventParser().parse((SNSEvent) event); 15 | } else if (event instanceof SQSEvent) { 16 | return new SqsEventParser().parse((SQSEvent) event); 17 | } else { 18 | return event; 19 | } 20 | } catch (Exception e) { 21 | Logger.error(e, "Fail to parse event"); 22 | } 23 | return event; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/event/IEventParser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.event; 2 | 3 | public interface IEventParser { 4 | Object parse(T event); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/event/SnsEvent.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.event; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | 9 | @AllArgsConstructor 10 | @Builder(toBuilder = true) 11 | @Data(staticConstructor = "of") 12 | public class SnsEvent { 13 | public List records; 14 | 15 | @AllArgsConstructor 16 | @Builder(toBuilder = true) 17 | @Data(staticConstructor = "of") 18 | public static class Record { 19 | public String message; 20 | public Map messageAttributes; 21 | public String messageId; 22 | } 23 | 24 | @AllArgsConstructor 25 | @Builder(toBuilder = true) 26 | @Data(staticConstructor = "of") 27 | public static class MessageAttribute { 28 | public String type; 29 | public String value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/event/SnsEventParser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.event; 2 | 3 | import com.amazonaws.services.lambda.runtime.events.SNSEvent; 4 | import java.util.HashMap; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class SnsEventParser implements IEventParser { 10 | 11 | @Override 12 | public Object parse(SNSEvent event) { 13 | List records = new LinkedList<>(); 14 | for (SNSEvent.SNSRecord rec : event.getRecords()) { 15 | Map attributes = new HashMap<>(); 16 | for (Map.Entry attributeEntry : 17 | rec.getSNS().getMessageAttributes().entrySet()) { 18 | attributes.put( 19 | attributeEntry.getKey(), 20 | SnsEvent.MessageAttribute.builder() 21 | .type(attributeEntry.getValue().getType()) 22 | .value(attributeEntry.getValue().getValue()) 23 | .build()); 24 | } 25 | records.add( 26 | SnsEvent.Record.builder() 27 | .message(rec.getSNS().getMessage()) 28 | .messageAttributes(attributes) 29 | .messageId(rec.getSNS().getMessageId()) 30 | .build()); 31 | } 32 | return SnsEvent.builder().records(records).build(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/event/SqsEvent.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.event; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | 9 | @AllArgsConstructor 10 | @Builder(toBuilder = true) 11 | @Data(staticConstructor = "of") 12 | public class SqsEvent { 13 | public List records; 14 | 15 | @Builder(toBuilder = true) 16 | @Data(staticConstructor = "of") 17 | public static class Record { 18 | public String body; 19 | public Map messageAttributes; 20 | public String messageId; 21 | } 22 | 23 | @AllArgsConstructor 24 | @Builder(toBuilder = true) 25 | @Data(staticConstructor = "of") 26 | public static class MessageAttribute { 27 | public String type; 28 | public String value; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/event/SqsEventParser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.event; 2 | 3 | import com.amazonaws.services.lambda.runtime.events.SQSEvent; 4 | import java.util.HashMap; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class SqsEventParser implements IEventParser { 10 | 11 | @Override 12 | public Object parse(SQSEvent event) { 13 | List records = new LinkedList<>(); 14 | for (SQSEvent.SQSMessage rec : event.getRecords()) { 15 | Map attributes = new HashMap<>(); 16 | for (Map.Entry attributeEntry : 17 | rec.getMessageAttributes().entrySet()) { 18 | attributes.put( 19 | attributeEntry.getKey(), 20 | SqsEvent.MessageAttribute.builder() 21 | .type(attributeEntry.getValue().getDataType()) 22 | .value(attributeEntry.getValue().getStringValue()) 23 | .build()); 24 | } 25 | records.add( 26 | SqsEvent.Record.builder() 27 | .body(rec.getBody()) 28 | .messageAttributes(attributes) 29 | .messageId(rec.getMessageId()) 30 | .build()); 31 | } 32 | return SqsEvent.builder().records(records).build(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v1/AwsSdkV1Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import com.amazonaws.Request; 4 | import com.amazonaws.Response; 5 | import io.lumigo.models.HttpSpan; 6 | import java.util.List; 7 | import org.pmw.tinylog.Logger; 8 | 9 | public interface AwsSdkV1Parser { 10 | String getParserType(); 11 | 12 | void parse(HttpSpan span, Request request, Response response); 13 | 14 | default void safeParse(HttpSpan span, Request request, Response response) { 15 | try { 16 | Logger.debug("Start parsing aws v1 request using: " + getParserType()); 17 | parse(span, request, response); 18 | Logger.debug("Finish parsing aws v1 request using: " + getParserType()); 19 | } catch (Throwable e) { 20 | Logger.error("Failed to parse extra aws v1 data using parser: " + getParserType(), e); 21 | } 22 | } 23 | 24 | default String getParameter(Request request, String key) { 25 | 26 | if (request.getParameters() != null 27 | && request.getParameters().get(key) != null 28 | && ((List) request.getParameters().get(key)).size() > 0) { 29 | return ((List) request.getParameters().get(key)).get(0).toString(); 30 | } 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v1/AwsSdkV1ParserFactory.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | public class AwsSdkV1ParserFactory { 4 | /** 5 | * @param serviceName - AWS service name 6 | * @return Relavant parser if exists or Default parser 7 | */ 8 | public static AwsSdkV1Parser getParser(String serviceName) { 9 | if (serviceName == null) { 10 | return new DoNothingV1Parser(); 11 | } 12 | switch (serviceName) { 13 | case "AmazonSNS": 14 | return new SnsV1Parser(); 15 | case "AmazonSQS": 16 | return new SqsV1Parser(); 17 | case "AmazonKinesis": 18 | return new KinesisV1Parser(); 19 | case "AmazonDynamoDB": 20 | case "AmazonDynamoDBv2": 21 | return new DynamoDBV1Parser(); 22 | default: 23 | return new DoNothingV1Parser(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v1/DoNothingV1Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import com.amazonaws.Request; 4 | import com.amazonaws.Response; 5 | import io.lumigo.models.HttpSpan; 6 | 7 | public class DoNothingV1Parser implements AwsSdkV1Parser { 8 | @Override 9 | public String getParserType() { 10 | return DoNothingV1Parser.class.getName(); 11 | } 12 | 13 | @Override 14 | public void parse(HttpSpan span, Request request, Response response) {} 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v1/DynamoDBV1Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import static io.lumigo.core.utils.StringUtils.dynamodbItemToHash; 4 | 5 | import com.amazonaws.AmazonWebServiceRequest; 6 | import com.amazonaws.Request; 7 | import com.amazonaws.Response; 8 | import com.amazonaws.services.dynamodbv2.model.*; 9 | import io.lumigo.models.HttpSpan; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class DynamoDBV1Parser implements AwsSdkV1Parser { 14 | @Override 15 | public String getParserType() { 16 | return DynamoDBV1Parser.class.getName(); 17 | } 18 | 19 | @Override 20 | public void parse(HttpSpan span, Request request, Response response) { 21 | String messageId = extractMessageId(request.getOriginalRequest()); 22 | if (messageId != null) span.getInfo().setMessageId(messageId); 23 | String tableName = extractTableName(request.getOriginalRequest()); 24 | if (tableName != null) span.getInfo().setResourceName(tableName); 25 | } 26 | 27 | private String extractMessageId(AmazonWebServiceRequest request) { 28 | if (request instanceof PutItemRequest) { 29 | return dynamodbItemToHash(((PutItemRequest) request).getItem()); 30 | } else if (request instanceof UpdateItemRequest) { 31 | return dynamodbItemToHash(((UpdateItemRequest) request).getKey()); 32 | } else if (request instanceof DeleteItemRequest) { 33 | return dynamodbItemToHash(((DeleteItemRequest) request).getKey()); 34 | } else if (request instanceof BatchWriteItemRequest) { 35 | Map> requests = 36 | ((BatchWriteItemRequest) request).getRequestItems(); 37 | WriteRequest firstRequest = requests.entrySet().iterator().next().getValue().get(0); 38 | if (firstRequest.getPutRequest() != null) { 39 | return dynamodbItemToHash(firstRequest.getPutRequest().getItem()); 40 | } else if (firstRequest.getDeleteRequest() != null) { 41 | return dynamodbItemToHash(firstRequest.getDeleteRequest().getKey()); 42 | } 43 | } 44 | return null; 45 | } 46 | 47 | private String extractTableName(AmazonWebServiceRequest request) { 48 | if (request instanceof PutItemRequest) { 49 | return ((PutItemRequest) request).getTableName(); 50 | } else if (request instanceof UpdateItemRequest) { 51 | return ((UpdateItemRequest) request).getTableName(); 52 | } else if (request instanceof DeleteItemRequest) { 53 | return ((DeleteItemRequest) request).getTableName(); 54 | } else if (request instanceof BatchWriteItemRequest) { 55 | Map> requests = 56 | ((BatchWriteItemRequest) request).getRequestItems(); 57 | return requests.entrySet().iterator().next().getKey(); 58 | } else if (request instanceof BatchGetItemRequest) { 59 | Map requests = 60 | ((BatchGetItemRequest) request).getRequestItems(); 61 | return requests.entrySet().iterator().next().getKey(); 62 | } else if (request instanceof GetItemRequest) { 63 | return ((GetItemRequest) request).getTableName(); 64 | } 65 | return null; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v1/KinesisV1Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import com.amazonaws.Request; 4 | import com.amazonaws.Response; 5 | import com.amazonaws.services.kinesis.model.PutRecordRequest; 6 | import com.amazonaws.services.kinesis.model.PutRecordResult; 7 | import com.amazonaws.services.kinesis.model.PutRecordsRequest; 8 | import com.amazonaws.services.kinesis.model.PutRecordsResult; 9 | import io.lumigo.models.HttpSpan; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | import org.pmw.tinylog.Logger; 13 | 14 | public class KinesisV1Parser implements AwsSdkV1Parser { 15 | @Override 16 | public String getParserType() { 17 | return KinesisV1Parser.class.getName(); 18 | } 19 | 20 | @Override 21 | public void parse(HttpSpan span, Request request, Response response) { 22 | if (request.getOriginalRequest() instanceof PutRecordRequest) { 23 | span.getInfo() 24 | .setResourceName( 25 | ((PutRecordRequest) request.getOriginalRequest()).getStreamName()); 26 | } 27 | if (request.getOriginalRequest() instanceof PutRecordsRequest) { 28 | span.getInfo() 29 | .setResourceName( 30 | ((PutRecordsRequest) request.getOriginalRequest()).getStreamName()); 31 | } 32 | List messageIds = extractMessageIds(response.getAwsResponse()); 33 | if (!messageIds.isEmpty()) span.getInfo().setMessageIds(messageIds); 34 | } 35 | 36 | private List extractMessageIds(Object response) { 37 | List result = new LinkedList<>(); 38 | if (response instanceof PutRecordsResult) { 39 | ((PutRecordsResult) response) 40 | .getRecords() 41 | .forEach( 42 | putRecordsResultEntry -> 43 | result.add(putRecordsResultEntry.getSequenceNumber())); 44 | return result; 45 | } 46 | if (response instanceof PutRecordResult) { 47 | result.add(((PutRecordResult) response).getSequenceNumber()); 48 | return result; 49 | } 50 | Logger.error("Failed to extract messageIds for Kinesis response"); 51 | return result; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v1/SnsV1Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import com.amazonaws.Request; 4 | import com.amazonaws.Response; 5 | import com.amazonaws.services.sns.model.PublishResult; 6 | import io.lumigo.models.HttpSpan; 7 | import org.pmw.tinylog.Logger; 8 | 9 | public class SnsV1Parser implements AwsSdkV1Parser { 10 | @Override 11 | public String getParserType() { 12 | return SnsV1Parser.class.getName(); 13 | } 14 | 15 | @Override 16 | public void parse(HttpSpan span, Request request, Response response) { 17 | String topicArn = getParameter(request, "TopicArn"); 18 | span.getInfo().setResourceName(topicArn); 19 | span.getInfo().setTargetArn(topicArn); 20 | span.getInfo().setMessageId(extractMessageId(response.getAwsResponse())); 21 | } 22 | 23 | private String extractMessageId(Object response) { 24 | try { 25 | if (response instanceof PublishResult) { 26 | return ((PublishResult) response).getMessageId(); 27 | } else { 28 | Logger.error("Failed to extract messageId for SNS response"); 29 | return null; 30 | } 31 | } catch (Exception e) { 32 | Logger.error(e, "Failed to extract messageId for SNS response"); 33 | return null; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v1/SqsV1Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import com.amazonaws.Request; 4 | import com.amazonaws.Response; 5 | import com.amazonaws.services.sqs.model.SendMessageRequest; 6 | import com.amazonaws.services.sqs.model.SendMessageResult; 7 | import io.lumigo.models.HttpSpan; 8 | import org.pmw.tinylog.Logger; 9 | 10 | public class SqsV1Parser implements AwsSdkV1Parser { 11 | @Override 12 | public String getParserType() { 13 | return SqsV1Parser.class.getName(); 14 | } 15 | 16 | @Override 17 | public void parse(HttpSpan span, Request request, Response response) { 18 | if (request.getOriginalRequest() instanceof SendMessageRequest) { 19 | String queueUrl = ((SendMessageRequest) request.getOriginalRequest()).getQueueUrl(); 20 | span.getInfo().setResourceName(queueUrl); 21 | Logger.debug("Got queueUrl : " + queueUrl); 22 | } else { 23 | Logger.error("Failed to extract queueUrl form SQS request"); 24 | } 25 | span.getInfo().setMessageId(extractMessageId(response.getAwsResponse())); 26 | } 27 | 28 | private String extractMessageId(Object response) { 29 | try { 30 | if (response instanceof SendMessageResult) { 31 | String messageId = ((SendMessageResult) response).getMessageId(); 32 | Logger.debug("Got messageId : " + messageId); 33 | return messageId; 34 | } else { 35 | Logger.error("Failed to extract messageId for SQS response"); 36 | return null; 37 | } 38 | } catch (Exception e) { 39 | Logger.error(e, "Failed to extract messageId for SQS response"); 40 | return null; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v2/AwsSdkV2Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import io.lumigo.models.HttpSpan; 4 | import org.pmw.tinylog.Logger; 5 | import software.amazon.awssdk.core.interceptor.Context; 6 | 7 | public interface AwsSdkV2Parser { 8 | String getParserType(); 9 | 10 | void parse(HttpSpan span, Context.AfterExecution context); 11 | 12 | default void safeParse(HttpSpan span, Context.AfterExecution context) { 13 | try { 14 | Logger.debug("Start parsing aws v2 request using: " + getParserType()); 15 | parse(span, context); 16 | Logger.debug("Finish parsing aws v2 request using: " + getParserType()); 17 | } catch (Throwable e) { 18 | Logger.error( 19 | "Failed to parse extra aws sdk v2 data using parser: " + getParserType(), e); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v2/AwsSdkV2ParserFactory.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | public class AwsSdkV2ParserFactory { 4 | /** 5 | * @param serviceName - AWS service name 6 | * @return Relavant parser if exists or Default parser 7 | */ 8 | public static AwsSdkV2Parser getParser(String serviceName) { 9 | if (serviceName == null) { 10 | return new DoNothingV2Parser(); 11 | } 12 | switch (serviceName) { 13 | case "Sns": 14 | return new SnsV2Parser(); 15 | case "Sqs": 16 | return new SqsV2Parser(); 17 | case "Kinesis": 18 | return new KinesisV2Parser(); 19 | case "DynamoDb": 20 | return new DynamoDBV2Parser(); 21 | default: 22 | return new DoNothingV2Parser(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v2/DoNothingV2Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import io.lumigo.models.HttpSpan; 4 | import software.amazon.awssdk.core.interceptor.Context; 5 | 6 | public class DoNothingV2Parser implements AwsSdkV2Parser { 7 | @Override 8 | public String getParserType() { 9 | return DoNothingV2Parser.class.getName(); 10 | } 11 | 12 | @Override 13 | public void parse(HttpSpan span, Context.AfterExecution context) {} 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v2/DynamoDBV2Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import io.lumigo.core.utils.AwsSdkV2Utils; 4 | import io.lumigo.models.HttpSpan; 5 | import java.util.List; 6 | import java.util.Map; 7 | import org.pmw.tinylog.Logger; 8 | import software.amazon.awssdk.core.SdkRequest; 9 | import software.amazon.awssdk.core.interceptor.Context; 10 | import software.amazon.awssdk.services.dynamodb.model.*; 11 | 12 | public class DynamoDBV2Parser implements AwsSdkV2Parser { 13 | @Override 14 | public String getParserType() { 15 | return DynamoDBV2Parser.class.getName(); 16 | } 17 | 18 | public void parse(HttpSpan span, Context.AfterExecution context) { 19 | SdkRequest request = context.request(); 20 | if (context.request().getValueForField("TableName", String.class).isPresent()) { 21 | context.request() 22 | .getValueForField("TableName", String.class) 23 | .ifPresent( 24 | tableName -> { 25 | span.getInfo().setResourceName(tableName); 26 | Logger.debug("Parsed TableName : " + tableName); 27 | }); 28 | } else if (request instanceof BatchWriteItemRequest 29 | && ((BatchWriteItemRequest) request).hasRequestItems()) { 30 | ((BatchWriteItemRequest) request) 31 | .requestItems().keySet().stream() 32 | .findFirst() 33 | .ifPresent( 34 | tableName -> { 35 | span.getInfo().setResourceName(tableName); 36 | Logger.debug("Parsed TableName : " + tableName); 37 | }); 38 | } else if (request instanceof BatchGetItemRequest) { 39 | ((BatchGetItemRequest) request) 40 | .requestItems().keySet().stream() 41 | .findFirst() 42 | .ifPresent( 43 | tableName -> { 44 | span.getInfo().setResourceName(tableName); 45 | Logger.debug("Parsed TableName : " + tableName); 46 | }); 47 | } else { 48 | Logger.warn("Failed to extract TableName form DynamoDB request"); 49 | } 50 | span.getInfo().setMessageId(extractMessageId(context.request())); 51 | } 52 | 53 | private String extractMessageId(SdkRequest request) { 54 | if (request instanceof PutItemRequest) { 55 | return AwsSdkV2Utils.calculateItemHash(((PutItemRequest) request).item()); 56 | } else if (request instanceof UpdateItemRequest) { 57 | return AwsSdkV2Utils.calculateItemHash(((UpdateItemRequest) request).key()); 58 | } else if (request instanceof DeleteItemRequest) { 59 | return AwsSdkV2Utils.calculateItemHash(((DeleteItemRequest) request).key()); 60 | } else if (request instanceof BatchWriteItemRequest) { 61 | Map> requests = 62 | ((BatchWriteItemRequest) request).requestItems(); 63 | WriteRequest firstRequest = requests.entrySet().iterator().next().getValue().get(0); 64 | if (firstRequest.putRequest() != null) { 65 | return AwsSdkV2Utils.calculateItemHash(firstRequest.putRequest().item()); 66 | } else if (firstRequest.deleteRequest() != null) { 67 | return AwsSdkV2Utils.calculateItemHash(firstRequest.deleteRequest().key()); 68 | } 69 | } 70 | return null; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v2/KinesisV2Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import io.lumigo.models.HttpSpan; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import org.pmw.tinylog.Logger; 7 | import software.amazon.awssdk.core.interceptor.Context; 8 | import software.amazon.awssdk.services.kinesis.model.PutRecordResponse; 9 | import software.amazon.awssdk.services.kinesis.model.PutRecordsResponse; 10 | 11 | public class KinesisV2Parser implements AwsSdkV2Parser { 12 | @Override 13 | public String getParserType() { 14 | return KinesisV2Parser.class.getName(); 15 | } 16 | 17 | @Override 18 | public void parse(HttpSpan span, Context.AfterExecution context) { 19 | if (context.request().getValueForField("StreamName", String.class).isPresent()) { 20 | context.request() 21 | .getValueForField("StreamName", String.class) 22 | .ifPresent( 23 | streamName -> { 24 | span.getInfo().setResourceName(streamName); 25 | Logger.debug("Parsed StreamName : " + streamName); 26 | }); 27 | } 28 | List messageIds = extractMessageIds(context.response()); 29 | if (!messageIds.isEmpty()) span.getInfo().setMessageIds(messageIds); 30 | } 31 | 32 | private List extractMessageIds(Object response) { 33 | List messageIds = new LinkedList<>(); 34 | if (response instanceof PutRecordsResponse) { 35 | ((PutRecordsResponse) response) 36 | .records() 37 | .forEach( 38 | putRecordsResultEntry -> 39 | messageIds.add(putRecordsResultEntry.sequenceNumber())); 40 | return messageIds; 41 | } 42 | if (response instanceof PutRecordResponse) { 43 | messageIds.add(((PutRecordResponse) response).sequenceNumber()); 44 | return messageIds; 45 | } 46 | Logger.error("Failed to extract messageIds for Kinesis response"); 47 | return messageIds; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v2/SnsV2Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import io.lumigo.models.HttpSpan; 4 | import org.pmw.tinylog.Logger; 5 | import software.amazon.awssdk.core.SdkResponse; 6 | import software.amazon.awssdk.core.interceptor.Context; 7 | import software.amazon.awssdk.services.sns.model.PublishResponse; 8 | 9 | public class SnsV2Parser implements AwsSdkV2Parser { 10 | @Override 11 | public String getParserType() { 12 | return SnsV2Parser.class.getName(); 13 | } 14 | 15 | @Override 16 | public void parse(HttpSpan span, Context.AfterExecution context) { 17 | if (context.request().getValueForField("TopicArn", String.class).isPresent()) { 18 | context.request() 19 | .getValueForField("TopicArn", String.class) 20 | .ifPresent( 21 | topicArn -> { 22 | Logger.debug("Parsed topicArn : " + topicArn); 23 | span.getInfo().setResourceName(topicArn); 24 | span.getInfo().setTargetArn(topicArn); 25 | }); 26 | } else { 27 | Logger.warn("Failed to extract topicArn"); 28 | } 29 | span.getInfo().setMessageId(extractMessageId(context.response())); 30 | } 31 | 32 | private String extractMessageId(SdkResponse response) { 33 | try { 34 | if (response instanceof PublishResponse) { 35 | return ((PublishResponse) response).messageId(); 36 | } else { 37 | Logger.error("Failed to extract messageId for SNS response"); 38 | return null; 39 | } 40 | } catch (Exception e) { 41 | Logger.error(e, "Failed to extract messageId for SNS response"); 42 | return null; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/parsers/v2/SqsV2Parser.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import io.lumigo.models.HttpSpan; 4 | import org.pmw.tinylog.Logger; 5 | import software.amazon.awssdk.core.SdkResponse; 6 | import software.amazon.awssdk.core.interceptor.Context; 7 | import software.amazon.awssdk.services.sqs.model.SendMessageResponse; 8 | 9 | public class SqsV2Parser implements AwsSdkV2Parser { 10 | @Override 11 | public String getParserType() { 12 | return SqsV2Parser.class.getName(); 13 | } 14 | 15 | @Override 16 | public void parse(HttpSpan span, Context.AfterExecution context) { 17 | if (context.request().getValueForField("QueueUrl", String.class).isPresent()) { 18 | context.request() 19 | .getValueForField("QueueUrl", String.class) 20 | .ifPresent( 21 | queueUrl -> { 22 | span.getInfo().setResourceName(queueUrl); 23 | Logger.debug("Parsed queueUrl : " + queueUrl); 24 | }); 25 | } else { 26 | Logger.warn("Failed to extract queueUrl form SQS request"); 27 | } 28 | span.getInfo().setMessageId(extractMessageId(context.response())); 29 | } 30 | 31 | private String extractMessageId(SdkResponse response) { 32 | try { 33 | if (response instanceof SendMessageResponse) { 34 | return ((SendMessageResponse) response).messageId(); 35 | } else { 36 | Logger.error("Failed to extract messageId for SQS response"); 37 | return null; 38 | } 39 | } catch (Exception e) { 40 | Logger.error(e, "Failed to extract messageId for SQS response"); 41 | return null; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/utils/AwsSdkV2Utils.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import lombok.experimental.UtilityClass; 8 | import software.amazon.awssdk.services.dynamodb.model.AttributeValue; 9 | 10 | @UtilityClass 11 | public class AwsSdkV2Utils { 12 | 13 | public String calculateItemHash(Map item) { 14 | Map simpleMap = AwsSdkV2Utils.convertAttributeMapToSimpleMap(item); 15 | return StringUtils.buildMd5Hash(JsonUtils.getObjectAsJsonString(simpleMap)); 16 | } 17 | 18 | public static Map convertAttributeMapToSimpleMap( 19 | Map attributeValueMap) { 20 | Map simpleMap = new HashMap<>(); 21 | attributeValueMap.forEach( 22 | (key, value) -> simpleMap.put(key, attributeValueToObject(value))); 23 | return simpleMap; 24 | } 25 | 26 | private static Object attributeValueToObject(AttributeValue value) { 27 | if (value == null) { 28 | return null; 29 | } else if (value.s() != null) { 30 | return value.s(); 31 | } else if (value.n() != null) { 32 | return value.n(); 33 | } else if (value.bool() != null) { 34 | return value.bool(); 35 | } else if (value.l() != null && !value.l().isEmpty()) { 36 | List list = new ArrayList<>(); 37 | for (AttributeValue v : value.l()) { 38 | list.add(attributeValueToObject(v)); 39 | } 40 | return list; 41 | } else if (value.m() != null && !value.m().isEmpty()) { 42 | return convertAttributeMapToSimpleMap(value.m()); 43 | } 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/utils/EnvUtil.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import java.util.Map; 4 | import org.pmw.tinylog.Logger; 5 | 6 | public class EnvUtil { 7 | public String getEnv(String key) { 8 | return System.getenv(key); 9 | } 10 | 11 | public Boolean getBooleanEnv(String key, Boolean dflt) { 12 | String value = getEnv(key); 13 | return value == null ? dflt : "true".equalsIgnoreCase(value); 14 | } 15 | 16 | public Integer getIntegerEnv(String key, Integer dflt) { 17 | try { 18 | String value = getEnv(key); 19 | return Integer.valueOf(value); 20 | } catch (Exception ignored) { 21 | Logger.info("No configuration to key " + key + ", use default " + dflt); 22 | } 23 | return dflt; 24 | } 25 | 26 | public String[] getStringArrayEnv(String key, String[] dflt) { 27 | String value = getEnv(key); 28 | if (value != null && !value.isEmpty()) { 29 | return value.split(","); 30 | } 31 | return dflt; 32 | } 33 | 34 | public Map getEnv() { 35 | return System.getenv(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.pmw.tinylog.Logger; 6 | 7 | public class JsonUtils { 8 | private static final ObjectMapper mapper = new ObjectMapper(); 9 | 10 | /** 11 | * @param o Any object 12 | * @return object json representation as string 13 | */ 14 | public static String getObjectAsJsonString(Object o) { 15 | if (o == null || o instanceof String) { 16 | return (String) o; 17 | } 18 | try { 19 | return mapper.writeValueAsString(o); 20 | } catch (JsonProcessingException e) { 21 | Logger.error(e, "Failed converting to json class {}", o.getClass().getName()); 22 | return null; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/utils/LRUCache.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | public class LRUCache extends LinkedHashMap { 7 | 8 | private static final long serialVersionUID = 1L; 9 | 10 | private int cacheSize; 11 | 12 | public LRUCache(int cacheSize) { 13 | super(16, 0.75f, true); 14 | this.cacheSize = cacheSize; 15 | } 16 | 17 | @Override 18 | protected boolean removeEldestEntry(Map.Entry eldest) { 19 | return size() >= cacheSize; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/utils/SecretScrubber.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.regex.Pattern; 7 | import org.json.JSONArray; 8 | import org.json.JSONObject; 9 | 10 | public class SecretScrubber { 11 | private List scrubbingPatterns; 12 | 13 | private static final String SECRET_PLACEHOLDER = "****"; 14 | 15 | public SecretScrubber(Map env) { 16 | this.scrubbingPatterns = new SecretScrubbingPatternProvider(env).getScrubbingPatterns(); 17 | } 18 | 19 | public String scrubStringifiedObject(String stringifiedObject) { 20 | try { 21 | JSONObject jsonObject = new JSONObject(stringifiedObject); 22 | return scrubJsonObject(jsonObject, this.scrubbingPatterns).toString(); 23 | } catch (Exception e) { 24 | return stringifiedObject; 25 | } 26 | } 27 | 28 | private JSONObject scrubJsonObject(JSONObject jsonObject, List patterns) { 29 | for (String key : jsonObject.keySet()) { 30 | Object value = jsonObject.get(key); 31 | 32 | if (value instanceof String && isSecret(key, patterns)) { 33 | jsonObject.put(key, SECRET_PLACEHOLDER); 34 | } else if (value instanceof JSONArray) { 35 | ArrayList scrubbedArray = new ArrayList<>(); 36 | 37 | for (Object item : (JSONArray) value) { 38 | if (item instanceof String && isSecret(key, patterns)) { 39 | scrubbedArray.add(SECRET_PLACEHOLDER); 40 | } else if (item instanceof JSONObject) { 41 | scrubbedArray.add(scrubJsonObject((JSONObject) item, patterns)); 42 | } else { 43 | scrubbedArray.add(item); 44 | } 45 | } 46 | 47 | jsonObject.put(key, scrubbedArray.toArray()); 48 | 49 | } else if (value instanceof JSONObject) { 50 | jsonObject.put(key, scrubJsonObject((JSONObject) value, patterns)); 51 | } 52 | } 53 | 54 | return jsonObject; 55 | } 56 | 57 | private boolean isSecret(String value, List patterns) { 58 | for (Pattern pattern : patterns) { 59 | if (pattern.matcher(value).matches()) { 60 | return true; 61 | } 62 | } 63 | 64 | return false; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/utils/SecretScrubbingPatternProvider.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import com.fasterxml.jackson.core.JsonFactory; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonToken; 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.regex.Pattern; 12 | 13 | public class SecretScrubbingPatternProvider { 14 | private static final JsonFactory JSON_FACTORY = new JsonFactory(); 15 | private static final List DEFAULT_PATTERN_STRINGS = 16 | Arrays.asList( 17 | ".*pass.*", 18 | ".*key.*", 19 | ".*secret.*", 20 | ".*credential.*", 21 | ".*passphrase.*", 22 | "SessionToken", 23 | "x-amz-security-token", 24 | "Signature", 25 | "Authorization"); 26 | private static final List DEFAULT_PATTERNS = 27 | stringListToPatterns(DEFAULT_PATTERN_STRINGS); 28 | private final List scrubbingPatterns; 29 | 30 | private static List stringListToPatterns(List patternStrings) { 31 | ArrayList patterns = new ArrayList<>(); 32 | for (String patternString : patternStrings) { 33 | patterns.add(Pattern.compile(patternString, Pattern.CASE_INSENSITIVE)); 34 | } 35 | 36 | return patterns; 37 | } 38 | 39 | private static List jsonListToPatternList(String jsonList) throws IOException { 40 | List patternStrings = new ArrayList(); 41 | 42 | try (JsonParser parser = JSON_FACTORY.createParser(jsonList)) { 43 | if (!JsonToken.START_ARRAY.equals(parser.nextToken())) { 44 | throw new IllegalArgumentException(); 45 | } 46 | while (!JsonToken.END_ARRAY.equals(parser.nextToken())) { 47 | if (parser.currentToken().equals(JsonToken.VALUE_STRING)) { 48 | patternStrings.add(parser.getText()); 49 | } else { 50 | throw new IllegalArgumentException(); 51 | } 52 | } 53 | 54 | return stringListToPatterns(patternStrings); 55 | } 56 | } 57 | 58 | private List buildBodyScrubbingPatterns(Map env) { 59 | String regexStringifiedList = env.get("LUMIGO_SECRET_MASKING_REGEX"); 60 | 61 | if (Strings.isBlank(regexStringifiedList)) { 62 | return DEFAULT_PATTERNS; 63 | } 64 | 65 | try { 66 | return jsonListToPatternList(regexStringifiedList); 67 | } catch (IOException e) { 68 | return DEFAULT_PATTERNS; 69 | } 70 | } 71 | 72 | public List getScrubbingPatterns() { 73 | return this.scrubbingPatterns; 74 | } 75 | 76 | public SecretScrubbingPatternProvider(Map env) { 77 | this.scrubbingPatterns = buildBodyScrubbingPatterns(env); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import com.amazonaws.services.dynamodbv2.document.ItemUtils; 4 | import com.amazonaws.services.dynamodbv2.model.AttributeValue; 5 | import java.io.*; 6 | import java.nio.charset.Charset; 7 | import java.nio.charset.StandardCharsets; 8 | import java.security.MessageDigest; 9 | import java.security.NoSuchAlgorithmException; 10 | import java.util.Map; 11 | import java.util.Random; 12 | import org.pmw.tinylog.Logger; 13 | 14 | public class StringUtils { 15 | 16 | private static final String candidateChars = "abcdefghijklmnopqrstuvwxyz1234567890"; 17 | 18 | public static String getMaxSizeString(String input, int maxStringSize) { 19 | if (input != null && input.length() > maxStringSize) { 20 | return input.substring(0, maxStringSize); 21 | } 22 | return input; 23 | } 24 | 25 | public static String randomStringAndNumbers(int size) { 26 | StringBuilder sb = new StringBuilder(); 27 | Random random = new Random(); 28 | for (int i = 0; i < size; i++) { 29 | sb.append(candidateChars.charAt(random.nextInt(candidateChars.length()))); 30 | } 31 | 32 | return sb.toString(); 33 | } 34 | 35 | public static String extractStringForStream(InputStream inputStream, int size) { 36 | if (inputStream != null && inputStream.markSupported()) { 37 | try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { 38 | Logger.info("Stream reset supported, convert to string"); 39 | byte[] buffer = new byte[size]; 40 | int read = inputStream.read(buffer); 41 | if (read > 0) { 42 | Logger.debug("Read {} bytes from input stream", read); 43 | byteArrayOutputStream.write(buffer, 0, size); 44 | String result = 45 | new String( 46 | byteArrayOutputStream.toByteArray(), 47 | Charset.defaultCharset()) 48 | .trim(); 49 | inputStream.reset(); 50 | return result; 51 | } else { 52 | Logger.info("No bytes can be read from stream"); 53 | } 54 | 55 | } catch (Throwable e) { 56 | Logger.error(e, "Failed to extract string from stream"); 57 | } 58 | } else { 59 | Logger.info("Stream markSupported is false or stream is null"); 60 | } 61 | return null; 62 | } 63 | 64 | private static String bytesToHex(byte[] bytes) { 65 | StringBuilder sb = new StringBuilder(); 66 | for (byte b : bytes) { 67 | sb.append(String.format("%02x", b)); 68 | } 69 | return sb.toString(); 70 | } 71 | 72 | public static String buildMd5Hash(String s) { 73 | try { 74 | MessageDigest md = MessageDigest.getInstance("MD5"); 75 | md.update(s.getBytes(Charset.defaultCharset())); 76 | return bytesToHex(md.digest()); 77 | } catch (NoSuchAlgorithmException e) { 78 | Logger.error(e, "Failed to build hash of item"); 79 | return null; 80 | } 81 | } 82 | 83 | public static String dynamodbItemToHash(Map item) { 84 | return buildMd5Hash(ItemUtils.toItem(item).toJSON()); 85 | } 86 | 87 | public static int getBase64Size(String value) { 88 | return (int) 89 | Math.round( 90 | (Math.floor((value.getBytes(StandardCharsets.UTF_8).length / 3) + 1) * 4)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/core/utils/Strings.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | public class Strings { 4 | public static boolean isBlank(String s) { 5 | int strLen; 6 | if (s == null || (strLen = s.length()) == 0) { 7 | return true; 8 | } 9 | for (int i = 0; i < strLen; i++) { 10 | if (!Character.isWhitespace(s.charAt(i))) { 11 | return false; 12 | } 13 | } 14 | return true; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/handlers/LumigoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.handlers; 2 | 3 | import io.lumigo.core.configuration.Configuration; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | 8 | @AllArgsConstructor 9 | @Builder(toBuilder = true) 10 | @Data(staticConstructor = "of") 11 | public class LumigoConfiguration { 12 | private String edgeHost; 13 | private String token; 14 | private Boolean verbose; 15 | private Boolean killSwitch; 16 | @Builder.Default private Boolean lazyLoading = true; 17 | 18 | public void init() { 19 | Configuration.getInstance().init(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/handlers/LumigoRequestExecutor.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.handlers; 2 | 3 | import com.amazonaws.services.lambda.runtime.Context; 4 | import io.lumigo.core.SpansContainer; 5 | import io.lumigo.core.configuration.Configuration; 6 | import io.lumigo.core.instrumentation.agent.Installer; 7 | import io.lumigo.core.network.Reporter; 8 | import io.lumigo.core.utils.EnvUtil; 9 | import java.util.concurrent.ExecutorService; 10 | import java.util.concurrent.Executors; 11 | import java.util.concurrent.Future; 12 | import java.util.function.Supplier; 13 | import lombok.AccessLevel; 14 | import lombok.Setter; 15 | import org.pmw.tinylog.Logger; 16 | 17 | public class LumigoRequestExecutor { 18 | 19 | protected ExecutorService executorService = Executors.newSingleThreadExecutor(); 20 | private static LumigoRequestExecutor instance = new LumigoRequestExecutor(); 21 | 22 | @Setter(AccessLevel.MODULE) 23 | private EnvUtil envUtil; 24 | 25 | @Setter(AccessLevel.MODULE) 26 | private Reporter reporter; 27 | 28 | @Setter(AccessLevel.MODULE) 29 | private SpansContainer spansContainer; 30 | 31 | public LumigoRequestExecutor() { 32 | try { 33 | this.envUtil = new EnvUtil(); 34 | this.reporter = new Reporter(); 35 | this.spansContainer = SpansContainer.getInstance(); 36 | } catch (RuntimeException ex) { 37 | Logger.error(ex, "Failed to init LumigoRequestHandler"); 38 | } 39 | } 40 | 41 | public static LumigoRequestExecutor getInstance() { 42 | return instance; 43 | } 44 | 45 | public static void init() { 46 | instance = new LumigoRequestExecutor(); 47 | } 48 | 49 | public static OUTPUT execute( 50 | INPUT input, Context context, Supplier handler) { 51 | return getInstance().executeInternal(input, context, handler); 52 | } 53 | 54 | private OUTPUT executeInternal( 55 | INPUT input, Context context, Supplier handler) { 56 | if (Configuration.getInstance().isKillingSwitchActivated()) { 57 | return handler.get(); 58 | } 59 | try { 60 | Logger.debug("Start {} Lumigo tracer", LumigoRequestExecutor.class.getName()); 61 | try { 62 | spansContainer.init(envUtil.getEnv(), reporter, context, input); 63 | Future submit = executorService.submit(() -> Installer.install()); 64 | spansContainer.start(); 65 | submit.get(); 66 | Logger.debug("Finish sending start message and instrumentation"); 67 | } catch (Throwable e) { 68 | Logger.error(e, "Failed to init span container"); 69 | } 70 | OUTPUT response = handler.get(); 71 | try { 72 | spansContainer.end(response); 73 | } catch (Throwable e) { 74 | Logger.error(e, "Failed to create end span"); 75 | } 76 | return response; 77 | } catch (Throwable throwable) { 78 | try { 79 | Logger.debug("Customer lambda had exception {}", throwable.getClass().getName()); 80 | spansContainer.endWithException(throwable); 81 | } catch (Throwable ex) { 82 | Logger.error(ex, "Failed to create end span"); 83 | } 84 | throw throwable; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/handlers/LumigoRequestHandler.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.handlers; 2 | 3 | import com.amazonaws.services.lambda.runtime.Context; 4 | import com.amazonaws.services.lambda.runtime.RequestHandler; 5 | import io.lumigo.core.SpansContainer; 6 | import io.lumigo.core.configuration.Configuration; 7 | import io.lumigo.core.instrumentation.agent.Installer; 8 | import io.lumigo.core.network.Reporter; 9 | import io.lumigo.core.utils.EnvUtil; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.Future; 13 | import lombok.AccessLevel; 14 | import lombok.Setter; 15 | import org.pmw.tinylog.Logger; 16 | 17 | public abstract class LumigoRequestHandler implements RequestHandler { 18 | 19 | protected ExecutorService executorService = Executors.newSingleThreadExecutor(); 20 | 21 | @Setter(AccessLevel.MODULE) 22 | private EnvUtil envUtil; 23 | 24 | @Setter(AccessLevel.MODULE) 25 | private Reporter reporter; 26 | 27 | @Setter(AccessLevel.MODULE) 28 | private SpansContainer spansContainer; 29 | 30 | public LumigoRequestHandler() { 31 | try { 32 | this.envUtil = new EnvUtil(); 33 | this.reporter = new Reporter(); 34 | this.spansContainer = SpansContainer.getInstance(); 35 | } catch (RuntimeException ex) { 36 | Logger.error(ex, "Failed to init LumigoRequestHandler"); 37 | } 38 | } 39 | 40 | @Override 41 | public OUTPUT handleRequest(INPUT input, Context context) { 42 | if (Configuration.getInstance().isKillingSwitchActivated()) { 43 | return doHandleRequest(input, context); 44 | } 45 | try { 46 | Logger.debug("Start {} Lumigo tracer", LumigoRequestHandler.class.getName()); 47 | Logger.debug("Envs {}", envUtil.getEnv()); 48 | try { 49 | spansContainer.init(envUtil.getEnv(), reporter, context, input); 50 | Future submit = executorService.submit(() -> Installer.install()); 51 | spansContainer.start(); 52 | submit.get(); 53 | Logger.debug("Finish sending start message and instrumentation"); 54 | } catch (Throwable e) { 55 | Logger.error(e, "Failed to init span container"); 56 | } 57 | OUTPUT response = doHandleRequest(input, context); 58 | try { 59 | spansContainer.end(response); 60 | } catch (Throwable e) { 61 | Logger.error(e, "Failed to create end span"); 62 | } 63 | return response; 64 | } catch (Throwable throwable) { 65 | try { 66 | Logger.debug("Customer lambda had exception {}", throwable.getClass().getName()); 67 | spansContainer.endWithException(throwable); 68 | } catch (Throwable ex) { 69 | Logger.error(ex, "Failed to create end span"); 70 | } 71 | throw throwable; 72 | } 73 | } 74 | 75 | public abstract OUTPUT doHandleRequest(INPUT input, Context context); 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/handlers/LumigoRequestStreamHandler.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.handlers; 2 | 3 | import com.amazonaws.services.lambda.runtime.Context; 4 | import com.amazonaws.services.lambda.runtime.RequestStreamHandler; 5 | import io.lumigo.core.SpansContainer; 6 | import io.lumigo.core.configuration.Configuration; 7 | import io.lumigo.core.instrumentation.agent.Installer; 8 | import io.lumigo.core.network.Reporter; 9 | import io.lumigo.core.utils.EnvUtil; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.OutputStream; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.Future; 16 | import lombok.AccessLevel; 17 | import lombok.Setter; 18 | import org.pmw.tinylog.Logger; 19 | 20 | public abstract class LumigoRequestStreamHandler implements RequestStreamHandler { 21 | 22 | protected ExecutorService executorService = Executors.newSingleThreadExecutor(); 23 | 24 | @Setter(AccessLevel.MODULE) 25 | private EnvUtil envUtil; 26 | 27 | @Setter(AccessLevel.MODULE) 28 | private Reporter reporter; 29 | 30 | @Setter(AccessLevel.MODULE) 31 | private SpansContainer spansContainer; 32 | 33 | public LumigoRequestStreamHandler() { 34 | try { 35 | this.envUtil = new EnvUtil(); 36 | this.reporter = new Reporter(); 37 | this.spansContainer = SpansContainer.getInstance(); 38 | } catch (RuntimeException ex) { 39 | Logger.error(ex, "Failed to init LumigoRequestStreamHandler"); 40 | } 41 | } 42 | 43 | @Override 44 | public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) 45 | throws IOException { 46 | if (Configuration.getInstance().isKillingSwitchActivated()) { 47 | doHandleRequest(inputStream, outputStream, context); 48 | return; 49 | } 50 | try { 51 | Logger.debug("Start {} Lumigo tracer", LumigoRequestStreamHandler.class.getName()); 52 | try { 53 | spansContainer.init(envUtil.getEnv(), reporter, context, null); 54 | Future submit = executorService.submit(() -> Installer.install()); 55 | spansContainer.start(); 56 | submit.get(); 57 | Logger.debug("Finish sending start message and instrumentation"); 58 | } catch (Throwable ex) { 59 | Logger.error(ex, "Failed to init span container"); 60 | } 61 | doHandleRequest(inputStream, outputStream, context); 62 | try { 63 | spansContainer.end(); 64 | } catch (Throwable ex) { 65 | Logger.error(ex, "Failed to create end span"); 66 | } 67 | } catch (Throwable throwable) { 68 | Logger.debug("Customer lambda had exception {}", throwable.getClass().getName()); 69 | try { 70 | spansContainer.endWithException(throwable); 71 | } catch (Throwable ex) { 72 | Logger.error(ex, "Failed to create end span"); 73 | } 74 | throw throwable; 75 | } 76 | } 77 | 78 | public abstract void doHandleRequest( 79 | InputStream inputStream, OutputStream outputStream, Context context) throws IOException; 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/models/BaseSpan.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.models; 2 | 3 | public interface BaseSpan extends Reportable {} 4 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/models/HttpSpan.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.lumigo.core.utils.SecretScrubber; 5 | import io.lumigo.core.utils.StringUtils; 6 | import java.util.Collections; 7 | import java.util.List; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | 12 | @AllArgsConstructor 13 | @Builder(toBuilder = true) 14 | @Data(staticConstructor = "of") 15 | public class HttpSpan implements BaseSpan { 16 | private Long started; 17 | private Long ended; 18 | private String id; 19 | private String type; 20 | private String transactionId; 21 | private String account; 22 | private String region; 23 | private String token; 24 | private Info info; 25 | private String parentId; 26 | 27 | @Builder(toBuilder = true) 28 | @Data(staticConstructor = "of") 29 | public static class Info { 30 | private Tracer tracer; 31 | private TraceId traceId; 32 | private HttpInfo httpInfo; 33 | private String messageId; 34 | private List messageIds; 35 | private String resourceName; 36 | private String targetArn; 37 | 38 | public Info( 39 | Tracer tracer, 40 | TraceId traceId, 41 | HttpInfo httpInfo, 42 | String messageId, 43 | List messageIds, 44 | String resourceName, 45 | String targetArn) { 46 | this.tracer = tracer; 47 | this.traceId = traceId; 48 | this.httpInfo = httpInfo; 49 | this.messageId = messageId; 50 | if (messageIds != null) { 51 | this.messageIds = Collections.unmodifiableList(messageIds); 52 | } 53 | this.resourceName = resourceName; 54 | this.targetArn = targetArn; 55 | } 56 | } 57 | 58 | @AllArgsConstructor 59 | @Builder(toBuilder = true) 60 | @Data(staticConstructor = "of") 61 | public static class TraceId { 62 | @JsonProperty("Root") 63 | private String root; 64 | } 65 | 66 | @AllArgsConstructor 67 | @Builder(toBuilder = true) 68 | @Data(staticConstructor = "of") 69 | public static class Tracer { 70 | private String version; 71 | } 72 | 73 | @AllArgsConstructor 74 | @Builder(toBuilder = true) 75 | @Data(staticConstructor = "of") 76 | public static class HttpInfo { 77 | private String host; 78 | private HttpData request; 79 | private HttpData response; 80 | } 81 | 82 | @AllArgsConstructor 83 | @Builder(toBuilder = true) 84 | @Data(staticConstructor = "of") 85 | public static class HttpData { 86 | private String headers; 87 | private String body; 88 | private String uri; 89 | private Integer statusCode; 90 | private String method; 91 | } 92 | 93 | @Override 94 | public BaseSpan scrub(SecretScrubber scrubber) { 95 | this.getInfo() 96 | .getHttpInfo() 97 | .getRequest() 98 | .setHeaders( 99 | scrubber.scrubStringifiedObject( 100 | this.getInfo().getHttpInfo().getRequest().getHeaders())); 101 | this.getInfo() 102 | .getHttpInfo() 103 | .getRequest() 104 | .setBody( 105 | scrubber.scrubStringifiedObject( 106 | this.getInfo().getHttpInfo().getRequest().getBody())); 107 | this.getInfo() 108 | .getHttpInfo() 109 | .getResponse() 110 | .setHeaders( 111 | scrubber.scrubStringifiedObject( 112 | this.getInfo().getHttpInfo().getResponse().getHeaders())); 113 | this.getInfo() 114 | .getHttpInfo() 115 | .getResponse() 116 | .setBody( 117 | scrubber.scrubStringifiedObject( 118 | this.getInfo().getHttpInfo().getResponse().getBody())); 119 | 120 | return this; 121 | } 122 | 123 | @Override 124 | public BaseSpan reduceSize(int maxFieldSize) { 125 | this.getInfo() 126 | .getHttpInfo() 127 | .getRequest() 128 | .setHeaders( 129 | StringUtils.getMaxSizeString( 130 | this.getInfo().getHttpInfo().getRequest().getHeaders(), 131 | maxFieldSize)); 132 | this.getInfo() 133 | .getHttpInfo() 134 | .getRequest() 135 | .setBody( 136 | StringUtils.getMaxSizeString( 137 | this.getInfo().getHttpInfo().getRequest().getBody(), maxFieldSize)); 138 | this.getInfo() 139 | .getHttpInfo() 140 | .getResponse() 141 | .setHeaders( 142 | StringUtils.getMaxSizeString( 143 | this.getInfo().getHttpInfo().getResponse().getHeaders(), 144 | maxFieldSize)); 145 | this.getInfo() 146 | .getHttpInfo() 147 | .getResponse() 148 | .setBody( 149 | StringUtils.getMaxSizeString( 150 | this.getInfo().getHttpInfo().getResponse().getBody(), 151 | maxFieldSize)); 152 | 153 | return this; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/models/Reportable.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.models; 2 | 3 | import io.lumigo.core.utils.SecretScrubber; 4 | 5 | public interface Reportable { 6 | T scrub(SecretScrubber scrubber); 7 | 8 | T reduceSize(int maxFieldSize); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/lumigo/models/Span.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.lumigo.core.configuration.Configuration; 5 | import io.lumigo.core.utils.JsonUtils; 6 | import io.lumigo.core.utils.SecretScrubber; 7 | import io.lumigo.core.utils.StringUtils; 8 | import java.util.List; 9 | import java.util.Locale; 10 | import lombok.AllArgsConstructor; 11 | import lombok.Builder; 12 | import lombok.Data; 13 | 14 | @AllArgsConstructor 15 | @Builder(toBuilder = true) 16 | @Data(staticConstructor = "of") 17 | public class Span implements BaseSpan { 18 | private String name; 19 | private long started; 20 | private long ended; 21 | private String runtime; 22 | private String id; 23 | private String type; 24 | private String memoryAllocated; 25 | private String transactionId; 26 | private String requestId; 27 | private String account; 28 | private long maxFinishTime; 29 | private String event; 30 | private String envs; 31 | private String region; 32 | private Long reporter_rtt; 33 | private Error error; 34 | private String token; 35 | private String return_value; 36 | private Info info; 37 | private String readiness; 38 | private String parentId; 39 | 40 | @AllArgsConstructor 41 | @Builder(toBuilder = true) 42 | @Data(staticConstructor = "of") 43 | public static class Info { 44 | private Tracer tracer; 45 | private TraceId traceId; 46 | private String logStreamName; 47 | private String logGroupName; 48 | private String triggeredBy; 49 | private String arn; 50 | private String httpMethod; 51 | private String resource; 52 | private String api; 53 | private String stage; 54 | private String messageId; 55 | private List messageIds; 56 | private long approxEventCreationTime; 57 | } 58 | 59 | @AllArgsConstructor 60 | @Builder(toBuilder = true) 61 | @Data(staticConstructor = "of") 62 | public static class Tracer { 63 | private String version; 64 | } 65 | 66 | @AllArgsConstructor 67 | @Builder(toBuilder = true) 68 | @Data(staticConstructor = "of") 69 | public static class TraceId { 70 | @JsonProperty("Root") 71 | private String root; 72 | } 73 | 74 | @AllArgsConstructor 75 | @Builder(toBuilder = true) 76 | @Data(staticConstructor = "of") 77 | public static class Error { 78 | private String type; 79 | private String message; 80 | private String stacktrace; 81 | } 82 | 83 | public enum READINESS { 84 | WARM, 85 | COLD; 86 | 87 | public String toString() { 88 | return name().toLowerCase(Locale.ENGLISH); 89 | } 90 | } 91 | 92 | @Override 93 | public Span scrub(SecretScrubber scrubber) { 94 | this.setEnvs( 95 | JsonUtils.getObjectAsJsonString(scrubber.scrubStringifiedObject(this.getEnvs()))); 96 | this.setEvent( 97 | JsonUtils.getObjectAsJsonString(scrubber.scrubStringifiedObject(this.getEvent()))); 98 | this.setReturn_value(scrubber.scrubStringifiedObject(this.getReturn_value())); 99 | return this; 100 | } 101 | 102 | @Override 103 | public Span reduceSize(int maxFieldSize) { 104 | this.setEnvs( 105 | StringUtils.getMaxSizeString( 106 | this.getEnvs(), Configuration.getInstance().maxSpanFieldSize())); 107 | this.setReturn_value(StringUtils.getMaxSizeString(this.getReturn_value(), maxFieldSize)); 108 | this.setEvent(StringUtils.getMaxSizeString(this.getEvent(), maxFieldSize)); 109 | return this; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/resources/lumigo-version.txt: -------------------------------------------------------------------------------- 1 | version=1.0.49 -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/configuration/ConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.configuration; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | import static org.mockito.ArgumentMatchers.any; 5 | import static org.mockito.Mockito.when; 6 | 7 | import io.lumigo.core.utils.EnvUtil; 8 | import io.lumigo.handlers.LumigoConfiguration; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import org.mockito.Mock; 12 | import org.mockito.MockitoAnnotations; 13 | import org.pmw.tinylog.Level; 14 | 15 | class ConfigurationTest { 16 | 17 | @Mock private EnvUtil envUtil; 18 | 19 | @BeforeEach 20 | void setUp() { 21 | MockitoAnnotations.initMocks(this); 22 | Configuration.getInstance().init(null); 23 | when(envUtil.getBooleanEnv(any(), any())).thenCallRealMethod(); 24 | } 25 | 26 | @Test 27 | void getLumigoToken_inline() { 28 | Configuration.getInstance().setEnvUtil(envUtil); 29 | when(envUtil.getEnv(Configuration.TOKEN_KEY)).thenReturn("abcd"); 30 | Configuration.getInstance().init(LumigoConfiguration.builder().token("1234").build()); 31 | 32 | assertEquals("1234", Configuration.getInstance().getLumigoToken()); 33 | } 34 | 35 | @Test 36 | void getLumigoToken_env() { 37 | Configuration.getInstance().setEnvUtil(envUtil); 38 | when(envUtil.getEnv(Configuration.TOKEN_KEY)).thenReturn("abcd"); 39 | 40 | assertEquals("abcd", Configuration.getInstance().getLumigoToken()); 41 | } 42 | 43 | @Test 44 | void getLumigoEdge_inline() { 45 | Configuration.getInstance().setEnvUtil(envUtil); 46 | when(envUtil.getEnv(Configuration.TRACER_HOST_KEY)).thenReturn("defaultUrl"); 47 | Configuration.getInstance() 48 | .init(LumigoConfiguration.builder().edgeHost("inlineUrl").build()); 49 | 50 | assertEquals("https://inlineUrl/api/spans", Configuration.getInstance().getLumigoEdge()); 51 | } 52 | 53 | @Test 54 | void getLumigoEdge_env() { 55 | Configuration.getInstance().setEnvUtil(envUtil); 56 | when(envUtil.getEnv(Configuration.TRACER_HOST_KEY)).thenReturn("defaultUrl"); 57 | 58 | assertEquals("https://defaultUrl/api/spans", Configuration.getInstance().getLumigoEdge()); 59 | } 60 | 61 | @Test 62 | void getLogLevel_env() { 63 | Configuration.getInstance().setEnvUtil(envUtil); 64 | when(envUtil.getEnv(Configuration.DEBUG_KEY)).thenReturn("true"); 65 | 66 | assertEquals(Level.DEBUG, Configuration.getInstance().getLogLevel()); 67 | } 68 | 69 | @Test 70 | void getLogLevel_default() { 71 | Configuration.getInstance().setEnvUtil(envUtil); 72 | assertEquals(Level.OFF, Configuration.getInstance().getLogLevel()); 73 | } 74 | 75 | @Test 76 | void isAwsEnvironment_true() { 77 | Configuration.getInstance().setEnvUtil(envUtil); 78 | when(envUtil.getEnv("LAMBDA_RUNTIME_DIR")).thenReturn("value"); 79 | 80 | assertTrue(Configuration.getInstance().isAwsEnvironment()); 81 | } 82 | 83 | @Test 84 | void isAwsEnvironment_false() { 85 | Configuration.getInstance().setEnvUtil(envUtil); 86 | assertFalse(Configuration.getInstance().isAwsEnvironment()); 87 | } 88 | 89 | @Test 90 | void isAmazonHost_true() { 91 | assertTrue(Configuration.getInstance().isAwsHost("https://sns.amazonaws.com")); 92 | } 93 | 94 | @Test 95 | void isAmazonHost_false() { 96 | assertFalse(Configuration.getInstance().isAwsHost("https://google.com")); 97 | } 98 | 99 | @Test 100 | void isLumigoVerboseMode_default_true() { 101 | Configuration.getInstance().setEnvUtil(envUtil); 102 | when(envUtil.getEnv(any())).thenReturn(null); 103 | 104 | assertTrue(Configuration.getInstance().isLumigoVerboseMode()); 105 | } 106 | 107 | @Test 108 | void isLumigoVerboseMode_env_false() { 109 | Configuration.getInstance().setEnvUtil(envUtil); 110 | when(envUtil.getEnv(Configuration.LUMIGO_VERBOSE)).thenReturn("false"); 111 | 112 | assertFalse(Configuration.getInstance().isLumigoVerboseMode()); 113 | } 114 | 115 | @Test 116 | void isLumigoVerboseMode_inline_false() { 117 | Configuration.getInstance().setEnvUtil(envUtil); 118 | when(envUtil.getEnv(Configuration.LUMIGO_VERBOSE)).thenReturn("false"); 119 | Configuration.getInstance().init(LumigoConfiguration.builder().verbose(true).build()); 120 | 121 | assertTrue(Configuration.getInstance().isLumigoVerboseMode()); 122 | } 123 | 124 | @Test 125 | void timeout_from_env() { 126 | Configuration.getInstance().setEnvUtil(envUtil); 127 | when(envUtil.getEnv(Configuration.REPORTER_TIMEOUT)).thenReturn("5000"); 128 | assertEquals(5000, Configuration.getInstance().getLumigoTimeout().toMillis()); 129 | 130 | assertTrue(Configuration.getInstance().isLumigoVerboseMode()); 131 | } 132 | 133 | @Test 134 | void timeout_default() { 135 | Configuration.getInstance().setEnvUtil(envUtil); 136 | assertEquals(3000, Configuration.getInstance().getLumigoTimeout().toMillis()); 137 | 138 | assertTrue(Configuration.getInstance().isLumigoVerboseMode()); 139 | } 140 | 141 | @Test 142 | void kill_switch_on() { 143 | Configuration.getInstance().setEnvUtil(envUtil); 144 | when(envUtil.getEnv(Configuration.LUMIGO_KILL_SWITCH)).thenReturn("true"); 145 | assertTrue(Configuration.getInstance().isKillingSwitchActivated()); 146 | } 147 | 148 | @Test 149 | void kill_switch_default_off() { 150 | Configuration.getInstance().setEnvUtil(envUtil); 151 | assertFalse(Configuration.getInstance().isKillingSwitchActivated()); 152 | } 153 | 154 | @Test 155 | void version() { 156 | assertNotNull(Configuration.getInstance().getLumigoTracerVersion()); 157 | } 158 | 159 | @Test 160 | void maxSpanFieldSize() { 161 | Configuration.getInstance().setEnvUtil(envUtil); 162 | when(envUtil.getIntegerEnv(Configuration.LUMIGO_MAX_ENTRY_SIZE, 1024)).thenReturn(1024); 163 | assertEquals(1024, Configuration.getInstance().maxSpanFieldSize()); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientInstrumentationTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.impl; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | import static org.mockito.ArgumentMatchers.any; 5 | import static org.mockito.Mockito.*; 6 | 7 | import com.amazonaws.Request; 8 | import com.amazonaws.Response; 9 | import com.amazonaws.http.HttpResponse; 10 | import io.lumigo.core.SpansContainer; 11 | import io.lumigo.core.configuration.Configuration; 12 | import io.lumigo.handlers.LumigoConfiguration; 13 | import java.net.URI; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import org.junit.jupiter.api.BeforeEach; 17 | import org.junit.jupiter.api.Test; 18 | import org.mockito.Mock; 19 | import org.mockito.MockitoAnnotations; 20 | 21 | class AmazonHttpClientInstrumentationTest { 22 | @Mock Request request; 23 | @Mock HttpResponse httpResponse; 24 | Response response = new Response("response", httpResponse); 25 | @Mock Map headers; 26 | 27 | @BeforeEach 28 | void setUp() { 29 | MockitoAnnotations.initMocks(this); 30 | initSpansContainer(); 31 | initConfiguration(); 32 | when(request.getHeaders()).thenReturn(headers); 33 | } 34 | 35 | @Test 36 | public void handling_enter_valid_request() { 37 | when(request.getEndpoint()).thenReturn(URI.create("https://sns.aws.com")); 38 | 39 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.methodEnter(request); 40 | 41 | assertNotNull( 42 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.startTimeMap.get( 43 | request.hashCode())); 44 | verify(headers, times(1)).put(eq("X-Amzn-Trace-Id"), any()); 45 | } 46 | 47 | @Test 48 | public void handling_enter_exception() { 49 | when(request.getHeaders()).thenThrow(new RuntimeException()); 50 | 51 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.methodEnter(request); 52 | 53 | assertNull( 54 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.startTimeMap.get( 55 | request.hashCode())); 56 | verify(headers, times(0)).put(eq("X-Amzn-Trace-Id"), any()); 57 | } 58 | 59 | @Test 60 | public void handling_exit_response_lumigo_internal_request() { 61 | when(request.getEndpoint()).thenReturn(URI.create("https://lumigo.io/api/spans")); 62 | 63 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.methodExit(request, response); 64 | 65 | assertNull( 66 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.handled.get( 67 | request.hashCode())); 68 | } 69 | 70 | @Test 71 | public void handling_exit_response_unknown_exception() { 72 | when(request.getEndpoint()).thenThrow(new RuntimeException()); 73 | 74 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.methodExit(request, response); 75 | 76 | assertNull( 77 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.handled.get( 78 | request.hashCode())); 79 | } 80 | 81 | @Test 82 | public void handling_exit_response_already_handled() { 83 | when(request.getEndpoint()).thenReturn(URI.create("https://not.lumigo.host/api/spans")); 84 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.handled.put( 85 | request.hashCode(), true); 86 | 87 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.methodExit(request, response); 88 | 89 | assertNull( 90 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.startTimeMap.get( 91 | request.hashCode())); 92 | assertNotNull( 93 | AmazonHttpClientInstrumentation.AmazonHttpClientAdvice.handled.get( 94 | request.hashCode())); 95 | } 96 | 97 | @Test 98 | public void check_typeMatcher() { 99 | assertNotNull(new AmazonHttpClientInstrumentation().getTypeMatcher()); 100 | } 101 | 102 | @Test 103 | public void check_transformer() { 104 | assertNotNull(new AmazonHttpClientInstrumentation().getTransformer()); 105 | } 106 | 107 | private void initSpansContainer() { 108 | Map env = new HashMap<>(); 109 | env.put("_X_AMZN_TRACE_ID", "Root=1-2-3;Another=456;Bla=789"); 110 | try { 111 | SpansContainer.getInstance().clear(); 112 | SpansContainer.getInstance().init(env, null, null, null); 113 | } catch (Exception e) { 114 | } 115 | } 116 | 117 | private void initConfiguration() { 118 | Configuration.init(LumigoConfiguration.builder().edgeHost("lumigo.io").build()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/instrumentation/impl/ApacheHttpInstrumentationTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.instrumentation.impl; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | import static org.mockito.Mockito.*; 5 | 6 | import io.lumigo.core.SpansContainer; 7 | import io.lumigo.core.configuration.Configuration; 8 | import io.lumigo.handlers.LumigoConfiguration; 9 | import java.net.URI; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import org.apache.http.HttpResponse; 13 | import org.apache.http.client.methods.HttpUriRequest; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Disabled; 16 | import org.junit.jupiter.api.Test; 17 | import org.mockito.Mock; 18 | import org.mockito.MockitoAnnotations; 19 | 20 | class ApacheHttpInstrumentationTest { 21 | 22 | @Mock HttpUriRequest request; 23 | @Mock HttpResponse response; 24 | 25 | @BeforeEach 26 | void setUp() { 27 | MockitoAnnotations.initMocks(this); 28 | initSpansContainer(); 29 | initConfiguration(); 30 | } 31 | 32 | @Test 33 | public void handling_enter_valid_request() { 34 | when(request.getURI()).thenReturn(URI.create("https://not.lumigo.host/api/spans")); 35 | 36 | ApacheHttpInstrumentation.ApacheHttpAdvice.methodEnter(request); 37 | 38 | assertNotNull( 39 | ApacheHttpInstrumentation.ApacheHttpAdvice.startTimeMap.get(request.hashCode())); 40 | } 41 | 42 | @Test 43 | public void handling_enter_lumigo_internal_request() { 44 | when(request.getURI()).thenReturn(URI.create("https://lumigo.io/api/spans")); 45 | 46 | ApacheHttpInstrumentation.ApacheHttpAdvice.methodEnter(request); 47 | 48 | assertNull(ApacheHttpInstrumentation.ApacheHttpAdvice.startTimeMap.get(request.hashCode())); 49 | } 50 | 51 | @Test 52 | public void handling_enter_exception() { 53 | when(request.getURI()).thenThrow(new RuntimeException()); 54 | 55 | ApacheHttpInstrumentation.ApacheHttpAdvice.methodEnter(request); 56 | 57 | assertNull(ApacheHttpInstrumentation.ApacheHttpAdvice.startTimeMap.get(request.hashCode())); 58 | } 59 | 60 | @Test 61 | public void handling_exit_response_lumigo_internal_request() { 62 | when(request.getURI()).thenReturn(URI.create("https://lumigo.io/api/spans")); 63 | 64 | ApacheHttpInstrumentation.ApacheHttpAdvice.methodExit(request, response); 65 | 66 | assertNull(ApacheHttpInstrumentation.ApacheHttpAdvice.handled.get(request.hashCode())); 67 | } 68 | 69 | @Test 70 | public void handling_exit_response_unknown_exception() { 71 | when(request.getURI()).thenThrow(new RuntimeException()); 72 | 73 | ApacheHttpInstrumentation.ApacheHttpAdvice.methodExit(request, response); 74 | 75 | assertNull(ApacheHttpInstrumentation.ApacheHttpAdvice.handled.get(request.hashCode())); 76 | } 77 | 78 | @Test 79 | public void handling_exit_response_already_handled() { 80 | when(request.getURI()).thenReturn(URI.create("https://not.lumigo.host/api/spans")); 81 | ApacheHttpInstrumentation.ApacheHttpAdvice.handled.put(request.hashCode(), true); 82 | 83 | ApacheHttpInstrumentation.ApacheHttpAdvice.methodExit(request, response); 84 | 85 | assertNotNull(ApacheHttpInstrumentation.ApacheHttpAdvice.handled.get(request.hashCode())); 86 | } 87 | 88 | @Disabled("Needs static mocking") 89 | @Test 90 | public void handling_exit_response_create_new_span() throws Exception { 91 | when(request.getURI()).thenReturn(URI.create("https://not.lumigo.host/api/spans")); 92 | 93 | ApacheHttpInstrumentation.ApacheHttpAdvice.methodExit(request, response); 94 | 95 | assertEquals(1, SpansContainer.getInstance().getSpans().size()); 96 | assertNotNull(ApacheHttpInstrumentation.ApacheHttpAdvice.handled.get(request.hashCode())); 97 | } 98 | 99 | @Test 100 | public void check_typeMatcher() { 101 | assertNotNull(new ApacheHttpInstrumentation().getTypeMatcher()); 102 | } 103 | 104 | @Test 105 | public void check_transformer() { 106 | assertNotNull(new ApacheHttpInstrumentation().getTransformer()); 107 | } 108 | 109 | private void initSpansContainer() { 110 | Map env = new HashMap<>(); 111 | env.put("_X_AMZN_TRACE_ID", "Root=1-2-3;Another=456;Bla=789"); 112 | try { 113 | SpansContainer.getInstance().clear(); 114 | SpansContainer.getInstance().init(env, null, null, null); 115 | } catch (Exception e) { 116 | } 117 | } 118 | 119 | private void initConfiguration() { 120 | Configuration.init(LumigoConfiguration.builder().edgeHost("lumigo.io").build()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/network/ReporterTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.network; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | import static org.mockito.Mockito.when; 6 | 7 | import io.lumigo.core.configuration.Configuration; 8 | import io.lumigo.core.utils.EnvUtil; 9 | import io.lumigo.models.Span; 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import org.junit.jupiter.api.BeforeEach; 14 | import org.junit.jupiter.api.Test; 15 | import org.mockito.Mock; 16 | import org.mockito.MockitoAnnotations; 17 | 18 | class ReporterTest { 19 | 20 | Reporter reporter = new Reporter(); 21 | 22 | @Mock EnvUtil envUtil; 23 | private Map env = new HashMap<>(); 24 | 25 | @BeforeEach 26 | void setUp() { 27 | MockitoAnnotations.initMocks(this); 28 | createMockedEnv(); 29 | Configuration.getInstance().setEnvUtil(envUtil); 30 | } 31 | 32 | private void createMockedEnv() { 33 | addEnvMock(Configuration.TRACER_HOST_KEY, "google.com"); 34 | when(envUtil.getEnv()).thenReturn(env); 35 | } 36 | 37 | private void addEnvMock(String key, String value) { 38 | env.put(key, value); 39 | when(envUtil.getEnv(key)).thenReturn(value); 40 | } 41 | 42 | @Test 43 | void reportSpans() throws IOException { 44 | addEnvMock("LAMBDA_RUNTIME_DIR", "/"); 45 | long l = reporter.reportSpans(Span.builder().build(), 900_000); 46 | assertTrue(l > 0); 47 | } 48 | 49 | @Test 50 | void reportSpanDropBySize() throws IOException { 51 | addEnvMock("LAMBDA_RUNTIME_DIR", "/"); 52 | long l = reporter.reportSpans(Span.builder().build(), 0); 53 | assertEquals(0, l); 54 | } 55 | 56 | @Test 57 | void reportSpans_not_aws_run() throws IOException { 58 | env.remove("LAMBDA_RUNTIME_DIR"); 59 | long l = reporter.reportSpans(Span.builder().build(), 900_000); 60 | assertEquals(0, l); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v1/AwsSdkV1ParserFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class AwsSdkV1ParserFactoryTest { 8 | 9 | @Test 10 | public void test_check_non_supported_value() { 11 | assertEquals( 12 | DoNothingV1Parser.class, 13 | AwsSdkV1ParserFactory.getParser("Not supported").getClass()); 14 | } 15 | 16 | @Test 17 | public void test_check_null_value() { 18 | assertEquals(DoNothingV1Parser.class, AwsSdkV1ParserFactory.getParser(null).getClass()); 19 | } 20 | 21 | @Test 22 | public void test_check_sns_value() { 23 | assertEquals(SnsV1Parser.class, AwsSdkV1ParserFactory.getParser("AmazonSNS").getClass()); 24 | } 25 | 26 | @Test 27 | public void test_check_sqs_value() { 28 | assertEquals(SqsV1Parser.class, AwsSdkV1ParserFactory.getParser("AmazonSQS").getClass()); 29 | } 30 | 31 | @Test 32 | public void test_check_kinesis_value() { 33 | assertEquals( 34 | KinesisV1Parser.class, AwsSdkV1ParserFactory.getParser("AmazonKinesis").getClass()); 35 | } 36 | 37 | @Test 38 | public void test_check_dynamodb_value() { 39 | assertEquals( 40 | DynamoDBV1Parser.class, 41 | AwsSdkV1ParserFactory.getParser("AmazonDynamoDB").getClass()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v1/DynamoDBV1ParserTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.mockito.Mockito.when; 5 | 6 | import com.amazonaws.AmazonWebServiceRequest; 7 | import com.amazonaws.Request; 8 | import com.amazonaws.Response; 9 | import com.amazonaws.services.dynamodbv2.model.*; 10 | import com.sun.tools.javac.util.List; 11 | import io.lumigo.models.HttpSpan; 12 | import java.util.Collections; 13 | import java.util.Map; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | import org.mockito.Mock; 17 | import org.mockito.MockitoAnnotations; 18 | 19 | class DynamoDBV1ParserTest { 20 | 21 | private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 22 | private DynamoDBV1Parser dynamoDBParser = new DynamoDBV1Parser(); 23 | @Mock Request request; 24 | @Mock GetItemRequest getItemRequest; 25 | @Mock BatchGetItemRequest batchGetItemRequest; 26 | @Mock PutItemRequest putItemRequest; 27 | @Mock UpdateItemRequest updateItemRequest; 28 | @Mock DeleteItemRequest deleteItemRequest; 29 | @Mock BatchWriteItemRequest batchWriteItemRequest; 30 | Response response; 31 | Map item; 32 | String itemHash; 33 | 34 | @BeforeEach 35 | void setUp() { 36 | MockitoAnnotations.initMocks(this); 37 | item = Collections.singletonMap("k", new AttributeValue("v")); 38 | itemHash = "44244ce1a15ee6d4dc270001564cb759"; 39 | } 40 | 41 | @Test 42 | void test_parse_ddb_unknown_request() { 43 | when(request.getOriginalRequest()).thenReturn(AmazonWebServiceRequest.NOOP); 44 | 45 | dynamoDBParser.safeParse(span, request, response); 46 | 47 | HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 48 | assertEquals(span, expectedSpan); 49 | } 50 | 51 | @Test 52 | void test_parse_ddb_get_item() { 53 | when(getItemRequest.getTableName()).thenReturn("tableName"); 54 | when(request.getOriginalRequest()).thenReturn(getItemRequest); 55 | 56 | dynamoDBParser.safeParse(span, request, response); 57 | 58 | HttpSpan expectedSpan = 59 | HttpSpan.builder() 60 | .info(HttpSpan.Info.builder().resourceName("tableName").build()) 61 | .build(); 62 | assertEquals(span, expectedSpan); 63 | } 64 | 65 | @Test 66 | void test_parse_ddb_batch_get_item() { 67 | when(batchGetItemRequest.getRequestItems()) 68 | .thenReturn(Collections.singletonMap("tableName", new KeysAndAttributes())); 69 | when(request.getOriginalRequest()).thenReturn(batchGetItemRequest); 70 | 71 | dynamoDBParser.safeParse(span, request, response); 72 | 73 | HttpSpan expectedSpan = 74 | HttpSpan.builder() 75 | .info(HttpSpan.Info.builder().resourceName("tableName").build()) 76 | .build(); 77 | assertEquals(span, expectedSpan); 78 | } 79 | 80 | @Test 81 | void test_parse_ddb_put_item() { 82 | when(putItemRequest.getTableName()).thenReturn("tableName"); 83 | when(putItemRequest.getItem()).thenReturn(item); 84 | when(request.getOriginalRequest()).thenReturn(putItemRequest); 85 | 86 | dynamoDBParser.safeParse(span, request, response); 87 | 88 | HttpSpan expectedSpan = 89 | HttpSpan.builder() 90 | .info( 91 | HttpSpan.Info.builder() 92 | .resourceName("tableName") 93 | .messageId(itemHash) 94 | .build()) 95 | .build(); 96 | assertEquals(span, expectedSpan); 97 | } 98 | 99 | @Test 100 | void test_parse_ddb_update_item() { 101 | when(updateItemRequest.getTableName()).thenReturn("tableName"); 102 | when(updateItemRequest.getKey()).thenReturn(item); 103 | when(request.getOriginalRequest()).thenReturn(updateItemRequest); 104 | 105 | dynamoDBParser.safeParse(span, request, response); 106 | 107 | HttpSpan expectedSpan = 108 | HttpSpan.builder() 109 | .info( 110 | HttpSpan.Info.builder() 111 | .resourceName("tableName") 112 | .messageId(itemHash) 113 | .build()) 114 | .build(); 115 | assertEquals(span, expectedSpan); 116 | } 117 | 118 | @Test 119 | void test_parse_ddb_delete_item() { 120 | when(deleteItemRequest.getTableName()).thenReturn("tableName"); 121 | when(deleteItemRequest.getKey()).thenReturn(item); 122 | when(request.getOriginalRequest()).thenReturn(deleteItemRequest); 123 | 124 | dynamoDBParser.safeParse(span, request, response); 125 | 126 | HttpSpan expectedSpan = 127 | HttpSpan.builder() 128 | .info( 129 | HttpSpan.Info.builder() 130 | .resourceName("tableName") 131 | .messageId(itemHash) 132 | .build()) 133 | .build(); 134 | assertEquals(span, expectedSpan); 135 | } 136 | 137 | @Test 138 | void test_parse_ddb_batch_write_item() { 139 | when(batchWriteItemRequest.getRequestItems()) 140 | .thenReturn( 141 | Collections.singletonMap( 142 | "tableName", List.of(new WriteRequest(new PutRequest(item))))); 143 | when(request.getOriginalRequest()).thenReturn(batchWriteItemRequest); 144 | 145 | dynamoDBParser.safeParse(span, request, response); 146 | 147 | HttpSpan expectedSpan = 148 | HttpSpan.builder() 149 | .info( 150 | HttpSpan.Info.builder() 151 | .resourceName("tableName") 152 | .messageId(itemHash) 153 | .build()) 154 | .build(); 155 | assertEquals(span, expectedSpan); 156 | } 157 | 158 | @Test 159 | void test_parse_ddb_batch_delete_item() { 160 | when(batchWriteItemRequest.getRequestItems()) 161 | .thenReturn( 162 | Collections.singletonMap( 163 | "tableName", List.of(new WriteRequest(new DeleteRequest(item))))); 164 | when(request.getOriginalRequest()).thenReturn(batchWriteItemRequest); 165 | 166 | dynamoDBParser.safeParse(span, request, response); 167 | 168 | HttpSpan expectedSpan = 169 | HttpSpan.builder() 170 | .info( 171 | HttpSpan.Info.builder() 172 | .resourceName("tableName") 173 | .messageId(itemHash) 174 | .build()) 175 | .build(); 176 | assertEquals(span, expectedSpan); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v1/KinesisV1ParserTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNull; 5 | import static org.mockito.Mockito.when; 6 | 7 | import com.amazonaws.Request; 8 | import com.amazonaws.Response; 9 | import com.amazonaws.http.HttpResponse; 10 | import com.amazonaws.services.kinesis.model.*; 11 | import io.lumigo.models.HttpSpan; 12 | import java.util.Arrays; 13 | import java.util.HashMap; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | import org.mockito.Mock; 17 | import org.mockito.MockitoAnnotations; 18 | 19 | class KinesisV1ParserTest { 20 | 21 | private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 22 | KinesisV1Parser kinesisParser = new KinesisV1Parser(); 23 | @Mock Request request; 24 | @Mock HttpResponse httpResponse; 25 | @Mock PutRecordsRequest putRecordsRequest; 26 | @Mock PutRecordsResult putRecordsResult; 27 | @Mock PutRecordRequest putRecordRequest; 28 | @Mock PutRecordResult putRecordResult; 29 | Response response; 30 | 31 | @BeforeEach 32 | void setUp() { 33 | MockitoAnnotations.initMocks(this); 34 | } 35 | 36 | @Test 37 | void test_parse_kinesis_parser_with_no_data() { 38 | when(request.getParameters()).thenReturn(new HashMap<>()); 39 | 40 | kinesisParser.safeParse(span, request, new Response(null, httpResponse)); 41 | 42 | assertNull(span.getInfo().getResourceName()); 43 | assertNull(span.getInfo().getTargetArn()); 44 | assertNull(span.getInfo().getMessageId()); 45 | assertNull(span.getInfo().getMessageIds()); 46 | } 47 | 48 | @Test 49 | void test_parse_kinesis_put_record_simple_flow() { 50 | response = new Response(putRecordResult, httpResponse); 51 | when(putRecordResult.getSequenceNumber()) 52 | .thenReturn("fee47356-6f6a-58c8-96dc-26d8aaa4631a"); 53 | when(putRecordRequest.getStreamName()).thenReturn("streamName"); 54 | when(request.getOriginalRequest()).thenReturn(putRecordRequest); 55 | 56 | kinesisParser.safeParse(span, request, response); 57 | 58 | HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 59 | expectedSpan.getInfo().setResourceName("streamName"); 60 | expectedSpan.getInfo().setMessageIds(Arrays.asList("fee47356-6f6a-58c8-96dc-26d8aaa4631a")); 61 | assertEquals(span, expectedSpan); 62 | } 63 | 64 | @Test 65 | void test_parse_kinesis_put_record_with_exception() { 66 | response = new Response(putRecordResult, httpResponse); 67 | when(putRecordResult.getSequenceNumber()).thenThrow(new RuntimeException()); 68 | when(request.getParameters()).thenReturn(new HashMap<>()); 69 | 70 | kinesisParser.safeParse(span, request, response); 71 | 72 | HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 73 | assertEquals(span, expectedSpan); 74 | } 75 | 76 | @Test 77 | void test_parse_kinesis_put_records_simple_flow() { 78 | response = new Response(putRecordsResult, httpResponse); 79 | PutRecordsResultEntry firstResult = new PutRecordsResultEntry(); 80 | PutRecordsResultEntry secResult = new PutRecordsResultEntry(); 81 | firstResult.setSequenceNumber("1"); 82 | secResult.setSequenceNumber("2"); 83 | PutRecordsResultEntry[] results = {firstResult, secResult}; 84 | when(putRecordsResult.getRecords()).thenReturn(Arrays.asList(results)); 85 | when(putRecordsRequest.getStreamName()).thenReturn("streamName"); 86 | when(request.getOriginalRequest()).thenReturn(putRecordsRequest); 87 | 88 | kinesisParser.safeParse(span, request, response); 89 | 90 | HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 91 | expectedSpan.getInfo().setResourceName("streamName"); 92 | expectedSpan.getInfo().setMessageIds(Arrays.asList("1", "2")); 93 | assertEquals(span, expectedSpan); 94 | } 95 | 96 | @Test 97 | void test_parse_kinesis_put_records_with_exception_from_request() { 98 | response = new Response(putRecordsResult, httpResponse); 99 | when(request.getOriginalRequest()).thenThrow(new RuntimeException()); 100 | 101 | kinesisParser.safeParse(span, request, response); 102 | 103 | HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 104 | assertEquals(span, expectedSpan); 105 | } 106 | 107 | @Test 108 | void test_parse_kinesis_put_records_with_exception_from_response() { 109 | response = new Response(putRecordsResult, httpResponse); 110 | when(putRecordsResult.getRecords()).thenThrow(new RuntimeException()); 111 | when(request.getParameters()).thenReturn(new HashMap<>()); 112 | 113 | kinesisParser.safeParse(span, request, response); 114 | 115 | HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 116 | assertEquals(span, expectedSpan); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v1/SnsV1ParserTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNull; 5 | import static org.mockito.Mockito.when; 6 | 7 | import com.amazonaws.Request; 8 | import com.amazonaws.Response; 9 | import com.amazonaws.http.HttpResponse; 10 | import com.amazonaws.services.sns.model.PublishResult; 11 | import io.lumigo.models.HttpSpan; 12 | import java.util.Arrays; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import org.junit.jupiter.api.BeforeEach; 17 | import org.junit.jupiter.api.Test; 18 | import org.mockito.Mock; 19 | import org.mockito.MockitoAnnotations; 20 | 21 | class SnsV1ParserTest { 22 | 23 | private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 24 | SnsV1Parser SnsParser = new SnsV1Parser(); 25 | @Mock Request request; 26 | @Mock HttpResponse httpResponse; 27 | @Mock PublishResult snsResult; 28 | Response response; 29 | 30 | @BeforeEach 31 | void setUp() { 32 | MockitoAnnotations.initMocks(this); 33 | response = new Response(snsResult, httpResponse); 34 | } 35 | 36 | @Test 37 | void test_parse_sns_with_full_details() { 38 | when(snsResult.getMessageId()).thenReturn("fee47356-6f6a-58c8-96dc-26d8aaa4631a"); 39 | Map> parameters = new HashMap<>(); 40 | parameters.put("TopicArn", Arrays.asList("topic")); 41 | when(request.getParameters()).thenReturn(parameters); 42 | 43 | SnsParser.safeParse(span, request, response); 44 | 45 | assertEquals("topic", span.getInfo().getResourceName()); 46 | assertEquals("topic", span.getInfo().getTargetArn()); 47 | assertEquals("fee47356-6f6a-58c8-96dc-26d8aaa4631a", span.getInfo().getMessageId()); 48 | } 49 | 50 | @Test 51 | void test_parse_sns_with_no_data() { 52 | when(request.getParameters()).thenReturn(new HashMap<>()); 53 | 54 | SnsParser.safeParse(span, request, new Response(null, httpResponse)); 55 | 56 | assertNull(span.getInfo().getResourceName()); 57 | assertNull(span.getInfo().getTargetArn()); 58 | assertNull(span.getInfo().getMessageId()); 59 | } 60 | 61 | @Test 62 | void test_parse_sns_with_exception() { 63 | when(snsResult.getMessageId()).thenThrow(new RuntimeException()); 64 | when(request.getParameters()).thenReturn(new HashMap<>()); 65 | 66 | SnsParser.safeParse(span, request, response); 67 | 68 | assertNull(span.getInfo().getResourceName()); 69 | assertNull(span.getInfo().getTargetArn()); 70 | assertNull(span.getInfo().getMessageId()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v1/SqsV1ParserTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v1; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNull; 5 | import static org.mockito.Mockito.when; 6 | 7 | import com.amazonaws.Request; 8 | import com.amazonaws.Response; 9 | import com.amazonaws.http.HttpResponse; 10 | import com.amazonaws.services.sqs.model.SendMessageRequest; 11 | import com.amazonaws.services.sqs.model.SendMessageResult; 12 | import io.lumigo.models.HttpSpan; 13 | import java.util.HashMap; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | import org.mockito.Mock; 17 | import org.mockito.MockitoAnnotations; 18 | 19 | class SqsV1ParserTest { 20 | 21 | private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 22 | SqsV1Parser sqsParser = new SqsV1Parser(); 23 | @Mock Request request; 24 | @Mock HttpResponse httpResponse; 25 | @Mock SendMessageResult sqsResult; 26 | @Mock SendMessageRequest sqsRequest; 27 | Response response; 28 | 29 | @BeforeEach 30 | void setUp() { 31 | MockitoAnnotations.initMocks(this); 32 | response = new Response(sqsResult, httpResponse); 33 | } 34 | 35 | @Test 36 | void test_parse_sqs_with_full_details() { 37 | when(sqsResult.getMessageId()).thenReturn("fee47356-6f6a-58c8-96dc-26d8aaa4631a"); 38 | when(sqsRequest.getQueueUrl()).thenReturn("queueUrl"); 39 | when(request.getOriginalRequest()).thenReturn(sqsRequest); 40 | 41 | sqsParser.safeParse(span, request, response); 42 | 43 | assertEquals("queueUrl", span.getInfo().getResourceName()); 44 | assertEquals("fee47356-6f6a-58c8-96dc-26d8aaa4631a", span.getInfo().getMessageId()); 45 | } 46 | 47 | @Test 48 | void test_parse_sqs_with_no_data() { 49 | when(request.getParameters()).thenReturn(new HashMap<>()); 50 | 51 | sqsParser.safeParse(span, request, new Response(null, httpResponse)); 52 | 53 | assertNull(span.getInfo().getResourceName()); 54 | assertNull(span.getInfo().getMessageId()); 55 | } 56 | 57 | @Test 58 | void test_parse_sqs_with_exception() { 59 | when(sqsResult.getMessageId()).thenThrow(new RuntimeException()); 60 | when(request.getParameters()).thenReturn(new HashMap<>()); 61 | 62 | sqsParser.safeParse(span, request, response); 63 | 64 | assertNull(span.getInfo().getResourceName()); 65 | assertNull(span.getInfo().getMessageId()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v2/AwsSdkV2ParserFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class AwsSdkV2ParserFactoryTest { 8 | 9 | @Test 10 | public void test_check_non_supported_value() { 11 | assertEquals( 12 | DoNothingV2Parser.class, 13 | AwsSdkV2ParserFactory.getParser("Not supported").getClass()); 14 | } 15 | 16 | @Test 17 | public void test_check_null_value() { 18 | assertEquals(DoNothingV2Parser.class, AwsSdkV2ParserFactory.getParser(null).getClass()); 19 | } 20 | 21 | @Test 22 | public void test_check_sns_value_v2() { 23 | assertEquals(SnsV2Parser.class, AwsSdkV2ParserFactory.getParser("Sns").getClass()); 24 | } 25 | 26 | @Test 27 | public void test_check_sqs_value_v2() { 28 | assertEquals(SqsV2Parser.class, AwsSdkV2ParserFactory.getParser("Sqs").getClass()); 29 | } 30 | 31 | @Test 32 | public void test_check_kinesis_value_v2() { 33 | assertEquals(KinesisV2Parser.class, AwsSdkV2ParserFactory.getParser("Kinesis").getClass()); 34 | } 35 | 36 | @Test 37 | public void test_check_dynamodb_value_v2() { 38 | assertEquals( 39 | DynamoDBV2Parser.class, AwsSdkV2ParserFactory.getParser("DynamoDb").getClass()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v2/KinesisV2ParserTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import io.lumigo.models.HttpSpan; 6 | import java.util.Arrays; 7 | import org.junit.jupiter.api.Test; 8 | import software.amazon.awssdk.core.interceptor.Context; 9 | import software.amazon.awssdk.core.interceptor.InterceptorContext; 10 | import software.amazon.awssdk.services.kinesis.model.PutRecordsRequest; 11 | import software.amazon.awssdk.services.kinesis.model.PutRecordsRequestEntry; 12 | import software.amazon.awssdk.services.kinesis.model.PutRecordsResponse; 13 | import software.amazon.awssdk.services.kinesis.model.PutRecordsResultEntry; 14 | 15 | class KinesisV2ParserTest { 16 | 17 | private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 18 | KinesisV2Parser kinesisParser = new KinesisV2Parser(); 19 | 20 | @Test 21 | void test_parse_kinesis_put_record_simple_flow_v2() { 22 | PutRecordsRequest putRequest = 23 | PutRecordsRequest.builder() 24 | .records(PutRecordsRequestEntry.builder().build()) 25 | .streamName("streamName") 26 | .build(); 27 | PutRecordsResponse putResponse = 28 | PutRecordsResponse.builder() 29 | .records( 30 | PutRecordsResultEntry.builder() 31 | .sequenceNumber("fee47356-6f6a-58c8-96dc-26d8aaa4631a") 32 | .build()) 33 | .build(); 34 | Context.AfterExecution context = 35 | InterceptorContext.builder().request(putRequest).response(putResponse).build(); 36 | 37 | kinesisParser.safeParse(span, context); 38 | 39 | HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 40 | expectedSpan.getInfo().setResourceName("streamName"); 41 | expectedSpan.getInfo().setMessageIds(Arrays.asList("fee47356-6f6a-58c8-96dc-26d8aaa4631a")); 42 | assertEquals(span, expectedSpan); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v2/SnsV2ParserTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import io.lumigo.models.HttpSpan; 6 | import org.junit.jupiter.api.Test; 7 | import software.amazon.awssdk.core.interceptor.Context; 8 | import software.amazon.awssdk.core.interceptor.InterceptorContext; 9 | import software.amazon.awssdk.services.sns.model.PublishRequest; 10 | import software.amazon.awssdk.services.sns.model.PublishResponse; 11 | 12 | class SnsV2ParserTest { 13 | 14 | private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 15 | SnsV2Parser SnsParser = new SnsV2Parser(); 16 | 17 | @Test 18 | void test_parse_sns_with_full_details_v2() { 19 | PublishRequest publishRequest = PublishRequest.builder().topicArn("topic").build(); 20 | PublishResponse publishResponse = 21 | PublishResponse.builder().messageId("fee47356-6f6a-58c8-96dc-26d8aaa4631a").build(); 22 | Context.AfterExecution context = 23 | InterceptorContext.builder() 24 | .request(publishRequest) 25 | .response(publishResponse) 26 | .build(); 27 | 28 | SnsParser.safeParse(span, context); 29 | 30 | assertEquals("topic", span.getInfo().getResourceName()); 31 | assertEquals("topic", span.getInfo().getTargetArn()); 32 | assertEquals("fee47356-6f6a-58c8-96dc-26d8aaa4631a", span.getInfo().getMessageId()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/parsers/v2/SqsV2ParserTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.parsers.v2; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import io.lumigo.models.HttpSpan; 6 | import org.junit.jupiter.api.Test; 7 | import software.amazon.awssdk.core.interceptor.Context; 8 | import software.amazon.awssdk.core.interceptor.InterceptorContext; 9 | import software.amazon.awssdk.services.sqs.model.SendMessageRequest; 10 | import software.amazon.awssdk.services.sqs.model.SendMessageResponse; 11 | 12 | class SqsV2ParserTest { 13 | 14 | private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); 15 | SqsV2Parser sqsParser = new SqsV2Parser(); 16 | 17 | @Test 18 | void test_parse_sqs_with_full_details_v2() { 19 | SendMessageRequest publishRequest = 20 | SendMessageRequest.builder().queueUrl("queueUrl").build(); 21 | SendMessageResponse messageResponse = 22 | SendMessageResponse.builder() 23 | .messageId("fee47356-6f6a-58c8-96dc-26d8aaa4631a") 24 | .build(); 25 | Context.AfterExecution context = 26 | InterceptorContext.builder() 27 | .request(publishRequest) 28 | .response(messageResponse) 29 | .build(); 30 | 31 | sqsParser.safeParse(span, context); 32 | 33 | assertEquals("queueUrl", span.getInfo().getResourceName()); 34 | assertEquals("fee47356-6f6a-58c8-96dc-26d8aaa4631a", span.getInfo().getMessageId()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/utils/AwsSdkV2UtilsTests.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | 6 | import com.google.common.collect.Lists; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import software.amazon.awssdk.services.dynamodb.model.AttributeValue; 14 | 15 | public class AwsSdkV2UtilsTests { 16 | 17 | private Map attributeValueMap; 18 | 19 | @BeforeEach 20 | void setUp() { 21 | attributeValueMap = new HashMap<>(); 22 | attributeValueMap.put("key", AttributeValue.builder().s("value").build()); 23 | attributeValueMap.put("key2", AttributeValue.builder().n("2").build()); 24 | attributeValueMap.put("key3", AttributeValue.builder().bool(true).build()); 25 | attributeValueMap.put( 26 | "key4", 27 | AttributeValue.builder() 28 | .m( 29 | Collections.singletonMap( 30 | "key", AttributeValue.builder().s("value4").build())) 31 | .build()); 32 | attributeValueMap.put( 33 | "key5", 34 | AttributeValue.builder() 35 | .l( 36 | AttributeValue.builder().s("value5").build(), 37 | AttributeValue.builder().s("value5.2").build()) 38 | .build()); 39 | } 40 | 41 | @Test 42 | void testCalculateItemHash() { 43 | String result = AwsSdkV2Utils.calculateItemHash(attributeValueMap); 44 | 45 | assertEquals("10ac224af47748812c94ade5be937d59", result); 46 | } 47 | 48 | @Test 49 | void testConvertAttributeMapToSimpleMap() { 50 | Map result = 51 | AwsSdkV2Utils.convertAttributeMapToSimpleMap(attributeValueMap); 52 | 53 | assertEquals(attributeValueMap.size(), result.size()); 54 | assertEquals("value", result.get("key")); 55 | assertEquals("2", result.get("key2")); 56 | assertEquals(true, result.get("key3")); 57 | assertEquals(Collections.singletonMap("key", "value4"), result.get("key4")); 58 | assertArrayEquals( 59 | Lists.newArrayList("value5", "value5.2").toArray(), 60 | ((ArrayList) result.get("key5")).toArray()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/utils/EnvUtilTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class EnvUtilTest { 8 | 9 | EnvUtil envUtil = new EnvUtil(); 10 | 11 | @Test 12 | void getEnv_by_property() { 13 | assertEquals(envUtil.getEnv("USER"), System.getenv("USER")); 14 | } 15 | 16 | @Test 17 | void getEnv() { 18 | for (String key : System.getenv().keySet()) { 19 | assertEquals(System.getenv(key), envUtil.getEnv(key)); 20 | } 21 | } 22 | 23 | @Test 24 | void getEnv_string_array_default() { 25 | String[] deflt = {"a", "b"}; 26 | 27 | assertEquals(deflt, envUtil.getStringArrayEnv("NOT_EXISTS", deflt)); 28 | } 29 | 30 | @Test 31 | void getEnv_string_array() { 32 | String[] arr = {System.getenv("HOME")}; 33 | 34 | String[] actual = envUtil.getStringArrayEnv("HOME", null); 35 | 36 | assertEquals(1, actual.length); 37 | assertEquals(arr[0], actual[0]); 38 | } 39 | 40 | @Test 41 | void getEnv_integer_deflt() { 42 | Integer deflt = 555; 43 | 44 | assertEquals(deflt, envUtil.getIntegerEnv("NOT_EXISTS", deflt)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/utils/JsonUtilsTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class JsonUtilsTest { 8 | 9 | @Test 10 | void testGetObjectAsJsonString_null() throws Exception { 11 | assertNull(null, JsonUtils.getObjectAsJsonString(null)); 12 | } 13 | 14 | @Test 15 | void testGetObjectAsJsonString_Object() throws Exception { 16 | assertEquals("{\"a\":1,\"b\":\"2\"}", JsonUtils.getObjectAsJsonString(new A())); 17 | } 18 | 19 | @Test 20 | void testGetObjectAsJsonString_string() throws Exception { 21 | assertEquals("aaa", JsonUtils.getObjectAsJsonString("aaa")); 22 | } 23 | 24 | @Test 25 | void testGetObjectAsJsonString_int() throws Exception { 26 | assertEquals("1", JsonUtils.getObjectAsJsonString(1)); 27 | } 28 | 29 | public class A { 30 | private int a = 1; 31 | private String b = "2"; 32 | 33 | public int getA() { 34 | return a; 35 | } 36 | 37 | public String getB() { 38 | return b; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/utils/LRUCacheTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class LRUCacheTest { 8 | 9 | LRUCache cache = new LRUCache<>(4); 10 | 11 | @Test 12 | public void check_LRU_only_by_insert() { 13 | cache.put(1, 1); 14 | cache.put(2, 2); 15 | cache.put(3, 3); 16 | cache.put(4, 4); 17 | 18 | assertNull(cache.get(1)); 19 | assertNotNull(cache.get(2)); 20 | assertNotNull(cache.get(3)); 21 | assertNotNull(cache.get(4)); 22 | } 23 | 24 | @Test 25 | public void check_LRU_by_use() { 26 | cache.put(1, 1); 27 | cache.put(2, 2); 28 | cache.put(3, 3); 29 | cache.get(1); 30 | cache.put(4, 4); 31 | 32 | assertNull(cache.get(2)); 33 | assertNotNull(cache.get(1)); 34 | assertNotNull(cache.get(3)); 35 | assertNotNull(cache.get(4)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/utils/SecretScrubberTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import io.lumigo.core.configuration.Configuration; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import org.junit.jupiter.api.DisplayName; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class SecretScrubberTest { 12 | static final Map noScrubbingOverridesEnv = new HashMap(); 13 | 14 | @Test 15 | @DisplayName("does not modify non-json payloads") 16 | void testSecretScrubbingUtils_does_not_scrub_non_json_payloads() { 17 | assertEquals( 18 | "123", new SecretScrubber(noScrubbingOverridesEnv).scrubStringifiedObject("123")); 19 | } 20 | 21 | @Test 22 | @DisplayName("scrubs a nested body value with default expressions") 23 | void testSecretScrubbingUtils_scrubs_json_payload_default() { 24 | String actual = 25 | new SecretScrubber(noScrubbingOverridesEnv) 26 | .scrubStringifiedObject( 27 | "{\"pass\":\"word\",\"key\":\"value\",\"secret\":\"stuff\",\"credential\":\"admin:admin\",\"passphrase\":\"SesameOpen\",\"SessionToken\":\"XyZ012x=\",\"x-amz-security-token\":\"amzToken123\",\"Signature\":\"yours truly\",\"authorization\":\"Bearer 123\"}"); 28 | String expected = 29 | "{\"authorization\":\"****\",\"credential\":\"****\",\"pass\":\"****\",\"SessionToken\":\"****\",\"Signature\":\"****\",\"passphrase\":\"****\",\"secret\":\"****\",\"x-amz-security-token\":\"****\",\"key\":\"****\"}"; 30 | 31 | assertEquals(expected, actual); 32 | } 33 | 34 | @Test 35 | @DisplayName( 36 | "scrubs body using the default patterns when LUMIGO_SECRET_MASKING_REGEX is invalid") 37 | void 38 | testSecretScrubbingUtils_scrubs_json_payload_default_used_when_invalid_pattern_list_provided() { 39 | String actual = 40 | new SecretScrubber(envWithMaskingRegex("[THIS IS NOT A VALID JSON ARRAY]")) 41 | .scrubStringifiedObject( 42 | "{\"pass\":\"word\",\"key\":\"value\",\"secret\":\"stuff\",\"credential\":\"admin:admin\",\"passphrase\":\"SesameOpen\",\"SessionToken\":\"XyZ012x=\",\"x-amz-security-token\":\"amzToken123\",\"Signature\":\"yours truly\",\"authorization\":\"Bearer 123\"}"); 43 | String expected = 44 | "{\"authorization\":\"****\",\"credential\":\"****\",\"pass\":\"****\",\"SessionToken\":\"****\",\"Signature\":\"****\",\"passphrase\":\"****\",\"secret\":\"****\",\"x-amz-security-token\":\"****\",\"key\":\"****\"}"; 45 | 46 | assertEquals(expected, actual); 47 | } 48 | 49 | @Test 50 | @DisplayName("scrubs body using default expressions - case insensitive") 51 | void testSecretScrubbingUtils_scrubs_json_payload_default_case_insensitive() { 52 | String actual = 53 | new SecretScrubber(noScrubbingOverridesEnv) 54 | .scrubStringifiedObject( 55 | "{\"pAss\":\"word\",\"KeY\":\"value\",\"seCRet\":\"stuff\",\"CrEdEntial\":\"admin:admin\",\"paSSPhrase\":\"SesameOpen\",\"seSSIOntOkEn\":\"XyZ012x=\",\"X-AMZ-security-token\":\"amzToken123\",\"SIGnatUre\":\"yours truly\",\"AuTHOrization\":\"Bearer 123\"}"); 56 | String expected = 57 | "{\"pAss\":\"****\",\"CrEdEntial\":\"****\",\"paSSPhrase\":\"****\",\"seSSIOntOkEn\":\"****\",\"SIGnatUre\":\"****\",\"seCRet\":\"****\",\"X-AMZ-security-token\":\"****\",\"KeY\":\"****\",\"AuTHOrization\":\"****\"}"; 58 | 59 | assertEquals(expected, actual); 60 | } 61 | 62 | @Test 63 | @DisplayName("scrubs body using LUMIGO_SECRET_MASKING_REGEX - top-level key") 64 | void testSecretScrubbingUtils_scrubs_json_payload_top_level_key() { 65 | String actual = 66 | new SecretScrubber(envWithMaskingRegex("[\".*topsecret.*\"]")) 67 | .scrubStringifiedObject("{\"topsecret\":\"stuff\"}"); 68 | String expected = "{\"topsecret\":\"****\"}"; 69 | 70 | assertEquals(expected, actual); 71 | } 72 | 73 | @Test 74 | @DisplayName("scrubs body using LUMIGO_SECRET_MASKING_REGEX - nested keys") 75 | void testSecretScrubbingUtils_scrubs_json_payload_nested_key() { 76 | String actual = 77 | new SecretScrubber(envWithMaskingRegex("[\".*topsecret.*\"]")) 78 | .scrubStringifiedObject("{\"some\": {\"topsecret\":\"stuff\"}, \"a\": 1}"); 79 | 80 | String expected = "{\"some\":{\"topsecret\":\"****\"},\"a\":1}"; 81 | 82 | assertEquals(expected, actual); 83 | } 84 | 85 | @Test 86 | @DisplayName("scrubs arrays using default expressions") 87 | void testSecretScrubbingUtils_scrubs_arrays() { 88 | String actual = 89 | new SecretScrubber(envWithMaskingRegex("[\".*topsecret.*\"]")) 90 | .scrubStringifiedObject( 91 | "{\"some\": [{\"topsecret\":\"stuff\"}, {\"a\":1}]}"); 92 | 93 | String expected = "{\"some\":[{\"topsecret\":\"****\"},{\"a\":1}]}"; 94 | 95 | assertEquals(expected, actual); 96 | } 97 | 98 | private Map envWithMaskingRegex(String maskingRegex) { 99 | return new HashMap() { 100 | { 101 | put(Configuration.LUMIGO_SECRET_MASKING_REGEX, maskingRegex); 102 | } 103 | }; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/core/utils/StringUtilsTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.core.utils; 2 | 3 | import static io.lumigo.core.utils.StringUtils.buildMd5Hash; 4 | import static io.lumigo.core.utils.StringUtils.dynamodbItemToHash; 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | import com.amazonaws.services.dynamodbv2.model.AttributeValue; 8 | import java.io.ByteArrayInputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.nio.charset.Charset; 12 | import java.util.Arrays; 13 | import java.util.Base64; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import org.apache.commons.io.IOUtils; 17 | import org.junit.jupiter.api.Test; 18 | 19 | class StringUtilsTest { 20 | 21 | static final char ch = '*'; 22 | 23 | @Test 24 | void test_getMaxSizeString_null() { 25 | assertNull(null, StringUtils.getMaxSizeString(null, 1024)); 26 | } 27 | 28 | @Test 29 | void test_getMaxSizeString_short_string() { 30 | assertEquals("aaaaa", StringUtils.getMaxSizeString("aaaaa", 1024)); 31 | } 32 | 33 | @Test 34 | void test_getMaxSizeString_long_string() { 35 | char[] charArray = new char[1500]; 36 | Arrays.fill(new char[1500], ch); 37 | String res = StringUtils.getMaxSizeString(new String(charArray), 1024); 38 | assertEquals(1024, res.length()); 39 | } 40 | 41 | @Test 42 | void test_randomStringAndNumbers_check_size() { 43 | assertEquals(5, StringUtils.randomStringAndNumbers(5).length()); 44 | } 45 | 46 | @Test 47 | void test_randomStringAndNumbers_check_only_contain_letters_and_numbers() { 48 | assertTrue(StringUtils.randomStringAndNumbers(50).matches("[a-z0-9]*")); 49 | } 50 | 51 | @Test 52 | void test_randomStringAndNumbers_check_random() { 53 | assertNotEquals( 54 | StringUtils.randomStringAndNumbers(50), StringUtils.randomStringAndNumbers(50)); 55 | } 56 | 57 | @Test 58 | void extractStringForStream_long() throws IOException { 59 | InputStream inputStream = 60 | new ByteArrayInputStream("123456789".getBytes(Charset.forName("UTF-8"))); 61 | assertEquals("12345", StringUtils.extractStringForStream(inputStream, 5)); 62 | assertEquals("123456789", IOUtils.toString(inputStream, Charset.forName("UTF-8"))); 63 | } 64 | 65 | @Test 66 | void extractStringForStream_short() throws IOException { 67 | InputStream inputStream = 68 | new ByteArrayInputStream("123456789".getBytes(Charset.forName("UTF-8"))); 69 | assertEquals("123456789", StringUtils.extractStringForStream(inputStream, 100)); 70 | assertEquals("123456789", IOUtils.toString(inputStream, Charset.forName("UTF-8"))); 71 | } 72 | 73 | @Test 74 | void extractStringForStream_empty_stream() throws IOException { 75 | InputStream empty = new ByteArrayInputStream(new byte[0]); 76 | assertNull(StringUtils.extractStringForStream(empty, 100)); 77 | } 78 | 79 | @Test 80 | void buildMd5Hash_happy_flow() { 81 | assertEquals("81dc9bdb52d04dc20036dbd8313ed055", buildMd5Hash("1234")); 82 | } 83 | 84 | @Test 85 | void dynamodbItemToHash_order_doesnt_matter() { 86 | Map item1 = 87 | new HashMap() { 88 | { 89 | put("k", new AttributeValue("v")); 90 | put("k2", new AttributeValue("v2")); 91 | } 92 | }; 93 | Map item2 = 94 | new HashMap() { 95 | { 96 | put("k2", new AttributeValue("v2")); 97 | put("k", new AttributeValue("v")); 98 | } 99 | }; 100 | assertEquals(dynamodbItemToHash(item1), dynamodbItemToHash(item2)); 101 | } 102 | 103 | @Test 104 | void getBase64Size() { 105 | String originalInput = "test input"; 106 | String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes()); 107 | 108 | assertEquals(encodedString.length(), StringUtils.getBase64Size(originalInput)); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/models/KafkaSpanTest.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.models; 2 | 3 | import io.lumigo.core.utils.SecretScrubber; 4 | import java.util.Collections; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.junit.Assert; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class KafkaSpanTest { 10 | 11 | @Test 12 | public void testScrub() throws Exception { 13 | KafkaSpan kafkaSpan = 14 | KafkaSpan.builder() 15 | .info( 16 | KafkaSpan.Info.builder() 17 | .kafkaInfo( 18 | KafkaSpan.KafkaProducerInfo.builder() 19 | .bootstrapServers( 20 | StringUtils.repeat( 21 | "bootstrapServer,", 10)) 22 | .record( 23 | KafkaSpan.KafkaProducerRecord 24 | .builder() 25 | .key("{\"key\":\"value\"}") 26 | .value( 27 | StringUtils.repeat( 28 | "value,", 29 | 10)) 30 | .headers( 31 | StringUtils.repeat( 32 | "headers,", 33 | 10)) 34 | .build()) 35 | .build()) 36 | .build()) 37 | .build(); 38 | 39 | KafkaSpan result = 40 | (KafkaSpan) 41 | kafkaSpan.scrub( 42 | new SecretScrubber( 43 | Collections.singletonMap( 44 | "LUMIGO_SECRET_MASKING_REGEX", ".*key.*"))); 45 | ; 46 | Assert.assertEquals( 47 | "{\"key\":\"****\"}", 48 | ((KafkaSpan.KafkaProducerInfo) result.getInfo().getKafkaInfo()) 49 | .getRecord() 50 | .getKey()); 51 | } 52 | 53 | @Test 54 | public void testReduceSizeProduce() throws Exception { 55 | 56 | KafkaSpan kafkaSpan = 57 | KafkaSpan.builder() 58 | .info( 59 | KafkaSpan.Info.builder() 60 | .kafkaInfo( 61 | KafkaSpan.KafkaProducerInfo.builder() 62 | .bootstrapServers( 63 | StringUtils.repeat( 64 | "bootstrapServer,", 10)) 65 | .record( 66 | KafkaSpan.KafkaProducerRecord 67 | .builder() 68 | .key( 69 | StringUtils.repeat( 70 | "key,", 10)) 71 | .value( 72 | StringUtils.repeat( 73 | "value,", 74 | 10)) 75 | .headers( 76 | StringUtils.repeat( 77 | "headers,", 78 | 10)) 79 | .build()) 80 | .build()) 81 | .build()) 82 | .build(); 83 | 84 | KafkaSpan result = (KafkaSpan) kafkaSpan.reduceSize(10); 85 | 86 | assert ((KafkaSpan.KafkaProducerInfo) result.getInfo().getKafkaInfo()) 87 | .getBootstrapServers() 88 | .length() 89 | == 10; 90 | assert ((KafkaSpan.KafkaProducerInfo) result.getInfo().getKafkaInfo()) 91 | .getRecord() 92 | .getKey() 93 | .length() 94 | == 10; 95 | assert ((KafkaSpan.KafkaProducerInfo) result.getInfo().getKafkaInfo()) 96 | .getRecord() 97 | .getValue() 98 | .length() 99 | == 10; 100 | assert ((KafkaSpan.KafkaProducerInfo) result.getInfo().getKafkaInfo()) 101 | .getRecord() 102 | .getHeaders() 103 | .length() 104 | == 10; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/test/java/io/lumigo/testUtils/JsonTestUtils.java: -------------------------------------------------------------------------------- 1 | package io.lumigo.testUtils; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONObject; 5 | 6 | public class JsonTestUtils { 7 | 8 | public static boolean compareJsonStrings(Object o1, Object o2) { 9 | if (o1 == null || o2 == null) { 10 | return o1 == o2; 11 | } 12 | 13 | try { 14 | JSONObject json1 = new JSONObject((String) o1); 15 | JSONObject json2 = new JSONObject((String) o2); 16 | return json1.similar(json2); 17 | } catch (Exception e1) { 18 | try { 19 | JSONArray json1 = new JSONArray((String) o1); 20 | JSONArray json2 = new JSONArray((String) o2); 21 | return json1.similar(json2); 22 | } catch (Exception e2) { 23 | return false; 24 | } 25 | } 26 | } 27 | } 28 | --------------------------------------------------------------------------------