Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions agent-api/src/main/java/com/intergral/deep/agent/api/IDeep.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,52 @@

package com.intergral.deep.agent.api;

import com.intergral.deep.agent.api.plugin.IPlugin;
import com.intergral.deep.agent.api.plugin.IPlugin.IPluginRegistration;
import com.intergral.deep.agent.api.tracepoint.ITracepoint.ITracepointRegistration;
import java.util.Collection;
import java.util.Map;

/**
* This type describes the main API for Deep.
* <p>
* This API can only be used after the agent has been loaded.
*/
public interface IDeep {

/**
* Get the version of deep being used.
*
* @return the sematic version of deep as a string e.g. 1.2.3
*/
String getVersion();

/**
* This allows the registration of custom plugins.
*
* @param plugin the plugin that can be used to decorate snapshots
* @return a {@link IPluginRegistration} that can be used to unregister the plugin
*/
IPluginRegistration registerPlugin(final IPlugin plugin);

/**
* Create a tracepoint that will only exist on this instance.
*
* @param path the path to the file
* @param line the line number
* @return a {@link ITracepointRegistration} that can be used to remove the tracepoint
*/
ITracepointRegistration registerTracepoint(final String path, final int line);

/**
* Create a tracepoint that will only exist on this instance.
*
* @param path the path to the file
* @param line the line number
* @param args the key value pairs that further define the tracepoint
* @param watches the list of watch expressions
* @return a {@link ITracepointRegistration} that can be used to remove the tracepoint
*/
ITracepointRegistration registerTracepoint(final String path, final int line, final Map<String, String> args,
final Collection<String> watches);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.intergral.deep.agent.api.plugin;
package com.intergral.deep.agent.api;

public interface IEventContext {
/**
* This is a generic interface from the result of a registration.
*/
public interface IRegistration {

String evaluateExpression(String expression) throws Throwable;
/**
* Unregister the item registered.
*/
void unregister();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 Intergral GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.intergral.deep.agent.api.plugin;

/**
* This exception is thrown when a plugin tried to evaluate an expression that fails.
*/
public class EvaluationException extends Exception {

/**
* The expression that was evaluated.
*/
private final String expression;

/**
* Create a new exception
*
* @param expression the expression that failed
* @param cause the failure
*/
public EvaluationException(final String expression, final Throwable cause) {
super("Could not evaluate expression: " + expression, cause);
this.expression = expression;
}

/**
* Get the expression
*
* @return {@link #expression}
*/
public String getExpression() {
return expression;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,60 @@

package com.intergral.deep.agent.api.plugin;

import com.intergral.deep.agent.api.IRegistration;
import com.intergral.deep.agent.api.resource.Resource;
import com.intergral.deep.agent.api.settings.ISettings;

/**
* This type is used to define a Deep Plugin.
* <p>
* Plugins can decorate the snapshot when they are captured allowing for additional metadata to be attached. e.g. OTEL span data.
*/
public interface IPlugin {

Resource decorate(final ISettings settings, final IEventContext snapshot);
/**
* This method is called by Deep after a snapshot is created.
* <p>
* This method is executed inline with the tracepoint code.
*
* @param settings the current settings of Deep
* @param snapshot the {@link ISnapshotContext} describing the snapshot
* @return a new {@link Resource} to be added to the snapshot, or {@code null} to do nothing
*/
Resource decorate(final ISettings settings, final ISnapshotContext snapshot);

/**
* The name of the plugin. This should be unique.
*
* @return the plugin name, defaults to the full class name
*/
default String name() {
return this.getClass().getName();
}

/**
* Is this plugin active.
* <p>
* By default, this will check the Deep settings for the plugin name. e.g. the setting com.intergral.deep.plugin.JavaPlugin.active=false
* will disable the JavaPlugin. The normal settings rules apply, e.g. deep. or DEEP_ as a prefix when using system properties or environment variables.
*
* @param settings the current deep settings.
* @return {@code false} if setting is 'false', otherwise {@code true}
*/
default boolean isActive(final ISettings settings) {
final String simpleName = this.getClass().getSimpleName();
final String simpleName = this.name();
final Boolean settingAs = settings.getSettingAs(String.format("%s.active", simpleName),
Boolean.class);
if (settingAs == null) {
return true;
}
return settingAs;
}

/**
* This type describes a registered plugin.
*/
interface IPluginRegistration extends IRegistration {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 Intergral GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.intergral.deep.agent.api.plugin;

/**
* This is the context passed to plugins. This allows for the data of a context to be exposed to the plugin in a controlled manor.
*/
public interface ISnapshotContext {

/**
* Evaluate an expression in the frame of the tracepoint that triggered this snapshot
*
* @param expression the express to evaluate
* @return the result of the expression as a string
* @throws EvaluationException if there were any issues evaluating the expression
*/
String evaluateExpression(String expression) throws EvaluationException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Intergral GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.intergral.deep.agent.api.tracepoint;

import com.intergral.deep.agent.api.IRegistration;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;

public interface ITracepoint {

String path();

int line();

default Map<String, String> args() {
return Collections.emptyMap();
}

default Collection<String> watches() {
return Collections.emptyList();
}

default String id() {
return UUID.randomUUID().toString();
}

interface ITracepointRegistration extends IRegistration {

}
}
27 changes: 18 additions & 9 deletions agent/src/main/java/com/intergral/deep/agent/AgentImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package com.intergral.deep.agent;

import com.intergral.deep.agent.api.DeepVersion;
import com.intergral.deep.agent.api.IDeep;
import com.intergral.deep.agent.api.hook.IDeepHook;
import com.intergral.deep.agent.api.reflection.IReflection;
Expand All @@ -26,32 +25,42 @@
import com.intergral.deep.agent.tracepoint.inst.TracepointInstrumentationService;
import java.lang.instrument.Instrumentation;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

public class AgentImpl {

private static final CountDownLatch LATCH = new CountDownLatch(1);
private static DeepAgent deepAgent;

public static void startup(final Instrumentation inst, final Map<String, String> args) {
final Settings settings = Settings.build(args);
Logger.configureLogging(settings);

final TracepointInstrumentationService tracepointInstrumentationService =
TracepointInstrumentationService.init(inst, settings);

final DeepAgent deepAgent = new DeepAgent(settings, tracepointInstrumentationService);
final DeepAgent deep = new DeepAgent(settings, tracepointInstrumentationService);

deep.start();

deepAgent = deep;
LATCH.countDown();
}

deepAgent.start();
public static Object awaitLoadAPI() throws InterruptedException {
LATCH.await();
return loadDeepAPI();
}

public static Object loadDeepAPI() {
if (deepAgent == null) {
throw new IllegalStateException("Must start DEEP first");
}
return new IDeepHook() {

@Override
public IDeep deepService() {
return new IDeep() {
@Override
public String getVersion() {
return DeepVersion.VERSION;
}
};
return deepAgent;
}

@Override
Expand Down
32 changes: 31 additions & 1 deletion agent/src/main/java/com/intergral/deep/agent/DeepAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@

package com.intergral.deep.agent;

import com.intergral.deep.agent.api.DeepVersion;
import com.intergral.deep.agent.api.IDeep;
import com.intergral.deep.agent.api.plugin.IPlugin;
import com.intergral.deep.agent.api.plugin.IPlugin.IPluginRegistration;
import com.intergral.deep.agent.api.resource.Resource;
import com.intergral.deep.agent.api.tracepoint.ITracepoint.ITracepointRegistration;
import com.intergral.deep.agent.grpc.GrpcService;
import com.intergral.deep.agent.plugins.PluginLoader;
import com.intergral.deep.agent.poll.LongPollService;
Expand All @@ -28,9 +32,13 @@
import com.intergral.deep.agent.tracepoint.TracepointConfigService;
import com.intergral.deep.agent.tracepoint.handler.Callback;
import com.intergral.deep.agent.tracepoint.inst.TracepointInstrumentationService;
import com.intergral.deep.agent.types.TracePointConfig;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class DeepAgent {
public class DeepAgent implements IDeep {

private final Settings settings;
private final GrpcService grpcService;
Expand Down Expand Up @@ -58,4 +66,26 @@ public void start() {
this.grpcService.start();
this.pollService.start(tracepointConfig);
}

@Override
public String getVersion() {
return DeepVersion.VERSION;
}

public IPluginRegistration registerPlugin(final IPlugin plugin) {
this.settings.addPlugin(plugin);
return () -> this.settings.removePlugin(plugin);
}

@Override
public ITracepointRegistration registerTracepoint(final String path, final int line) {
return registerTracepoint(path, line, Collections.emptyMap(), Collections.emptyList());
}

@Override
public ITracepointRegistration registerTracepoint(final String path, final int line, final Map<String, String> args,
final Collection<String> watches) {
final TracePointConfig tracePointConfig = this.tracepointConfig.addCustom(path, line, args, watches);
return () -> this.tracepointConfig.removeCustom(tracePointConfig);
}
}
Loading