15 changed files with 1790 additions and 401 deletions
@ -1,46 +1,124 @@
@@ -1,46 +1,124 @@
|
||||
<?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"> |
||||
|
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>spring-data-mongodb-distribution</artifactId> |
||||
|
||||
<packaging>pom</packaging> |
||||
|
||||
<name>Spring Data MongoDB - Distribution</name> |
||||
<description>Distribution build for Spring Data MongoDB</description> |
||||
|
||||
<parent> |
||||
<groupId>org.springframework.data</groupId> |
||||
<artifactId>spring-data-mongodb-parent</artifactId> |
||||
<version>4.0.0-SNAPSHOT</version> |
||||
<relativePath>../pom.xml</relativePath> |
||||
</parent> |
||||
|
||||
<properties> |
||||
<project.root>${basedir}/..</project.root> |
||||
<dist.key>SDMONGO</dist.key> |
||||
</properties> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-assembly-plugin</artifactId> |
||||
</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> |
||||
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> |
||||
|
||||
<artifactId>spring-data-mongodb-distribution</artifactId> |
||||
|
||||
<packaging>pom</packaging> |
||||
|
||||
<name>Spring Data MongoDB - Distribution</name> |
||||
<description>Distribution build for Spring Data MongoDB</description> |
||||
|
||||
<parent> |
||||
<groupId>org.springframework.data</groupId> |
||||
<artifactId>spring-data-mongodb-parent</artifactId> |
||||
<version>4.0.0-SNAPSHOT</version> |
||||
<relativePath>../pom.xml</relativePath> |
||||
</parent> |
||||
|
||||
<properties> |
||||
<project.root>${basedir}/..</project.root> |
||||
<dist.key>SDMONGO</dist.key> |
||||
|
||||
<!-- Observability --> |
||||
<micrometer-docs-generator.version>1.0.0-SNAPSHOT</micrometer-docs-generator.version> |
||||
<micrometer-docs-generator.inputPath>${maven.multiModuleProjectDirectory}/spring-data-mongodb/ |
||||
</micrometer-docs-generator.inputPath> |
||||
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern> |
||||
<micrometer-docs-generator.outputPath>${maven.multiModuleProjectDirectory}/target/ |
||||
</micrometer-docs-generator.outputPath> |
||||
</properties> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-assembly-plugin</artifactId> |
||||
</plugin> |
||||
<plugin> |
||||
<groupId>org.codehaus.mojo</groupId> |
||||
<artifactId>exec-maven-plugin</artifactId> |
||||
<executions> |
||||
<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> |
||||
|
||||
@ -1,361 +1,391 @@
@@ -1,361 +1,391 @@
|
||||
<?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"> |
||||
|
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>spring-data-mongodb</artifactId> |
||||
|
||||
<name>Spring Data MongoDB - Core</name> |
||||
<description>MongoDB support for Spring Data</description> |
||||
|
||||
<parent> |
||||
<groupId>org.springframework.data</groupId> |
||||
<artifactId>spring-data-mongodb-parent</artifactId> |
||||
<version>4.0.0-SNAPSHOT</version> |
||||
<relativePath>../pom.xml</relativePath> |
||||
</parent> |
||||
|
||||
<properties> |
||||
<objenesis>1.3</objenesis> |
||||
<equalsverifier>1.7.8</equalsverifier> |
||||
<java-module-name>spring.data.mongodb</java-module-name> |
||||
<project.root>${basedir}/..</project.root> |
||||
<multithreadedtc>1.01</multithreadedtc> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
|
||||
<!-- Spring --> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-tx</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-context</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-beans</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-core</artifactId> |
||||
<exclusions> |
||||
<exclusion> |
||||
<groupId>commons-logging</groupId> |
||||
<artifactId>commons-logging</artifactId> |
||||
</exclusion> |
||||
</exclusions> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-expression</artifactId> |
||||
</dependency> |
||||
|
||||
<!-- Spring Data --> |
||||
<dependency> |
||||
<groupId>${project.groupId}</groupId> |
||||
<artifactId>spring-data-commons</artifactId> |
||||
<version>${springdata.commons}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.querydsl</groupId> |
||||
<artifactId>querydsl-mongodb</artifactId> |
||||
<version>${querydsl}</version> |
||||
<optional>true</optional> |
||||
<exclusions> |
||||
<exclusion> |
||||
<groupId>org.mongodb</groupId> |
||||
<artifactId>mongo-java-driver</artifactId> |
||||
</exclusion> |
||||
</exclusions> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.querydsl</groupId> |
||||
<artifactId>querydsl-apt</artifactId> |
||||
<version>${querydsl}</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>javax.annotation</groupId> |
||||
<artifactId>jsr250-api</artifactId> |
||||
<version>1.0</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.google.code.findbugs</groupId> |
||||
<artifactId>jsr305</artifactId> |
||||
<version>3.0.2</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<!-- reactive --> |
||||
|
||||
<dependency> |
||||
<groupId>org.mongodb</groupId> |
||||
<artifactId>mongodb-driver-sync</artifactId> |
||||
<version>${mongo}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.mongodb</groupId> |
||||
<artifactId>mongodb-driver-reactivestreams</artifactId> |
||||
<version>${mongo.reactivestreams}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.projectreactor</groupId> |
||||
<artifactId>reactor-core</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.projectreactor</groupId> |
||||
<artifactId>reactor-test</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.reactivex.rxjava3</groupId> |
||||
<artifactId>rxjava</artifactId> |
||||
<version>${rxjava3}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<!-- CDI --> |
||||
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 --> |
||||
|
||||
<dependency> |
||||
<groupId>javax.interceptor</groupId> |
||||
<artifactId>javax.interceptor-api</artifactId> |
||||
<version>1.2.1</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>jakarta.enterprise</groupId> |
||||
<artifactId>jakarta.enterprise.cdi-api</artifactId> |
||||
<version>${cdi}</version> |
||||
<scope>provided</scope> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>jakarta.annotation</groupId> |
||||
<artifactId>jakarta.annotation-api</artifactId> |
||||
<version>${jakarta-annotation-api}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.openwebbeans</groupId> |
||||
<artifactId>openwebbeans-se</artifactId> |
||||
<classifier>jakarta</classifier> |
||||
<version>${webbeans}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.openwebbeans</groupId> |
||||
<artifactId>openwebbeans-spi</artifactId> |
||||
<classifier>jakarta</classifier> |
||||
<version>${webbeans}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.openwebbeans</groupId> |
||||
<artifactId>openwebbeans-impl</artifactId> |
||||
<classifier>jakarta</classifier> |
||||
<version>${webbeans}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<!-- JSR 303 Validation --> |
||||
<dependency> |
||||
<groupId>jakarta.validation</groupId> |
||||
<artifactId>jakarta.validation-api</artifactId> |
||||
<version>${validation}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.objenesis</groupId> |
||||
<artifactId>objenesis</artifactId> |
||||
<version>${objenesis}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.hibernate</groupId> |
||||
<artifactId>hibernate-validator</artifactId> |
||||
<version>7.0.1.Final</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>jakarta.el</groupId> |
||||
<artifactId>jakarta.el-api</artifactId> |
||||
<version>4.0.0</version> |
||||
<scope>provided</scope> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.glassfish</groupId> |
||||
<artifactId>jakarta.el</artifactId> |
||||
<version>4.0.2</version> |
||||
<scope>provided</scope> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.fasterxml.jackson.core</groupId> |
||||
<artifactId>jackson-databind</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>nl.jqno.equalsverifier</groupId> |
||||
<artifactId>equalsverifier</artifactId> |
||||
<version>${equalsverifier}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-webmvc</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>de.schauderhaft.degraph</groupId> |
||||
<artifactId>degraph-check</artifactId> |
||||
<version>0.1.4</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>edu.umd.cs.mtc</groupId> |
||||
<artifactId>multithreadedtc</artifactId> |
||||
<version>${multithreadedtc}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.junit-pioneer</groupId> |
||||
<artifactId>junit-pioneer</artifactId> |
||||
<version>0.5.3</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>jakarta.transaction</groupId> |
||||
<artifactId>jakarta.transaction-api</artifactId> |
||||
<version>2.0.0</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<!-- Kotlin extension --> |
||||
<dependency> |
||||
<groupId>org.jetbrains.kotlin</groupId> |
||||
<artifactId>kotlin-stdlib</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.jetbrains.kotlin</groupId> |
||||
<artifactId>kotlin-reflect</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.jetbrains.kotlinx</groupId> |
||||
<artifactId>kotlinx-coroutines-core</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.jetbrains.kotlinx</groupId> |
||||
<artifactId>kotlinx-coroutines-reactor</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.mockk</groupId> |
||||
<artifactId>mockk</artifactId> |
||||
<version>${mockk}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<!-- jMolecules --> |
||||
|
||||
<dependency> |
||||
<groupId>org.jmolecules</groupId> |
||||
<artifactId>jmolecules-ddd</artifactId> |
||||
<version>${jmolecules}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
</dependencies> |
||||
|
||||
<build> |
||||
|
||||
<plugins> |
||||
|
||||
<plugin> |
||||
<groupId>com.mysema.maven</groupId> |
||||
<artifactId>apt-maven-plugin</artifactId> |
||||
<version>${apt}</version> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>com.querydsl</groupId> |
||||
<artifactId>querydsl-apt</artifactId> |
||||
<version>${querydsl}</version> |
||||
</dependency> |
||||
</dependencies> |
||||
<executions> |
||||
<execution> |
||||
<phase>generate-test-sources</phase> |
||||
<goals> |
||||
<goal>test-process</goal> |
||||
</goals> |
||||
<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 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> |
||||
|
||||
<artifactId>spring-data-mongodb</artifactId> |
||||
|
||||
<name>Spring Data MongoDB - Core</name> |
||||
<description>MongoDB support for Spring Data</description> |
||||
|
||||
<parent> |
||||
<groupId>org.springframework.data</groupId> |
||||
<artifactId>spring-data-mongodb-parent</artifactId> |
||||
<version>4.0.0-SNAPSHOT</version> |
||||
<relativePath>../pom.xml</relativePath> |
||||
</parent> |
||||
|
||||
<properties> |
||||
<objenesis>1.3</objenesis> |
||||
<equalsverifier>1.7.8</equalsverifier> |
||||
<java-module-name>spring.data.mongodb</java-module-name> |
||||
<project.root>${basedir}/..</project.root> |
||||
<multithreadedtc>1.01</multithreadedtc> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
|
||||
<!-- Spring --> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-tx</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-context</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-beans</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-core</artifactId> |
||||
<exclusions> |
||||
<exclusion> |
||||
<groupId>commons-logging</groupId> |
||||
<artifactId>commons-logging</artifactId> |
||||
</exclusion> |
||||
</exclusions> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-expression</artifactId> |
||||
</dependency> |
||||
|
||||
<!-- Spring Data --> |
||||
<dependency> |
||||
<groupId>${project.groupId}</groupId> |
||||
<artifactId>spring-data-commons</artifactId> |
||||
<version>${springdata.commons}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.querydsl</groupId> |
||||
<artifactId>querydsl-mongodb</artifactId> |
||||
<version>${querydsl}</version> |
||||
<optional>true</optional> |
||||
<exclusions> |
||||
<exclusion> |
||||
<groupId>org.mongodb</groupId> |
||||
<artifactId>mongo-java-driver</artifactId> |
||||
</exclusion> |
||||
</exclusions> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.querydsl</groupId> |
||||
<artifactId>querydsl-apt</artifactId> |
||||
<version>${querydsl}</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>javax.annotation</groupId> |
||||
<artifactId>jsr250-api</artifactId> |
||||
<version>1.0</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.google.code.findbugs</groupId> |
||||
<artifactId>jsr305</artifactId> |
||||
<version>3.0.2</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<!-- reactive --> |
||||
|
||||
<dependency> |
||||
<groupId>org.mongodb</groupId> |
||||
<artifactId>mongodb-driver-sync</artifactId> |
||||
<version>${mongo}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.mongodb</groupId> |
||||
<artifactId>mongodb-driver-reactivestreams</artifactId> |
||||
<version>${mongo.reactivestreams}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.projectreactor</groupId> |
||||
<artifactId>reactor-core</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.projectreactor</groupId> |
||||
<artifactId>reactor-test</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.reactivex.rxjava3</groupId> |
||||
<artifactId>rxjava</artifactId> |
||||
<version>${rxjava3}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<!-- CDI --> |
||||
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 --> |
||||
|
||||
<dependency> |
||||
<groupId>javax.interceptor</groupId> |
||||
<artifactId>javax.interceptor-api</artifactId> |
||||
<version>1.2.1</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>jakarta.enterprise</groupId> |
||||
<artifactId>jakarta.enterprise.cdi-api</artifactId> |
||||
<version>${cdi}</version> |
||||
<scope>provided</scope> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>jakarta.annotation</groupId> |
||||
<artifactId>jakarta.annotation-api</artifactId> |
||||
<version>${jakarta-annotation-api}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.openwebbeans</groupId> |
||||
<artifactId>openwebbeans-se</artifactId> |
||||
<classifier>jakarta</classifier> |
||||
<version>${webbeans}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.openwebbeans</groupId> |
||||
<artifactId>openwebbeans-spi</artifactId> |
||||
<classifier>jakarta</classifier> |
||||
<version>${webbeans}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.openwebbeans</groupId> |
||||
<artifactId>openwebbeans-impl</artifactId> |
||||
<classifier>jakarta</classifier> |
||||
<version>${webbeans}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<!-- JSR 303 Validation --> |
||||
<dependency> |
||||
<groupId>jakarta.validation</groupId> |
||||
<artifactId>jakarta.validation-api</artifactId> |
||||
<version>${validation}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.objenesis</groupId> |
||||
<artifactId>objenesis</artifactId> |
||||
<version>${objenesis}</version> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.micrometer</groupId> |
||||
<artifactId>micrometer-observation</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.micrometer</groupId> |
||||
<artifactId>micrometer-tracing-api</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.hibernate</groupId> |
||||
<artifactId>hibernate-validator</artifactId> |
||||
<version>7.0.1.Final</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>jakarta.el</groupId> |
||||
<artifactId>jakarta.el-api</artifactId> |
||||
<version>4.0.0</version> |
||||
<scope>provided</scope> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.glassfish</groupId> |
||||
<artifactId>jakarta.el</artifactId> |
||||
<version>4.0.2</version> |
||||
<scope>provided</scope> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.fasterxml.jackson.core</groupId> |
||||
<artifactId>jackson-databind</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>nl.jqno.equalsverifier</groupId> |
||||
<artifactId>equalsverifier</artifactId> |
||||
<version>${equalsverifier}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-webmvc</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>de.schauderhaft.degraph</groupId> |
||||
<artifactId>degraph-check</artifactId> |
||||
<version>0.1.4</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>edu.umd.cs.mtc</groupId> |
||||
<artifactId>multithreadedtc</artifactId> |
||||
<version>${multithreadedtc}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.junit-pioneer</groupId> |
||||
<artifactId>junit-pioneer</artifactId> |
||||
<version>0.5.3</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>jakarta.transaction</groupId> |
||||
<artifactId>jakarta.transaction-api</artifactId> |
||||
<version>2.0.0</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<!-- Kotlin extension --> |
||||
<dependency> |
||||
<groupId>org.jetbrains.kotlin</groupId> |
||||
<artifactId>kotlin-stdlib</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.jetbrains.kotlin</groupId> |
||||
<artifactId>kotlin-reflect</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.jetbrains.kotlinx</groupId> |
||||
<artifactId>kotlinx-coroutines-core</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.jetbrains.kotlinx</groupId> |
||||
<artifactId>kotlinx-coroutines-reactor</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.mockk</groupId> |
||||
<artifactId>mockk</artifactId> |
||||
<version>${mockk}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.micrometer</groupId> |
||||
<artifactId>micrometer-test</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>io.micrometer</groupId> |
||||
<artifactId>micrometer-tracing-test</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.micrometer</groupId> |
||||
<artifactId>micrometer-tracing-integration-test</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<!-- jMolecules --> |
||||
|
||||
<dependency> |
||||
<groupId>org.jmolecules</groupId> |
||||
<artifactId>jmolecules-ddd</artifactId> |
||||
<version>${jmolecules}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
</dependencies> |
||||
|
||||
<build> |
||||
|
||||
<plugins> |
||||
|
||||
<plugin> |
||||
<groupId>com.mysema.maven</groupId> |
||||
<artifactId>apt-maven-plugin</artifactId> |
||||
<version>${apt}</version> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>com.querydsl</groupId> |
||||
<artifactId>querydsl-apt</artifactId> |
||||
<version>${querydsl}</version> |
||||
</dependency> |
||||
</dependencies> |
||||
<executions> |
||||
<execution> |
||||
<phase>generate-test-sources</phase> |
||||
<goals> |
||||
<goal>test-process</goal> |
||||
</goals> |
||||
<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> |
||||
|
||||
@ -0,0 +1,78 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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