15 changed files with 1790 additions and 401 deletions
@ -1,46 +1,124 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion> |
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
<artifactId>spring-data-mongodb-distribution</artifactId> |
<artifactId>spring-data-mongodb-distribution</artifactId> |
||||||
|
|
||||||
<packaging>pom</packaging> |
<packaging>pom</packaging> |
||||||
|
|
||||||
<name>Spring Data MongoDB - Distribution</name> |
<name>Spring Data MongoDB - Distribution</name> |
||||||
<description>Distribution build for Spring Data MongoDB</description> |
<description>Distribution build for Spring Data MongoDB</description> |
||||||
|
|
||||||
<parent> |
<parent> |
||||||
<groupId>org.springframework.data</groupId> |
<groupId>org.springframework.data</groupId> |
||||||
<artifactId>spring-data-mongodb-parent</artifactId> |
<artifactId>spring-data-mongodb-parent</artifactId> |
||||||
<version>4.0.0-SNAPSHOT</version> |
<version>4.0.0-SNAPSHOT</version> |
||||||
<relativePath>../pom.xml</relativePath> |
<relativePath>../pom.xml</relativePath> |
||||||
</parent> |
</parent> |
||||||
|
|
||||||
<properties> |
<properties> |
||||||
<project.root>${basedir}/..</project.root> |
<project.root>${basedir}/..</project.root> |
||||||
<dist.key>SDMONGO</dist.key> |
<dist.key>SDMONGO</dist.key> |
||||||
</properties> |
|
||||||
|
<!-- Observability --> |
||||||
<build> |
<micrometer-docs-generator.version>1.0.0-SNAPSHOT</micrometer-docs-generator.version> |
||||||
<plugins> |
<micrometer-docs-generator.inputPath>${maven.multiModuleProjectDirectory}/spring-data-mongodb/ |
||||||
<plugin> |
</micrometer-docs-generator.inputPath> |
||||||
<groupId>org.apache.maven.plugins</groupId> |
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern> |
||||||
<artifactId>maven-assembly-plugin</artifactId> |
<micrometer-docs-generator.outputPath>${maven.multiModuleProjectDirectory}/target/ |
||||||
</plugin> |
</micrometer-docs-generator.outputPath> |
||||||
<plugin> |
</properties> |
||||||
<groupId>org.asciidoctor</groupId> |
|
||||||
<artifactId>asciidoctor-maven-plugin</artifactId> |
<build> |
||||||
<configuration> |
<plugins> |
||||||
<attributes> |
<plugin> |
||||||
<mongo-reactivestreams>${mongo.reactivestreams}</mongo-reactivestreams> |
<groupId>org.apache.maven.plugins</groupId> |
||||||
<reactor>${reactor}</reactor> |
<artifactId>maven-assembly-plugin</artifactId> |
||||||
</attributes> |
</plugin> |
||||||
</configuration> |
<plugin> |
||||||
</plugin> |
<groupId>org.codehaus.mojo</groupId> |
||||||
</plugins> |
<artifactId>exec-maven-plugin</artifactId> |
||||||
|
<executions> |
||||||
</build> |
<execution> |
||||||
|
<id>generate-metrics-metadata</id> |
||||||
|
<phase>prepare-package</phase> |
||||||
|
<goals> |
||||||
|
<goal>java</goal> |
||||||
|
</goals> |
||||||
|
<configuration> |
||||||
|
<mainClass>io.micrometer.docs.metrics.DocsFromSources</mainClass> |
||||||
|
</configuration> |
||||||
|
</execution> |
||||||
|
<execution> |
||||||
|
<id>generate-tracing-metadata</id> |
||||||
|
<phase>prepare-package</phase> |
||||||
|
<goals> |
||||||
|
<goal>java</goal> |
||||||
|
</goals> |
||||||
|
<configuration> |
||||||
|
<mainClass>io.micrometer.docs.spans.DocsFromSources</mainClass> |
||||||
|
</configuration> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>io.micrometer</groupId> |
||||||
|
<artifactId>micrometer-docs-generator-spans</artifactId> |
||||||
|
<version>${micrometer-docs-generator.version}</version> |
||||||
|
<type>jar</type> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>io.micrometer</groupId> |
||||||
|
<artifactId>micrometer-docs-generator-metrics</artifactId> |
||||||
|
<version>${micrometer-docs-generator.version}</version> |
||||||
|
<type>jar</type> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
<configuration> |
||||||
|
<includePluginDependencies>true</includePluginDependencies> |
||||||
|
<arguments> |
||||||
|
<argument>${micrometer-docs-generator.inputPath}</argument> |
||||||
|
<argument>${micrometer-docs-generator.inclusionPattern}</argument> |
||||||
|
<argument>${micrometer-docs-generator.outputPath}</argument> |
||||||
|
</arguments> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
<plugin> |
||||||
|
<groupId>org.asciidoctor</groupId> |
||||||
|
<artifactId>asciidoctor-maven-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<attributes> |
||||||
|
<mongo-reactivestreams>${mongo.reactivestreams}</mongo-reactivestreams> |
||||||
|
<reactor>${reactor}</reactor> |
||||||
|
</attributes> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
|
||||||
|
</build> |
||||||
|
|
||||||
|
<repositories> |
||||||
|
<repository> |
||||||
|
<id>spring-snapshots</id> |
||||||
|
<name>Spring Snapshots</name> |
||||||
|
<url>https://repo.spring.io/snapshot</url> <!-- For Snapshots --> |
||||||
|
<snapshots> |
||||||
|
<enabled>true</enabled> |
||||||
|
</snapshots> |
||||||
|
<releases> |
||||||
|
<enabled>false</enabled> |
||||||
|
</releases> |
||||||
|
</repository> |
||||||
|
<repository> |
||||||
|
<id>spring-milestones</id> |
||||||
|
<name>Spring Milestones</name> |
||||||
|
<url>https://repo.spring.io/milestone</url> <!-- For Milestones --> |
||||||
|
<snapshots> |
||||||
|
<enabled>false</enabled> |
||||||
|
</snapshots> |
||||||
|
</repository> |
||||||
|
</repositories> |
||||||
|
|
||||||
</project> |
</project> |
||||||
|
|||||||
@ -1,361 +1,391 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
<modelVersion>4.0.0</modelVersion> |
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
<artifactId>spring-data-mongodb</artifactId> |
|
||||||
|
<artifactId>spring-data-mongodb</artifactId> |
||||||
<name>Spring Data MongoDB - Core</name> |
|
||||||
<description>MongoDB support for Spring Data</description> |
<name>Spring Data MongoDB - Core</name> |
||||||
|
<description>MongoDB support for Spring Data</description> |
||||||
<parent> |
|
||||||
<groupId>org.springframework.data</groupId> |
<parent> |
||||||
<artifactId>spring-data-mongodb-parent</artifactId> |
<groupId>org.springframework.data</groupId> |
||||||
<version>4.0.0-SNAPSHOT</version> |
<artifactId>spring-data-mongodb-parent</artifactId> |
||||||
<relativePath>../pom.xml</relativePath> |
<version>4.0.0-SNAPSHOT</version> |
||||||
</parent> |
<relativePath>../pom.xml</relativePath> |
||||||
|
</parent> |
||||||
<properties> |
|
||||||
<objenesis>1.3</objenesis> |
<properties> |
||||||
<equalsverifier>1.7.8</equalsverifier> |
<objenesis>1.3</objenesis> |
||||||
<java-module-name>spring.data.mongodb</java-module-name> |
<equalsverifier>1.7.8</equalsverifier> |
||||||
<project.root>${basedir}/..</project.root> |
<java-module-name>spring.data.mongodb</java-module-name> |
||||||
<multithreadedtc>1.01</multithreadedtc> |
<project.root>${basedir}/..</project.root> |
||||||
</properties> |
<multithreadedtc>1.01</multithreadedtc> |
||||||
|
</properties> |
||||||
<dependencies> |
|
||||||
|
<dependencies> |
||||||
<!-- Spring --> |
|
||||||
<dependency> |
<!-- Spring --> |
||||||
<groupId>org.springframework</groupId> |
<dependency> |
||||||
<artifactId>spring-tx</artifactId> |
<groupId>org.springframework</groupId> |
||||||
</dependency> |
<artifactId>spring-tx</artifactId> |
||||||
<dependency> |
</dependency> |
||||||
<groupId>org.springframework</groupId> |
<dependency> |
||||||
<artifactId>spring-context</artifactId> |
<groupId>org.springframework</groupId> |
||||||
</dependency> |
<artifactId>spring-context</artifactId> |
||||||
<dependency> |
</dependency> |
||||||
<groupId>org.springframework</groupId> |
<dependency> |
||||||
<artifactId>spring-beans</artifactId> |
<groupId>org.springframework</groupId> |
||||||
</dependency> |
<artifactId>spring-beans</artifactId> |
||||||
<dependency> |
</dependency> |
||||||
<groupId>org.springframework</groupId> |
<dependency> |
||||||
<artifactId>spring-core</artifactId> |
<groupId>org.springframework</groupId> |
||||||
<exclusions> |
<artifactId>spring-core</artifactId> |
||||||
<exclusion> |
<exclusions> |
||||||
<groupId>commons-logging</groupId> |
<exclusion> |
||||||
<artifactId>commons-logging</artifactId> |
<groupId>commons-logging</groupId> |
||||||
</exclusion> |
<artifactId>commons-logging</artifactId> |
||||||
</exclusions> |
</exclusion> |
||||||
</dependency> |
</exclusions> |
||||||
<dependency> |
</dependency> |
||||||
<groupId>org.springframework</groupId> |
<dependency> |
||||||
<artifactId>spring-expression</artifactId> |
<groupId>org.springframework</groupId> |
||||||
</dependency> |
<artifactId>spring-expression</artifactId> |
||||||
|
</dependency> |
||||||
<!-- Spring Data --> |
|
||||||
<dependency> |
<!-- Spring Data --> |
||||||
<groupId>${project.groupId}</groupId> |
<dependency> |
||||||
<artifactId>spring-data-commons</artifactId> |
<groupId>${project.groupId}</groupId> |
||||||
<version>${springdata.commons}</version> |
<artifactId>spring-data-commons</artifactId> |
||||||
</dependency> |
<version>${springdata.commons}</version> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>com.querydsl</groupId> |
<dependency> |
||||||
<artifactId>querydsl-mongodb</artifactId> |
<groupId>com.querydsl</groupId> |
||||||
<version>${querydsl}</version> |
<artifactId>querydsl-mongodb</artifactId> |
||||||
<optional>true</optional> |
<version>${querydsl}</version> |
||||||
<exclusions> |
<optional>true</optional> |
||||||
<exclusion> |
<exclusions> |
||||||
<groupId>org.mongodb</groupId> |
<exclusion> |
||||||
<artifactId>mongo-java-driver</artifactId> |
<groupId>org.mongodb</groupId> |
||||||
</exclusion> |
<artifactId>mongo-java-driver</artifactId> |
||||||
</exclusions> |
</exclusion> |
||||||
</dependency> |
</exclusions> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>com.querydsl</groupId> |
<dependency> |
||||||
<artifactId>querydsl-apt</artifactId> |
<groupId>com.querydsl</groupId> |
||||||
<version>${querydsl}</version> |
<artifactId>querydsl-apt</artifactId> |
||||||
<scope>provided</scope> |
<version>${querydsl}</version> |
||||||
</dependency> |
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>javax.annotation</groupId> |
<dependency> |
||||||
<artifactId>jsr250-api</artifactId> |
<groupId>javax.annotation</groupId> |
||||||
<version>1.0</version> |
<artifactId>jsr250-api</artifactId> |
||||||
<optional>true</optional> |
<version>1.0</version> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>com.google.code.findbugs</groupId> |
<dependency> |
||||||
<artifactId>jsr305</artifactId> |
<groupId>com.google.code.findbugs</groupId> |
||||||
<version>3.0.2</version> |
<artifactId>jsr305</artifactId> |
||||||
<optional>true</optional> |
<version>3.0.2</version> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<!-- reactive --> |
|
||||||
|
<!-- reactive --> |
||||||
<dependency> |
|
||||||
<groupId>org.mongodb</groupId> |
<dependency> |
||||||
<artifactId>mongodb-driver-sync</artifactId> |
<groupId>org.mongodb</groupId> |
||||||
<version>${mongo}</version> |
<artifactId>mongodb-driver-sync</artifactId> |
||||||
<optional>true</optional> |
<version>${mongo}</version> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>org.mongodb</groupId> |
<dependency> |
||||||
<artifactId>mongodb-driver-reactivestreams</artifactId> |
<groupId>org.mongodb</groupId> |
||||||
<version>${mongo.reactivestreams}</version> |
<artifactId>mongodb-driver-reactivestreams</artifactId> |
||||||
<optional>true</optional> |
<version>${mongo.reactivestreams}</version> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>io.projectreactor</groupId> |
<dependency> |
||||||
<artifactId>reactor-core</artifactId> |
<groupId>io.projectreactor</groupId> |
||||||
<optional>true</optional> |
<artifactId>reactor-core</artifactId> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>io.projectreactor</groupId> |
<dependency> |
||||||
<artifactId>reactor-test</artifactId> |
<groupId>io.projectreactor</groupId> |
||||||
<optional>true</optional> |
<artifactId>reactor-test</artifactId> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>io.reactivex.rxjava3</groupId> |
<dependency> |
||||||
<artifactId>rxjava</artifactId> |
<groupId>io.reactivex.rxjava3</groupId> |
||||||
<version>${rxjava3}</version> |
<artifactId>rxjava</artifactId> |
||||||
<optional>true</optional> |
<version>${rxjava3}</version> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<!-- CDI --> |
|
||||||
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 --> |
<!-- CDI --> |
||||||
|
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 --> |
||||||
<dependency> |
|
||||||
<groupId>javax.interceptor</groupId> |
<dependency> |
||||||
<artifactId>javax.interceptor-api</artifactId> |
<groupId>javax.interceptor</groupId> |
||||||
<version>1.2.1</version> |
<artifactId>javax.interceptor-api</artifactId> |
||||||
<scope>test</scope> |
<version>1.2.1</version> |
||||||
</dependency> |
<scope>test</scope> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>jakarta.enterprise</groupId> |
<dependency> |
||||||
<artifactId>jakarta.enterprise.cdi-api</artifactId> |
<groupId>jakarta.enterprise</groupId> |
||||||
<version>${cdi}</version> |
<artifactId>jakarta.enterprise.cdi-api</artifactId> |
||||||
<scope>provided</scope> |
<version>${cdi}</version> |
||||||
<optional>true</optional> |
<scope>provided</scope> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>jakarta.annotation</groupId> |
<dependency> |
||||||
<artifactId>jakarta.annotation-api</artifactId> |
<groupId>jakarta.annotation</groupId> |
||||||
<version>${jakarta-annotation-api}</version> |
<artifactId>jakarta.annotation-api</artifactId> |
||||||
<scope>test</scope> |
<version>${jakarta-annotation-api}</version> |
||||||
</dependency> |
<scope>test</scope> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>org.apache.openwebbeans</groupId> |
<dependency> |
||||||
<artifactId>openwebbeans-se</artifactId> |
<groupId>org.apache.openwebbeans</groupId> |
||||||
<classifier>jakarta</classifier> |
<artifactId>openwebbeans-se</artifactId> |
||||||
<version>${webbeans}</version> |
<classifier>jakarta</classifier> |
||||||
<scope>test</scope> |
<version>${webbeans}</version> |
||||||
</dependency> |
<scope>test</scope> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>org.apache.openwebbeans</groupId> |
<dependency> |
||||||
<artifactId>openwebbeans-spi</artifactId> |
<groupId>org.apache.openwebbeans</groupId> |
||||||
<classifier>jakarta</classifier> |
<artifactId>openwebbeans-spi</artifactId> |
||||||
<version>${webbeans}</version> |
<classifier>jakarta</classifier> |
||||||
<scope>test</scope> |
<version>${webbeans}</version> |
||||||
</dependency> |
<scope>test</scope> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>org.apache.openwebbeans</groupId> |
<dependency> |
||||||
<artifactId>openwebbeans-impl</artifactId> |
<groupId>org.apache.openwebbeans</groupId> |
||||||
<classifier>jakarta</classifier> |
<artifactId>openwebbeans-impl</artifactId> |
||||||
<version>${webbeans}</version> |
<classifier>jakarta</classifier> |
||||||
<scope>test</scope> |
<version>${webbeans}</version> |
||||||
</dependency> |
<scope>test</scope> |
||||||
|
</dependency> |
||||||
<!-- JSR 303 Validation --> |
|
||||||
<dependency> |
<!-- JSR 303 Validation --> |
||||||
<groupId>jakarta.validation</groupId> |
<dependency> |
||||||
<artifactId>jakarta.validation-api</artifactId> |
<groupId>jakarta.validation</groupId> |
||||||
<version>${validation}</version> |
<artifactId>jakarta.validation-api</artifactId> |
||||||
<optional>true</optional> |
<version>${validation}</version> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>org.objenesis</groupId> |
<dependency> |
||||||
<artifactId>objenesis</artifactId> |
<groupId>org.objenesis</groupId> |
||||||
<version>${objenesis}</version> |
<artifactId>objenesis</artifactId> |
||||||
<optional>true</optional> |
<version>${objenesis}</version> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>org.hibernate</groupId> |
<dependency> |
||||||
<artifactId>hibernate-validator</artifactId> |
<groupId>io.micrometer</groupId> |
||||||
<version>7.0.1.Final</version> |
<artifactId>micrometer-observation</artifactId> |
||||||
<scope>test</scope> |
<optional>true</optional> |
||||||
</dependency> |
</dependency> |
||||||
|
|
||||||
<dependency> |
<dependency> |
||||||
<groupId>jakarta.el</groupId> |
<groupId>io.micrometer</groupId> |
||||||
<artifactId>jakarta.el-api</artifactId> |
<artifactId>micrometer-tracing-api</artifactId> |
||||||
<version>4.0.0</version> |
<optional>true</optional> |
||||||
<scope>provided</scope> |
</dependency> |
||||||
<optional>true</optional> |
|
||||||
</dependency> |
<dependency> |
||||||
|
<groupId>org.hibernate</groupId> |
||||||
<dependency> |
<artifactId>hibernate-validator</artifactId> |
||||||
<groupId>org.glassfish</groupId> |
<version>7.0.1.Final</version> |
||||||
<artifactId>jakarta.el</artifactId> |
<scope>test</scope> |
||||||
<version>4.0.2</version> |
</dependency> |
||||||
<scope>provided</scope> |
|
||||||
<optional>true</optional> |
<dependency> |
||||||
</dependency> |
<groupId>jakarta.el</groupId> |
||||||
|
<artifactId>jakarta.el-api</artifactId> |
||||||
<dependency> |
<version>4.0.0</version> |
||||||
<groupId>com.fasterxml.jackson.core</groupId> |
<scope>provided</scope> |
||||||
<artifactId>jackson-databind</artifactId> |
<optional>true</optional> |
||||||
<optional>true</optional> |
</dependency> |
||||||
</dependency> |
|
||||||
|
<dependency> |
||||||
<dependency> |
<groupId>org.glassfish</groupId> |
||||||
<groupId>nl.jqno.equalsverifier</groupId> |
<artifactId>jakarta.el</artifactId> |
||||||
<artifactId>equalsverifier</artifactId> |
<version>4.0.2</version> |
||||||
<version>${equalsverifier}</version> |
<scope>provided</scope> |
||||||
<scope>test</scope> |
<optional>true</optional> |
||||||
</dependency> |
</dependency> |
||||||
|
|
||||||
<dependency> |
<dependency> |
||||||
<groupId>org.springframework</groupId> |
<groupId>com.fasterxml.jackson.core</groupId> |
||||||
<artifactId>spring-webmvc</artifactId> |
<artifactId>jackson-databind</artifactId> |
||||||
<scope>test</scope> |
<optional>true</optional> |
||||||
</dependency> |
</dependency> |
||||||
|
|
||||||
<dependency> |
<dependency> |
||||||
<groupId>de.schauderhaft.degraph</groupId> |
<groupId>nl.jqno.equalsverifier</groupId> |
||||||
<artifactId>degraph-check</artifactId> |
<artifactId>equalsverifier</artifactId> |
||||||
<version>0.1.4</version> |
<version>${equalsverifier}</version> |
||||||
<scope>test</scope> |
<scope>test</scope> |
||||||
</dependency> |
</dependency> |
||||||
|
|
||||||
<dependency> |
<dependency> |
||||||
<groupId>edu.umd.cs.mtc</groupId> |
<groupId>org.springframework</groupId> |
||||||
<artifactId>multithreadedtc</artifactId> |
<artifactId>spring-webmvc</artifactId> |
||||||
<version>${multithreadedtc}</version> |
<scope>test</scope> |
||||||
<scope>test</scope> |
</dependency> |
||||||
</dependency> |
|
||||||
|
<dependency> |
||||||
<dependency> |
<groupId>de.schauderhaft.degraph</groupId> |
||||||
<groupId>org.junit-pioneer</groupId> |
<artifactId>degraph-check</artifactId> |
||||||
<artifactId>junit-pioneer</artifactId> |
<version>0.1.4</version> |
||||||
<version>0.5.3</version> |
<scope>test</scope> |
||||||
<scope>test</scope> |
</dependency> |
||||||
</dependency> |
|
||||||
|
<dependency> |
||||||
<dependency> |
<groupId>edu.umd.cs.mtc</groupId> |
||||||
<groupId>jakarta.transaction</groupId> |
<artifactId>multithreadedtc</artifactId> |
||||||
<artifactId>jakarta.transaction-api</artifactId> |
<version>${multithreadedtc}</version> |
||||||
<version>2.0.0</version> |
<scope>test</scope> |
||||||
<scope>test</scope> |
</dependency> |
||||||
</dependency> |
|
||||||
|
<dependency> |
||||||
<!-- Kotlin extension --> |
<groupId>org.junit-pioneer</groupId> |
||||||
<dependency> |
<artifactId>junit-pioneer</artifactId> |
||||||
<groupId>org.jetbrains.kotlin</groupId> |
<version>0.5.3</version> |
||||||
<artifactId>kotlin-stdlib</artifactId> |
<scope>test</scope> |
||||||
<optional>true</optional> |
</dependency> |
||||||
</dependency> |
|
||||||
|
<dependency> |
||||||
<dependency> |
<groupId>jakarta.transaction</groupId> |
||||||
<groupId>org.jetbrains.kotlin</groupId> |
<artifactId>jakarta.transaction-api</artifactId> |
||||||
<artifactId>kotlin-reflect</artifactId> |
<version>2.0.0</version> |
||||||
<optional>true</optional> |
<scope>test</scope> |
||||||
</dependency> |
</dependency> |
||||||
|
|
||||||
<dependency> |
<!-- Kotlin extension --> |
||||||
<groupId>org.jetbrains.kotlinx</groupId> |
<dependency> |
||||||
<artifactId>kotlinx-coroutines-core</artifactId> |
<groupId>org.jetbrains.kotlin</groupId> |
||||||
<optional>true</optional> |
<artifactId>kotlin-stdlib</artifactId> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>org.jetbrains.kotlinx</groupId> |
<dependency> |
||||||
<artifactId>kotlinx-coroutines-reactor</artifactId> |
<groupId>org.jetbrains.kotlin</groupId> |
||||||
<optional>true</optional> |
<artifactId>kotlin-reflect</artifactId> |
||||||
</dependency> |
<optional>true</optional> |
||||||
|
</dependency> |
||||||
<dependency> |
|
||||||
<groupId>io.mockk</groupId> |
<dependency> |
||||||
<artifactId>mockk</artifactId> |
<groupId>org.jetbrains.kotlinx</groupId> |
||||||
<version>${mockk}</version> |
<artifactId>kotlinx-coroutines-core</artifactId> |
||||||
<scope>test</scope> |
<optional>true</optional> |
||||||
</dependency> |
</dependency> |
||||||
|
|
||||||
<!-- jMolecules --> |
<dependency> |
||||||
|
<groupId>org.jetbrains.kotlinx</groupId> |
||||||
<dependency> |
<artifactId>kotlinx-coroutines-reactor</artifactId> |
||||||
<groupId>org.jmolecules</groupId> |
<optional>true</optional> |
||||||
<artifactId>jmolecules-ddd</artifactId> |
</dependency> |
||||||
<version>${jmolecules}</version> |
|
||||||
<scope>test</scope> |
<dependency> |
||||||
</dependency> |
<groupId>io.mockk</groupId> |
||||||
|
<artifactId>mockk</artifactId> |
||||||
</dependencies> |
<version>${mockk}</version> |
||||||
|
<scope>test</scope> |
||||||
<build> |
</dependency> |
||||||
|
|
||||||
<plugins> |
<dependency> |
||||||
|
<groupId>io.micrometer</groupId> |
||||||
<plugin> |
<artifactId>micrometer-test</artifactId> |
||||||
<groupId>com.mysema.maven</groupId> |
<scope>test</scope> |
||||||
<artifactId>apt-maven-plugin</artifactId> |
</dependency> |
||||||
<version>${apt}</version> |
<dependency> |
||||||
<dependencies> |
<groupId>io.micrometer</groupId> |
||||||
<dependency> |
<artifactId>micrometer-tracing-test</artifactId> |
||||||
<groupId>com.querydsl</groupId> |
<scope>test</scope> |
||||||
<artifactId>querydsl-apt</artifactId> |
</dependency> |
||||||
<version>${querydsl}</version> |
|
||||||
</dependency> |
<dependency> |
||||||
</dependencies> |
<groupId>io.micrometer</groupId> |
||||||
<executions> |
<artifactId>micrometer-tracing-integration-test</artifactId> |
||||||
<execution> |
<scope>test</scope> |
||||||
<phase>generate-test-sources</phase> |
</dependency> |
||||||
<goals> |
|
||||||
<goal>test-process</goal> |
<!-- jMolecules --> |
||||||
</goals> |
|
||||||
<configuration> |
<dependency> |
||||||
<outputDirectory>target/generated-test-sources</outputDirectory> |
<groupId>org.jmolecules</groupId> |
||||||
<processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor> |
<artifactId>jmolecules-ddd</artifactId> |
||||||
</configuration> |
<version>${jmolecules}</version> |
||||||
</execution> |
<scope>test</scope> |
||||||
</executions> |
</dependency> |
||||||
</plugin> |
|
||||||
|
</dependencies> |
||||||
<plugin> |
|
||||||
<groupId>org.apache.maven.plugins</groupId> |
<build> |
||||||
<artifactId>maven-surefire-plugin</artifactId> |
|
||||||
<configuration> |
<plugins> |
||||||
<useSystemClassLoader>false</useSystemClassLoader> |
|
||||||
<useFile>false</useFile> |
<plugin> |
||||||
<includes> |
<groupId>com.mysema.maven</groupId> |
||||||
<include>**/*Tests.java</include> |
<artifactId>apt-maven-plugin</artifactId> |
||||||
</includes> |
<version>${apt}</version> |
||||||
<excludes> |
<dependencies> |
||||||
<exclude>**/PerformanceTests.java</exclude> |
<dependency> |
||||||
<exclude>**/ReactivePerformanceTests.java</exclude> |
<groupId>com.querydsl</groupId> |
||||||
</excludes> |
<artifactId>querydsl-apt</artifactId> |
||||||
<systemPropertyVariables> |
<version>${querydsl}</version> |
||||||
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file> |
</dependency> |
||||||
<reactor.trace.cancel>true</reactor.trace.cancel> |
</dependencies> |
||||||
</systemPropertyVariables> |
<executions> |
||||||
</configuration> |
<execution> |
||||||
</plugin> |
<phase>generate-test-sources</phase> |
||||||
|
<goals> |
||||||
</plugins> |
<goal>test-process</goal> |
||||||
|
</goals> |
||||||
</build> |
<configuration> |
||||||
|
<outputDirectory>target/generated-test-sources</outputDirectory> |
||||||
|
<processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor> |
||||||
|
</configuration> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-surefire-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<useSystemClassLoader>false</useSystemClassLoader> |
||||||
|
<useFile>false</useFile> |
||||||
|
<includes> |
||||||
|
<include>**/*Tests.java</include> |
||||||
|
</includes> |
||||||
|
<excludes> |
||||||
|
<exclude>**/PerformanceTests.java</exclude> |
||||||
|
<exclude>**/ReactivePerformanceTests.java</exclude> |
||||||
|
</excludes> |
||||||
|
<systemPropertyVariables> |
||||||
|
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file> |
||||||
|
<reactor.trace.cancel>true</reactor.trace.cancel> |
||||||
|
</systemPropertyVariables> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
</plugins> |
||||||
|
|
||||||
|
</build> |
||||||
</project> |
</project> |
||||||
|
|||||||
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import io.micrometer.common.Tag; |
||||||
|
import io.micrometer.common.Tags; |
||||||
|
|
||||||
|
import com.mongodb.connection.ConnectionDescription; |
||||||
|
import com.mongodb.connection.ConnectionId; |
||||||
|
import com.mongodb.event.CommandStartedEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* Default {@link MongoHandlerTagsProvider} implementation. |
||||||
|
* |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
public class DefaultMongoHandlerTagsProvider implements MongoHandlerTagsProvider { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Tags getLowCardinalityTags(MongoHandlerContext context) { |
||||||
|
|
||||||
|
Tags tags = Tags.empty(); |
||||||
|
|
||||||
|
if (context.getCollectionName() != null) { |
||||||
|
tags = tags.and(MongoObservation.LowCardinalityCommandTags.MONGODB_COLLECTION.of(context.getCollectionName())); |
||||||
|
} |
||||||
|
|
||||||
|
Tag connectionTag = connectionTag(context.getCommandStartedEvent()); |
||||||
|
if (connectionTag != null) { |
||||||
|
tags = tags.and(connectionTag); |
||||||
|
} |
||||||
|
|
||||||
|
return tags; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Tags getHighCardinalityTags(MongoHandlerContext context) { |
||||||
|
|
||||||
|
return Tags.of(MongoObservation.HighCardinalityCommandTags.MONGODB_COMMAND |
||||||
|
.of(context.getCommandStartedEvent().getCommandName())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Extract connection details for a MongoDB connection into a {@link Tag}. |
||||||
|
* |
||||||
|
* @param event |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static Tag connectionTag(CommandStartedEvent event) { |
||||||
|
|
||||||
|
ConnectionDescription connectionDescription = event.getConnectionDescription(); |
||||||
|
|
||||||
|
if (connectionDescription != null) { |
||||||
|
|
||||||
|
ConnectionId connectionId = connectionDescription.getConnectionId(); |
||||||
|
if (connectionId != null) { |
||||||
|
return MongoObservation.LowCardinalityCommandTags.MONGODB_CLUSTER_ID |
||||||
|
.of(connectionId.getServerId().getClusterId().getValue()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,134 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import io.micrometer.observation.Observation; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.LinkedHashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
import org.bson.BsonDocument; |
||||||
|
import org.bson.BsonValue; |
||||||
|
import org.springframework.lang.Nullable; |
||||||
|
|
||||||
|
import com.mongodb.RequestContext; |
||||||
|
import com.mongodb.event.CommandFailedEvent; |
||||||
|
import com.mongodb.event.CommandStartedEvent; |
||||||
|
import com.mongodb.event.CommandSucceededEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* A {@link Observation.Context} that contains MongoDB events. |
||||||
|
* |
||||||
|
* @author Marcin Grzejszczak |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
public class MongoHandlerContext extends Observation.Context { |
||||||
|
|
||||||
|
/** |
||||||
|
* @see https://docs.mongodb.com/manual/reference/command for the command reference
|
||||||
|
*/ |
||||||
|
private static final Set<String> COMMANDS_WITH_COLLECTION_NAME = new LinkedHashSet<>( |
||||||
|
Arrays.asList("aggregate", "count", "distinct", "mapReduce", "geoSearch", "delete", "find", "findAndModify", |
||||||
|
"insert", "update", "collMod", "compact", "convertToCapped", "create", "createIndexes", "drop", "dropIndexes", |
||||||
|
"killCursors", "listIndexes", "reIndex")); |
||||||
|
|
||||||
|
private final CommandStartedEvent commandStartedEvent; |
||||||
|
private final RequestContext requestContext; |
||||||
|
private final String collectionName; |
||||||
|
|
||||||
|
private CommandSucceededEvent commandSucceededEvent; |
||||||
|
private CommandFailedEvent commandFailedEvent; |
||||||
|
|
||||||
|
public MongoHandlerContext(CommandStartedEvent commandStartedEvent, RequestContext requestContext) { |
||||||
|
|
||||||
|
this.commandStartedEvent = commandStartedEvent; |
||||||
|
this.requestContext = requestContext; |
||||||
|
this.collectionName = getCollectionName(commandStartedEvent); |
||||||
|
} |
||||||
|
|
||||||
|
public CommandStartedEvent getCommandStartedEvent() { |
||||||
|
return this.commandStartedEvent; |
||||||
|
} |
||||||
|
|
||||||
|
public RequestContext getRequestContext() { |
||||||
|
return this.requestContext; |
||||||
|
} |
||||||
|
|
||||||
|
public String getCollectionName() { |
||||||
|
return this.collectionName; |
||||||
|
} |
||||||
|
|
||||||
|
public String getContextualName() { |
||||||
|
|
||||||
|
if (this.collectionName == null) { |
||||||
|
return this.commandStartedEvent.getCommandName(); |
||||||
|
} |
||||||
|
|
||||||
|
return this.commandStartedEvent.getCommandName() + " " + this.collectionName; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCommandSucceededEvent(CommandSucceededEvent commandSucceededEvent) { |
||||||
|
this.commandSucceededEvent = commandSucceededEvent; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCommandFailedEvent(CommandFailedEvent commandFailedEvent) { |
||||||
|
this.commandFailedEvent = commandFailedEvent; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Transform the command name into a collection name; |
||||||
|
* |
||||||
|
* @param event the {@link CommandStartedEvent} |
||||||
|
* @return the name of the collection based on the command |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
private static String getCollectionName(CommandStartedEvent event) { |
||||||
|
|
||||||
|
String commandName = event.getCommandName(); |
||||||
|
BsonDocument command = event.getCommand(); |
||||||
|
|
||||||
|
if (COMMANDS_WITH_COLLECTION_NAME.contains(commandName)) { |
||||||
|
|
||||||
|
String collectionName = getNonEmptyBsonString(command.get(commandName)); |
||||||
|
|
||||||
|
if (collectionName != null) { |
||||||
|
return collectionName; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Some other commands, like getMore, have a field like {"collection": collectionName}.
|
||||||
|
return getNonEmptyBsonString(command.get("collection")); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Utility method to convert {@link BsonValue} into a plain string. |
||||||
|
* |
||||||
|
* @return trimmed string from {@code bsonValue} or null if the trimmed string was empty or the value wasn't a string |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
private static String getNonEmptyBsonString(BsonValue bsonValue) { |
||||||
|
|
||||||
|
if (bsonValue == null || !bsonValue.isString()) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
String stringValue = bsonValue.asString().getValue().trim(); |
||||||
|
|
||||||
|
return stringValue.isEmpty() ? null : stringValue; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import io.micrometer.observation.Observation; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link Observation.TagsProvider} for {@link MongoHandlerContext}. |
||||||
|
* |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
public interface MongoHandlerTagsProvider extends Observation.TagsProvider<MongoHandlerContext> { |
||||||
|
|
||||||
|
@Override |
||||||
|
default boolean supportsContext(Observation.Context context) { |
||||||
|
return context instanceof MongoHandlerContext; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,97 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import io.micrometer.common.docs.TagKey; |
||||||
|
import io.micrometer.observation.docs.DocumentedObservation; |
||||||
|
|
||||||
|
/** |
||||||
|
* A MongoDB-based {@link io.micrometer.observation.Observation}. |
||||||
|
* |
||||||
|
* @author Marcin Grzejszczak |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 1.0.0 |
||||||
|
*/ |
||||||
|
enum MongoObservation implements DocumentedObservation { |
||||||
|
|
||||||
|
/** |
||||||
|
* Timer created around a MongoDB command execution. |
||||||
|
*/ |
||||||
|
MONGODB_COMMAND_OBSERVATION { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return "spring.data.mongodb.command"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TagKey[] getLowCardinalityTagKeys() { |
||||||
|
return LowCardinalityCommandTags.values(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TagKey[] getHighCardinalityTagKeys() { |
||||||
|
return HighCardinalityCommandTags.values(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getPrefix() { |
||||||
|
return "spring.data.mongodb"; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Enums related to low cardinality tags for MongoDB commands. |
||||||
|
*/ |
||||||
|
enum LowCardinalityCommandTags implements TagKey { |
||||||
|
|
||||||
|
/** |
||||||
|
* MongoDB collection name. |
||||||
|
*/ |
||||||
|
MONGODB_COLLECTION { |
||||||
|
@Override |
||||||
|
public String getKey() { |
||||||
|
return "spring.data.mongodb.collection"; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* MongoDB cluster identifier. |
||||||
|
*/ |
||||||
|
MONGODB_CLUSTER_ID { |
||||||
|
@Override |
||||||
|
public String getKey() { |
||||||
|
return "spring.data.mongodb.cluster_id"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Enums related to high cardinality tags for MongoDB commands. |
||||||
|
*/ |
||||||
|
enum HighCardinalityCommandTags implements TagKey { |
||||||
|
|
||||||
|
/** |
||||||
|
* MongoDB command value. |
||||||
|
*/ |
||||||
|
MONGODB_COMMAND { |
||||||
|
@Override |
||||||
|
public String getKey() { |
||||||
|
return "spring.data.mongodb.command"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,179 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import io.micrometer.observation.Observation; |
||||||
|
import io.micrometer.observation.ObservationRegistry; |
||||||
|
|
||||||
|
import org.apache.commons.logging.Log; |
||||||
|
import org.apache.commons.logging.LogFactory; |
||||||
|
|
||||||
|
import com.mongodb.RequestContext; |
||||||
|
import com.mongodb.event.CommandFailedEvent; |
||||||
|
import com.mongodb.event.CommandListener; |
||||||
|
import com.mongodb.event.CommandStartedEvent; |
||||||
|
import com.mongodb.event.CommandSucceededEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* Implement MongoDB's {@link CommandListener} using Micrometer's {@link Observation} API. |
||||||
|
* |
||||||
|
* @see https://github.com/openzipkin/brave/blob/release-5.13.0/instrumentation/mongodb/src/main/java/brave/mongodb/TraceMongoCommandListener.java
|
||||||
|
* @author OpenZipkin Brave Authors |
||||||
|
* @author Marcin Grzejszczak |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
public final class MongoObservationCommandListener |
||||||
|
implements CommandListener, Observation.TagsProviderAware<MongoHandlerTagsProvider> { |
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(MongoObservationCommandListener.class); |
||||||
|
|
||||||
|
private final ObservationRegistry observationRegistry; |
||||||
|
|
||||||
|
private MongoHandlerTagsProvider tagsProvider; |
||||||
|
|
||||||
|
public MongoObservationCommandListener(ObservationRegistry observationRegistry) { |
||||||
|
|
||||||
|
this.observationRegistry = observationRegistry; |
||||||
|
this.tagsProvider = new DefaultMongoHandlerTagsProvider(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void commandStarted(CommandStartedEvent event) { |
||||||
|
|
||||||
|
if (log.isDebugEnabled()) { |
||||||
|
log.debug("Instrumenting the command started event"); |
||||||
|
} |
||||||
|
|
||||||
|
String databaseName = event.getDatabaseName(); |
||||||
|
|
||||||
|
if ("admin".equals(databaseName)) { |
||||||
|
return; // don't instrument commands like "endSessions"
|
||||||
|
} |
||||||
|
|
||||||
|
RequestContext requestContext = event.getRequestContext(); |
||||||
|
|
||||||
|
if (requestContext == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Observation parent = observationFromContext(requestContext); |
||||||
|
|
||||||
|
if (log.isDebugEnabled()) { |
||||||
|
log.debug("Found the following observation passed from the mongo context [" + parent + "]"); |
||||||
|
} |
||||||
|
|
||||||
|
if (parent == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
setupObservability(event, requestContext); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void commandSucceeded(CommandSucceededEvent event) { |
||||||
|
|
||||||
|
if (event.getRequestContext() == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Observation observation = event.getRequestContext().getOrDefault(Observation.class, null); |
||||||
|
if (observation == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
MongoHandlerContext context = event.getRequestContext().get(MongoHandlerContext.class); |
||||||
|
context.setCommandSucceededEvent(event); |
||||||
|
|
||||||
|
if (log.isDebugEnabled()) { |
||||||
|
log.debug("Command succeeded - will stop observation [" + observation + "]"); |
||||||
|
} |
||||||
|
|
||||||
|
observation.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void commandFailed(CommandFailedEvent event) { |
||||||
|
|
||||||
|
if (event.getRequestContext() == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Observation observation = event.getRequestContext().getOrDefault(Observation.class, null); |
||||||
|
if (observation == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
MongoHandlerContext context = event.getRequestContext().get(MongoHandlerContext.class); |
||||||
|
context.setCommandFailedEvent(event); |
||||||
|
|
||||||
|
if (log.isDebugEnabled()) { |
||||||
|
log.debug("Command failed - will stop observation [" + observation + "]"); |
||||||
|
} |
||||||
|
|
||||||
|
observation.error(event.getThrowable()); |
||||||
|
observation.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Extract the {@link Observation} from MongoDB's {@link RequestContext}. |
||||||
|
* |
||||||
|
* @param context |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static Observation observationFromContext(RequestContext context) { |
||||||
|
|
||||||
|
Observation observation = context.getOrDefault(Observation.class, null); |
||||||
|
|
||||||
|
if (observation != null) { |
||||||
|
|
||||||
|
if (log.isDebugEnabled()) { |
||||||
|
log.debug("Found a observation in mongo context [" + observation + "]"); |
||||||
|
} |
||||||
|
return observation; |
||||||
|
} |
||||||
|
|
||||||
|
if (log.isDebugEnabled()) { |
||||||
|
log.debug("No observation was found - will not create any child spans"); |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private void setupObservability(CommandStartedEvent event, RequestContext requestContext) { |
||||||
|
|
||||||
|
MongoHandlerContext observationContext = new MongoHandlerContext(event, requestContext); |
||||||
|
|
||||||
|
Observation observation = MongoObservation.MONGODB_COMMAND_OBSERVATION |
||||||
|
.observation(this.observationRegistry, observationContext) //
|
||||||
|
.contextualName(observationContext.getContextualName()) //
|
||||||
|
.tagsProvider(this.tagsProvider) //
|
||||||
|
.start(); |
||||||
|
|
||||||
|
requestContext.put(Observation.class, observation); |
||||||
|
requestContext.put(MongoHandlerContext.class, observationContext); |
||||||
|
|
||||||
|
if (log.isDebugEnabled()) { |
||||||
|
log.debug( |
||||||
|
"Created a child observation [" + observation + "] for mongo instrumentation and put it in mongo context"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setTagsProvider(MongoHandlerTagsProvider mongoHandlerTagsProvider) { |
||||||
|
this.tagsProvider = mongoHandlerTagsProvider; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,117 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import io.micrometer.observation.Observation; |
||||||
|
import io.micrometer.tracing.Span; |
||||||
|
import io.micrometer.tracing.Tracer; |
||||||
|
import io.micrometer.tracing.handler.TracingObservationHandler; |
||||||
|
|
||||||
|
import java.net.InetSocketAddress; |
||||||
|
|
||||||
|
import org.apache.commons.logging.Log; |
||||||
|
import org.apache.commons.logging.LogFactory; |
||||||
|
|
||||||
|
import com.mongodb.MongoSocketException; |
||||||
|
import com.mongodb.connection.ConnectionDescription; |
||||||
|
import com.mongodb.event.CommandStartedEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* A {@link TracingObservationHandler} that handles {@link MongoHandlerContext}. It configures a span specific to Mongo |
||||||
|
* operations. |
||||||
|
* |
||||||
|
* @author Marcin Grzejszczak |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
public class MongoTracingObservationHandler implements TracingObservationHandler<MongoHandlerContext> { |
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(MongoTracingObservationHandler.class); |
||||||
|
|
||||||
|
private final Tracer tracer; |
||||||
|
|
||||||
|
private boolean setRemoteIpAndPortEnabled; |
||||||
|
|
||||||
|
public MongoTracingObservationHandler(Tracer tracer) { |
||||||
|
this.tracer = tracer; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Tracer getTracer() { |
||||||
|
return this.tracer; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStart(MongoHandlerContext context) { |
||||||
|
|
||||||
|
CommandStartedEvent event = context.getCommandStartedEvent(); |
||||||
|
|
||||||
|
Span.Builder builder = this.tracer.spanBuilder() //
|
||||||
|
.name(context.getContextualName()) //
|
||||||
|
.kind(Span.Kind.CLIENT) //
|
||||||
|
.remoteServiceName("mongodb-" + event.getDatabaseName()); |
||||||
|
|
||||||
|
if (this.setRemoteIpAndPortEnabled) { |
||||||
|
|
||||||
|
ConnectionDescription connectionDescription = event.getConnectionDescription(); |
||||||
|
|
||||||
|
if (connectionDescription != null) { |
||||||
|
|
||||||
|
try { |
||||||
|
|
||||||
|
InetSocketAddress socketAddress = connectionDescription.getServerAddress().getSocketAddress(); |
||||||
|
builder.remoteIpAndPort(socketAddress.getAddress().getHostAddress(), socketAddress.getPort()); |
||||||
|
} catch (MongoSocketException e) { |
||||||
|
if (log.isDebugEnabled()) { |
||||||
|
log.debug("Ignored exception when setting remote ip and port", e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
getTracingContext(context).setSpan(builder.start()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStop(MongoHandlerContext context) { |
||||||
|
|
||||||
|
Span span = getRequiredSpan(context); |
||||||
|
tagSpan(context, span); |
||||||
|
|
||||||
|
context.getRequestContext().delete(Observation.class); |
||||||
|
context.getRequestContext().delete(MongoHandlerContext.class); |
||||||
|
|
||||||
|
span.end(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean supportsContext(Observation.Context context) { |
||||||
|
return context instanceof MongoHandlerContext; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Should remote ip and port be set on the span. |
||||||
|
* |
||||||
|
* @return {@code true} when the remote ip and port should be set |
||||||
|
*/ |
||||||
|
public boolean isSetRemoteIpAndPortEnabled() { |
||||||
|
return this.setRemoteIpAndPortEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSetRemoteIpAndPortEnabled(boolean setRemoteIpAndPortEnabled) { |
||||||
|
this.setRemoteIpAndPortEnabled = setRemoteIpAndPortEnabled; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,170 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry; |
||||||
|
import io.micrometer.core.instrument.observation.TimerObservationHandler; |
||||||
|
import io.micrometer.core.instrument.simple.SimpleMeterRegistry; |
||||||
|
import io.micrometer.observation.Observation; |
||||||
|
import io.micrometer.observation.ObservationRegistry; |
||||||
|
import io.micrometer.tracing.Span; |
||||||
|
import io.micrometer.tracing.test.simple.SimpleTracer; |
||||||
|
import io.micrometer.tracing.test.simple.SpanAssert; |
||||||
|
import io.micrometer.tracing.test.simple.TracerAssert; |
||||||
|
|
||||||
|
import org.bson.BsonDocument; |
||||||
|
import org.bson.BsonString; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.springframework.data.mongodb.observability.MongoObservation.HighCardinalityCommandTags; |
||||||
|
import org.springframework.data.mongodb.observability.MongoObservation.LowCardinalityCommandTags; |
||||||
|
|
||||||
|
import com.mongodb.ServerAddress; |
||||||
|
import com.mongodb.connection.ClusterId; |
||||||
|
import com.mongodb.connection.ConnectionDescription; |
||||||
|
import com.mongodb.connection.ServerId; |
||||||
|
import com.mongodb.event.CommandFailedEvent; |
||||||
|
import com.mongodb.event.CommandStartedEvent; |
||||||
|
import com.mongodb.event.CommandSucceededEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* Series of test cases exercising {@link MongoObservationCommandListener} to ensure proper creation of {@link Span}s. |
||||||
|
* |
||||||
|
* @author Marcin Grzejszczak |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
class MongoObservationCommandListenerForTracingTests { |
||||||
|
|
||||||
|
SimpleTracer simpleTracer; |
||||||
|
|
||||||
|
MongoTracingObservationHandler handler; |
||||||
|
|
||||||
|
MeterRegistry meterRegistry; |
||||||
|
ObservationRegistry observationRegistry; |
||||||
|
|
||||||
|
MongoObservationCommandListener listener; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
void setup() { |
||||||
|
|
||||||
|
this.simpleTracer = new SimpleTracer(); |
||||||
|
this.handler = new MongoTracingObservationHandler(simpleTracer); |
||||||
|
|
||||||
|
this.meterRegistry = new SimpleMeterRegistry(); |
||||||
|
this.observationRegistry = ObservationRegistry.create(); |
||||||
|
this.observationRegistry.observationConfig().observationHandler(new TimerObservationHandler(meterRegistry)); |
||||||
|
this.observationRegistry.observationConfig().observationHandler(handler); |
||||||
|
|
||||||
|
this.listener = new MongoObservationCommandListener(observationRegistry); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void successfullyCompletedCommandShouldCreateSpanWhenParentSampleInRequestContext() { |
||||||
|
|
||||||
|
// given
|
||||||
|
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt(); |
||||||
|
|
||||||
|
// when
|
||||||
|
commandStartedAndSucceeded(testRequestContext); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThatMongoSpanIsClientWithTags().hasIpThatIsBlank().hasPortThatIsNotSet(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void successfullyCompletedCommandShouldCreateSpanWithAddressInfoWhenParentSampleInRequestContextAndHandlerAddressInfoEnabled() { |
||||||
|
|
||||||
|
// given
|
||||||
|
handler.setSetRemoteIpAndPortEnabled(true); |
||||||
|
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt(); |
||||||
|
|
||||||
|
// when
|
||||||
|
commandStartedAndSucceeded(testRequestContext); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThatMongoSpanIsClientWithTags().hasIpThatIsNotBlank().hasPortThatIsSet(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void commandWithErrorShouldCreateTimerWhenParentSampleInRequestContext() { |
||||||
|
|
||||||
|
// given
|
||||||
|
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt(); |
||||||
|
|
||||||
|
// when
|
||||||
|
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||||
|
new ConnectionDescription( //
|
||||||
|
new ServerId( //
|
||||||
|
new ClusterId("description"), //
|
||||||
|
new ServerAddress("localhost", 1234))), //
|
||||||
|
"database", "insert", //
|
||||||
|
new BsonDocument("collection", new BsonString("user")))); |
||||||
|
listener.commandFailed( //
|
||||||
|
new CommandFailedEvent(testRequestContext, 0, null, "insert", 0, new IllegalAccessException())); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThatMongoSpanIsClientWithTags().assertThatThrowable().isInstanceOf(IllegalAccessException.class); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a parent {@link Observation} then wrap it inside a {@link TestRequestContext}. |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
private TestRequestContext createTestRequestContextWithParentObservationAndStartIt() { |
||||||
|
|
||||||
|
Observation parent = Observation.start("name", observationRegistry); |
||||||
|
return TestRequestContext.withObservation(parent); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Execute MongoDB's {@link com.mongodb.event.CommandListener#commandStarted(CommandStartedEvent)} and |
||||||
|
* {@link com.mongodb.event.CommandListener#commandSucceeded(CommandSucceededEvent)} operations against the |
||||||
|
* {@link TestRequestContext} in order to inject some test data. |
||||||
|
* |
||||||
|
* @param testRequestContext |
||||||
|
*/ |
||||||
|
private void commandStartedAndSucceeded(TestRequestContext testRequestContext) { |
||||||
|
|
||||||
|
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||||
|
new ConnectionDescription( //
|
||||||
|
new ServerId( //
|
||||||
|
new ClusterId("description"), //
|
||||||
|
new ServerAddress("localhost", 1234))), //
|
||||||
|
"database", "insert", //
|
||||||
|
new BsonDocument("collection", new BsonString("user")))); |
||||||
|
|
||||||
|
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "insert", null, 0)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a base MongoDB-based {@link SpanAssert} using Micrometer Tracing's fluent API. Other test methods can apply |
||||||
|
* additional assertions. |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private SpanAssert assertThatMongoSpanIsClientWithTags() { |
||||||
|
|
||||||
|
return TracerAssert.assertThat(simpleTracer).onlySpan() //
|
||||||
|
.hasNameEqualTo("insert user") //
|
||||||
|
.hasKindEqualTo(Span.Kind.CLIENT) //
|
||||||
|
.hasRemoteServiceNameEqualTo("mongodb-database") //
|
||||||
|
.hasTag(HighCardinalityCommandTags.MONGODB_COMMAND.getKey(), "insert") //
|
||||||
|
.hasTag(LowCardinalityCommandTags.MONGODB_COLLECTION.getKey(), "user") //
|
||||||
|
.hasTagWithKey(LowCardinalityCommandTags.MONGODB_CLUSTER_ID.getKey()); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,186 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import static io.micrometer.core.tck.MeterRegistryAssert.*; |
||||||
|
|
||||||
|
import io.micrometer.common.Tags; |
||||||
|
import io.micrometer.core.instrument.MeterRegistry; |
||||||
|
import io.micrometer.core.instrument.observation.TimerObservationHandler; |
||||||
|
import io.micrometer.core.instrument.simple.SimpleMeterRegistry; |
||||||
|
import io.micrometer.observation.Observation; |
||||||
|
import io.micrometer.observation.ObservationRegistry; |
||||||
|
|
||||||
|
import org.bson.BsonDocument; |
||||||
|
import org.bson.BsonString; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.springframework.data.mongodb.observability.MongoObservation.HighCardinalityCommandTags; |
||||||
|
import org.springframework.data.mongodb.observability.MongoObservation.LowCardinalityCommandTags; |
||||||
|
|
||||||
|
import com.mongodb.ServerAddress; |
||||||
|
import com.mongodb.connection.ClusterId; |
||||||
|
import com.mongodb.connection.ConnectionDescription; |
||||||
|
import com.mongodb.connection.ServerId; |
||||||
|
import com.mongodb.event.CommandFailedEvent; |
||||||
|
import com.mongodb.event.CommandStartedEvent; |
||||||
|
import com.mongodb.event.CommandSucceededEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* Series of test cases exercising {@link MongoObservationCommandListener}. |
||||||
|
* |
||||||
|
* @author Marcin Grzejszczak |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
class MongoObservationCommandListenerTests { |
||||||
|
|
||||||
|
ObservationRegistry observationRegistry; |
||||||
|
MeterRegistry meterRegistry; |
||||||
|
|
||||||
|
MongoObservationCommandListener listener; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
void setup() { |
||||||
|
|
||||||
|
this.meterRegistry = new SimpleMeterRegistry(); |
||||||
|
this.observationRegistry = ObservationRegistry.create(); |
||||||
|
this.observationRegistry.observationConfig().observationHandler(new TimerObservationHandler(meterRegistry)); |
||||||
|
|
||||||
|
this.listener = new MongoObservationCommandListener(observationRegistry); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void commandStartedShouldNotInstrumentWhenAdminDatabase() { |
||||||
|
|
||||||
|
// when
|
||||||
|
listener.commandStarted(new CommandStartedEvent(null, 0, null, "admin", "", null)); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(meterRegistry).hasNoMetrics(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void commandStartedShouldNotInstrumentWhenNoRequestContext() { |
||||||
|
|
||||||
|
// when
|
||||||
|
listener.commandStarted(new CommandStartedEvent(null, 0, null, "some name", "", null)); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(meterRegistry).hasNoMetrics(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void commandStartedShouldNotInstrumentWhenNoParentSampleInRequestContext() { |
||||||
|
|
||||||
|
// when
|
||||||
|
listener.commandStarted(new CommandStartedEvent(new TestRequestContext(), 0, null, "some name", "", null)); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(meterRegistry).hasNoMetrics(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void successfullyCompletedCommandShouldCreateTimerWhenParentSampleInRequestContext() { |
||||||
|
|
||||||
|
// given
|
||||||
|
Observation parent = Observation.start("name", observationRegistry); |
||||||
|
TestRequestContext testRequestContext = TestRequestContext.withObservation(parent); |
||||||
|
|
||||||
|
// when
|
||||||
|
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||||
|
new ConnectionDescription( //
|
||||||
|
new ServerId( //
|
||||||
|
new ClusterId("description"), //
|
||||||
|
new ServerAddress("localhost", 1234))), |
||||||
|
"database", "insert", //
|
||||||
|
new BsonDocument("collection", new BsonString("user")))); |
||||||
|
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "insert", null, 0)); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThatTimerRegisteredWithTags(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void successfullyCompletedCommandWithCollectionHavingCommandNameShouldCreateTimerWhenParentSampleInRequestContext() { |
||||||
|
|
||||||
|
// given
|
||||||
|
Observation parent = Observation.start("name", observationRegistry); |
||||||
|
TestRequestContext testRequestContext = TestRequestContext.withObservation(parent); |
||||||
|
|
||||||
|
// when
|
||||||
|
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||||
|
new ConnectionDescription( //
|
||||||
|
new ServerId( //
|
||||||
|
new ClusterId("description"), //
|
||||||
|
new ServerAddress("localhost", 1234))), //
|
||||||
|
"database", "aggregate", //
|
||||||
|
new BsonDocument("aggregate", new BsonString("user")))); |
||||||
|
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "aggregate", null, 0)); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThatTimerRegisteredWithTags(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void successfullyCompletedCommandWithoutClusterInformationShouldCreateTimerWhenParentSampleInRequestContext() { |
||||||
|
|
||||||
|
// given
|
||||||
|
Observation parent = Observation.start("name", observationRegistry); |
||||||
|
TestRequestContext testRequestContext = TestRequestContext.withObservation(parent); |
||||||
|
|
||||||
|
// when
|
||||||
|
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, null, "database", "insert", |
||||||
|
new BsonDocument("collection", new BsonString("user")))); |
||||||
|
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "insert", null, 0)); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(meterRegistry).hasTimerWithNameAndTags(HighCardinalityCommandTags.MONGODB_COMMAND.getKey(), |
||||||
|
Tags.of(LowCardinalityCommandTags.MONGODB_COLLECTION.of("user"))); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void commandWithErrorShouldCreateTimerWhenParentSampleInRequestContext() { |
||||||
|
|
||||||
|
// given
|
||||||
|
Observation parent = Observation.start("name", observationRegistry); |
||||||
|
TestRequestContext testRequestContext = TestRequestContext.withObservation(parent); |
||||||
|
|
||||||
|
// when
|
||||||
|
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||||
|
new ConnectionDescription( //
|
||||||
|
new ServerId( //
|
||||||
|
new ClusterId("description"), //
|
||||||
|
new ServerAddress("localhost", 1234))), //
|
||||||
|
"database", "insert", //
|
||||||
|
new BsonDocument("collection", new BsonString("user")))); |
||||||
|
listener.commandFailed( //
|
||||||
|
new CommandFailedEvent(testRequestContext, 0, null, "insert", 0, new IllegalAccessException())); |
||||||
|
|
||||||
|
// then
|
||||||
|
assertThatTimerRegisteredWithTags(); |
||||||
|
} |
||||||
|
|
||||||
|
private void assertThatTimerRegisteredWithTags() { |
||||||
|
|
||||||
|
assertThat(meterRegistry) //
|
||||||
|
.hasTimerWithNameAndTags(HighCardinalityCommandTags.MONGODB_COMMAND.getKey(), |
||||||
|
Tags.of(LowCardinalityCommandTags.MONGODB_COLLECTION.getKey(), "user")) //
|
||||||
|
.hasTimerWithNameAndTagKeys(HighCardinalityCommandTags.MONGODB_COMMAND.getKey(), |
||||||
|
LowCardinalityCommandTags.MONGODB_CLUSTER_ID.getKey()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import io.micrometer.observation.Observation; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
import com.mongodb.RequestContext; |
||||||
|
|
||||||
|
/** |
||||||
|
* A {@link Map}-based {@link RequestContext}. (For test purposes only). |
||||||
|
* |
||||||
|
* @author Marcin Grzejszczak |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
class TestRequestContext implements RequestContext { |
||||||
|
|
||||||
|
private final Map<Object, Object> map = new HashMap<>(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public <T> T get(Object key) { |
||||||
|
return (T) map.get(key); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean hasKey(Object key) { |
||||||
|
return map.containsKey(key); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEmpty() { |
||||||
|
return map.isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void put(Object key, Object value) { |
||||||
|
map.put(key, value); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void delete(Object key) { |
||||||
|
map.remove(key); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int size() { |
||||||
|
return map.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Stream<Map.Entry<Object, Object>> stream() { |
||||||
|
return map.entrySet().stream(); |
||||||
|
} |
||||||
|
|
||||||
|
static TestRequestContext withObservation(Observation value) { |
||||||
|
|
||||||
|
TestRequestContext testRequestContext = new TestRequestContext(); |
||||||
|
testRequestContext.put(Observation.class, value); |
||||||
|
return testRequestContext; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,198 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2022 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.data.mongodb.observability; |
||||||
|
|
||||||
|
import static org.springframework.data.mongodb.test.util.Assertions.*; |
||||||
|
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry; |
||||||
|
import io.micrometer.core.instrument.observation.TimerObservationHandler; |
||||||
|
import io.micrometer.core.instrument.simple.SimpleMeterRegistry; |
||||||
|
import io.micrometer.observation.Observation; |
||||||
|
import io.micrometer.observation.ObservationHandler; |
||||||
|
import io.micrometer.observation.ObservationRegistry; |
||||||
|
import io.micrometer.tracing.test.SampleTestRunner; |
||||||
|
import io.micrometer.tracing.test.reporter.BuildingBlocks; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Deque; |
||||||
|
import java.util.List; |
||||||
|
import java.util.function.BiConsumer; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Disabled; |
||||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.beans.factory.config.PropertiesFactoryBean; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.core.io.ClassPathResource; |
||||||
|
import org.springframework.data.mongodb.MongoDatabaseFactory; |
||||||
|
import org.springframework.data.mongodb.core.MongoOperations; |
||||||
|
import org.springframework.data.mongodb.core.MongoTemplate; |
||||||
|
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; |
||||||
|
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; |
||||||
|
import org.springframework.data.mongodb.core.convert.MappingMongoConverter; |
||||||
|
import org.springframework.data.mongodb.core.convert.MongoConverter; |
||||||
|
import org.springframework.data.mongodb.core.mapping.MongoMappingContext; |
||||||
|
import org.springframework.data.mongodb.repository.Person; |
||||||
|
import org.springframework.data.mongodb.repository.PersonRepository; |
||||||
|
import org.springframework.data.mongodb.repository.SampleEvaluationContextExtension; |
||||||
|
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; |
||||||
|
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; |
||||||
|
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries; |
||||||
|
import org.springframework.test.context.ContextConfiguration; |
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension; |
||||||
|
|
||||||
|
import com.mongodb.ConnectionString; |
||||||
|
import com.mongodb.MongoClientSettings; |
||||||
|
import com.mongodb.RequestContext; |
||||||
|
import com.mongodb.WriteConcern; |
||||||
|
import com.mongodb.client.MongoClients; |
||||||
|
import com.mongodb.client.SynchronousContextProvider; |
||||||
|
|
||||||
|
/** |
||||||
|
* Collection of tests that log metrics and tracing with an external tracing tool. Since this external tool must be up |
||||||
|
* and running after the test is completed, this test is ONLY run manually. Needed: |
||||||
|
* {@code docker run -p 9411:9411 openzipkin/zipkin} and {@code docker run -p 27017:27017 mongo:latest} (either from |
||||||
|
* Docker Desktop or within separate shells). |
||||||
|
* |
||||||
|
* @author Greg Turnquist |
||||||
|
* @since 4.0.0 |
||||||
|
*/ |
||||||
|
@Disabled("Run this manually to visually test spans in Zipkin") |
||||||
|
@ExtendWith(SpringExtension.class) |
||||||
|
@ContextConfiguration |
||||||
|
public class ZipkinIntegrationTests extends SampleTestRunner { |
||||||
|
|
||||||
|
private static final MeterRegistry METER_REGISTRY = new SimpleMeterRegistry(); |
||||||
|
private static final ObservationRegistry OBSERVATION_REGISTRY = ObservationRegistry.create(); |
||||||
|
|
||||||
|
static { |
||||||
|
OBSERVATION_REGISTRY.observationConfig().observationHandler(new TimerObservationHandler(METER_REGISTRY)); |
||||||
|
} |
||||||
|
|
||||||
|
@Autowired PersonRepository repository; |
||||||
|
|
||||||
|
ZipkinIntegrationTests() { |
||||||
|
super(SampleRunnerConfig.builder().build(), OBSERVATION_REGISTRY, METER_REGISTRY); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public BiConsumer<BuildingBlocks, Deque<ObservationHandler>> customizeObservationHandlers() { |
||||||
|
|
||||||
|
return (buildingBlocks, observationHandlers) -> { |
||||||
|
observationHandlers.addLast(new MongoTracingObservationHandler(buildingBlocks.getTracer())); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TracingSetup[] getTracingSetup() { |
||||||
|
return new TracingSetup[] { TracingSetup.ZIPKIN_BRAVE }; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public SampleTestRunnerConsumer yourCode() { |
||||||
|
|
||||||
|
return (tracer, meterRegistry) -> { |
||||||
|
|
||||||
|
repository.deleteAll(); |
||||||
|
repository.save(new Person("Dave", "Matthews", 42)); |
||||||
|
List<Person> people = repository.findByLastname("Matthews"); |
||||||
|
|
||||||
|
assertThat(people).hasSize(1); |
||||||
|
assertThat(people.get(0)).extracting("firstname", "lastname").containsExactly("Dave", "Matthews"); |
||||||
|
|
||||||
|
repository.deleteAll(); |
||||||
|
|
||||||
|
System.out.println(((SimpleMeterRegistry) meterRegistry).getMetersAsString()); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
@Configuration |
||||||
|
@EnableMongoRepositories |
||||||
|
static class TestConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
MongoObservationCommandListener mongoObservationCommandListener(ObservationRegistry registry) { |
||||||
|
return new MongoObservationCommandListener(registry); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
MongoDatabaseFactory mongoDatabaseFactory(MongoObservationCommandListener commandListener, |
||||||
|
ObservationRegistry registry) { |
||||||
|
|
||||||
|
ConnectionString connectionString = new ConnectionString( |
||||||
|
String.format("mongodb://%s:%s/?w=majority&uuidrepresentation=javaLegacy", "127.0.0.1", 27017)); |
||||||
|
|
||||||
|
RequestContext requestContext = TestRequestContext.withObservation(Observation.start("name", registry)); |
||||||
|
SynchronousContextProvider contextProvider = () -> requestContext; |
||||||
|
|
||||||
|
MongoClientSettings settings = MongoClientSettings.builder() //
|
||||||
|
.addCommandListener(commandListener) //
|
||||||
|
.contextProvider(contextProvider) //
|
||||||
|
.applyConnectionString(connectionString) //
|
||||||
|
.build(); |
||||||
|
|
||||||
|
return new SimpleMongoClientDatabaseFactory(MongoClients.create(settings), "observable"); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
MappingMongoConverter mongoConverter(MongoDatabaseFactory factory) { |
||||||
|
|
||||||
|
MongoMappingContext mappingContext = new MongoMappingContext(); |
||||||
|
mappingContext.afterPropertiesSet(); |
||||||
|
|
||||||
|
return new MappingMongoConverter(new DefaultDbRefResolver(factory), mappingContext); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory, MongoConverter mongoConverter) { |
||||||
|
|
||||||
|
MongoTemplate template = new MongoTemplate(mongoDatabaseFactory, mongoConverter); |
||||||
|
template.setWriteConcern(WriteConcern.JOURNALED); |
||||||
|
return template; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
public PropertiesFactoryBean namedQueriesProperties() { |
||||||
|
|
||||||
|
PropertiesFactoryBean bean = new PropertiesFactoryBean(); |
||||||
|
bean.setLocation(new ClassPathResource("META-INF/mongo-named-queries.properties")); |
||||||
|
return bean; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
MongoRepositoryFactoryBean<PersonRepository, Person, String> repositoryFactoryBean(MongoOperations operations, |
||||||
|
PropertiesFactoryBean namedQueriesProperties) throws IOException { |
||||||
|
|
||||||
|
MongoRepositoryFactoryBean<PersonRepository, Person, String> factoryBean = new MongoRepositoryFactoryBean<>( |
||||||
|
PersonRepository.class); |
||||||
|
factoryBean.setMongoOperations(operations); |
||||||
|
factoryBean.setNamedQueries(new PropertiesBasedNamedQueries(namedQueriesProperties.getObject())); |
||||||
|
factoryBean.setCreateIndexesForQueryMethods(true); |
||||||
|
return factoryBean; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
SampleEvaluationContextExtension contextExtension() { |
||||||
|
return new SampleEvaluationContextExtension(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
ObservationRegistry registry() { |
||||||
|
return OBSERVATION_REGISTRY; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue