diff --git a/src/main/java/io/bitdive/parent/init/MonitoringStarting.java b/src/main/java/io/bitdive/parent/init/MonitoringStarting.java index 1ff3e23..b710f37 100644 --- a/src/main/java/io/bitdive/parent/init/MonitoringStarting.java +++ b/src/main/java/io/bitdive/parent/init/MonitoringStarting.java @@ -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.*; @@ -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); } } diff --git a/src/main/java/io/bitdive/parent/trasirovka/agent/byte_buddy_agent/db/cached/ByteBuddyCachedOpenSearchReqest.java b/src/main/java/io/bitdive/parent/trasirovka/agent/byte_buddy_agent/db/cached/ByteBuddyCachedOpenSearchReqest.java index 4897f17..4cd15e6 100644 --- a/src/main/java/io/bitdive/parent/trasirovka/agent/byte_buddy_agent/db/cached/ByteBuddyCachedOpenSearchReqest.java +++ b/src/main/java/io/bitdive/parent/trasirovka/agent/byte_buddy_agent/db/cached/ByteBuddyCachedOpenSearchReqest.java @@ -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) @@ -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 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() @@ -52,22 +80,37 @@ public static Object intercept(@SuperCall Callable 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 zuper, @This Object resp) throws Exception { + // Check if we have cached data byte[] cached = (byte[]) resp.getClass() .getDeclaredField("cachedBody") .get(resp); @@ -78,21 +121,21 @@ public static Object intercept(@SuperCall Callable 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); } @@ -109,14 +152,14 @@ public static Object intercept(@SuperCall Callable 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()); } @@ -129,12 +172,19 @@ public static Object intercept(@SuperCall Callable 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]; @@ -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) { @@ -155,12 +213,12 @@ 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()); @@ -168,6 +226,7 @@ public static Object newByteArrayEntity(byte[] bytes, String contentTypeStr) thr } 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()); diff --git a/src/main/java/io/bitdive/parent/trasirovka/agent/utils/ReflectionUtils.java b/src/main/java/io/bitdive/parent/trasirovka/agent/utils/ReflectionUtils.java index 181b748..cd2d2e8 100644 --- a/src/main/java/io/bitdive/parent/trasirovka/agent/utils/ReflectionUtils.java +++ b/src/main/java/io/bitdive/parent/trasirovka/agent/utils/ReflectionUtils.java @@ -20,48 +20,87 @@ import java.util.Set; import java.util.stream.Stream; +/** + * Utility class for reflection operations and object serialization. + * Класс утилит для операций рефлексии и сериализации объектов. + */ public class ReflectionUtils { + /** + * Set of sensitive keywords to mask in serialization. + * Набор чувствительных ключевых слов для маскировки при сериализации. + */ public static final Set SENSITIVE_KEYWORDS = new HashSet<>(Arrays.asList( "password", "pass", "secret", "token", "key", "apikey", "auth", "credential" )); + /** + * Maximum size for collections during serialization. + * Максимальный размер коллекций при сериализации. + */ private static final int MAX_COLLECTION_SIZE = YamlParserConfig .getProfilingConfig().getMonitoring().getSerialization().getMaxElementCollection(); + /** + * Packages to exclude from serialization. + * Пакеты, исключаемые из сериализации. + */ private static final String[] EXCLUDED_PACKAGES = YamlParserConfig .getProfilingConfig().getMonitoring().getSerialization().getExcludedPackages(); + + /** + * Indicator for truncated collections. + * Индикатор для усеченных коллекций. + */ private static final String INDICATOR = "..."; + /** + * ObjectMapper configured for safe serialization with masking and size limits. + * ObjectMapper, настроенный для безопасной сериализации с маскировкой и ограничениями размера. + */ static ObjectMapper mapper = new ObjectMapper(); static { - - + // Configure ObjectMapper for safe serialization + // Настройка ObjectMapper для безопасной сериализации mapper.enable(SerializationFeature.WRITE_SELF_REFERENCES_AS_NULL); mapper.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + // Register module to limit collection sizes + // Регистрация модуля для ограничения размеров коллекций SimpleModule module = new SimpleModule(); module.setSerializerModifier(new CollectionSizeLimiter(MAX_COLLECTION_SIZE, INDICATOR)); mapper.registerModule(module); + // Register module to mask sensitive data + // Регистрация модуля для маскировки чувствительных данных SimpleModule moduleMask = new SimpleModule(); moduleMask.setSerializerModifier(new MaskingFilter(SENSITIVE_KEYWORDS)); mapper.registerModule(moduleMask); + // Register module to ignore certain packages + // Регистрация модуля для игнорирования определенных пакетов SimpleModule ignoreModule = new SimpleModule(); ignoreModule.setSerializerModifier(new PackageBasedSerializerModifier(EXCLUDED_PACKAGES)); mapper.registerModule(ignoreModule); + // Register additional modules for data handling + // Регистрация дополнительных модулей для обработки данных mapper.registerModule(new FlowDataToPlaceholderModule()); + // Configure deserialization and serialization features + // Настройка функций десериализации и сериализации mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + // Register time and JDK modules + // Регистрация модулей времени и JDK mapper.registerModule(new JavaTimeModule()); mapper.registerModule(new Jdk8Module()); mapper.registerModule(new AfterburnerModule()); + // Optionally register Hibernate module if available + // Опционально регистрировать модуль Hibernate, если доступен Optional.ofNullable(HibernateModuleLoader.registerHibernateModule()) .ifPresent(moduleHibernate -> mapper.registerModule(moduleHibernate) @@ -70,6 +109,13 @@ public class ReflectionUtils { } + /** + * Converts an object to its string representation using configured ObjectMapper. + * Преобразует объект в строковое представление с использованием настроенного ObjectMapper. + * + * @param obj the object to convert / объект для преобразования + * @return the string representation or error message / строковое представление или сообщение об ошибке + */ public static String objectToString(Object obj) { try { if (obj == null) { @@ -86,43 +132,47 @@ public static String objectToString(Object obj) { /** + * Get the value of a field by name from the target object. * Получить значение поля с именем fieldName у объекта target. * - * @param target объект, у которого читаем поле - * @param fieldName имя поля (может быть приватным, в суперклассах) - * @return значение поля - * @throws Exception если поле не найдено или доступ к нему невозможен + * @param target the object from which to read the field / объект, у которого читаем поле + * @param fieldName the name of the field (can be private, in superclasses) / имя поля (может быть приватным, в суперклассах) + * @return the field value / значение поля + * @throws Exception if the field is not found or access is denied / если поле не найдено или доступ к нему невозможен */ public static Object getFieldValue(Object target, String fieldName) throws Exception { if (target == null) { throw new IllegalArgumentException("Target object is null"); } Field field = findField(target.getClass(), fieldName); + Field field = findField(target.getClass(), fieldName); field.setAccessible(true); return field.get(target); } /** + * Invoke a method without arguments. * Вызвать метод без аргументов. * - * @param target объект, у которого вызываем метод - * @param methodName имя метода - * @return результат вызова - * @throws Exception если метод не найден или при вызове произошла ошибка + * @param target the object on which to invoke the method / объект, у которого вызываем метод + * @param methodName the name of the method / имя метода + * @return the result of the method call / результат вызова + * @throws Exception if the method is not found or an error occurs during invocation / если метод не найден или при вызове произошла ошибка */ public static Object invokeMethod(Object target, String methodName) throws Exception { return invokeMethod(target, methodName, new Class[0], new Object[0]); } /** + * Invoke a method with parameters. * Вызвать метод с параметрами. * - * @param target объект, у которого вызываем метод - * @param methodName имя метода - * @param parameterTypes массив типов параметров - * @param args аргументы метода - * @return результат вызова - * @throws Exception если метод не найден или при вызове произошла ошибка + * @param target the object on which to invoke the method / объект, у которого вызываем метод + * @param methodName the name of the method / имя метода + * @param parameterTypes array of parameter types / массив типов параметров + * @param args the method arguments / аргументы метода + * @return the result of the method call / результат вызова + * @throws Exception if the method is not found or an error occurs during invocation / если метод не найден или при вызове произошла ошибка */ public static Object invokeMethod(Object target, String methodName, @@ -136,8 +186,18 @@ public static Object invokeMethod(Object target, return method.invoke(target, args); } + // ----- Helper methods for finding fields and methods in class hierarchy ----- // ----- Вспомогательные методы поиска поля и метода в иерархии классов ----- + /** + * Find a field in the class hierarchy. + * Найти поле в иерархии классов. + * + * @param clazz the class to search in / класс для поиска + * @param name the field name / имя поля + * @return the field / поле + * @throws NoSuchFieldException if the field is not found / если поле не найдено + */ private static Field findField(Class clazz, String name) throws NoSuchFieldException { Class current = clazz; while (current != null) { @@ -150,6 +210,16 @@ private static Field findField(Class clazz, String name) throws NoSuchFieldEx throw new NoSuchFieldException("Field '" + name + "' not found in " + clazz); } + /** + * Find a method in the class hierarchy. + * Найти метод в иерархии классов. + * + * @param clazz the class to search in / класс для поиска + * @param name the method name / имя метода + * @param parameterTypes the parameter types / типы параметров + * @return the method / метод + * @throws NoSuchMethodException if the method is not found / если метод не найден + */ private static Method findMethod(Class clazz, String name, Class[] parameterTypes) throws NoSuchMethodException {