├── .gitignore ├── License.txt ├── README.md ├── ThirdPartyNotices.txt ├── build.gradle ├── docker ├── Dockerfile └── requirements.txt ├── gradle ├── remote.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── python ├── .gitignore ├── appinsights │ ├── __init__.py │ ├── dockercollector.py │ ├── dockerconvertors.py │ ├── dockerinjector.py │ ├── dockerwrapper.py │ └── program.py ├── bootstrap.py └── tests │ ├── __init__.py │ └── appinsights_tests │ ├── TestDockerCollector.py │ ├── TestDockerConvertors.py │ ├── TestDockerInjector.py │ ├── TestDockerWrapper.py │ └── __init__.py ├── sdk └── ApplicationInsights.xml ├── settings.gradle └── src ├── main └── java │ └── com │ └── microsoft │ └── applicationinsights │ ├── AgentBootstrapper.java │ ├── agent │ ├── DockerAgent.java │ └── DockerContainerContextAgent.java │ ├── common │ ├── ApplicationInsightsSender.java │ ├── ArrayUtils.java │ ├── Constants.java │ ├── StringUtils.java │ └── TelemetryFactory.java │ ├── contracts │ ├── ContainerStateEvent.java │ └── ContainerStatsMetric.java │ ├── providers │ ├── EventProvider.java │ ├── MetricProvider.java │ └── StateProvider.java │ └── python │ ├── ContainerContextPythonBoostrapper.java │ ├── ContainerStatePythonBootstrapper.java │ ├── MetricCollectionPythonBoostrapper.java │ ├── ProcessBuilder.java │ ├── PythonBootstrapper.java │ └── PythonProcessBuilder.java └── test ├── java └── com │ └── microsoft │ └── applicationinsights │ ├── AgentBootstrapperTests.java │ ├── agent │ └── DockerAgentTests.java │ ├── common │ ├── ApplicationInsightsSenderTests.java │ ├── ArrayUtilsTests.java │ ├── StringUtilsTests.java │ ├── TelemetryFactoryTests.java │ └── TestConstants.java │ ├── contracts │ ├── ContainerStateEventTests.java │ └── ContainerStatsMetricTests.java │ ├── providers │ ├── MetricProviderTests.java │ └── StateProviderTests.java │ └── python │ └── MetricProviderPythonBootstrapperTests.java └── resources ├── test_metric_event.py └── test_state_event.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Python exclusions 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | #Visual Studio files 7 | *.o 8 | *.d 9 | *.so 10 | *.class 11 | *.sdf 12 | *.opensdf 13 | *.suo 14 | *.user 15 | Debug/ 16 | Release/ 17 | ipch/ 18 | 19 | # Eclipse 20 | .classpath 21 | .project 22 | .settings/ 23 | 24 | # Gradle 25 | .gradle 26 | build/ 27 | 28 | # Ignore Gradle GUI config 29 | gradle-app.setting 30 | 31 | ## Directory-based project format: 32 | .idea 33 | # if you remove the above rule, at least ignore the following: 34 | 35 | 36 | # User-specific stuff: 37 | # .idea/workspace.xml 38 | # .idea/tasks.xml 39 | # .idea/dictionaries 40 | 41 | 42 | # Sensitive or high-churn files: 43 | # .idea/dataSources.ids 44 | # .idea/dataSources.xml 45 | # .idea/sqlDataSources.xml 46 | # .idea/dynamic.xml 47 | # .idea/uiDesigner.xml 48 | 49 | 50 | # Gradle: 51 | # .idea/gradle.xml 52 | # .idea/libraries 53 | 54 | 55 | # Mongo Explorer plugin: 56 | # .idea/mongoSettings.xml 57 | 58 | 59 | ## File-based project format: 60 | *.ipr 61 | *.iws 62 | 63 | 64 | ## Plugin-specific files: 65 | 66 | # IntelliJ 67 | out/ 68 | *.iml 69 | 70 | # mpeltonen/sbt-idea plugin 71 | .idea_modules/ 72 | 73 | 74 | # JIRA plugin 75 | atlassian-ide-plugin.xml 76 | 77 | 78 | # Crashlytics plugin (for Android Studio and IntelliJ) 79 | com_crashlytics_export_strings.xml 80 | 81 | # Mac 82 | .DS_Store 83 | 84 | # Click-Once directory 85 | publish/ 86 | 87 | # Windows image file caches 88 | Thumbs.db 89 | ehthumbs.db 90 | 91 | # Folder config file 92 | Desktop.ini 93 | 94 | # Recycle Bin used on file shares 95 | $RECYCLE.BIN/ 96 | /Backend/OrderService/.gradle/1.11/taskArtifacts 97 | /Backend/OrderService/build 98 | /Backend/OrderService/.idea 99 | Backend/OrderService/OrderService.iml 100 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | ApplicationInsights-Docker 2 | Copyright (c) Microsoft Corporation 3 | All rights reserved. 4 | 5 | MIT License 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 7 | software and associated documentation files (the ""Software""), to deal in the Software 8 | without restriction, including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit 10 | persons to whom the Software is furnished to do so, subject to the following conditions: 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 14 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 15 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 16 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 17 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 18 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo is [not currently maintained or supported](https://docs.microsoft.com/en-us/azure/azure-monitor/app/docker) by Microsoft. Check out our official documentation for the latest investments in [container monitoring](https://docs.microsoft.com/en-us/azure/azure-monitor/insights/container-insights-overview). 2 | 3 | Application Insights for Docker 4 | =============================== 5 | 6 | Visual Studio [Application Insights][appinsights-overview] for Docker helps you monitor your containerized applications by collecting telemetry about the performance and activity of your Docker host, Docker containers and the applications running within them. 7 | The Application Insights container talks to the Docker agent and sends telemetry data back to [Application Insights][appinsights-home], providing you with diagnostics and data analysis tools. 8 | 9 | ## What is this repo? 10 | 11 | This repo contains the source code for Application Insights for Docker image. 12 | For more information, see [Application Insights for Docker image homepage][appinsights-docker-image] in Docker Hub. 13 | 14 | 15 | ## Microsoft Open Source Code of Conduct 16 | 17 | 18 | 19 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 20 | 21 | [appinsights-home]: https://azure.microsoft.com/en-us/services/application-insights/ 22 | [appinsights-overview]: https://azure.microsoft.com/en-us/documentation/articles/app-insights-overview/ 23 | [appinsights-docker-image]: https://hub.docker.com/r/microsoft/applicationinsights/ 24 | -------------------------------------------------------------------------------- /ThirdPartyNotices.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ApplicationInsights-Docker/95b86bfdfadcd9aa9b243447b32dce0a053bb0d0/ThirdPartyNotices.txt -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.github.johnrengelman.shadow' version '1.2.1' 3 | } 4 | 5 | apply plugin: 'java' 6 | apply from: "$rootDir/gradle/remote.gradle" 7 | 8 | version = '0.9' 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | compile group: 'com.microsoft.azure', name: 'applicationinsights-core', version: '1.0.3' 16 | compile group: 'com.google.code.gson', name: 'gson', version: '1.7.2' 17 | 18 | testCompile 'junit:junit:4.11' 19 | testCompile group: 'org.mockito', name: 'mockito-all', version: '1.8.0' 20 | } 21 | 22 | // region Prepare Docker build folder 23 | 24 | project.ext.localDockerDir = "$rootDir/build/docker" 25 | task copyPythonScripts(type: Copy) { 26 | from "$rootDir/python" 27 | into "$localDockerDir/python" 28 | exclude "**/__pycache__", "**/.idea" 29 | } 30 | 31 | task copyDockerFiles(type: Copy) { 32 | from "$rootDir/docker" 33 | into localDockerDir 34 | } 35 | 36 | task copyApplicationLibs(type: Copy) { 37 | from project.buildDir.absolutePath + "/libs" 38 | 39 | // TODO: remove when the Java SDK bug will be fixed (config file parsing failed when provided as resource) 40 | from "$rootDir/sdk" 41 | 42 | into localDockerDir 43 | } 44 | 45 | // Installing python required libraries. 46 | task installPythonLibraties << { 47 | File theInfoFile = new File("$rootDir/docker/requirements.txt") 48 | theInfoFile.eachLine { line -> 49 | exec { 50 | executable "python" 51 | args "-m", "pip", "install", line 52 | } 53 | } 54 | } 55 | 56 | task runPythonTests << { 57 | exec { 58 | executable "python" 59 | args "-m", "unittest", "discover", "-s", "$rootDir/python" 60 | } 61 | } 62 | 63 | tasks.test.dependsOn installPythonLibraties 64 | tasks.runPythonTests.dependsOn installPythonLibraties 65 | tasks.test.finalizedBy runPythonTests 66 | 67 | // Creating a fat-jar instead of the original one, to be added to the image more easily. 68 | 69 | // TODO: create a specific task to prepare docker folder. 70 | jar.enabled = false 71 | tasks.shadowJar.finalizedBy copyPythonScripts, copyDockerFiles, copyApplicationLibs 72 | 73 | shadowJar { 74 | classifier = '' 75 | } 76 | 77 | // endregion Prepare Docker build folder -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8u66 2 | 3 | RUN apt-get -y -qq update 4 | RUN apt-get -y -qq remove python 5 | RUN apt-get -y -qq autoremove 6 | RUN apt-get -y -qq install python3.4 7 | RUN ln -s /usr/bin/python3.4 /usr/bin/python 8 | 9 | # TODO: run a script to install all libraries from requirements.txt 10 | RUN apt-get -y -qq install python3-pip 11 | RUN python -m pip install python-dateutil==2.4.2 12 | 13 | # docker-py is dependent on the 'requests' module which currently has a bug. Therefore, the docker-py 14 | # must be installed last otherwise no other modules can be installed. 15 | RUN python -m pip install docker-py==1.3.1 16 | 17 | COPY . /usr/appinsights/docker 18 | WORKDIR /usr/appinsights/docker 19 | 20 | # TODO: library version as parameter. 21 | ENTRYPOINT ["java","-cp", "/usr/appinsights/docker/ApplicationInsights-Docker-0.9.jar", "com.microsoft.applicationinsights.AgentBootstrapper"] -------------------------------------------------------------------------------- /docker/requirements.txt: -------------------------------------------------------------------------------- 1 | python-dateutil 2 | docker-py 3 | -------------------------------------------------------------------------------- /gradle/remote.gradle: -------------------------------------------------------------------------------- 1 | task remoteDeployment { 2 | dependsOn clean 3 | 4 | def remoteSettings = System.getenv("DOCKER_REMOTE_SETTINGS_FILE_PATH") 5 | 6 | if (remoteSettings) { 7 | Properties props = new Properties() 8 | props.load(new FileInputStream(remoteSettings)) 9 | 10 | project.ext.machineName = props.get("machineName") 11 | project.ext.userName = props.get("userName") 12 | project.ext.pass = props.get("pass") 13 | project.ext.ikey = props.get("ikey") 14 | } else { 15 | ext.requriedProperties = ["machineName", "userName", "pass"] 16 | } 17 | 18 | group = 'verification' 19 | description = 'Builds and starts Application Insights container on remote machine.' 20 | 21 | // TODO: check plink, pscp installations first. 22 | 23 | // TODO: Change after creating specific task for creating the docker folder. 24 | dependsOn shadowJar 25 | 26 | def imageName = "ai-develop-remote"; 27 | def containerName = imageName 28 | def remoteDockerDir = "/remote/docker" 29 | 30 | doLast { 31 | logger.info("Deploying image to $userName@$machineName.") 32 | 33 | try 34 | { 35 | runRemoteCommand("Cleaning exiting remote Docker resources.", "rm -rf $remoteDockerDir") 36 | runRemoteCommand("Creating remote Docker folder.", "mkdir $remoteDockerDir") 37 | runRemoteCopy("Copying local Docker folder to remote machine.", "$localDockerDir/*", "$userName@$machineName:$remoteDockerDir") 38 | runRemoteCommand("Building Docker image on remote machine.", "cd $remoteDockerDir; docker build -t $imageName .") 39 | runRemoteCommand("Killing existing container, if exists.", "docker kill $containerName && docker rm $containerName || true") 40 | runRemoteCommand("Starting Docker image.", "docker run -v /var/run/docker.sock:/docker.sock --name $containerName -d $imageName ikey=$ikey") 41 | } 42 | finally 43 | { 44 | // TODO: anything to finalize? 45 | } 46 | } 47 | } 48 | 49 | def runRemoteCopy(description, from, into) { 50 | logger.info("[runRemoteCopy] $description") 51 | exec { 52 | executable "pscp.exe" 53 | args "-r", "-pw", pass, "\"$from\"", "\"$into\"" 54 | } 55 | } 56 | 57 | def runRemoteCommand(description, command) { 58 | logger.info("[runRemoteCommand] $description") 59 | exec { 60 | executable "plink.exe" 61 | args "-pw", pass, "$userName@$machineName", "\"$command\"" 62 | } 63 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ApplicationInsights-Docker/95b86bfdfadcd9aa9b243447b32dce0a053bb0d0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jul 22 11:14:28 IDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /python/.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 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ -------------------------------------------------------------------------------- /python/appinsights/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | # __author__ = 'galha' 23 | from . import program 24 | from . import dockercollector 25 | -------------------------------------------------------------------------------- /python/appinsights/dockercollector.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | __author__ = 'galha' 23 | 24 | import concurrent.futures 25 | import time 26 | import dateutil.parser 27 | from appinsights.dockerwrapper import DockerWrapperError 28 | from appinsights import dockerconvertors 29 | 30 | 31 | class DockerCollector(object): 32 | """ The application insights docker collector, 33 | used to collect data from the docker remote API (events, and performance counters) 34 | """ 35 | 36 | _cmd_template = "/bin/sh -c \"[ -f {file} ] && cat {file}\"" 37 | 38 | def _default_print(text): 39 | print(text, flush=True) 40 | 41 | def __init__(self, docker_wrapper, docker_injector, samples_in_each_metric=2, send_event=_default_print, 42 | sdk_file='/usr/appinsights/docker/sdk.info'): 43 | """ Initializes a new instance of the class. 44 | 45 | :param docker_wrapper: A docker client wrapper instance 46 | :param docker_injector: A docker docker injector instance 47 | :param samples_in_each_metric: The Number of samples to use in each metric 48 | :param send_event: Function to send event 49 | :param sdk_file: The sdk file location 50 | :return: 51 | """ 52 | super().__init__() 53 | assert docker_wrapper is not None, 'docker_client cannot be None' 54 | assert docker_injector is not None, 'docker_injector cannot be None' 55 | assert samples_in_each_metric > 1, 'samples_in_each_metric must be greater than 1, given: {0}'.format( 56 | samples_in_each_metric) 57 | self._sdk_file = sdk_file 58 | self._docker_wrapper = docker_wrapper 59 | self._docker_injector = docker_injector 60 | self._samples_in_each_metric = samples_in_each_metric 61 | self._send_event = send_event 62 | self._my_container_id = None 63 | self._containers_state = {} 64 | 65 | def collect_stats_and_send(self): 66 | """ 67 | Collects docker metrics from docker and sends them to sender 68 | cpu, memory, rx_bytes ,tx_bytes, blkio metrics 69 | """ 70 | 71 | if self._my_container_id is None: 72 | self._my_container_id = self._docker_injector.get_my_container_id() 73 | 74 | host_name = self._docker_wrapper.get_host_name() 75 | containers = self._docker_wrapper.get_containers() 76 | self._update_containers_state(containers=containers) 77 | containers_without_sdk = [v['container'] for k, v in self._containers_state.items() if 78 | k == self._my_container_id or v['ikey'] is None] 79 | 80 | with concurrent.futures.ThreadPoolExecutor(max_workers=max(len(containers), 30)) as executor: 81 | container_stats = list( 82 | executor.map( 83 | lambda container: (container, self._docker_wrapper.get_stats(container=container, 84 | stats_to_bring=self._samples_in_each_metric)), 85 | containers_without_sdk)) 86 | 87 | for container, stats in [(container, stats) for container, stats in container_stats if len(stats) > 1]: 88 | metrics = dockerconvertors.convert_to_metrics(stats) 89 | properties = dockerconvertors.get_container_properties(container, host_name) 90 | for metric in metrics: 91 | self._send_event({'metric': metric, 'properties': properties}) 92 | 93 | def collect_container_events(self): 94 | """ Collects the container events (start, stop, die, pause, unpause) 95 | and sends then using the send_event function given in the constructor 96 | :return: 97 | """ 98 | event_name_template = 'docker-container-{0}' 99 | host_name = self._docker_wrapper.get_host_name() 100 | for event in self._docker_wrapper.get_events(): 101 | status = event['status'] 102 | if status not in ['start', 'stop', 'die', 'restart', 'pause', 'unpause']: 103 | continue 104 | 105 | event_name = event_name_template.format(status) 106 | inspect = self._docker_wrapper.get_inspection(event) 107 | properties = dockerconvertors.get_container_properties_from_inspect(inspect, host_name) 108 | 109 | ikey_to_send_event = self._get_container_sdk_ikey_from_containers_state(properties['Docker container id']) 110 | 111 | properties['docker-status'] = status 112 | properties['docker-Created'] = inspect['Created'] 113 | properties['docker-StartedAt'] = inspect['State']['StartedAt'] 114 | properties['docker-RestartCount'] = inspect['RestartCount'] 115 | 116 | if status in ['stop', 'die']: 117 | properties['docker-FinishedAt'] = inspect['State']['FinishedAt'] 118 | properties['docker-ExitCode'] = inspect['State']['ExitCode'] 119 | 120 | error = inspect['State']['Error'] 121 | properties['docker-Error'] = error if (error is not None) else "" 122 | duration = dateutil.parser.parse(properties['docker-FinishedAt']) - dateutil.parser.parse( 123 | properties['docker-StartedAt']) 124 | duration_seconds = duration.total_seconds() 125 | properties['docker-duration-seconds'] = duration_seconds 126 | properties['docker-duration-minutes'] = duration_seconds / 60 127 | properties['docker-duration-hours'] = duration_seconds / 3600 128 | properties['docker-duration-days'] = duration_seconds / 86400 129 | event_data = {'name': event_name, 'ikey': ikey_to_send_event if ikey_to_send_event is not None else '', 'properties': properties} 130 | self._send_event(event_data) 131 | 132 | @staticmethod 133 | def remove_old_containers(current_containers, new_containers): 134 | """ 135 | This function removes all old containers that have been stopped. 136 | 137 | :param current_containers: The containers currently in cache. 138 | :param new_containers: The latest containers collection. 139 | :rtype : dict 140 | """ 141 | curr_containers_ids = {c['Id']: c for c in new_containers} 142 | keys = [k for k in current_containers] 143 | for key in [key for key in keys if key not in curr_containers_ids]: 144 | if current_containers[key]['unregistered'] is None: 145 | current_containers[key]['unregistered'] = time.time() 146 | else: 147 | if current_containers[key]['unregistered'] < time.time() - 60: 148 | del current_containers[key] 149 | 150 | return current_containers 151 | 152 | def _get_container_sdk_info(self, container): 153 | try: 154 | result = self._docker_wrapper.run_command(container, 155 | DockerCollector._cmd_template.format(file=self._sdk_file)) 156 | result = result.strip() 157 | 158 | return result if result != '' else None 159 | except DockerWrapperError: 160 | return None 161 | 162 | def _get_container_sdk_ikey_from_containers_state(self, container_id): 163 | if container_id not in self._containers_state.keys(): 164 | containers = self._docker_wrapper.get_containers() 165 | self._update_containers_state(containers=containers) 166 | 167 | if container_id in self._containers_state.keys(): 168 | return self._containers_state[container_id]['ikey'] 169 | else: 170 | return None 171 | 172 | def _update_containers_state(self, containers): 173 | self._containers_state = DockerCollector.remove_old_containers(self._containers_state, containers) 174 | with concurrent.futures.ThreadPoolExecutor(max_workers=max(len(containers), 30)) as executor: 175 | list(executor.map(lambda c: self._update_container_state(c), containers)) 176 | 177 | def _update_container_state(self, container): 178 | id = container['Id'] 179 | if id not in self._containers_state: 180 | for i in range(5): 181 | ikey = self._get_container_sdk_ikey(container) 182 | self._containers_state[id] = {'ikey': ikey, 'registered': time.time(), 'unregistered': None, 'container': container} 183 | 184 | if ikey is not None: 185 | return ikey 186 | 187 | time.sleep(1) 188 | 189 | return None 190 | 191 | status = self._containers_state[id] 192 | if status['ikey'] is not None: 193 | return status['ikey'] 194 | 195 | if status['registered'] > time.time() - 60: 196 | ikey = self._get_container_sdk_ikey(container) 197 | status['ikey'] = ikey 198 | return ikey 199 | 200 | return None 201 | 202 | def _get_container_sdk_ikey(self, container): 203 | sdk_info_file_content = self._get_container_sdk_info(container) 204 | if sdk_info_file_content is None: 205 | return None 206 | splits = sdk_info_file_content.split('=') 207 | if len(splits) < 2: 208 | return None 209 | return sdk_info_file_content.split('=')[1] -------------------------------------------------------------------------------- /python/appinsights/dockerconvertors.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | __author__ = 'galha' 23 | import statistics 24 | 25 | def convert_to_metrics(stats): 26 | """ convert the docker container stats list to ai metrices 27 | :param stats: The docker container stats list 28 | :return: List of Ai Metrics (cpu, rx, tx, blkio), 29 | each metric is a dict of (name, value, count, min, max, and std) 30 | """ 31 | assert stats is not None and len(stats)>1 ,"stats should have at least 2 samples in it" 32 | return [get_cpu_metric(stats=stats), 33 | get_simple_metric( 34 | metric_name='Available Bytes', 35 | func=lambda stat: stat['memory_stats']['limit'] - stat['memory_stats']['usage'], 36 | stats=stats 37 | ), 38 | get_per_second_metric( 39 | metric_name='Docker RX Bytes', 40 | func=lambda stat: stat['network']['rx_bytes'], 41 | stats=stats 42 | ), 43 | get_per_second_metric( 44 | metric_name='Docker TX Bytes', 45 | func=lambda stat: stat['network']['tx_bytes'], 46 | stats=stats 47 | ), 48 | get_per_second_metric( 49 | metric_name='Docker Blkio Bytes', 50 | func= get_total_blkio, 51 | stats=stats 52 | )] 53 | 54 | def get_total_blkio(stat): 55 | """ Gets the total blkio out of the docker stat 56 | :param stat: The docker stat 57 | :return: The blkio 58 | """ 59 | io_list = stat['blkio_stats']['io_service_bytes_recursive'] 60 | if len(io_list)>0: 61 | total_dics = list(filter(lambda dic: dic['op'] == 'Total', io_list)) 62 | if len(total_dics)>0: 63 | return total_dics[0]['value'] 64 | else: 65 | return 0 66 | 67 | def get_cpu_metric(stats): 68 | """ Gets the cpu metric from the docker stats list 69 | :param stats: The docker stats list 70 | :return: A cpu metric 71 | """ 72 | assert stats is not None and len(stats)>1 ,\ 73 | "the 'stats' samples must contain more than 1 statistics in order to calclulate the cpu metric" 74 | 75 | cpu_list = [stat['cpu_stats']['cpu_usage']['total_usage'] for time, stat in stats] 76 | system_cpu_list = [stat['cpu_stats']['system_cpu_usage'] for time, stat in stats] 77 | cpu2 = cpu_list[1:] 78 | cpu1 = cpu_list[:len(cpu_list) - 1] 79 | system2 = system_cpu_list[1:] 80 | system1 = system_cpu_list[: len(system_cpu_list) - 1] 81 | cpu_percents = [100.0 * (cpu_curr - cpu_prev) / (system_curr - system_prev) for 82 | cpu_curr, cpu_prev, system_curr, system_prev in list(zip(cpu2, cpu1, system2, system1))] 83 | 84 | return {'name':'% Processor Time', 85 | 'value':statistics.mean(cpu_percents), 86 | 'count':len(cpu_percents), 87 | 'min':min(cpu_percents), 88 | 'max':max(cpu_percents), 89 | 'std':statistics.stdev(cpu_percents) if len(cpu_percents) > 1 else None} 90 | 91 | def get_per_second_metric(metric_name, func, stats): 92 | """ Gets a per second metric out of the docker stats list, 93 | per second valuates the time difference in time between every two samples 94 | :param metric_name: The metric name 95 | :param func: A function which gets the value of the sample out of the stat object 96 | :param stats: The docker stats list 97 | :return: Ai metric 98 | """ 99 | assert metric_name is not None, "metric_name shoud not be None" 100 | assert func is not None, "func should not be None" 101 | assert stats is not None and len(stats)>1, "stats should have more than 1 samples in it" 102 | stats2 = stats[1:] 103 | stats1 = stats[:len(stats) - 1] 104 | samples = [(func(s2)-func(s1)) / (time2 - time1) for (time2, s2), (time1, s1) in list(zip(stats2, stats1))] 105 | return {'name':metric_name, 106 | 'value':statistics.mean(samples), 107 | 'count':len(samples), 108 | 'min':min(samples), 109 | 'max':max(samples), 110 | 'std':statistics.stdev(samples) if len(samples) > 1 else None} 111 | 112 | def get_simple_metric(metric_name, func, stats): 113 | """ Gets an ai metric from the stats list (count, average, min, max and std) 114 | :param metric_name: The metric name 115 | :param func: A function which gets the value of the sample out of the stat object 116 | :param stats: The docker stats list 117 | :return: Ai metric 118 | """ 119 | assert metric_name is not None 120 | assert func is not None 121 | assert stats is not None and len(stats)>1 122 | samples = [func(stat) for time, stat in stats] 123 | return {'name': metric_name, 124 | 'value':statistics.mean(samples), 125 | 'count':len(samples), 126 | 'min':min(samples), 127 | 'max':max(samples), 128 | 'std':statistics.stdev(samples) if len(samples) > 1 else None} 129 | 130 | def get_container_properties(container, host_name): 131 | """ Gets the container properties from a container object 132 | :param container: The container object 133 | :param host_name: The host name 134 | :return: dict of (Docker host, Docker image, Docker container id, Docker container name) 135 | """ 136 | return {'Docker host': host_name, 137 | 'Docker image': container.get('Image', 'N/A'), 138 | 'Docker container id': container.get('Id', 'N/A'), 139 | 'Docker container name': container.get('Names', ['N/A'])[0]} 140 | 141 | 142 | def get_container_properties_from_inspect(inspect, host_name): 143 | """ Gets the container properties from an inspect object 144 | :param inspect: The inspect object 145 | :param host_name: The host name 146 | :return: dict of (Docker host, Docker image, Docker container id, Docker container name) 147 | """ 148 | return {'Docker host': host_name, 149 | 'Docker image': inspect['Config'].get('Image', 'N/A') if 'Config' in inspect else 'N/A', 150 | 'Docker container id': inspect.get('Id', 'N/A'), 151 | 'Docker container name': inspect.get('Names', [inspect.get('Name', 'N/A')])[0]} 152 | 153 | -------------------------------------------------------------------------------- /python/appinsights/dockerinjector.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | import sys 23 | from appinsights.dockerwrapper import DockerWrapperError 24 | 25 | __author__ = 'galha' 26 | 27 | import concurrent.futures, os, re 28 | from appinsights import dockerconvertors 29 | 30 | 31 | class DockerInjector(object): 32 | """ 33 | Application insights docker container injector, 34 | used to inject the container context to running containers 35 | """ 36 | _default_bash = "bash" 37 | _mkdir_template = "mkdir -p \"{directory}\"" 38 | _create_file_template = "/bin/sh -c \"[ ! -f {directory}/{file} ] && `printf '{properties}' > {directory}/{file}` && echo created file || echo file already exists\"" 39 | 40 | def __init__(self, docker_wrapper, docker_info_path): 41 | """ Initializes a new instance of the class. 42 | :param docker_wrapper: a docker wrapper instance 43 | :param docker_info_path: (str). The file path where to inject the context 44 | :return: 45 | """ 46 | self._docker_wrapper = docker_wrapper 47 | self._docker_info_path = docker_info_path 48 | self._host_name = None 49 | self._dirName = os.path.dirname(docker_info_path) 50 | self._fileName = os.path.basename(docker_info_path) 51 | self._my_container_id = None 52 | 53 | def inject_context(self): 54 | """ Injects the context to all running containers 55 | :return: 56 | """ 57 | containers = self._docker_wrapper.get_containers() 58 | if self._host_name is None: 59 | self._host_name = self._docker_wrapper.get_host_name() 60 | 61 | with concurrent.futures.ThreadPoolExecutor(max_workers=30) as executor: 62 | results = list( 63 | executor.map(lambda container: (container["Id"], self.inject_container(container)),containers)) 64 | 65 | return results 66 | 67 | def start(self): 68 | """ start to inject the context to all running containers, 69 | and listen to containers events to inject to inject the context to new created containers. 70 | :return: 71 | """ 72 | with concurrent.futures.ThreadPoolExecutor(max_workers=30) as executor: 73 | executor.submit(lambda : self.inject_context()) 74 | executor.map( 75 | lambda event: self.inject_container(event), 76 | filter( 77 | lambda event: event['status'] in ['start', 'restart', 'unpause'], 78 | self._docker_wrapper.get_events())) 79 | 80 | @property 81 | def docker_info_path(self): 82 | """ Gets the docker info file path 83 | :return: The docker info file path 84 | """ 85 | return self._docker_info_path 86 | 87 | def get_my_container_id(self): 88 | """ Gets the container id of the caller. 89 | :return: The container Id of the caller, None if the call was made not within a container 90 | """ 91 | if self._my_container_id is not None: 92 | return self._my_container_id 93 | 94 | if not os.path.exists(self.docker_info_path): 95 | self.inject_context(); 96 | 97 | # we are not running in a container 98 | if not os.path.exists(self.docker_info_path): 99 | return None 100 | 101 | # get the context from the injected file 102 | with open(self.docker_info_path, mode='r') as f: 103 | context = f.read() 104 | match = re.search('Docker container id=([a-zA-Z0-9]+)', context) 105 | if match: 106 | self._my_container_id = match.group(1) 107 | return self._my_container_id 108 | 109 | # this happens only when we run the code not within a container 110 | return None 111 | 112 | def inject_container(self, container): 113 | """ Injects the container context into the given container 114 | :param container: The container to inject 115 | :return: 116 | """ 117 | try: 118 | mkdir_cmd = DockerInjector._mkdir_template.format(directory=self._dirName) 119 | self._docker_wrapper.run_command(container=container, cmd=mkdir_cmd) 120 | properties = self._get_properties(container) 121 | properties_string = ",".join(["{key}={value}".format(key=k, value=v) for k, v in properties.items()]) 122 | docker_info_cmd = DockerInjector._create_file_template.format( 123 | directory=self._dirName, 124 | file=self._fileName, 125 | properties=properties_string) 126 | 127 | result = self._docker_wrapper.run_command(container=container, cmd=docker_info_cmd) 128 | return result 129 | except DockerWrapperError as e: 130 | return e 131 | 132 | def _get_properties(self, item): 133 | if 'status' in item: 134 | inspect = self._docker_wrapper.get_inspection(item) 135 | return dockerconvertors.get_container_properties_from_inspect(inspect=inspect, host_name=self._host_name) 136 | return dockerconvertors.get_container_properties(container=item, host_name=self._host_name) -------------------------------------------------------------------------------- /python/appinsights/dockerwrapper.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | __author__ = 'galha' 23 | 24 | import requests 25 | from requests.packages.urllib3.exceptions import ReadTimeoutError, HTTPError 26 | from itertools import islice 27 | from docker import errors 28 | from docker import Client 29 | import time 30 | 31 | def get_production_docker_wrapper(base_url): 32 | return ProductionWrapper(base_url=base_url) 33 | 34 | 35 | class DockerClientWrapper(object): 36 | """ A wrapper class on the docker client 37 | """ 38 | 39 | def __init__(self, docker_client): 40 | """ Initializes a new instance of the class. 41 | :param docker_client: 42 | :return: 43 | """ 44 | assert docker_client is not None, 'docker_client cannot be None' 45 | self._client = docker_client 46 | 47 | def get_host_name(self): 48 | return self._client.info().get('Name', 'N/A') 49 | 50 | def get_containers(self): 51 | return self._client.containers() 52 | 53 | def get_stats(self, container, stats_to_bring): 54 | stats = [] 55 | try: 56 | for stat in islice(self._client.stats(container=container, decode=True), 0, stats_to_bring, 1): 57 | stats.append((time.time(), stat)) 58 | except (errors.APIError, ReadTimeoutError, requests.exceptions.ReadTimeout, HTTPError): 59 | pass 60 | return stats 61 | 62 | def run_command(self, container, cmd): 63 | try: 64 | exec_id = self._client.exec_create(container, cmd) 65 | output = self._client.exec_start(exec_id=exec_id) 66 | return output.decode('utf-8') 67 | except (errors.APIError, ReadTimeoutError, requests.exceptions.ReadTimeout, HTTPError) as e: 68 | raise DockerWrapperError(e) 69 | 70 | def get_events(self): 71 | for event in self._client.events(decode=True): 72 | if 'id' in event: 73 | event['Id'] = event['id'] 74 | yield event 75 | 76 | def get_inspection(self, container): 77 | try: 78 | return self._client.inspect_container(container=container) 79 | except (errors.APIError, ReadTimeoutError, requests.exceptions.ReadTimeout, HTTPError) as e: 80 | raise DockerWrapperError(e) 81 | 82 | 83 | class ProductionWrapper(object): 84 | def __init__(self, base_url): 85 | self._fast_operations_client = DockerClientWrapper(Client(base_url=base_url, timeout=10)) 86 | self._slow_operations_client = DockerClientWrapper(Client(base_url=base_url, timeout=60)) 87 | 88 | def get_host_name(self): 89 | return self._fast_operations_client.get_host_name() 90 | 91 | def get_containers(self): 92 | return self._fast_operations_client.get_containers() 93 | 94 | def get_stats(self, container, stats_to_bring): 95 | return self._fast_operations_client.get_stats(container=container, stats_to_bring=stats_to_bring) 96 | 97 | def run_command(self, container, cmd): 98 | return self._slow_operations_client.run_command(container=container, cmd=cmd) 99 | 100 | def get_events(self): 101 | return self._fast_operations_client.get_events() 102 | 103 | def get_inspection(self, container): 104 | return self._slow_operations_client.get_inspection(container=container) 105 | 106 | 107 | class DockerWrapperError(Exception): 108 | pass 109 | -------------------------------------------------------------------------------- /python/appinsights/program.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | __author__ = 'galha' 23 | 24 | from appinsights.dockercollector import DockerCollector 25 | from appinsights.dockerwrapper import get_production_docker_wrapper 26 | from appinsights.dockerinjector import DockerInjector 27 | import time 28 | import sys 29 | 30 | def run_injector(docker_socket, docker_info_path): 31 | injector = DockerInjector( 32 | get_production_docker_wrapper(base_url=docker_socket), 33 | docker_info_path=docker_info_path) 34 | 35 | while True: 36 | injector.start() 37 | time.sleep(30) 38 | 39 | def run_collect_performance_counters(docker_socket, sdk_file, docker_info_file, collect_interval): 40 | docker_wrapper=get_production_docker_wrapper(base_url=docker_socket) 41 | docker_injector = DockerInjector(docker_wrapper=docker_wrapper, docker_info_path=docker_info_file) 42 | collector = DockerCollector( 43 | docker_wrapper=docker_wrapper, 44 | docker_injector=docker_injector, 45 | samples_in_each_metric=5, 46 | sdk_file=sdk_file) 47 | 48 | while True: 49 | collector.collect_stats_and_send() 50 | time.sleep(float(collect_interval)) 51 | 52 | def run_collect_containers_events(docker_socket, docker_info_file, sdk_file): 53 | docker_wrapper=get_production_docker_wrapper(base_url=docker_socket) 54 | docker_injector = DockerInjector(docker_wrapper=docker_wrapper, docker_info_path=docker_info_file) 55 | collector = DockerCollector( 56 | docker_wrapper=docker_wrapper, 57 | docker_injector=docker_injector, 58 | samples_in_each_metric=5, 59 | sdk_file=sdk_file) 60 | while True: 61 | try: 62 | collector.collect_container_events() 63 | except Exception as e: 64 | print(e, file=sys.stderr) 65 | time.sleep(10) -------------------------------------------------------------------------------- /python/bootstrap.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | # The name of this file shouldn't be changed. 23 | # The purpose of this file is to avoid changing the Java code if the python file names will be changed. 24 | # This file will be executed by the com.microsoft.applicationinsights.agent.AgentBootstrapper, and will invoke the script for fetching data from Docker Remote API. 25 | import argparse 26 | import os 27 | from appinsights import program 28 | 29 | _docker_socket = 'unix:///docker.sock' 30 | _docker_info_path = '/usr/appinsights/docker/docker.info' 31 | _sdk_info_file = '/usr/appinsights/docker/sdk.info' 32 | 33 | parser = argparse.ArgumentParser(description="Application Insights container collector/injector") 34 | parser.add_argument("method", help="The method to run.", choices=['collect', 'inject', 'custom', 'events']) 35 | parser.add_argument("--script", help="The script to run when choosing 'custom' method") 36 | parser.add_argument("--collect-interval", help="The interval (in seconds) in which the performance counters are collected", default=10) 37 | 38 | args = parser.parse_args() 39 | method = args.method 40 | script = args.script 41 | collect_interval = args.collect_interval 42 | 43 | methods = {'collect': lambda: program.run_collect_performance_counters(docker_socket=_docker_socket, sdk_file=_sdk_info_file, docker_info_file=_docker_info_path, collect_interval=collect_interval), 44 | 'inject': lambda: program.run_injector(docker_socket=_docker_socket, docker_info_path=_docker_info_path), 45 | 'custom': lambda: os.system(script), 46 | 'events': lambda : program.run_collect_containers_events(docker_socket=_docker_socket, docker_info_file=_docker_info_path, sdk_file=_sdk_info_file)} 47 | 48 | assert method in methods 49 | methods[method]() 50 | -------------------------------------------------------------------------------- /python/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # -------------------------------------------------------------------------------- /python/tests/appinsights_tests/TestDockerCollector.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | import time 23 | 24 | __author__ = 'galha' 25 | 26 | import unittest 27 | from unittest.mock import patch, MagicMock 28 | from unittest.mock import Mock, mock_open 29 | from appinsights.dockerwrapper import DockerClientWrapper, DockerWrapperError 30 | from appinsights.dockercollector import DockerCollector 31 | 32 | class TestDockerCollector(unittest.TestCase): 33 | def test_collect_and_send(self): 34 | events = [] 35 | properties = {'p1':'v1','p2':'v2'} 36 | metrics = ['m1','m2','m3'] 37 | containers = [{'Id':'c1','ikey':'k1'}, {'Id':'c2','ikey':'k2'}, {'Id':'c3','ikey':'k3'}] 38 | stats = ['s1','s2','s3'] 39 | host_name = 'host' 40 | with patch('appinsights.dockerconvertors.get_container_properties') as properties_mock: 41 | with patch('appinsights.dockerconvertors.convert_to_metrics') as to_metric_mock: 42 | properties_mock.return_value = properties 43 | to_metric_mock.return_value = metrics 44 | wrapper_mock = Mock() 45 | wrapper_mock.get_host_name.return_value = host_name 46 | wrapper_mock.get_containers.return_value = containers 47 | wrapper_mock.get_stats.return_value = stats 48 | wrapper_mock.run_command.return_value = '' 49 | injector_mock = Mock() 50 | injector_mock.get_my_container_id.return_value = 'c1' 51 | collector = DockerCollector(wrapper_mock, injector_mock, 3, lambda x: events.append(x)) 52 | collector.collect_stats_and_send() 53 | expected_metrics = [{'metric':metric, 'properties': properties} for container in containers for metric in metrics] 54 | expectedEventsCount = len(containers)*len(metrics) 55 | self.assertEqual(expectedEventsCount, len(events)) 56 | for sent_event in events: 57 | self.assertIn(sent_event ,expected_metrics) 58 | 59 | def test_collect_and_send_dont_send_events_when_no_containers(self): 60 | events = [] 61 | properties = {'p1':'v1','p2':'v2'} 62 | metrics = ['m1','m2','m3'] 63 | containers = [] 64 | stats = ['s1','s2','s3'] 65 | host_name = 'host' 66 | with patch('appinsights.dockerconvertors.get_container_properties') as properties_mock: 67 | with patch('appinsights.dockerconvertors.convert_to_metrics') as to_metric_mock: 68 | properties_mock.return_value = properties 69 | to_metric_mock.return_value = metrics 70 | wrapper_mock = Mock(spec=DockerClientWrapper) 71 | wrapper_mock.get_host_name.return_value = host_name 72 | wrapper_mock.get_containers.return_value = containers 73 | wrapper_mock.get_stats.return_value = stats 74 | wrapper_mock.run_command.return_value = '' 75 | injector_mock = Mock() 76 | injector_mock.get_my_container_id.return_value = 'c1' 77 | collector = DockerCollector(wrapper_mock, injector_mock, 3, lambda x: events.append(x)) 78 | collector.collect_stats_and_send() 79 | self.assertEqual(0, len(events)) 80 | 81 | def test_collect_and_send_dont_send_events_when_no_metrics(self): 82 | events = [] 83 | properties = {'p1':'v1','p2':'v2'} 84 | metrics = [] 85 | containers = [{'Id':'c1'}, {'Id':'c2'}] 86 | stats = ['s1','s2'] 87 | host_name = 'host' 88 | with patch('appinsights.dockerconvertors.get_container_properties') as properties_mock: 89 | with patch('appinsights.dockerconvertors.convert_to_metrics') as to_metric_mock: 90 | properties_mock.return_value = properties 91 | to_metric_mock.return_value = metrics 92 | wrapper_mock = Mock(spec=DockerClientWrapper) 93 | wrapper_mock.get_host_name.return_value = host_name 94 | wrapper_mock.get_containers.return_value = containers 95 | wrapper_mock.get_stats.return_value = stats 96 | wrapper_mock.run_command.return_value = '' 97 | injector_mock=Mock() 98 | injector_mock.get_my_container_id.return_value = 'c1' 99 | collector = DockerCollector(wrapper_mock, injector_mock ,3, lambda x: events.append(x)) 100 | collector.collect_stats_and_send() 101 | self.assertEqual(0, len(events)) 102 | 103 | def test_collect_and_send_dont_sends_only_metrics_on_the_sender_container_when_sdk_is_running(self): 104 | events = [] 105 | properties = {'p1':'v1','p2':'v2'} 106 | metrics = ['m1','m2','m3'] 107 | containers = [{'Id':'c1'}, {'Id':'c2'}, {'Id':'c3'}] 108 | stats = ['s1','s2','s3'] 109 | host_name = 'host' 110 | with patch('appinsights.dockerconvertors.get_container_properties') as properties_mock: 111 | with patch('appinsights.dockerconvertors.convert_to_metrics') as to_metric_mock: 112 | properties_mock.return_value = properties 113 | to_metric_mock.return_value = metrics 114 | wrapper_mock = Mock(spec=DockerClientWrapper) 115 | wrapper_mock.get_host_name.return_value = host_name 116 | wrapper_mock.get_containers.return_value = containers 117 | wrapper_mock.get_stats.return_value = stats 118 | wrapper_mock.run_command.return_value = 'InstrumentationKey=ikey' 119 | injector_mock = Mock() 120 | injector_mock.get_my_container_id.return_value = 'c1' 121 | collector = DockerCollector(wrapper_mock, injector_mock,3, lambda x: events.append(x)) 122 | collector.collect_stats_and_send() 123 | self.assertEqual(3, len(events)) 124 | wrapper_mock.get_stats.call_with(container={'Id':'c1'}) 125 | self.assertEqual(1, wrapper_mock.get_stats.call_count) 126 | 127 | def test_when_docker_wrapper_raises_on_run_commnad_it_assume_there_is_no_sdk_and_send_events(self): 128 | events = [] 129 | properties = {'p1':'v1','p2':'v2'} 130 | metrics = ['m1','m2','m3'] 131 | containers = [{'Id':'c1'}] 132 | stats = ['s1','s2','s3'] 133 | host_name = 'host' 134 | with patch('appinsights.dockerconvertors.get_container_properties') as properties_mock: 135 | with patch('appinsights.dockerconvertors.convert_to_metrics') as to_metric_mock: 136 | properties_mock.return_value = properties 137 | to_metric_mock.return_value = metrics 138 | wrapper_mock = Mock(spec=DockerClientWrapper) 139 | wrapper_mock.get_host_name.return_value = host_name 140 | wrapper_mock.get_containers.return_value = containers 141 | wrapper_mock.get_stats.return_value = stats 142 | wrapper_mock.run_command.side_effect = DockerWrapperError('container is paused') 143 | injector_mock = Mock() 144 | injector_mock.get_my_container_id.return_value = 'c1' 145 | collector = DockerCollector(wrapper_mock, injector_mock, 3, lambda x: events.append(x)) 146 | collector.collect_stats_and_send() 147 | self.assertEqual(len(metrics), len(events)) 148 | 149 | def test_sender_sends_events_even_when_it_has_sdk(self): 150 | events = [] 151 | properties = {'p1':'v1','p2':'v2'} 152 | metrics = ['m1','m2','m3'] 153 | containers = [{'Id':'c1'}] 154 | stats = ['s1','s2','s3'] 155 | host_name = 'host' 156 | with patch('appinsights.dockerconvertors.get_container_properties') as properties_mock: 157 | with patch('appinsights.dockerconvertors.convert_to_metrics') as to_metric_mock: 158 | properties_mock.return_value = properties 159 | to_metric_mock.return_value = metrics 160 | wrapper_mock = Mock(spec=DockerClientWrapper) 161 | wrapper_mock.get_host_name.return_value = host_name 162 | wrapper_mock.get_containers.return_value = containers 163 | wrapper_mock.get_stats.return_value = stats 164 | wrapper_mock.run_command.return_value = 'InstrumentationKey=ikey' 165 | injector_mock = Mock() 166 | injector_mock.get_my_container_id.return_value = 'c1' 167 | collector = DockerCollector(wrapper_mock, injector_mock, 3, lambda x: events.append(x)) 168 | collector.collect_stats_and_send() 169 | self.assertEqual(len(metrics), len(events)) 170 | self.assertEqual(3, len(events)) 171 | 172 | def test_old_container_is_not_removed_immediately(self): 173 | new_containers = [{'Id':'c2','ikey':'k2', 'unregistered': None}] 174 | current_containers = {'c1': {'Id':'c1','ikey':'k1', 'unregistered': time.time()}} 175 | 176 | updated_containers = DockerCollector.remove_old_containers(current_containers, new_containers) 177 | 178 | self.assertEqual(current_containers['c1']['Id'], updated_containers['c1']['Id']) 179 | 180 | def test_old_container_is_removed_after_threshold(self): 181 | new_containers = [{'Id':'c2','ikey':'k2', 'unregistered': None}] 182 | current_containers = {'c1': {'Id':'c1','ikey':'k1', 'unregistered': time.time() - 70}} 183 | 184 | updated_containers = DockerCollector.remove_old_containers(current_containers, new_containers) 185 | 186 | self.assertTrue(len(updated_containers) == 0) 187 | -------------------------------------------------------------------------------- /python/tests/appinsights_tests/TestDockerConvertors.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | __author__ = 'galha' 23 | import unittest 24 | from appinsights import dockerconvertors 25 | 26 | 27 | class TestDockerConvertors(unittest.TestCase): 28 | def test_get_total_blkio_when_total_exists(self): 29 | expected = 40 30 | stat = {'blkio_stats': {'io_service_bytes_recursive': [{'op': 'Total', 'value': expected}]}} 31 | actual = dockerconvertors.get_total_blkio(stat) 32 | self.assertEqual(expected, actual) 33 | 34 | def test_get_total_blkio_when_total_not_exists(self): 35 | expected = 0 36 | stat = {'blkio_stats': {'io_service_bytes_recursive': []}} 37 | actual = dockerconvertors.get_total_blkio(stat) 38 | self.assertEqual(expected, actual) 39 | 40 | def test_get_cpu_metric(self): 41 | samples = [(0, 0), (1, 2), (2, 4), (4, 100)] 42 | expected_avg = 34.0277778 43 | expected_min = 2.0833333 44 | expected_max = 50 45 | expected_std = 27.6647004 46 | stats = [(system, {'cpu_stats': {'cpu_usage': {'total_usage': cpu}, 'system_cpu_usage': system}}) for 47 | cpu, system in samples] 48 | metric = dockerconvertors.get_cpu_metric(stats) 49 | self.assertEqual('% Processor Time', metric['name']) 50 | self.assertEqual(len(samples) - 1, metric['count']) 51 | self.assertAlmostEqual(expected_avg, metric['value'], delta=0.01) 52 | self.assertAlmostEqual(expected_min, metric['min'], delta=0.01) 53 | self.assertAlmostEqual(expected_max, metric['max'], delta=0.01) 54 | self.assertAlmostEqual(expected_std, metric['std'], delta=0.01) 55 | 56 | def test_get_cpu_metric_trows_when_stats_is_none(self): 57 | self.assertRaises(AssertionError, dockerconvertors.get_cpu_metric, None) 58 | 59 | def test_get_cpu_metric_trows_when_stats_is_has_only_one_stat(self): 60 | stat = [(0, {'cpu_stats': {'cpu_usage': {'total_usage': 0}, 'system_cpu_usage': 0}})] 61 | self.assertRaises(AssertionError, dockerconvertors.get_cpu_metric, stat) 62 | 63 | def test_get_cpu_metric_trows_when_stats_is_empty(self): 64 | stat = [] 65 | self.assertRaises(AssertionError, dockerconvertors.get_cpu_metric, stat) 66 | 67 | def test_per_second_metric(self): 68 | samples = [(0, 1), (1, 2), (2, 3), (3, 10)] 69 | expected_metric = {'name': 'm1', 'count': len(samples) - 1, 'value': 3, 'min': 1, 'max': 7, 'std': 3.464101615} 70 | actual_metric = dockerconvertors.get_per_second_metric('m1', lambda s: s, samples) 71 | self._assert_metrics_equals(expected_metric=expected_metric, actual_metric=actual_metric) 72 | 73 | def test_per_second_metric_rais_when_metric_is_none(self): 74 | self.assertRaises(AssertionError, dockerconvertors.get_per_second_metric, None, lambda s: s, [(0, 1), (2, 3)]) 75 | 76 | def test_per_second_metric_rais_when_func_is_none(self): 77 | self.assertRaises(AssertionError, dockerconvertors.get_per_second_metric, "metric1", None, [(0, 1), (2, 3)]) 78 | 79 | def test_per_second_metric_rais_when_stats_has_only_one_stats(self): 80 | self.assertRaises(AssertionError, dockerconvertors.get_per_second_metric, 'm1', lambda s: s, [(0, 0)]) 81 | 82 | def test_per_second_metric_rais_when_stats_has_zero_stats(self): 83 | self.assertRaises(AssertionError, dockerconvertors.get_per_second_metric, 'm1', lambda s: s, []) 84 | 85 | def test_get_simple_metric(self): 86 | samples = [(0, 123), (1, 2321), (3, 2312), (4, -234)] 87 | expected_metric = {'name': 'm1', 'value': 1130.5, 'count': len(samples), 'min': -234, 'max': 2321, 88 | 'std': 1377.213249} 89 | actual_metric = dockerconvertors.get_simple_metric('m1', lambda s: s, samples) 90 | self._assert_metrics_equals(expected_metric=expected_metric, actual_metric=actual_metric) 91 | 92 | def test_get_simple_metric_rais_when_metric_is_none(self): 93 | self.assertRaises(AssertionError, dockerconvertors.get_simple_metric, None, lambda s: s, [(0, 1), (2, 3)]) 94 | 95 | def test_get_simple_metric_rais_when_func_is_none(self): 96 | self.assertRaises(AssertionError, dockerconvertors.get_simple_metric, "metric1", None, [(0, 1), (2, 3)]) 97 | 98 | def test_get_simple_metric_rais_when_stats_has_only_one_stats(self): 99 | self.assertRaises(AssertionError, dockerconvertors.get_simple_metric, 'm1', lambda s: s, [(0, 0)]) 100 | 101 | def test_get_simple_metric_rais_when_stats_has_zero_stats(self): 102 | self.assertRaises(AssertionError, dockerconvertors.get_simple_metric, 'm1', lambda s: s, []) 103 | 104 | def _assert_metrics_equals(self, expected_metric, actual_metric): 105 | self.assertEqual(expected_metric['name'], actual_metric['name']) 106 | self.assertEqual(expected_metric['count'], actual_metric['count']) 107 | self.assertAlmostEqual(expected_metric['value'], actual_metric['value'], delta=0.001) 108 | self.assertAlmostEqual(expected_metric['min'], actual_metric['min'], delta=0.001) 109 | self.assertAlmostEqual(expected_metric['max'], actual_metric['max'], delta=0.001) 110 | self.assertAlmostEqual(expected_metric['std'], actual_metric['std'], delta=0.001) 111 | 112 | def test_convert_to_metrics(self): 113 | time_samples = [0, 10, 20, 30, 40, 50, 60] 114 | cpu_samples = [0, 1, 2, 5, 9, 19, 22] 115 | system_samples = [0, 10, 20, 30, 40, 50, 60] 116 | blkio_samples = [0, 1, 20, 30, 200, 350, 700] 117 | rx_samples = [0, 0, 0, 101, 120, 120, 230] 118 | tx_samples = [0, 0, 10, 10, 200, 250, 260] 119 | memory_samples = [1000000, 1000000, 2000000, 2020000, 3000000, 2000000, 1000000] 120 | memory_limit = 8000000 121 | 122 | samples = [(time, {'cpu_stats': {'cpu_usage': {'total_usage': cpu}, 'system_cpu_usage': system}, 123 | 'memory_stats': {'limit': memory_limit, 'usage': mem}, 'network': {'rx_bytes': rx, 'tx_bytes': tx}, 124 | 'blkio_stats': {'io_service_bytes_recursive': [{'op': 'Total', 'value': blkio}]}}) 125 | for time, cpu, system, blkio, rx, tx, mem in 126 | zip(time_samples, cpu_samples, system_samples, blkio_samples, rx_samples, tx_samples, 127 | memory_samples)] 128 | 129 | expected_metrics = { 130 | '% Processor Time': {'name': '% Processor Time', 'count': 6, 'value': 36.66666667, 'min': 10, 'max': 100, 'std': 33.26659987}, 131 | 'Available Bytes': {'name': 'Available Bytes', 'count': 7, 'value': 6282857.142857143, 'min': 5000000, 132 | 'max': 7000000, 'std': 757225.5121101482}, 133 | 'Docker RX Bytes':{'name': 'Docker RX Bytes', 'count': 6, 'value': 3.833333333, 'min': 0, 'max': 11, 'std': 5.262192192}, 134 | 'Docker TX Bytes': {'name': 'Docker TX Bytes', 'count': 6, 'value': 4.333333333, 'min': 0, 'max': 19, 'std': 7.420691792}, 135 | 'Docker Blkio Bytes': {'name': 'Docker Blkio Bytes', 'count': 6, 'value': 11.66666667, 'min': 0.1, 'max': 35, 'std': 13.61582413}} 136 | 137 | actual_metrics = dockerconvertors.convert_to_metrics(samples) 138 | 139 | for metric in actual_metrics: 140 | self.assertTrue(metric['name'] in expected_metrics) 141 | self._assert_metrics_equals(expected_metrics[metric['name']], metric) 142 | 143 | def test_get_container_properties_from_inspect(self): 144 | expected = {'Docker host': 'host1', 'Docker image': 'image1', 'Docker container id': 'c1', 'Docker container name': 'container1'} 145 | inspect = {'Name':expected['Docker container name'], 'Id':expected['Docker container id'], 'Config':{'Image': expected['Docker image']}} 146 | properties = dockerconvertors.get_container_properties_from_inspect(inspect, expected['Docker host']) 147 | self.assertDictEqual(expected, properties) -------------------------------------------------------------------------------- /python/tests/appinsights_tests/TestDockerInjector.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | __author__ = 'galha' 23 | 24 | import builtins 25 | from concurrent.futures.thread import ThreadPoolExecutor 26 | import time 27 | from unittest.mock import Mock, patch, mock_open 28 | import unittest 29 | from appinsights.dockerinjector import DockerInjector 30 | import re 31 | 32 | 33 | class TestDockerInjector(unittest.TestCase): 34 | def test_inject(self): 35 | expected = 'file already exists' 36 | container = {'Id': 'c1'} 37 | wrapper_mock = Mock() 38 | wrapper_mock.get_containers.return_value = [container] 39 | wrapper_mock.run_command.return_value = 'file already exists' 40 | injector = DockerInjector(docker_wrapper=wrapper_mock, docker_info_path="/path/docker.info") 41 | results = injector.inject_context() 42 | for c, result in results: 43 | self.assertEqual(container['Id'], c) 44 | self.assertEqual(expected, result) 45 | 46 | def test_get_my_container_id_when_file_exists(self): 47 | template = 'Docker container name=/boring_brattain,Docker image=ttt,Docker container id={0},Docker host=galha-ubuntu' 48 | expected_id = 'cd9d134b64807148faa24a17519c8e1a2650b825d4d38944ac54281b2dd1d94e' 49 | data = template.format(expected_id) 50 | with patch('os.path.exists') as exists: 51 | exists.return_value = True 52 | with patch.object(builtins, 'open', mock_open(read_data=data)): 53 | wrapper_mock = Mock() 54 | wrapper_mock.get_containers.return_value = [{'Id': 'c1'}] 55 | wrapper_mock.run_command.return_value = 'file already exists' 56 | injector = DockerInjector(docker_wrapper=wrapper_mock, docker_info_path="/path/docker.info") 57 | id = injector.get_my_container_id() 58 | self.assertEqual(0, wrapper_mock.run_command.call_count) 59 | self.assertEqual(expected_id, id) 60 | 61 | def test_get_my_container_id_when_file_exists_with_new_line(self): 62 | template = 'Docker container name=/boring_brattain,Docker image=ttt,Docker host=galha-ubuntu,Docker container id={0}\n' 63 | expected_id = 'cd9d134b64807148faa24a17519c8e1a2650b825d4d38944ac54281b2dd1d94e' 64 | data = template.format(expected_id) 65 | with patch('os.path.exists') as exists: 66 | exists.return_value = True 67 | with patch.object(builtins, 'open', mock_open(read_data=data)): 68 | wrapper_mock = Mock() 69 | wrapper_mock.get_containers.return_value = [{'Id': 'c1'}] 70 | wrapper_mock.run_command.return_value = 'file already exists' 71 | injector = DockerInjector(docker_wrapper=wrapper_mock, docker_info_path="/path/docker.info") 72 | id = injector.get_my_container_id() 73 | self.assertEqual(0, wrapper_mock.run_command.call_count) 74 | self.assertEqual(expected_id, id) 75 | 76 | def test_get_my_container_id_when_file_not_exists(self): 77 | template = 'Docker container name=/boring_brattain,Docker image=ttt,Docker container id={0},Docker host=galha-ubuntu' 78 | expected_id = 'cd9d134b64807148faa24a17519c8e1a2650b825d4d38944ac54281b2dd1d94e' 79 | data = template.format(expected_id) 80 | with patch('os.path.exists') as exists: 81 | exists.side_effect = [False, True] 82 | with patch.object(builtins, 'open', mock_open(read_data=data)): 83 | wrapper_mock = Mock() 84 | wrapper_mock.get_containers.return_value = [{'Id': 'c1'}] 85 | wrapper_mock.run_command.return_value = 'file already exists' 86 | injector = DockerInjector(docker_wrapper=wrapper_mock, docker_info_path="/path/docker.info") 87 | id = injector.get_my_container_id() 88 | self.assertEqual(2, wrapper_mock.run_command.call_count) 89 | self.assertEqual(expected_id, id) 90 | 91 | def test_start(self): 92 | expected_c1 = {'Docker container id': 'c1', 'Docker host': 'host', 'Docker container name': 'name1', 'Docker image': 'image1'} 93 | expected_c2 = {'Docker container id': 'c2', 'Docker host': 'host', 'Docker container name': 'name2', 'Docker image': 'image2'} 94 | container = {'Id': 'c1', 'Image':'image1', 'Names':['name1']} 95 | 96 | wrapper_mock = Mock() 97 | wrapper_mock.get_containers.return_value = [container] 98 | wrapper_mock.get_host_name.return_value='host' 99 | wrapper_mock.run_command.return_value = 'file already exists' 100 | wrapper_mock.get_events.return_value = [{'time': 1439388853, 'Id': 'c2', 'id': 'c2', 'from': 'image2', 'status': 'start'}] 101 | wrapper_mock.get_inspection.return_value = {'Id': 'c2', 'status': 'start', 'Config':{'Image':'image2'}, 'Name':'name2'} 102 | 103 | def assert_func(): 104 | res = wrapper_mock.run_command.mock_calls 105 | start = time.time() 106 | while len(res) < 4 and time.time() - start < 100: 107 | time.sleep(1) 108 | self.assertEqual(4, len(res)) 109 | 110 | injector = DockerInjector(docker_wrapper=wrapper_mock, docker_info_path="/path/docker.info") 111 | with ThreadPoolExecutor(max_workers=3) as ex: 112 | ex.submit(lambda: injector.start()) 113 | result = ex.submit(lambda: assert_func()) 114 | result.result() 115 | calls_argument_keys = [keys for name, args, keys in wrapper_mock.run_command.mock_calls] 116 | c1_calls = [d for d in calls_argument_keys if 'container' in d and 'Id' in d['container'] and d['container']['Id'] == 'c1'] 117 | c2_calls = [d for d in calls_argument_keys if 'container' in d and 'Id' in d['container'] and d['container']['Id'] == 'c2'] 118 | self.assertEqual(2, len(c1_calls)) 119 | self.assertEqual(2, len(c2_calls)) 120 | c1_data = c1_calls[1]['cmd'] 121 | m1 = re.search('printf \'(.+)\'', c1_data) 122 | self.assertTrue(m1) 123 | actual_c1= {key:val for key,val in [token.split('=') for token in m1.group(1).strip(' ').split(',')]} 124 | self.assertDictEqual(expected_c1, actual_c1) 125 | c2_data = c2_calls[1]['cmd'] 126 | m2 = re.search('printf \'(.+)\'', c2_data) 127 | self.assertTrue(m2) 128 | actual_c2= {key:val for key,val in [token.split('=') for token in m2.group(1).strip(' ').split(',')]} 129 | self.assertDictEqual(expected_c2, actual_c2) -------------------------------------------------------------------------------- /python/tests/appinsights_tests/TestDockerWrapper.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | from requests.packages.urllib3.exceptions import ReadTimeoutError 23 | 24 | __author__ = 'galha' 25 | import unittest 26 | from appinsights.dockerwrapper import DockerClientWrapper, DockerWrapperError, get_production_docker_wrapper, \ 27 | ProductionWrapper 28 | from unittest.mock import Mock, patch, call 29 | from docker import Client 30 | from docker.errors import APIError 31 | 32 | 33 | class TestDockerClientWrapper(unittest.TestCase): 34 | def test_constructor_raise_when_docker_client_is_none(self): 35 | self.assertRaises(AssertionError, DockerClientWrapper, None) 36 | 37 | def test_get_host_name_gets_the_client_host_name(self): 38 | expecte_host_name = 'host' 39 | mock = Mock(spec=Client) 40 | mock.info.return_value = {'Name': expecte_host_name} 41 | wrapper = DockerClientWrapper(mock) 42 | actual = wrapper.get_host_name() 43 | self.assertEqual(expecte_host_name, actual) 44 | 45 | def test_get_host_name_returns_na_when_no_host_name_found(self): 46 | expecte_host_name = 'N/A' 47 | mock = Mock(spec=Client) 48 | mock.info.return_value = {} 49 | wrapper = DockerClientWrapper(mock) 50 | actual = wrapper.get_host_name() 51 | self.assertEqual(expecte_host_name, actual) 52 | 53 | def test_get_containers_gets_the_client_containers(self): 54 | expected_containers = ['c1', 'c2', 'c3'] 55 | mock = Mock(spec=Client) 56 | mock.containers.return_value = expected_containers 57 | wrapper = DockerClientWrapper(mock) 58 | actual = wrapper.get_containers() 59 | self.assertEqual(expected_containers, actual) 60 | 61 | def test_get_stats_gets_the_client_stats(self): 62 | expected_stats = ['s1', 's2', 's3'] 63 | mock = Mock(spec=Client) 64 | mock.stats.return_value = expected_stats 65 | wrapper = DockerClientWrapper(mock) 66 | actual = wrapper.get_stats('c1', 3) 67 | self.assertEqual(expected_stats, [stat for time, stat in actual]) 68 | 69 | def test_get_stats_gets_requested_number_of_stats_from_the_client(self): 70 | mock = Mock(spec=Client) 71 | mock.stats.return_value = map(lambda i: "s{0}".format(i), range(0, 100000)) 72 | expected = ["s0", "s1", "s2"] 73 | wrapper = DockerClientWrapper(mock) 74 | actual = wrapper.get_stats('c1', 3) 75 | self.assertEqual(expected, [stat for time, stat in actual]) 76 | 77 | def test_get_stats_return_empty_list_on_api_error(self): 78 | mock = Mock() 79 | mock.stats.side_effect = APIError("boom", "boom", "boom") 80 | wrapper = DockerClientWrapper(mock) 81 | actual = wrapper.get_stats('c1', 3) 82 | self.assertEqual([], [stat for time, stat in actual]) 83 | 84 | def test_get_stats_return_empty_list_on_ReadTimeoutError(self): 85 | mock = Mock() 86 | mock.stats.side_effect = ReadTimeoutError('pool', 'url', 'message') 87 | wrapper = DockerClientWrapper(mock) 88 | actual = wrapper.get_stats('c1', 3) 89 | self.assertEqual([], [stat for time, stat in actual]) 90 | 91 | def test_get_stats_return_empty_list_on_timeout(self): 92 | mock = Mock() 93 | mock.stats.side_effect = ReadTimeoutError('pool', 'url', 'message') 94 | wrapper = DockerClientWrapper(mock) 95 | actual = wrapper.get_stats('c1', 3) 96 | self.assertEqual([], [stat for time, stat in actual]) 97 | 98 | def test_run_command(self): 99 | expectedResult = b'result' 100 | mock = Mock() 101 | mock.exec_create.return_value = 'exec1' 102 | mock.exec_start.return_value = expectedResult 103 | wrapper = DockerClientWrapper(mock) 104 | result = wrapper.run_command('c1', 'ls') 105 | self.assertEqual(expectedResult.decode('utf-8'), result) 106 | 107 | def test_run_command_raise_docker_wrapper_error(self): 108 | expectedResult = b'result' 109 | mock = Mock() 110 | mock.exec_create.side_effect = ReadTimeoutError('pool', 'url', 'message') 111 | mock.exec_start.return_value = expectedResult 112 | wrapper = DockerClientWrapper(mock) 113 | self.assertRaises(DockerWrapperError, wrapper.run_command, 'c1', 'ls') 114 | 115 | def test_production_wrapper(self): 116 | with patch('appinsights.dockerwrapper.ProductionWrapper') as mock: 117 | get_production_docker_wrapper("unix://docker.sock") 118 | self.assertEqual(1, mock.call_count) 119 | mock.assert_has_calls([call(base_url="unix://docker.sock")]) 120 | 121 | def test_production_wrapper_uses_two_clients(self): 122 | with patch('appinsights.dockerwrapper.DockerClientWrapper') as mock: 123 | m1, m2 = Mock(), Mock() 124 | mock.side_effect = [m1, m2] 125 | wrapper = ProductionWrapper("unix://docker.sock") 126 | self.assertEqual(2, mock.call_count) 127 | wrapper.get_containers() 128 | wrapper.run_command(None, None) 129 | m1c = m1.get_containers.call_count 130 | m1r = m1.run_command.call_count 131 | m2c = m2.get_containers.call_count 132 | m2r = m2.run_command.call_count 133 | self.assertEqual(1, m1c + m1r) 134 | self.assertEqual(1, m2c + m2r) 135 | self.assertEqual(1, m1c + m2c) 136 | self.assertEqual(1, m1r + m2r) 137 | 138 | def test_get_inspect(self): 139 | expectedResult = 'result' 140 | mock = Mock() 141 | mock.inspect_container.return_value = expectedResult 142 | wrapper = DockerClientWrapper(mock) 143 | inspection = wrapper.get_inspection({'Id':'c1'}) 144 | self.assertEqual(expectedResult, inspection) 145 | 146 | -------------------------------------------------------------------------------- /python/tests/appinsights_tests/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # -------------------------------------------------------------------------------- /sdk/ApplicationInsights.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | False 32 | 33 | 34 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ApplicationInsights-Docker' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/AgentBootstrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights; 23 | 24 | import com.microsoft.applicationinsights.common.ApplicationInsightsSender; 25 | import com.microsoft.applicationinsights.agent.DockerAgent; 26 | import com.microsoft.applicationinsights.agent.DockerContainerContextAgent; 27 | import com.microsoft.applicationinsights.python.ContainerContextPythonBoostrapper; 28 | import com.microsoft.applicationinsights.python.ContainerStatePythonBootstrapper; 29 | import com.microsoft.applicationinsights.python.MetricCollectionPythonBoostrapper; 30 | import com.microsoft.applicationinsights.python.PythonBootstrapper; 31 | 32 | import java.io.IOException; 33 | import java.util.HashMap; 34 | 35 | /** 36 | * Created by yonisha on 7/22/2015. 37 | * 38 | * The agent is executed by the Docker ENTRYPOINT command. 39 | */ 40 | public class AgentBootstrapper { 41 | 42 | // region Consts 43 | 44 | private static final int DEFAULT_COLLECT_INTERVAL = 45; 45 | private static final String CONTAINER_USAGE_COMMAND = 46 | "docker run -v /var/run/docker.sock:/docker.sock -d microsoft/applicationinsights ikey="; 47 | 48 | // endregion Consts 49 | 50 | // region Public 51 | 52 | public static void main(String[] args) throws IOException, InterruptedException { 53 | System.out.println("Starting Application Insights Docker agent."); 54 | HashMap argumentsMap = parseArguments(args); 55 | 56 | String instrumentationKey = argumentsMap.get("ikey"); 57 | if (instrumentationKey == null) { 58 | System.out.println("Usage: " + CONTAINER_USAGE_COMMAND); 59 | 60 | return; 61 | } 62 | 63 | // TODO: Create object for argument verification (type, existence, default value etc.) 64 | ApplicationInsightsSender applicationInsightsSender = new ApplicationInsightsSender(instrumentationKey); 65 | 66 | int sampleRateFromArgument = getSampleRateFromArgument(argumentsMap); 67 | PythonBootstrapper metricCollectionBootstrapper = new MetricCollectionPythonBoostrapper(sampleRateFromArgument); 68 | Thread metricCollectionAgentThread = createMetricCollectionProcess(applicationInsightsSender, metricCollectionBootstrapper); 69 | 70 | PythonBootstrapper containerStateBootstrapper = new ContainerStatePythonBootstrapper(); 71 | Thread containerStateThread = createContainerStateProcess(applicationInsightsSender, containerStateBootstrapper); 72 | 73 | Thread containerContextAgentThread = createContainerContextProcess(); 74 | 75 | AgentBootstrapper agentBootstrapper = new AgentBootstrapper(); 76 | agentBootstrapper.run(applicationInsightsSender, metricCollectionAgentThread, containerStateThread, containerContextAgentThread); 77 | 78 | System.out.println("Shutting down Application Insights Docker agent."); 79 | } 80 | 81 | // TODO: move to a dedicate object 82 | private static int getSampleRateFromArgument(HashMap argumentsMap) { 83 | final String intervalArgument = "collect-interval"; 84 | String sampleIntervalStr = argumentsMap.get(intervalArgument); 85 | 86 | int sampleInterval = DEFAULT_COLLECT_INTERVAL; 87 | if (sampleIntervalStr != null) { 88 | try { 89 | sampleInterval = (int)Double.parseDouble(sampleIntervalStr); 90 | } catch (NumberFormatException e) { 91 | System.err.print("Failed to parse '" + sampleIntervalStr + "' as '" + intervalArgument + "' argument - must be a number."); 92 | } 93 | } else { 94 | System.out.println("No collect interval argument provided."); 95 | } 96 | 97 | System.out.println("Collect interval is set to " + sampleInterval + " seconds."); 98 | 99 | return sampleInterval; 100 | } 101 | 102 | public void run( 103 | ApplicationInsightsSender applicationInsightsSender, 104 | Thread metricCollectionAgentThread, 105 | Thread containerStateThread, 106 | Thread containerContextAgentThread) throws InterruptedException { 107 | 108 | // Starting threads. 109 | metricCollectionAgentThread.start(); 110 | containerContextAgentThread.start(); 111 | containerStateThread.start(); 112 | 113 | // Waiting for all threads. 114 | metricCollectionAgentThread.join(); 115 | containerStateThread.join(); 116 | containerContextAgentThread.join(); 117 | } 118 | 119 | // endregion public 120 | 121 | // region Private 122 | 123 | private static HashMap parseArguments(String[] args) { 124 | HashMap arguments = new HashMap(); 125 | 126 | for (String arg : args) { 127 | String[] kv = arg.split("="); 128 | if (kv.length == 2) { 129 | arguments.put(kv[0], kv[1]); 130 | } 131 | } 132 | 133 | return arguments; 134 | } 135 | 136 | protected static Thread createMetricCollectionProcess(ApplicationInsightsSender aiSender, PythonBootstrapper metricCollectionBootstrapper) { 137 | System.out.println("Starting metric collection process."); 138 | DockerAgent dockerMetricAgent = new DockerAgent(metricCollectionBootstrapper, aiSender); 139 | Thread metricCollectionAgentThread = new Thread(dockerMetricAgent); 140 | metricCollectionAgentThread.setDaemon(true); 141 | 142 | return metricCollectionAgentThread; 143 | } 144 | 145 | protected static Thread createContainerContextProcess() { 146 | System.out.println("Starting container context process."); 147 | PythonBootstrapper containerContextBootstrapper = new ContainerContextPythonBoostrapper(); 148 | DockerContainerContextAgent containerContextAgent = new DockerContainerContextAgent(containerContextBootstrapper); 149 | Thread containerContextAgentThread = new Thread(containerContextAgent); 150 | containerContextAgentThread.setDaemon(true); 151 | 152 | return containerContextAgentThread; 153 | } 154 | 155 | protected static Thread createContainerStateProcess(ApplicationInsightsSender aiSender, PythonBootstrapper containerStateBootstrapper) { 156 | System.out.println("Starting container state process."); 157 | DockerAgent containerStateAgent = new DockerAgent(containerStateBootstrapper, aiSender); 158 | Thread containerStateThread = new Thread(containerStateAgent); 159 | containerStateThread.setDaemon(true); 160 | 161 | return containerStateThread; 162 | } 163 | 164 | // endregion Private 165 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/agent/DockerAgent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.agent; 23 | 24 | import com.microsoft.applicationinsights.common.ApplicationInsightsSender; 25 | import com.microsoft.applicationinsights.providers.EventProvider; 26 | import com.microsoft.applicationinsights.python.PythonBootstrapper; 27 | 28 | import java.io.IOException; 29 | 30 | /** 31 | * Created by yonisha on 8/5/2015. 32 | */ 33 | public class DockerAgent implements Runnable { 34 | private ApplicationInsightsSender applicationInsightsSender; 35 | private PythonBootstrapper pythonBootstrapper; 36 | private boolean shouldStop = false; 37 | 38 | // region Ctor 39 | 40 | public DockerAgent(PythonBootstrapper pythonBootstrapper, ApplicationInsightsSender applicationInsightsSender) { 41 | this.applicationInsightsSender = applicationInsightsSender; 42 | this.pythonBootstrapper = pythonBootstrapper; 43 | } 44 | 45 | // endregion Ctor 46 | 47 | // region Public 48 | 49 | /** 50 | * This method starts a Python process using the provided bootstrapper. 51 | * If, for any reason, the Python process exits, we start another process and continue to collect events. 52 | */ 53 | public void run() { 54 | 55 | // TODO: check python exit code and check if killed intentionally. 56 | while (!shouldStop && this.pythonBootstrapper.getExitValue() != 0) { 57 | 58 | try { 59 | this.pythonBootstrapper.start(false); 60 | } catch (IOException e) {} 61 | 62 | EventProvider eventProvider = (EventProvider)this.pythonBootstrapper.getResult(); 63 | if (eventProvider != null) { 64 | collectAndSendEvents(eventProvider, this.applicationInsightsSender); 65 | } 66 | 67 | String processExitInfo = this.pythonBootstrapper.getProcessExitInfo(); 68 | System.out.println(processExitInfo); 69 | } 70 | } 71 | 72 | // endregion Public 73 | 74 | // region Private 75 | 76 | protected void stop() { 77 | this.shouldStop = true; 78 | } 79 | 80 | private void collectAndSendEvents(EventProvider eventProvider, ApplicationInsightsSender applicationInsightsSender) { 81 | System.out.println("Starting to collect events from: " + this.pythonBootstrapper.getClass().getSimpleName()); 82 | 83 | while (true) { 84 | T event = eventProvider.getNext(); 85 | 86 | // Event can be null in two cases: 87 | // 1) The underlying JSON is corrupted and cannot be serialized. In that case, make sure only event JSONs 88 | // strings are printed to the STDOUT in the python scripts. Any other user traces are not allowed. 89 | // 2) The Python process has exited. In that case, the agent will start another Python process. 90 | if (event == null) { 91 | if (!this.pythonBootstrapper.isAlive()) { 92 | break; 93 | } else { 94 | continue; 95 | } 96 | } 97 | 98 | applicationInsightsSender.track(event); 99 | } 100 | } 101 | 102 | // endregion Private 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/agent/DockerContainerContextAgent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.agent; 23 | 24 | import com.microsoft.applicationinsights.python.PythonBootstrapper; 25 | 26 | import java.io.IOException; 27 | 28 | /** 29 | * Created by yonisha on 7/28/2015. 30 | */ 31 | public class DockerContainerContextAgent implements Runnable { 32 | private PythonBootstrapper pythonBootstrapper; 33 | private boolean shouldStop = false; 34 | 35 | // region Ctor 36 | 37 | public DockerContainerContextAgent(PythonBootstrapper pythonBootstrapper) { 38 | this.pythonBootstrapper = pythonBootstrapper; 39 | } 40 | 41 | // endregion Ctor 42 | 43 | // region Public 44 | 45 | public void run() { 46 | 47 | // TODO: check python exit code and check if killed intentionally. 48 | while (!shouldStop && this.pythonBootstrapper.getExitValue() != 0) { 49 | try { 50 | this.pythonBootstrapper.start(true); 51 | } catch (IOException e) { 52 | String simpleName = this.pythonBootstrapper.getClass().getSimpleName(); 53 | System.out.println(simpleName + " failed with exception: " + e.getMessage()); 54 | 55 | String processExitInfo = this.pythonBootstrapper.getProcessExitInfo(); 56 | System.out.println(processExitInfo); 57 | } 58 | } 59 | } 60 | 61 | // endregion Public 62 | 63 | // region Private 64 | 65 | protected void stop() { 66 | this.shouldStop = true; 67 | } 68 | 69 | // endregion Private 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/common/ApplicationInsightsSender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | import com.microsoft.applicationinsights.TelemetryClient; 25 | import com.microsoft.applicationinsights.TelemetryConfiguration; 26 | import com.microsoft.applicationinsights.contracts.ContainerStateEvent; 27 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 28 | import com.microsoft.applicationinsights.telemetry.Telemetry; 29 | 30 | /** 31 | * Created by yonisha on 7/23/2015. 32 | */ 33 | public class ApplicationInsightsSender { 34 | private TelemetryClient telemetryClient; 35 | private TelemetryFactory telemetryFactory; 36 | 37 | // region Ctor 38 | 39 | // Ctor for testability purposes. 40 | protected ApplicationInsightsSender(TelemetryClient telemetryClient, TelemetryFactory telemetryFactory) { 41 | this.telemetryClient = telemetryClient; 42 | this.telemetryFactory = telemetryFactory; 43 | } 44 | 45 | public ApplicationInsightsSender(String instrumentationKey) { 46 | this(initializeTelemetryClient(instrumentationKey), new TelemetryFactory()); 47 | } 48 | 49 | // endregion Ctor 50 | 51 | // region Public 52 | 53 | public void track(T metric) { 54 | Telemetry telemetry; 55 | 56 | if (metric instanceof ContainerStatsMetric) { 57 | telemetry = this.telemetryFactory.createMetricTelemetry((ContainerStatsMetric) metric); 58 | } else if (metric instanceof ContainerStateEvent) { 59 | telemetry = this.telemetryFactory.createEventTelemetry((ContainerStateEvent) metric); 60 | } else { 61 | System.err.println("Unknown metric: " + metric.getClass().getSimpleName()); 62 | 63 | return; 64 | } 65 | 66 | this.telemetryClient.track(telemetry); 67 | } 68 | 69 | // endregion Public 70 | 71 | // region Private 72 | 73 | private static TelemetryClient initializeTelemetryClient(String instrumentationKey) { 74 | TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.getActive(); 75 | telemetryConfiguration.setInstrumentationKey(instrumentationKey); 76 | return new TelemetryClient(telemetryConfiguration); 77 | } 78 | 79 | // endregion Private 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/common/ArrayUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | 28 | /** 29 | * Created by yonisha on 8/3/2015. 30 | */ 31 | public class ArrayUtils { 32 | public static String[] addFirst(String string, String[] current) { 33 | List strings = new ArrayList(Arrays.asList(current)); 34 | strings.add(0, string); 35 | 36 | return strings.toArray(new String[strings.size()]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/common/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | /** 25 | * Created by yonisha on 8/12/2015. 26 | */ 27 | public class Constants { 28 | public static final String DOCKER_HOST_PROPERTY_KEY = "Docker host"; 29 | public static final String DOCKER_IMAGE_PROPERTY_KEY = "Docker image"; 30 | public static final String DOCKER_CONTAINER_NAME_PROPERTY_KEY = "Docker container name"; 31 | public static final String DOCKER_CONTAINER_ID_PROPERTY_KEY = "Docker container id"; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/common/StringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | /** 25 | * Created by yonisha on 8/31/2015. 26 | */ 27 | public class StringUtils { 28 | public static boolean isNullOrEmpty(String str) { 29 | return str == null || str.isEmpty(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/common/TelemetryFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | import com.microsoft.applicationinsights.contracts.ContainerStateEvent; 25 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 26 | import com.microsoft.applicationinsights.telemetry.EventTelemetry; 27 | import com.microsoft.applicationinsights.telemetry.MetricTelemetry; 28 | import com.microsoft.applicationinsights.telemetry.PerformanceCounterTelemetry; 29 | import com.microsoft.applicationinsights.telemetry.Telemetry; 30 | 31 | import java.util.Map; 32 | 33 | /** 34 | * Created by yonisha on 8/12/2015. 35 | */ 36 | public class TelemetryFactory { 37 | 38 | // region Public 39 | 40 | public Telemetry createEventTelemetry(ContainerStateEvent stateEvent) { 41 | EventTelemetry telemetry = new EventTelemetry(stateEvent.getName()); 42 | 43 | if (!StringUtils.isNullOrEmpty(stateEvent.getInstrumentationKey())) { 44 | telemetry.getContext().setInstrumentationKey(stateEvent.getInstrumentationKey()); 45 | } 46 | 47 | // Setting operation in order to be able to correlate events related to the same container. 48 | String containerId = stateEvent.getProperties().get(com.microsoft.applicationinsights.common.Constants.DOCKER_CONTAINER_ID_PROPERTY_KEY); 49 | telemetry.getContext().getOperation().setId(containerId); 50 | telemetry.getContext().getOperation().setName(stateEvent.getName()); 51 | 52 | telemetry.getProperties().putAll(stateEvent.getProperties()); 53 | 54 | return telemetry; 55 | } 56 | 57 | public Telemetry createMetricTelemetry(ContainerStatsMetric containerStatsMetric) { 58 | Telemetry telemetry; 59 | String metricName = containerStatsMetric.getMetricName(); 60 | 61 | // If the given metric is one of the build-in PC in Ibiza, we track it as a performance counter telemetry. 62 | // Otherwise the given metric is sent as a custom metric. 63 | if (metricName.equalsIgnoreCase(com.microsoft.applicationinsights.internal.perfcounter.Constants.CPU_PC_COUNTER_NAME) || metricName.equalsIgnoreCase(com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_MEMORY_PC_COUNTER_NAME)) { 64 | telemetry = createPerformanceCounterTelemetry(containerStatsMetric); 65 | } else { 66 | MetricTelemetry metricTelemetry = new MetricTelemetry(metricName, containerStatsMetric.getValue()); 67 | metricTelemetry.setMin(containerStatsMetric.getMin()); 68 | metricTelemetry.setMax(containerStatsMetric.getMax()); 69 | metricTelemetry.setCount(containerStatsMetric.getCount()); 70 | metricTelemetry.setStandardDeviation(containerStatsMetric.getStdDev()); 71 | 72 | telemetry = metricTelemetry; 73 | } 74 | 75 | Map properties = telemetry.getProperties(); 76 | properties.put(com.microsoft.applicationinsights.common.Constants.DOCKER_HOST_PROPERTY_KEY, containerStatsMetric.getDockerHost()); 77 | properties.put(com.microsoft.applicationinsights.common.Constants.DOCKER_IMAGE_PROPERTY_KEY, containerStatsMetric.getDockerImage()); 78 | properties.put(com.microsoft.applicationinsights.common.Constants.DOCKER_CONTAINER_NAME_PROPERTY_KEY, containerStatsMetric.getDockerContainerName()); 79 | properties.put(com.microsoft.applicationinsights.common.Constants.DOCKER_CONTAINER_ID_PROPERTY_KEY, containerStatsMetric.getDockerContainerId()); 80 | 81 | return telemetry; 82 | } 83 | 84 | private PerformanceCounterTelemetry createPerformanceCounterTelemetry(ContainerStatsMetric containerStatsMetric) { 85 | PerformanceCounterTelemetry performanceCounterTelemetry = null; 86 | 87 | String metricName = containerStatsMetric.getMetricName(); 88 | if (metricName.equalsIgnoreCase(com.microsoft.applicationinsights.internal.perfcounter.Constants.CPU_PC_COUNTER_NAME)) { 89 | performanceCounterTelemetry = new PerformanceCounterTelemetry( 90 | com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_CPU_PC_CATEGORY_NAME, 91 | com.microsoft.applicationinsights.internal.perfcounter.Constants.CPU_PC_COUNTER_NAME, 92 | com.microsoft.applicationinsights.internal.perfcounter.Constants.INSTANCE_NAME_TOTAL, 93 | containerStatsMetric.getValue()); 94 | } else if (metricName.equalsIgnoreCase(com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_MEMORY_PC_COUNTER_NAME)) { 95 | performanceCounterTelemetry = new PerformanceCounterTelemetry( 96 | com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_MEMORY_PC_CATEGORY_NAME, 97 | com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_MEMORY_PC_COUNTER_NAME, 98 | "", 99 | containerStatsMetric.getValue()); 100 | } 101 | 102 | return performanceCounterTelemetry; 103 | } 104 | 105 | // endregion Public 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/contracts/ContainerStateEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.contracts; 23 | 24 | import com.google.gson.JsonElement; 25 | import com.google.gson.JsonObject; 26 | import com.google.gson.JsonParser; 27 | 28 | import java.util.HashMap; 29 | import java.util.Map; 30 | 31 | /** 32 | * Created by yonisha on 8/5/2015. 33 | */ 34 | public class ContainerStateEvent { 35 | 36 | // region Members 37 | 38 | private String eventName; 39 | private String ikey; 40 | private Map properties = new HashMap(); 41 | 42 | // endregion Members 43 | 44 | // region Ctor 45 | 46 | public ContainerStateEvent(String json) { 47 | this.deserialize(json); 48 | } 49 | 50 | // endregion Ctor 51 | 52 | // region Public 53 | 54 | public String getName() { 55 | return this.eventName; 56 | } 57 | 58 | public Map getProperties() { 59 | return this.properties; 60 | } 61 | 62 | public String getInstrumentationKey() { 63 | return ikey; 64 | } 65 | 66 | // endregion Public 67 | 68 | // region Private 69 | 70 | private void deserialize(String json) { 71 | JsonObject jsonObj = new JsonParser().parse(json).getAsJsonObject(); 72 | this.eventName = jsonObj.get("name").getAsString(); 73 | this.ikey = jsonObj.get("ikey").getAsString(); 74 | 75 | JsonObject propertiesObject = jsonObj.getAsJsonObject("properties"); 76 | 77 | for (Map.Entry kv : propertiesObject.entrySet()) { 78 | this.properties.put(kv.getKey(), kv.getValue().getAsString()); 79 | } 80 | } 81 | 82 | // endregion Private 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/contracts/ContainerStatsMetric.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.contracts; 23 | 24 | import com.google.gson.JsonObject; 25 | import com.google.gson.JsonParser; 26 | import com.microsoft.applicationinsights.common.Constants; 27 | 28 | /** 29 | * Created by yonisha on 7/22/2015. 30 | */ 31 | public class ContainerStatsMetric { 32 | 33 | // region Members 34 | 35 | private String dockerHost; 36 | private String dockerImage; 37 | private String dockerContainerName; 38 | private String dockerContainerId; 39 | 40 | private String metricName; 41 | private double value; 42 | private int count; 43 | private double min; 44 | private double max; 45 | private double stdDev; 46 | 47 | // endregion Members 48 | 49 | // region Ctor 50 | 51 | public ContainerStatsMetric(String json) { 52 | deserialize(json); 53 | } 54 | 55 | // endregion Ctor 56 | 57 | // region Public Methods 58 | 59 | public String getDockerHost() { 60 | return dockerHost; 61 | } 62 | 63 | public String getDockerImage() { 64 | return dockerImage; 65 | } 66 | 67 | public String getDockerContainerName() { 68 | return dockerContainerName; 69 | } 70 | 71 | public String getDockerContainerId() { 72 | return dockerContainerId; 73 | } 74 | 75 | public String getMetricName() { 76 | return metricName; 77 | } 78 | 79 | public double getValue() { 80 | return value; 81 | } 82 | 83 | public int getCount() { 84 | return count; 85 | } 86 | 87 | public double getMin() { 88 | return min; 89 | } 90 | 91 | public double getMax() { 92 | return max; 93 | } 94 | 95 | public double getStdDev() { 96 | return stdDev; 97 | } 98 | 99 | // endregion Public Methods 100 | 101 | // region Private Methods 102 | 103 | private void deserialize(String json) { 104 | JsonObject jsonObj = new JsonParser().parse(json).getAsJsonObject(); 105 | JsonObject metricJson = jsonObj.getAsJsonObject("metric"); 106 | 107 | this.metricName = metricJson.get("name").getAsString(); 108 | this.value = metricJson.get("value").getAsDouble(); 109 | this.count = metricJson.get("count").getAsInt(); 110 | this.min = metricJson.get("min").getAsDouble(); 111 | this.max = metricJson.get("max").getAsDouble(); 112 | this.stdDev = metricJson.get("std").getAsDouble(); 113 | 114 | JsonObject propertiesJson = jsonObj.getAsJsonObject("properties"); 115 | this.dockerHost = propertiesJson.get(Constants.DOCKER_HOST_PROPERTY_KEY).getAsString(); 116 | this.dockerImage = propertiesJson.get(Constants.DOCKER_IMAGE_PROPERTY_KEY).getAsString(); 117 | this.dockerContainerName = propertiesJson.get(Constants.DOCKER_CONTAINER_NAME_PROPERTY_KEY).getAsString(); 118 | this.dockerContainerId = propertiesJson.get(Constants.DOCKER_CONTAINER_ID_PROPERTY_KEY).getAsString(); 119 | } 120 | 121 | // endregion Private Methods 122 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/providers/EventProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.providers; 23 | 24 | import java.io.BufferedReader; 25 | import java.io.IOException; 26 | 27 | /** 28 | * Created by yonisha on 8/5/2015. 29 | */ 30 | public abstract class EventProvider { 31 | 32 | private BufferedReader inputBuffer; 33 | 34 | public EventProvider(BufferedReader inputBuffer) { 35 | this.inputBuffer = inputBuffer; 36 | } 37 | 38 | public T getNext() { 39 | String json; 40 | 41 | try { 42 | json = inputBuffer.readLine(); 43 | } catch (IOException e) { 44 | System.out.println("Failed to read event from input stream with exception: " + e.getMessage()); 45 | 46 | return null; 47 | } 48 | 49 | if (json == null || json.isEmpty()) { 50 | return null; 51 | } 52 | 53 | return deserialize(json); 54 | } 55 | 56 | // TODO: Implement a generic method and remove inherited classes. 57 | protected abstract T deserialize(String json); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/providers/MetricProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.providers; 23 | 24 | import com.google.gson.JsonSyntaxException; 25 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 26 | 27 | import java.io.BufferedReader; 28 | 29 | /** 30 | * Created by yonisha on 7/23/2015. 31 | */ 32 | public class MetricProvider extends EventProvider { 33 | 34 | public MetricProvider(BufferedReader inputBuffer) { 35 | super(inputBuffer); 36 | } 37 | 38 | protected ContainerStatsMetric deserialize(String json) { 39 | ContainerStatsMetric containerStatsMetric = null; 40 | 41 | try { 42 | containerStatsMetric = new ContainerStatsMetric(json); 43 | } catch (JsonSyntaxException e) { 44 | System.out.println("Failed to deserialize JSON to container metric: " + json); 45 | } 46 | 47 | return containerStatsMetric; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/providers/StateProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.providers; 23 | 24 | import com.google.gson.JsonSyntaxException; 25 | import com.microsoft.applicationinsights.contracts.ContainerStateEvent; 26 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 27 | 28 | import java.io.BufferedReader; 29 | 30 | /** 31 | * Created by yonisha on 8/5/2015. 32 | */ 33 | public class StateProvider extends EventProvider { 34 | 35 | // region Ctor 36 | 37 | public StateProvider(BufferedReader bufferedReader) { 38 | super(bufferedReader); 39 | } 40 | 41 | // endregion Ctor 42 | 43 | // region Private 44 | 45 | @Override 46 | protected ContainerStateEvent deserialize(String json) { 47 | ContainerStateEvent containerStateEvent = null; 48 | 49 | try { 50 | containerStateEvent = new ContainerStateEvent(json); 51 | } catch (JsonSyntaxException e) { 52 | System.out.println("Failed to deserialize JSON to container state: " + json); 53 | } 54 | 55 | return containerStateEvent; 56 | } 57 | 58 | // endregion Private 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/python/ContainerContextPythonBoostrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.python; 23 | 24 | /** 25 | * Created by yonisha on 7/28/2015. 26 | */ 27 | public class ContainerContextPythonBoostrapper extends PythonBootstrapper { 28 | 29 | private static final String BOOTSTRAPPER_ARG = "inject"; 30 | 31 | public ContainerContextPythonBoostrapper() { 32 | super(BOOTSTRAPPER_ARG); 33 | } 34 | 35 | @Override 36 | public Object getResult() { 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/python/ContainerStatePythonBootstrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.python; 23 | 24 | import java.io.BufferedReader; 25 | import java.io.InputStreamReader; 26 | 27 | import com.microsoft.applicationinsights.providers.StateProvider; 28 | 29 | /** 30 | * Created by yonisha on 8/5/2015. 31 | */ 32 | public class ContainerStatePythonBootstrapper extends PythonBootstrapper { 33 | 34 | // region Members 35 | 36 | private static final String BOOTSTRAPPER_ARG = "events"; 37 | 38 | // endregion Members 39 | 40 | // region Ctors 41 | 42 | public ContainerStatePythonBootstrapper(String... bootstrapperParams) { 43 | super(bootstrapperParams); 44 | } 45 | 46 | public ContainerStatePythonBootstrapper() { 47 | this(BOOTSTRAPPER_ARG); 48 | } 49 | 50 | // endregion Ctors 51 | 52 | // region Public 53 | 54 | @Override 55 | public StateProvider getResult() { 56 | return new StateProvider(new BufferedReader(new InputStreamReader(process.getInputStream()))); 57 | } 58 | 59 | // endregion Public 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/python/MetricCollectionPythonBoostrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.python; 23 | 24 | import com.microsoft.applicationinsights.providers.MetricProvider; 25 | 26 | import java.io.BufferedReader; 27 | import java.io.InputStreamReader; 28 | 29 | /** 30 | * Created by yonisha on 7/28/2015. 31 | */ 32 | public class MetricCollectionPythonBoostrapper extends PythonBootstrapper { 33 | 34 | private static final String BOOTSTRAPPER_ARG = "collect"; 35 | 36 | // region Ctors 37 | 38 | protected MetricCollectionPythonBoostrapper(ProcessBuilder processBuilder) { 39 | super(processBuilder); 40 | } 41 | 42 | public MetricCollectionPythonBoostrapper(String... bootstrapperParams) { 43 | super(bootstrapperParams); 44 | } 45 | 46 | public MetricCollectionPythonBoostrapper(int collectInterval) { 47 | this(BOOTSTRAPPER_ARG, generateCollectIntervalArgument(collectInterval)); 48 | } 49 | 50 | // endregion Ctors 51 | 52 | // region Public methods 53 | 54 | @Override 55 | public MetricProvider getResult() { 56 | return new MetricProvider(new BufferedReader(new InputStreamReader(process.getInputStream()))); 57 | } 58 | 59 | // endregion Public methods 60 | 61 | // region Private methods 62 | 63 | private static String generateCollectIntervalArgument(int collectInterval) { 64 | return String.format("--collect-interval=%s", String.valueOf(collectInterval)); 65 | } 66 | 67 | // endregion Private methods 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/python/ProcessBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.python; 23 | 24 | import java.io.IOException; 25 | 26 | /** 27 | * Created by yonisha on 7/26/2015. 28 | */ 29 | public interface ProcessBuilder { 30 | Process start() throws IOException; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/python/PythonBootstrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.python; 23 | 24 | import java.io.IOException; 25 | import com.microsoft.applicationinsights.common.ArrayUtils; 26 | 27 | /** 28 | * Created by yonisha on 7/22/2015. 29 | */ 30 | public abstract class PythonBootstrapper { 31 | 32 | // region Members 33 | 34 | private static final String PYTHON_BOOTSTRAP_SCRIPT = "python/bootstrap.py"; 35 | protected ProcessBuilder processBuilder; 36 | protected Process process; 37 | 38 | // endregion Members 39 | 40 | // region Ctor 41 | 42 | protected PythonBootstrapper(ProcessBuilder processBuilder) { 43 | this.processBuilder = processBuilder; 44 | } 45 | 46 | public PythonBootstrapper(String... bootstrapperArgs) { 47 | String[] updatedParams = ArrayUtils.addFirst(PYTHON_BOOTSTRAP_SCRIPT, bootstrapperArgs); 48 | 49 | this.processBuilder = new PythonProcessBuilder(updatedParams); 50 | } 51 | 52 | // endregion Ctor 53 | 54 | // region Public 55 | 56 | public void start(boolean waitForExit) throws IOException { 57 | closePreviousProcessResources(); 58 | 59 | try { 60 | this.process = processBuilder.start(); 61 | if (waitForExit) { 62 | this.process.waitFor(); 63 | } 64 | } catch (IOException e) { 65 | System.out.println(this.getClass().getSimpleName() + " failed to start python process with error: " + e.getMessage()); 66 | } catch (InterruptedException e) { 67 | System.out.println(this.getClass().getSimpleName() + " has been interrupted. Error: " + e.getMessage()); 68 | } 69 | } 70 | 71 | public boolean isAlive() { 72 | if (process == null) { 73 | return false; 74 | } 75 | 76 | return this.getExitValue() == -1; 77 | } 78 | 79 | public abstract T getResult(); 80 | 81 | public int getExitValue() { 82 | if (this.process == null) { 83 | return -1; 84 | } 85 | 86 | try { 87 | return this.process.exitValue(); 88 | } catch (java.lang.IllegalThreadStateException e) { 89 | return -1; 90 | } 91 | } 92 | 93 | public String getProcessExitInfo() { 94 | String bootstrapperClassName = this.getClass().getSimpleName(); 95 | 96 | return "Python bootstrapper " + bootstrapperClassName + " has exited with exit code: " + this.getExitValue(); 97 | } 98 | 99 | // endregion Public 100 | 101 | // region Private 102 | 103 | protected void closePreviousProcessResources() { 104 | if (process != null) { 105 | try { 106 | if (this.process.getInputStream() != null) { 107 | process.getInputStream().close(); 108 | } 109 | if (this.process.getErrorStream() != null) { 110 | process.getErrorStream().close(); 111 | } 112 | } catch (IOException e) { 113 | } 114 | } 115 | } 116 | 117 | // endregion Private 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/applicationinsights/python/PythonProcessBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.python; 23 | 24 | import com.microsoft.applicationinsights.common.ArrayUtils; 25 | 26 | import java.io.IOException; 27 | 28 | /** 29 | * Created by yonisha on 7/26/2015. 30 | */ 31 | public class PythonProcessBuilder implements ProcessBuilder { 32 | 33 | private final java.lang.ProcessBuilder processBuilder; 34 | private final String PYTHON_EXE_NAME = "python"; 35 | 36 | public PythonProcessBuilder(String... builderParams) { 37 | String[] updatedParams = ArrayUtils.addFirst(PYTHON_EXE_NAME, builderParams); 38 | 39 | this.processBuilder = new java.lang.ProcessBuilder(updatedParams); 40 | this.processBuilder.redirectError(java.lang.ProcessBuilder.Redirect.INHERIT); 41 | } 42 | 43 | public Process start() throws IOException { 44 | return processBuilder.start(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/AgentBootstrapperTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights; 23 | 24 | import com.microsoft.applicationinsights.common.ApplicationInsightsSender; 25 | import com.microsoft.applicationinsights.contracts.ContainerStateEvent; 26 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 27 | import com.microsoft.applicationinsights.python.ContainerStatePythonBootstrapper; 28 | import com.microsoft.applicationinsights.python.MetricCollectionPythonBoostrapper; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | 32 | import java.io.File; 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | import java.nio.file.Files; 36 | import java.nio.file.StandardCopyOption; 37 | 38 | import static org.mockito.Matchers.any; 39 | import static org.mockito.Mockito.mock; 40 | import static org.mockito.Mockito.times; 41 | import static org.mockito.Mockito.verify; 42 | 43 | /** 44 | * Created by yonisha on 8/3/2015. 45 | */ 46 | public class AgentBootstrapperTests { 47 | private static final String PYTHON_TEST_METRIC_EVENT_SCRIPT_FILENAME = "test_metric_event.py"; 48 | private static final String PYTHON_TEST_STATE_EVENT_SCRIPT_FILENAME = "test_state_event.py"; 49 | private AgentBootstrapper agentBootstrapperUnderTest = new AgentBootstrapper(); 50 | private ApplicationInsightsSender aiSenderMock; 51 | 52 | @Before 53 | public void initTest() { 54 | aiSenderMock = mock(ApplicationInsightsSender.class); 55 | } 56 | 57 | @Test 58 | public void testMetricCollectionProcess() throws InterruptedException, IOException { 59 | String testPythonScriptPath = getTestPythonScriptPath(PYTHON_TEST_METRIC_EVENT_SCRIPT_FILENAME); 60 | 61 | MetricCollectionPythonBoostrapper metricCollectionPythonBoostrapper = new MetricCollectionPythonBoostrapper("custom", "--script", testPythonScriptPath); 62 | Thread thread = AgentBootstrapper.createMetricCollectionProcess(aiSenderMock, metricCollectionPythonBoostrapper); 63 | 64 | agentBootstrapperUnderTest.run(aiSenderMock, thread, createWorklessThread(), createWorklessThread()); 65 | 66 | verify(aiSenderMock, times(1)).track(any(ContainerStatsMetric.class)); 67 | } 68 | 69 | @Test 70 | public void testContainerStateProcess() throws InterruptedException, IOException { 71 | String testPythonScriptPath = getTestPythonScriptPath(PYTHON_TEST_STATE_EVENT_SCRIPT_FILENAME); 72 | 73 | ContainerStatePythonBootstrapper bootstrapper = new ContainerStatePythonBootstrapper("custom", "--script", testPythonScriptPath); 74 | Thread thread = AgentBootstrapper.createContainerStateProcess(aiSenderMock, bootstrapper); 75 | 76 | agentBootstrapperUnderTest.run(aiSenderMock, createWorklessThread(), thread, createWorklessThread()); 77 | 78 | verify(aiSenderMock, times(1)).track(any(ContainerStateEvent.class)); 79 | } 80 | 81 | private String getTestPythonScriptPath(String filename) throws IOException { 82 | ClassLoader classLoader = AgentBootstrapperTests.class.getClassLoader(); 83 | InputStream resourceAsStream = classLoader.getResourceAsStream(filename); 84 | 85 | // ProcessBuilder cannot handle path containing spaces. Therefore, we copy the test python script into 86 | // a temp location and loading it from there. 87 | String tempDir = System.getProperty("java.io.tmpdir"); 88 | File tempPython = new File(tempDir, filename); 89 | Files.copy(resourceAsStream, tempPython.toPath(), StandardCopyOption.REPLACE_EXISTING); 90 | System.out.println("Test python script has been copied to : " + tempPython); 91 | 92 | return tempPython.getAbsolutePath(); 93 | } 94 | 95 | private Thread createWorklessThread() { 96 | return new Thread(new Runnable() { 97 | @Override 98 | public void run() { 99 | } 100 | }); 101 | } 102 | } -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/agent/DockerAgentTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.agent; 23 | 24 | import com.microsoft.applicationinsights.common.ApplicationInsightsSender; 25 | import com.microsoft.applicationinsights.common.TestConstants; 26 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 27 | import com.microsoft.applicationinsights.providers.MetricProvider; 28 | import com.microsoft.applicationinsights.python.PythonBootstrapper; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | import org.mockito.invocation.InvocationOnMock; 32 | import org.mockito.stubbing.Answer; 33 | 34 | import java.io.IOException; 35 | 36 | import static org.mockito.Mockito.*; 37 | 38 | /** 39 | * Created by yonisha on 7/26/2015. 40 | */ 41 | public class DockerAgentTests { 42 | private ApplicationInsightsSender applicationInsightsSender; 43 | private PythonBootstrapper pythonBootstrapper; 44 | private DockerAgent agentUnderTest; 45 | private MetricProvider metricProviderMock; 46 | 47 | @Before 48 | public void testInit() throws IOException { 49 | initializeMocks(); 50 | 51 | agentUnderTest.run(); 52 | } 53 | 54 | @Test 55 | public void testWhenAgentStartPythonBoostrapperStarts() throws IOException { 56 | verify(pythonBootstrapper, atLeastOnce()).start(false); 57 | } 58 | 59 | @Test 60 | public void testWhenAgentStartsMetricsBeingCollected() throws IOException { 61 | verify(metricProviderMock, atLeastOnce()).getNext(); 62 | } 63 | 64 | @Test 65 | public void testMetricIsSent() { 66 | verify(this.applicationInsightsSender, atLeastOnce()).track(any(ContainerStatsMetric.class)); 67 | } 68 | 69 | @Test 70 | public void testOnlyOnePythonProcessStarts() throws IOException { 71 | verify(this.pythonBootstrapper, times(1)).start(false); 72 | } 73 | 74 | @Test 75 | public void testWhenMetricProviderReturnsNullAndPythonProcessAliveNoNewPythonProcessIsStarted() throws IOException { 76 | initializeMocks(); 77 | 78 | final int[] numOfTrackedEvents = new int[1]; 79 | when(this.metricProviderMock.getNext()).thenAnswer(new Answer() { 80 | @Override 81 | public Object answer(InvocationOnMock invocation) throws Throwable { 82 | numOfTrackedEvents[0] = numOfTrackedEvents[0] + 1; 83 | if (numOfTrackedEvents[0] >= 3) { 84 | agentUnderTest.stop(); 85 | } 86 | 87 | return numOfTrackedEvents[0] == 1 || numOfTrackedEvents[0] == 3 ? new ContainerStatsMetric(TestConstants.DEFAULT_METRIC_EVENT) : null; 88 | } 89 | }); 90 | 91 | when(this.pythonBootstrapper.isAlive()).thenAnswer(new Answer() { 92 | @Override 93 | public Object answer(InvocationOnMock invocation) throws Throwable { 94 | return numOfTrackedEvents[0] <= 3; 95 | } 96 | }); 97 | 98 | this.agentUnderTest.run(); 99 | 100 | verify(this.pythonBootstrapper, times(1)).start(false); 101 | verify(this.metricProviderMock, times(4)).getNext(); 102 | verify(this.applicationInsightsSender, times(2)).track(any(ContainerStatsMetric.class)); 103 | } 104 | 105 | @Test 106 | public void testWhenPythonProcessNotAliveThenNewProcessIsStarted() throws IOException { 107 | initializeMocks(); 108 | 109 | final int[] numOfTrackedEvents = new int[1]; 110 | when(this.metricProviderMock.getNext()).thenAnswer(new Answer() { 111 | @Override 112 | public Object answer(InvocationOnMock invocation) throws Throwable { 113 | numOfTrackedEvents[0] = numOfTrackedEvents[0] + 1; 114 | if (numOfTrackedEvents[0] == 2) { 115 | agentUnderTest.stop(); 116 | } 117 | 118 | return null; 119 | } 120 | }); 121 | 122 | this.agentUnderTest.run(); 123 | 124 | verify(this.pythonBootstrapper, times(2)).start(false); 125 | } 126 | 127 | private void initializeMocks() throws IOException { 128 | final int numberOfEventsToSend = 5; 129 | final int[] numOfTrackedEvents = new int[1]; 130 | 131 | this.metricProviderMock = mock(MetricProvider.class); 132 | when(this.metricProviderMock.getNext()).thenAnswer(new Answer() { 133 | @Override 134 | public Object answer(InvocationOnMock invocation) throws Throwable { 135 | numOfTrackedEvents[0] = numOfTrackedEvents[0] + 1; 136 | if (numOfTrackedEvents[0] == numberOfEventsToSend) { 137 | agentUnderTest.stop(); 138 | } 139 | 140 | return numOfTrackedEvents[0] <= numberOfEventsToSend ? new ContainerStatsMetric(TestConstants.DEFAULT_METRIC_EVENT) : null; 141 | } 142 | }); 143 | 144 | this.applicationInsightsSender = mock(ApplicationInsightsSender.class); 145 | 146 | this.pythonBootstrapper = mock(PythonBootstrapper.class); 147 | when(this.pythonBootstrapper.getResult()).thenReturn(this.metricProviderMock); 148 | when(this.pythonBootstrapper.isAlive()).thenReturn(false); 149 | 150 | // 4 has no specific meaning here, just requires exit value != 0. 151 | when(this.pythonBootstrapper.getExitValue()).thenReturn(4); 152 | 153 | this.agentUnderTest = new DockerAgent(pythonBootstrapper, applicationInsightsSender); 154 | } 155 | } -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/common/ApplicationInsightsSenderTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | import com.microsoft.applicationinsights.TelemetryClient; 25 | import com.microsoft.applicationinsights.contracts.ContainerStateEvent; 26 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 27 | import com.microsoft.applicationinsights.extensibility.context.OperationContext; 28 | import com.microsoft.applicationinsights.internal.perfcounter.Constants; 29 | import com.microsoft.applicationinsights.telemetry.EventTelemetry; 30 | import com.microsoft.applicationinsights.telemetry.MetricTelemetry; 31 | import com.microsoft.applicationinsights.telemetry.PerformanceCounterTelemetry; 32 | import com.microsoft.applicationinsights.telemetry.Telemetry; 33 | import org.junit.Assert; 34 | import org.junit.Before; 35 | import org.junit.Test; 36 | import org.mockito.Matchers; 37 | import org.mockito.Mockito; 38 | import org.mockito.invocation.InvocationOnMock; 39 | import org.mockito.stubbing.Answer; 40 | 41 | import java.util.ArrayList; 42 | import java.util.List; 43 | 44 | import static org.mockito.Matchers.any; 45 | import static org.mockito.Mockito.mock; 46 | import static org.mockito.Mockito.times; 47 | 48 | /** 49 | * Created by yonisha on 7/23/2015. 50 | */ 51 | public class ApplicationInsightsSenderTests { 52 | private final String METRIC_TEMPLATE = "{'metric':{'name':'%s','value':0,'count':0,'min':0,'max':0,'std':0},'properties':{'Docker image':'x','Docker host':'x','Docker container id':'x','Docker container name':'x'}}"; 53 | 54 | private TelemetryClient telemetryClientMock; 55 | private ApplicationInsightsSender defaultSenderUnderTest; 56 | private List telemetries = new ArrayList(); 57 | 58 | @Before 59 | public void testInitialize() { 60 | initializeTelemetryClientMock(); 61 | defaultSenderUnderTest = new ApplicationInsightsSender(telemetryClientMock, new TelemetryFactory()); 62 | telemetries = new ArrayList(); 63 | } 64 | 65 | @Test 66 | public void testCustomMetricClassifiedCorrectly() { 67 | testMetricClassifiedCorrectly(false, MetricTelemetry.class); 68 | } 69 | 70 | @Test 71 | public void testPerformanceCounterClassifiedCorrectly() { 72 | testMetricClassifiedCorrectly(true, PerformanceCounterTelemetry.class); 73 | } 74 | 75 | @Test 76 | public void testContaienrStateMetricClassifiedCorrectly() { 77 | trackContainerStateMetric(); 78 | 79 | Mockito.verify(telemetryClientMock, times(1)).track(any(Telemetry.class)); 80 | Assert.assertTrue(telemetries.get(0) instanceof EventTelemetry); 81 | } 82 | 83 | @Test 84 | public void testContainerStateTelemetryEventAssignedWithCorrelationId() { 85 | trackContainerStateMetric(); 86 | 87 | OperationContext operation = telemetries.get(0).getContext().getOperation(); 88 | Assert.assertEquals("con_id" , operation.getId()); 89 | Assert.assertEquals("docker-container-state" , operation.getName()); 90 | } 91 | 92 | private void trackContainerStateMetric() { 93 | ContainerStateEvent containerStateEvent = new ContainerStateEvent(TestConstants.DEFAULT_STATE_EVENT); 94 | defaultSenderUnderTest.track(containerStateEvent); 95 | } 96 | 97 | private void testMetricClassifiedCorrectly(boolean generatePerformanceCounterMetricName, Class expectedTelemetryType) { 98 | ContainerStatsMetric containerStatsMetric = createContainerStatsMetric(generatePerformanceCounterMetricName); 99 | 100 | defaultSenderUnderTest.track(containerStatsMetric); 101 | 102 | Mockito.verify(telemetryClientMock, times(1)).track(any(Telemetry.class)); 103 | Assert.assertTrue(expectedTelemetryType.isInstance(telemetries.get(0))); 104 | } 105 | 106 | private ContainerStatsMetric createContainerStatsMetric(boolean isPerformanceCounter) { 107 | String metricName = isPerformanceCounter ? Constants.CPU_PC_COUNTER_NAME : "non_pc"; 108 | String metricJson = String.format(METRIC_TEMPLATE, metricName); 109 | 110 | return new ContainerStatsMetric(metricJson); 111 | } 112 | 113 | private void initializeTelemetryClientMock() { 114 | telemetryClientMock = mock(com.microsoft.applicationinsights.TelemetryClient.class); 115 | Mockito.doAnswer(new Answer() { 116 | @Override 117 | public Object answer(InvocationOnMock invocation) throws Throwable { 118 | Telemetry telemetry = ((Telemetry) invocation.getArguments()[0]); 119 | telemetries.add(telemetry); 120 | 121 | return null; 122 | } 123 | }).when(telemetryClientMock).track(Matchers.any(Telemetry.class)); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/common/ArrayUtilsTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | import org.junit.Assert; 25 | import org.junit.Test; 26 | 27 | /** 28 | * Created by yonisha on 8/3/2015. 29 | */ 30 | public class ArrayUtilsTests { 31 | @Test 32 | public void testStringAddedFirstSuccessfully() { 33 | String newString = "some_string"; 34 | String[] array = {"first", "second"}; 35 | 36 | String[] updatedArray = ArrayUtils.addFirst(newString, array); 37 | 38 | Assert.assertEquals(newString, updatedArray[0]); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/common/StringUtilsTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | import org.junit.Assert; 25 | import org.junit.Test; 26 | 27 | /** 28 | * Created by yonisha on 8/31/2015. 29 | */ 30 | public class StringUtilsTests { 31 | 32 | @Test 33 | public void testEmptyStringReturnsTrue() { 34 | Assert.assertTrue(StringUtils.isNullOrEmpty("")); 35 | } 36 | 37 | @Test 38 | public void testNullStringReturnsTrue() { 39 | Assert.assertTrue(StringUtils.isNullOrEmpty(null)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/common/TelemetryFactoryTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | import com.microsoft.applicationinsights.contracts.ContainerStateEvent; 25 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 26 | import com.microsoft.applicationinsights.telemetry.EventTelemetry; 27 | import com.microsoft.applicationinsights.telemetry.MetricTelemetry; 28 | import com.microsoft.applicationinsights.telemetry.PerformanceCounterTelemetry; 29 | import com.microsoft.applicationinsights.telemetry.Telemetry; 30 | import org.junit.Assert; 31 | import org.junit.Test; 32 | 33 | /** 34 | * Created by yonisha on 8/12/2015. 35 | */ 36 | public class TelemetryFactoryTests { 37 | 38 | private static final String METRIC_TEMPLATE = "{'metric':{'name':'%s','value':0,'count':0,'min':0,'max':0,'std':0},'properties':{'Docker image':'%s','Docker host':'%s','Docker container id':'%s','Docker container name':'%s'}}"; 39 | private TelemetryFactory telemetryFactoryUnderTest = new TelemetryFactory(); 40 | 41 | @Test 42 | public void testCreateEventTelemetry() { 43 | ContainerStateEvent containerStateEvent = new ContainerStateEvent(TestConstants.DEFAULT_STATE_EVENT); 44 | 45 | EventTelemetry eventTelemetry = (EventTelemetry)this.telemetryFactoryUnderTest.createEventTelemetry(containerStateEvent); 46 | 47 | Assert.assertEquals("docker-container-state", eventTelemetry.getName()); 48 | Assert.assertEquals("con_id", eventTelemetry.getProperties().get(Constants.DOCKER_CONTAINER_ID_PROPERTY_KEY)); 49 | } 50 | 51 | @Test 52 | public void testCreateCustomMetricTelemetry() { 53 | ContainerStatsMetric customMetric = new ContainerStatsMetric(TestConstants.DEFAULT_METRIC_EVENT); 54 | 55 | MetricTelemetry metricTelemetry = (MetricTelemetry)this.telemetryFactoryUnderTest.createMetricTelemetry(customMetric); 56 | 57 | Assert.assertEquals("name", metricTelemetry.getName()); 58 | } 59 | 60 | @Test 61 | public void testProcessorCpuStatsGeneratesCorrectPerformanceCounterTelemetry() { 62 | String cpuPcCounterName = com.microsoft.applicationinsights.internal.perfcounter.Constants.CPU_PC_COUNTER_NAME; 63 | 64 | PerformanceCounterTelemetry telemetry = (PerformanceCounterTelemetry) createPerformanceCounterTelemetryAccordingToMetricName(cpuPcCounterName); 65 | 66 | Assert.assertEquals(com.microsoft.applicationinsights.internal.perfcounter.Constants.CPU_PC_COUNTER_NAME, telemetry.getCounterName()); 67 | Assert.assertEquals(com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_CPU_PC_CATEGORY_NAME, telemetry.getCategoryName()); 68 | Assert.assertEquals(com.microsoft.applicationinsights.internal.perfcounter.Constants.INSTANCE_NAME_TOTAL, telemetry.getInstanceName()); 69 | } 70 | 71 | @Test 72 | public void testAvailableMemoryStatsGeneratesCorrectPerformanceCounterTelemetry() { 73 | String availableMemoryCounterName = com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_MEMORY_PC_COUNTER_NAME; 74 | 75 | PerformanceCounterTelemetry telemetry = (PerformanceCounterTelemetry) createPerformanceCounterTelemetryAccordingToMetricName(availableMemoryCounterName); 76 | 77 | Assert.assertEquals(com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_MEMORY_PC_CATEGORY_NAME, telemetry.getCategoryName()); 78 | Assert.assertEquals(com.microsoft.applicationinsights.internal.perfcounter.Constants.TOTAL_MEMORY_PC_COUNTER_NAME, telemetry.getCounterName()); 79 | } 80 | 81 | @Test 82 | public void testEventTelemetryUpdatedWithInstrumentationKeyIfProvided() { 83 | ContainerStateEvent containerStateEvent = new ContainerStateEvent(TestConstants.DEFAULT_STATE_EVENT); 84 | 85 | EventTelemetry eventTelemetry = (EventTelemetry)this.telemetryFactoryUnderTest.createEventTelemetry(containerStateEvent); 86 | 87 | Assert.assertEquals("instrumentation_key", eventTelemetry.getContext().getInstrumentationKey()); 88 | } 89 | 90 | @Test 91 | public void testEventTelemetryNotUpdatedWithInstrumentationKeyIfNotProvided() { 92 | String stateEventJsonWithoutIkey = TestConstants.DEFAULT_STATE_EVENT.replace("instrumentation_key", ""); 93 | ContainerStateEvent containerStateEvent = new ContainerStateEvent(stateEventJsonWithoutIkey); 94 | 95 | EventTelemetry eventTelemetry = (EventTelemetry)this.telemetryFactoryUnderTest.createEventTelemetry(containerStateEvent); 96 | 97 | Assert.assertEquals(null, eventTelemetry.getContext().getInstrumentationKey()); 98 | } 99 | 100 | private Telemetry createPerformanceCounterTelemetryAccordingToMetricName(String counterName) { 101 | 102 | String metric = String.format(METRIC_TEMPLATE, counterName, "some_host", "some_image", "some_con_name", "some_con_id"); 103 | ContainerStatsMetric customMetric = new ContainerStatsMetric(metric); 104 | 105 | PerformanceCounterTelemetry pcTelemetry = (PerformanceCounterTelemetry)this.telemetryFactoryUnderTest.createMetricTelemetry(customMetric); 106 | 107 | return pcTelemetry; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/common/TestConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.common; 23 | 24 | /** 25 | * Created by yonisha on 7/26/2015. 26 | */ 27 | public class TestConstants { 28 | public static final String DEFAULT_STATE_EVENT = "{'name':'docker-container-state','ikey':'instrumentation_key','properties':{'Docker image':'ubuntu','Created':'2015-08-06T09:01:55.3422261Z','StartedAt':'2015-08-06T09:01:55.7843711Z','Docker container name':'/modest_jang0','RestartCount':0,'status':'start','Docker container id':'con_id','Docker host':'galha-ubuntu'}}"; 29 | public static final String DEFAULT_METRIC_EVENT = "{'metric':{'name':'name','value':0,'count':0,'min':0,'max':0,'std':0},'properties':{'Docker image':'x','Docker host':'x','Docker container id':'x','Docker container name':'x'}}"; 30 | public static final String DEFAULT_METRIC_TEMPLATE = "{'metric':{'name':'%s','value':%s,'count':%s,'min':%s,'max':%s,'std':%s},'properties':{'Docker image':'%s','Docker host':'%s','Docker container id':'%s','Docker container name':'%s'}}"; 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/contracts/ContainerStateEventTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.contracts; 23 | 24 | import com.microsoft.applicationinsights.common.TestConstants; 25 | import org.junit.Assert; 26 | import org.junit.Test; 27 | 28 | /** 29 | * Created by yonisha on 8/6/2015. 30 | */ 31 | public class ContainerStateEventTests { 32 | 33 | @Test 34 | public void testStateEventJsonParsedSuccessfully() { 35 | ContainerStateEvent containerStateEvent = new ContainerStateEvent(TestConstants.DEFAULT_STATE_EVENT); 36 | 37 | Assert.assertEquals("docker-container-state", containerStateEvent.getName()); 38 | Assert.assertEquals("ubuntu", containerStateEvent.getProperties().get("Docker image")); 39 | } 40 | 41 | @Test 42 | public void testStateEventIkeyParsedSuccessfully() { 43 | ContainerStateEvent containerStateEvent = new ContainerStateEvent(TestConstants.DEFAULT_STATE_EVENT); 44 | 45 | Assert.assertEquals("instrumentation_key", containerStateEvent.getInstrumentationKey()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/contracts/ContainerStatsMetricTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.contracts; 23 | 24 | import com.microsoft.applicationinsights.common.TestConstants; 25 | import org.junit.Assert; 26 | import org.junit.Test; 27 | 28 | /** 29 | * Created by yonisha on 7/22/2015. 30 | */ 31 | public class ContainerStatsMetricTests { 32 | private final String METRIC_NAME = "docker-memory-usage"; 33 | private final String DOCKER_HOST = "ubuntu-vm"; 34 | private final String DOCKER_IMAGE = "hybrid"; 35 | private final String DOCKER_CONTAINER_NAME = "carlos"; 36 | private final String DOCKER_CONTAINER_ID = "some_id"; 37 | 38 | private final float VALUE = 0.01f; 39 | private final float MIN = 0.02f; 40 | private final float MAX = 0.03f; 41 | private final float STD_DEV = 0.04f; 42 | private final int COUNT = 5; 43 | 44 | private String defaultMetric = String.format(TestConstants.DEFAULT_METRIC_TEMPLATE, METRIC_NAME, VALUE, COUNT, MIN, MAX, STD_DEV, DOCKER_IMAGE, DOCKER_HOST, DOCKER_CONTAINER_ID, DOCKER_CONTAINER_NAME); 45 | 46 | @Test 47 | public void testMetricJsonDeserializedSuccessfully() { 48 | ContainerStatsMetric containerStatsMetric = new ContainerStatsMetric(defaultMetric); 49 | 50 | Assert.assertEquals(METRIC_NAME, containerStatsMetric.getMetricName()); 51 | Assert.assertEquals(DOCKER_HOST, containerStatsMetric.getDockerHost()); 52 | Assert.assertEquals(DOCKER_IMAGE, containerStatsMetric.getDockerImage()); 53 | Assert.assertEquals(DOCKER_CONTAINER_NAME, containerStatsMetric.getDockerContainerName()); 54 | Assert.assertEquals(DOCKER_CONTAINER_ID, containerStatsMetric.getDockerContainerId()); 55 | Assert.assertEquals(VALUE, containerStatsMetric.getValue(), 0.001); 56 | Assert.assertEquals(MIN, containerStatsMetric.getMin(), 0.001); 57 | Assert.assertEquals(MAX, containerStatsMetric.getMax(), 0.001); 58 | Assert.assertEquals(STD_DEV, containerStatsMetric.getStdDev(), 0.001); 59 | Assert.assertEquals(COUNT, containerStatsMetric.getCount()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/providers/MetricProviderTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.providers; 23 | 24 | import com.microsoft.applicationinsights.common.TestConstants; 25 | import com.microsoft.applicationinsights.contracts.ContainerStatsMetric; 26 | import org.junit.Assert; 27 | import org.junit.Test; 28 | 29 | import java.io.BufferedReader; 30 | import java.io.IOException; 31 | import java.io.StringReader; 32 | 33 | /** 34 | * Created by yonisha on 7/23/2015. 35 | */ 36 | public class MetricProviderTests { 37 | 38 | @Test 39 | public void testValidJsonResultsWithContainerStatsMetric() throws IOException { 40 | ContainerStatsMetric metric = createProviderAndGetMetric(TestConstants.DEFAULT_METRIC_EVENT); 41 | 42 | Assert.assertNotNull(metric); 43 | } 44 | 45 | @Test 46 | public void testCorruptedJsonNotThrowException() throws IOException { 47 | ContainerStatsMetric metric = createProviderAndGetMetric(" { some corrupted json } "); 48 | 49 | Assert.assertNull(metric); 50 | } 51 | 52 | private ContainerStatsMetric createProviderAndGetMetric(String providerInputString) throws IOException { 53 | BufferedReader inputBuffer = new BufferedReader(new StringReader(providerInputString)); 54 | 55 | MetricProvider metricProvider = new MetricProvider(inputBuffer); 56 | return metricProvider.getNext(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/providers/StateProviderTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.providers; 23 | 24 | import com.microsoft.applicationinsights.common.TestConstants; 25 | import com.microsoft.applicationinsights.contracts.ContainerStateEvent; 26 | import org.junit.Assert; 27 | import org.junit.Test; 28 | 29 | import java.io.BufferedReader; 30 | import java.io.IOException; 31 | import java.io.StringReader; 32 | 33 | /** 34 | * Created by yonisha on 8/5/2015. 35 | */ 36 | public class StateProviderTests { 37 | 38 | @Test 39 | public void testValidJsonResultsWithContainerStateEvent() throws IOException { 40 | ContainerStateEvent event = createProviderAndGetState(TestConstants.DEFAULT_STATE_EVENT); 41 | 42 | Assert.assertNotNull(event); 43 | } 44 | 45 | @Test 46 | public void testCorruptedJsonNotThrowException() throws IOException { 47 | ContainerStateEvent event = createProviderAndGetState(" { some corrupted json } "); 48 | 49 | Assert.assertNull(event); 50 | } 51 | 52 | private ContainerStateEvent createProviderAndGetState(String providerInputString) throws IOException { 53 | BufferedReader inputBuffer = new BufferedReader(new StringReader(providerInputString)); 54 | 55 | StateProvider provider = new StateProvider(inputBuffer); 56 | return provider.getNext(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/applicationinsights/python/MetricProviderPythonBootstrapperTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ApplicationInsights-Docker 3 | * Copyright (c) Microsoft Corporation 4 | * All rights reserved. 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | * software and associated documentation files (the ""Software""), to deal in the Software 9 | * without restriction, including without limitation the rights to use, copy, modify, merge, 10 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in all copies or 13 | * substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | * DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.microsoft.applicationinsights.python; 23 | 24 | import org.junit.Assert; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | 28 | import java.io.BufferedInputStream; 29 | import java.io.IOException; 30 | 31 | import static org.mockito.Mockito.*; 32 | 33 | /** 34 | * Created by yonisha on 7/26/2015. 35 | */ 36 | public class MetricProviderPythonBootstrapperTests { 37 | 38 | private com.microsoft.applicationinsights.python.ProcessBuilder processBuilderMock = mock(ProcessBuilder.class); 39 | private Process processMock = mock(Process.class); 40 | private PythonBootstrapper bootstrapperUnderTest = new MetricCollectionPythonBoostrapper(processBuilderMock); 41 | private BufferedInputStream inputStreamMock = mock(BufferedInputStream.class); 42 | 43 | @Before 44 | public void testInit() throws IOException { 45 | when(processBuilderMock.start()).thenReturn(processMock); 46 | when(processMock.getInputStream()).thenReturn(inputStreamMock); 47 | this.bootstrapperUnderTest = new MetricCollectionPythonBoostrapper(processBuilderMock); 48 | this.bootstrapperUnderTest.start(false); 49 | } 50 | 51 | @Test 52 | public void testIsAliveReturnsTrueWhenProcessAlive() throws IOException { 53 | when(processMock.exitValue()).thenThrow(new java.lang.IllegalThreadStateException()); 54 | 55 | Assert.assertTrue(bootstrapperUnderTest.isAlive()); 56 | } 57 | 58 | @Test 59 | public void testIsAliveReturnsFalseProcessNotStartedYet() { 60 | Assert.assertFalse(bootstrapperUnderTest.isAlive()); 61 | } 62 | 63 | @Test 64 | public void testIsAliveReturnsFalseWhenProcessExisted() { 65 | when(processMock.exitValue()).thenReturn(0); 66 | 67 | Assert.assertFalse(bootstrapperUnderTest.isAlive()); 68 | } 69 | 70 | @Test 71 | public void testBoostrapperStartsNewProcess() throws IOException { 72 | verify(processBuilderMock, times(1)).start(); 73 | } 74 | 75 | @Test 76 | public void testPreviousProcessResourcesAreClosed() throws IOException { 77 | bootstrapperUnderTest.start(false); 78 | 79 | verify(inputStreamMock, times(1)).close(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/resources/test_metric_event.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | # __author__ = 'yonisha' 23 | 24 | print("{'metric':{'name':'docker-memory-usage','value':0.028,'count':5,'min':0.028,'max':0.028,'std':0.0},'properties':{'Docker image':'hybrid','Docker host':'gh-ubuntu','Docker container id':'46cb90a','Docker container name':'/pensive_carson'}}") -------------------------------------------------------------------------------- /src/test/resources/test_state_event.py: -------------------------------------------------------------------------------- 1 | # 2 | # ApplicationInsights-Docker 3 | # Copyright (c) Microsoft Corporation 4 | # All rights reserved. 5 | # 6 | # MIT License 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 8 | # software and associated documentation files (the ""Software""), to deal in the Software 9 | # without restriction, including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # The above copyright notice and this permission notice shall be included in all copies or 13 | # substantial portions of the Software. 14 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 15 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 16 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 17 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | # 21 | 22 | # __author__ = 'yonisha' 23 | 24 | print("{'name':'docker-container-state','ikey':'instrumentation_key','properties':{'Docker image':'ubuntu','docker-Created':'2015-08-06T09:01:55.3422261Z','docker-StartedAt':'2015-08-06T09:01:55.7843711Z','Docker container name':'/modest_jang0','docker-RestartCount':0,'docker-status':'start','Docker container id':'22e26598fcda9d72c6c7aa55c9ee5c91b52ff73c31202f163f1bcbf2668c2f18','Docker host':'galha-ubuntu'}}") --------------------------------------------------------------------------------