Skip to content

Commit

Permalink
Publish spring events for query and proxy-method executions
Browse files Browse the repository at this point in the history
Add a new listener that publishes spring events.

Closes #37
  • Loading branch information
ttddyy committed May 17, 2024
1 parent 95ffc67 commit a8eceb4
Show file tree
Hide file tree
Showing 11 changed files with 422 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import net.ttddyy.dsproxy.transform.ParameterTransformer;
import net.ttddyy.dsproxy.transform.QueryTransformer;
import net.ttddyy.observation.boot.autoconfigure.JdbcProperties.TraceType;
import net.ttddyy.observation.boot.event.JdbcEventPublishingListener;
import net.ttddyy.observation.tracing.ConnectionObservationConvention;
import net.ttddyy.observation.tracing.ConnectionTracingObservationHandler;
import net.ttddyy.observation.tracing.DataSourceObservationListener;
Expand Down Expand Up @@ -64,6 +65,7 @@
import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
Expand All @@ -86,7 +88,12 @@
@ConditionalOnProperty(prefix = "jdbc.datasource-proxy", name = "enabled", havingValue = "true", matchIfMissing = true)
public class DataSourceObservationAutoConfiguration {

public static final int OBSERVATION_LISTENER_ORDER = 1000;

public static final int EVENT_PUBLISHING_LISTENER_ORDER = OBSERVATION_LISTENER_ORDER + 100;

@Bean
@Order(OBSERVATION_LISTENER_ORDER)
public DataSourceObservationListener dataSourceObservationListener(ObjectProvider<ObservationRegistry> registry,
JdbcProperties jdbcProperties,
ObjectProvider<ConnectionObservationConvention> connectionObservationConventions,
Expand Down Expand Up @@ -135,6 +142,13 @@ public static DataSourceObservationBeanPostProcessor dataSourceObservationBeanPo
proxyDataSourceBuilderCustomizers);
}

@Bean
@Order(EVENT_PUBLISHING_LISTENER_ORDER)
@ConditionalOnProperty(prefix = "jdbc.event", name = "enabled", havingValue = "true")
JdbcEventPublishingListener jdbcEventPublishingListener(ApplicationEventPublisher publisher) {
return new JdbcEventPublishingListener(publisher);
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "com.zaxxer.hikari.HikariDataSource")
static class Hikari {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public class JdbcProperties {

private DataSourceProxy datasourceProxy = new DataSourceProxy();

/**
* Query and method execution events.
*/
private Event event = new Event();

public Set<TraceType> getIncludes() {
return this.includes;
}
Expand All @@ -69,6 +74,14 @@ public void setDatasourceProxy(DataSourceProxy datasourceProxy) {
this.datasourceProxy = datasourceProxy;
}

public Event getEvent() {
return this.event;
}

public void setEvent(Event event) {
this.event = event;
}

public static class DataSourceProxy {

/**
Expand Down Expand Up @@ -323,4 +336,21 @@ public enum TraceType {

}

public static class Event {

/**
* Enable publishing query/method execution events.
*/
private boolean enabled;

public boolean isEnabled() {
return this.enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2024 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 net.ttddyy.observation.boot.event;

import org.springframework.context.ApplicationEvent;

/**
* Base class for JDBC event.
*
* @author Tadaya Tsuyukubo
* @since 1.1
*/
public abstract class JdbcEvent extends ApplicationEvent {

public JdbcEvent(Object source) {
super(source);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2024 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 net.ttddyy.observation.boot.event;

import java.util.List;

import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo;
import net.ttddyy.dsproxy.listener.MethodExecutionContext;
import net.ttddyy.dsproxy.listener.MethodExecutionListener;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;

import org.springframework.context.ApplicationEventPublisher;

/**
* A datasource-proxy listener that publishes spring events for jdbc interactions.
*
* @author Tadaya Tsuyukubo
* @since 1.1
*/
public class JdbcEventPublishingListener implements QueryExecutionListener, MethodExecutionListener {

private final ApplicationEventPublisher publisher;

public JdbcEventPublishingListener(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}

@Override
public void beforeMethod(MethodExecutionContext executionContext) {
this.publisher.publishEvent(new JdbcMethodExecutionEvent(true, executionContext));
}

@Override
public void afterMethod(MethodExecutionContext executionContext) {
this.publisher.publishEvent(new JdbcMethodExecutionEvent(false, executionContext));
}

@Override
public void beforeQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
this.publisher.publishEvent(new JdbcQueryExecutionEvent(true, execInfo, queryInfoList));
}

@Override
public void afterQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
this.publisher.publishEvent(new JdbcQueryExecutionEvent(false, execInfo, queryInfoList));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2024 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 net.ttddyy.observation.boot.event;

import net.ttddyy.dsproxy.listener.MethodExecutionContext;

/**
* An event published when JDBC proxy methods are invoked.
* <p>
* The event is published right before and after executing the proxy methods.
*
* @author Tadaya Tsuyukubo
* @since 1.1
*/
public class JdbcMethodExecutionEvent extends JdbcEvent {

private final boolean before;

private final MethodExecutionContext executionContext;

public JdbcMethodExecutionEvent(boolean before, MethodExecutionContext executionContext) {
super(executionContext.getProxy());
this.before = before;
this.executionContext = executionContext;
}

/**
* {@code true} when the event is published right before performing the method.
* @return {@code true} if before execution
*/
public boolean isBefore() {
return this.before;
}

/**
* {@code true} when the event is published right after performing the method.
* @return {@code true} if after execution
*/
public boolean isAfter() {
return !this.before;
}

public MethodExecutionContext getExecutionContext() {
return this.executionContext;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2024 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 net.ttddyy.observation.boot.event;

import java.util.List;

import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo;

/**
* An event published when queries are executed.
* <p>
* The event is published right before and after executing the queries.
*
* @author Tadaya Tsuyukubo
* @since 1.1
*/
public class JdbcQueryExecutionEvent extends JdbcEvent {

private final boolean before;

private final ExecutionInfo execInfo;

private final List<QueryInfo> queryInfoList;

public JdbcQueryExecutionEvent(boolean isBefore, ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
super(execInfo.getStatement());
this.before = isBefore;
this.execInfo = execInfo;
this.queryInfoList = queryInfoList;
}

/**
* {@code true} when the event is published right before executing queries.
* @return {@code true} if before execution
*/
public boolean isBefore() {
return this.before;
}

/**
* {@code true} when the event is published right after executing queries.
* @return {@code true} if after execution
*/
public boolean isAfter() {
return !this.before;
}

public ExecutionInfo getExecInfo() {
return this.execInfo;
}

public List<QueryInfo> getQueryInfoList() {
return this.queryInfoList;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022-2023 the original author or authors.
* Copyright 2022-2024 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.
Expand Down Expand Up @@ -36,6 +36,7 @@
import net.ttddyy.dsproxy.proxy.ProxyConfig;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import net.ttddyy.observation.boot.event.JdbcEventPublishingListener;
import net.ttddyy.observation.tracing.ConnectionObservationConvention;
import net.ttddyy.observation.tracing.ConnectionTracingObservationHandler;
import net.ttddyy.observation.tracing.DataSourceBaseObservationHandler;
Expand Down Expand Up @@ -239,6 +240,20 @@ void includeNotSpecified() {
});
}

@Test
void event() {
ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceObservationAutoConfiguration.class))
.withBean(ObservationRegistry.class, ObservationRegistry::create)
.withBean(Tracer.class, () -> mock(Tracer.class));
runner.run((context) -> {
assertThat(context).doesNotHaveBean(JdbcEventPublishingListener.class);
});
runner.withPropertyValues("jdbc.event.enabled=true").run((context) -> {
assertThat(context).hasSingleBean(JdbcEventPublishingListener.class);
});
}

@ParameterizedTest
@MethodSource
void includeTypes(String property, Set<Class<? extends DataSourceBaseObservationHandler>> handlers,
Expand Down
Loading

0 comments on commit a8eceb4

Please sign in to comment.