Skip to content
Draft
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
28 changes: 27 additions & 1 deletion src/main/java/io/bitdive/parent/init/MonitoringStarting.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package io.bitdive.parent.init;


/**
* MonitoringStarting class is responsible for initializing the monitoring system
* by setting up various ByteBuddy agents for instrumenting different components
* such as HTTP requests, database operations, messaging, and more.
*/
import io.bitdive.parent.safety_config.VaultGettingConfig;
import io.bitdive.parent.trasirovka.agent.byte_buddy_agent.*;
import io.bitdive.parent.trasirovka.agent.byte_buddy_agent.db.*;
Expand All @@ -11,36 +15,58 @@
import java.lang.instrument.Instrumentation;

public class MonitoringStarting {
/**
* Initializes the monitoring system by installing ByteBuddy agents
* for various components to enable tracing and profiling.
*/
public static void init() {
// Initialize Vault configuration connection
VaultGettingConfig.initVaultConnect();

// Install ByteBuddy agent for instrumentation
Instrumentation instrumentation = ByteBuddyAgent.install();

// Initialize basic agent for general monitoring
ByteBuddyAgentBasic.init(instrumentation);

// Initialize thread-related agents
ByteBuddyAgentThread.init(instrumentation);
ByteBuddyAgentThreadCreator.init(instrumentation);

// Initialize HTTP client and response agents
ByteBuddySimpleClientHttpResponse.init(instrumentation);
ByteBuddyAgentRestTemplateRequestWeb.init(instrumentation);

// Initialize Tomcat-specific agents
ByteBuddyAgentCoyoteInputStream.init(instrumentation); // Captures raw body bytes
ByteBuddyAgentResponseWeb.init(instrumentation);

// Initialize SQL and database agents
ByteBuddyAgentSql.init(instrumentation);
ByteBuddyAgentCatalinaResponse.init(instrumentation);
ByteBuddyAgentFeignRequestWeb.init(instrumentation);
ByteBuddyAgentSqlDriver.init(instrumentation);

// Initialize messaging agents (Kafka)
ByteBuddyAgentKafkaSend.init(instrumentation);
ByteBuddyAgentKafkaInterceptor.init(instrumentation);
KafkaConsumerAgent.init(instrumentation);

// Initialize NoSQL database agents
ByteBuddyAgentCassandra.init(instrumentation);
ByteBuddyAgentMongoDelegate.init(instrumentation);
ByteBuddyAgentRedis.init(instrumentation);
ByteBuddyAgentNeo4j.init(instrumentation);

// Initialize search engine agents (OpenSearch)
ByteBuddyAgentOpenSearch.init(instrumentation);
ByteBuddyCachedOpenSearchResponse.init(instrumentation);
ByteBuddyCachedOpenSearchReqest.init(instrumentation);

// Initialize SOAP web service agent
ByteBuddyAgentSoap.init(instrumentation);

// Initialize Spring WebSocket agent
ByteBuddyAgentSpringRawWs.init(instrumentation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,22 @@
import java.lang.reflect.Method;
import java.util.concurrent.Callable;

/**
* ByteBuddy agent for caching OpenSearch request bodies and content types.
* This agent intercepts the OpenSearch client's Request class to cache the entity body
* and Content-Type header for repeated access, improving performance by avoiding
* multiple reads of the input stream.
*/
public final class ByteBuddyCachedOpenSearchReqest {

/**
* Initializes the ByteBuddy agent for OpenSearch request caching.
* Transforms the org.opensearch.client.Request class to add caching fields
* and intercept getEntity() and getHeader() methods.
*
* @param inst the instrumentation instance
* @return the resettable class file transformer
*/
public static ResettableClassFileTransformer init(Instrumentation inst) {
return new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
Expand All @@ -35,14 +49,28 @@ public static ResettableClassFileTransformer init(Instrumentation inst) {
}

/*--------------------------------------------------------------*/
/**
* Interceptor for the getHeader method to return cached Content-Type if available.
*/
public static class GetHeaderInterceptor {

/**
* Intercepts calls to getHeader method.
* If requesting Content-Type and it's cached, returns the cached value.
* Otherwise, proceeds with the original method.
*
* @param zuper the original method call
* @param resp the request object
* @param headerName the header name being requested
* @return the header value
* @throws Exception if an error occurs
*/
@RuntimeType
public static Object intercept(@SuperCall Callable<Object> zuper,
@This Object resp,
@Argument(0) String headerName) throws Exception {

// Если запрашивают Content-Type и у нас есть кешированное значение, возвращаем его
// If requesting Content-Type and we have a cached value, return it
if ("Content-Type".equalsIgnoreCase(headerName)) {
try {
String cachedContentType = (String) resp.getClass()
Expand All @@ -52,22 +80,37 @@ public static Object intercept(@SuperCall Callable<Object> zuper,
return cachedContentType;
}
} catch (Exception e) {
// Игнорируем и просто продолжаем к оригинальному методу
// Ignore and proceed to original method
}
}

// Обычное поведение для других заголовков
// Default behavior for other headers
return zuper.call();
}
}

/*--------------------------------------------------------------*/
/**
* Interceptor for the getEntity method to cache and return the entity body.
* On first call, reads the input stream, caches the bytes and content type,
* and returns a new ByteArrayEntity. Subsequent calls return the cached data.
*/
public static class GetEntityInterceptor {

/**
* Intercepts calls to getEntity method.
* Returns cached entity if available, otherwise reads and caches the original entity.
*
* @param zuper the original method call
* @param resp the request object
* @return the entity object
* @throws Exception if an error occurs
*/
@RuntimeType
public static Object intercept(@SuperCall Callable<Object> zuper,
@This Object resp) throws Exception {

// Check if we have cached data
byte[] cached = (byte[]) resp.getClass()
.getDeclaredField("cachedBody")
.get(resp);
Expand All @@ -78,21 +121,21 @@ public static Object intercept(@SuperCall Callable<Object> zuper,
return newByteArrayEntity(cached, contentType);
}

/* --- первый вызов: читаем оригинальный поток --- */
/* --- First call: read the original stream --- */
Object origEntity = zuper.call(); // HttpEntity
if (origEntity == null) return null;

// Сохраняем Content-Type заголовок
// Cache the Content-Type header
String contentType = null;
try {
// Получаем Content-Type из оригинального Entity
// Get Content-Type from the original Entity
Method ctMethod = origEntity.getClass().getMethod("getContentType");
Object headerObj = ctMethod.invoke(origEntity);
if (headerObj != null) {
Method valMethod = headerObj.getClass().getMethod("getValue");
contentType = (String) valMethod.invoke(headerObj);

// Сохраняем Content-Type в поле
// Store Content-Type in the field
resp.getClass().getDeclaredField("cachedContentType")
.set(resp, contentType);
}
Expand All @@ -109,14 +152,14 @@ public static Object intercept(@SuperCall Callable<Object> zuper,
if (in != null) {
byte[] bytes = readAll(in);

// кладём в поле
// Store in the field
resp.getClass().getDeclaredField("cachedBody")
.set(resp, bytes);

return newByteArrayEntity(bytes, contentType);
}
} catch (Exception e) {
// Если чтение потока не удалось, логируем и возвращаем оригинальный entity
// If reading the stream failed, log and return original entity
if (LoggerStatusContent.isErrorsOrDebug()) {
System.err.println("Error reading entity content: " + e.getMessage());
}
Expand All @@ -129,12 +172,19 @@ public static Object intercept(@SuperCall Callable<Object> zuper,
return origEntity;
}

// Если дошли до сюда, значит getContent вернул null
// If we reach here, getContent returned null
return origEntity;
}

/* ---- helpers ------------------------------------ */
/* ---- Helper methods ------------------------------------ */

/**
* Reads all bytes from an InputStream.
*
* @param in the input stream
* @return the byte array
* @throws Exception if an error occurs
*/
public static byte[] readAll(InputStream in) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[8192];
Expand All @@ -143,7 +193,15 @@ public static byte[] readAll(InputStream in) throws Exception {
return out.toByteArray();
}

// Создаем ByteArrayEntity с Content-Type напрямую из строки
/**
* Creates a new ByteArrayEntity with the given bytes and content type.
* Handles both Apache HttpClient 4 and 5 versions.
*
* @param bytes the byte array
* @param contentTypeStr the content type string
* @return the ByteArrayEntity instance
* @throws Exception if creation fails
*/
public static Object newByteArrayEntity(byte[] bytes, String contentTypeStr) throws Exception {
Object ct = null;
if (contentTypeStr != null) {
Expand All @@ -155,19 +213,20 @@ public static Object newByteArrayEntity(byte[] bytes, String contentTypeStr) thr
Class<?> ct5 = Class.forName("org.apache.hc.core5.http.ContentType");
ct = ct5.getMethod("parse", String.class).invoke(null, contentTypeStr);
} catch (Exception e) {
// Если не удалось получить ContentType, попробуем создать без него
// If unable to get ContentType, try to create without it
}
}
}

try { // HC4
try { // HttpClient 4
Class<?> bae4 = Class.forName("org.apache.http.entity.ByteArrayEntity");
if (ct != null) {
Constructor<?> c = bae4.getConstructor(byte[].class, ct.getClass());
return c.newInstance(bytes, ct);
}
return bae4.getConstructor(byte[].class).newInstance(bytes);
} catch (ClassNotFoundException ignore) {
// HttpClient 5
Class<?> bae5 = Class.forName("org.apache.hc.core5.http.io.entity.ByteArrayEntity");
if (ct != null) {
Constructor<?> c = bae5.getConstructor(byte[].class, ct.getClass());
Expand Down
Loading