This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-graalvm-distro.git


The following commit(s) were added to refs/heads/main by this push:
     new 29310a7  Auto-generate reflection metadata for HTTP handlers, GraphQL 
resolvers/types
29310a7 is described below

commit 29310a714f52e579232c4f558c6cd98c1f5c9006
Author: Wu Sheng <[email protected]>
AuthorDate: Tue Feb 24 23:39:14 2026 +0800

    Auto-generate reflection metadata for HTTP handlers, GraphQL resolvers/types
    
    Precompiler now scans classpath to auto-generate reflect-config.json 
entries for:
    - Armeria HTTP handlers (19 classes with @Post/@Get/@Path + 
ExceptionHandlerFunction impls)
    - GraphQL resolvers (32 classes implementing 
GraphQLQueryResolver/GraphQLMutationResolver)
    - GraphQL DTO types (182 classes matched from .graphqls schema files)
    - Config POJOs (LALConfigs, MeterConfig, etc. deserialized by 
Jackson/SnakeYAML)
    
    Added console-only log4j2.xml for native image to avoid 
RollingFileAppender's
    deep reflection chain. Renamed static reflect-config to 
log4j2-reflect-config.json
    with Log4j2 plugin visitors and console appender entries only.
    
    Verified: native binary boots with 0 errors, GraphQL returns version and 
health.
---
 build-tools/precompiler/pom.xml                    |  50 ++++
 .../server/buildtools/precompiler/Precompiler.java | 300 +++++++++++++++++++++
 .../src/main/assembly/native-distribution.xml      |   9 +-
 .../oap-graalvm-native/log4j2-reflect-config.json  |  86 ++++++
 .../oap-graalvm-native/native-image.properties     |   1 +
 .../oap-graalvm-native/reflect-config.json         |  44 ---
 oap-graalvm-native/src/main/resources/log4j2.xml   |  42 +++
 7 files changed, 487 insertions(+), 45 deletions(-)

diff --git a/build-tools/precompiler/pom.xml b/build-tools/precompiler/pom.xml
index af1cfff..e71d028 100644
--- a/build-tools/precompiler/pom.xml
+++ b/build-tools/precompiler/pom.xml
@@ -108,6 +108,56 @@
             <artifactId>agent-analyzer</artifactId>
         </dependency>
 
+        <!-- Query plugins (GraphQL resolvers, HTTP handlers, adapter types) 
-->
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>query-graphql-plugin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>status-query-plugin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>promql-plugin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>logql-plugin</artifactId>
+        </dependency>
+
+        <!-- Health checker (HealthCheckerHttpService) -->
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>server-health-checker</artifactId>
+        </dependency>
+
+        <!-- Receiver plugins with HTTP handlers -->
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>skywalking-management-receiver-plugin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>skywalking-trace-receiver-plugin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>skywalking-log-receiver-plugin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>skywalking-event-receiver-plugin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>skywalking-telegraf-receiver-plugin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>aws-firehose-receiver</artifactId>
+        </dependency>
+
         <!-- Jackson for config data JSON serialization -->
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
diff --git 
a/build-tools/precompiler/src/main/java/org/apache/skywalking/oap/server/buildtools/precompiler/Precompiler.java
 
b/build-tools/precompiler/src/main/java/org/apache/skywalking/oap/server/buildtools/precompiler/Precompiler.java
index 766d785..f61eeef 100644
--- 
a/build-tools/precompiler/src/main/java/org/apache/skywalking/oap/server/buildtools/precompiler/Precompiler.java
+++ 
b/build-tools/precompiler/src/main/java/org/apache/skywalking/oap/server/buildtools/precompiler/Precompiler.java
@@ -21,14 +21,17 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.reflect.ClassPath;
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.Reader;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -36,11 +39,14 @@ import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import lombok.extern.slf4j.Slf4j;
@@ -196,6 +202,16 @@ public class Precompiler {
         writeManifest(annotationScanDir.resolve("StorageBuilders.txt"),
             scanStorageBuilders(allClasses));
 
+        // ---- Armeria HTTP handler scanning ----
+        writeManifest(annotationScanDir.resolve("ArmeriaHandlers.txt"),
+            scanArmeriaHandlers(allClasses));
+
+        // ---- GraphQL resolver and type scanning ----
+        writeManifest(annotationScanDir.resolve("GraphQLResolvers.txt"),
+            scanGraphQLResolvers(allClasses));
+        writeManifest(annotationScanDir.resolve("GraphQLTypes.txt"),
+            scanGraphQLTypes(allClasses));
+
         // ---- MAL pre-compilation ----
         compileMAL(outputDir, allClasses);
 
@@ -790,6 +806,262 @@ public class Precompiler {
         return result;
     }
 
+    /**
+     * Scan for Armeria HTTP handler classes — classes with methods annotated 
with
+     * {@code @Post}, {@code @Get}, or {@code @Path} from {@code 
com.linecorp.armeria.server.annotation}.
+     * Also collects classes referenced in {@code @ExceptionHandler} and 
{@code @RequestConverter}.
+     * Armeria's annotatedService().build(handler) uses reflection to discover 
these annotations;
+     * without reflection metadata, routes are silently not registered 
(returning 404).
+     */
+    private static List<String> scanArmeriaHandlers(
+        ImmutableSet<ClassPath.ClassInfo> allClasses) {
+
+        // Load Armeria annotation classes if available
+        Class<? extends Annotation> postAnno = 
loadAnnotation("com.linecorp.armeria.server.annotation.Post");
+        Class<? extends Annotation> getAnno = 
loadAnnotation("com.linecorp.armeria.server.annotation.Get");
+        Class<? extends Annotation> pathAnno = 
loadAnnotation("com.linecorp.armeria.server.annotation.Path");
+
+        if (postAnno == null && getAnno == null && pathAnno == null) {
+            log.warn("Armeria annotations not found on classpath, skipping 
HTTP handler scan");
+            return Collections.emptyList();
+        }
+
+        Set<String> result = new HashSet<>();
+        for (ClassPath.ClassInfo classInfo : allClasses) {
+            try {
+                Class<?> aClass = classInfo.load();
+                if (aClass.isInterface() || 
Modifier.isAbstract(aClass.getModifiers())) {
+                    continue;
+                }
+                // Check if any method has Armeria routing annotations
+                for (Method method : aClass.getDeclaredMethods()) {
+                    if ((postAnno != null && 
method.isAnnotationPresent(postAnno))
+                        || (getAnno != null && 
method.isAnnotationPresent(getAnno))
+                        || (pathAnno != null && 
method.isAnnotationPresent(pathAnno))) {
+                        result.add(aClass.getName());
+                        // Also collect @ExceptionHandler referenced classes
+                        collectExceptionHandlerClasses(aClass, result);
+                        break;
+                    }
+                }
+            } catch (NoClassDefFoundError | Exception ignored) {
+            }
+        }
+
+        // Also scan for ExceptionHandlerFunction implementations 
(instantiated by Armeria via reflection)
+        Class<?> ehfInterface = 
loadClass("com.linecorp.armeria.server.annotation.ExceptionHandlerFunction");
+        if (ehfInterface != null) {
+            for (ClassPath.ClassInfo classInfo : allClasses) {
+                try {
+                    Class<?> aClass = classInfo.load();
+                    if (!aClass.isInterface()
+                        && !Modifier.isAbstract(aClass.getModifiers())
+                        && ehfInterface.isAssignableFrom(aClass)) {
+                        result.add(aClass.getName());
+                    }
+                } catch (NoClassDefFoundError | Exception ignored) {
+                }
+            }
+        }
+
+        List<String> sorted = new ArrayList<>(result);
+        Collections.sort(sorted);
+        log.info("Scanned Armeria HTTP handlers: {} classes", sorted.size());
+        return sorted;
+    }
+
+    /**
+     * Collect classes referenced by @ExceptionHandler annotation on the 
handler class.
+     * Also scans @RequestConverter classes referenced on methods.
+     */
+    private static void collectExceptionHandlerClasses(Class<?> handlerClass, 
Set<String> result) {
+        try {
+            Class<? extends Annotation> ehAnno = loadAnnotation(
+                "com.linecorp.armeria.server.annotation.ExceptionHandler");
+            if (ehAnno == null) {
+                return;
+            }
+            // Check class-level @ExceptionHandler
+            for (Annotation anno : handlerClass.getAnnotations()) {
+                if (ehAnno.isInstance(anno)) {
+                    try {
+                        Method valueMethod = 
anno.getClass().getMethod("value");
+                        Class<?>[] handlerClasses = (Class<?>[]) 
valueMethod.invoke(anno);
+                        for (Class<?> hc : handlerClasses) {
+                            result.add(hc.getName());
+                        }
+                    } catch (Exception ignored) {
+                    }
+                }
+            }
+        } catch (NoClassDefFoundError | Exception ignored) {
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Class<? extends Annotation> loadAnnotation(String fqcn) {
+        try {
+            Class<?> c = Class.forName(fqcn);
+            if (c.isAnnotation()) {
+                return (Class<? extends Annotation>) c;
+            }
+        } catch (ClassNotFoundException | NoClassDefFoundError ignored) {
+        }
+        return null;
+    }
+
+    /**
+     * Scan for GraphQL resolver classes — implementations of
+     * {@code graphql.kickstart.tools.GraphQLQueryResolver} or
+     * {@code graphql.kickstart.tools.GraphQLMutationResolver}.
+     * graphql-java-tools reflects on methods to map GraphQL schema operations 
to Java methods.
+     */
+    private static List<String> scanGraphQLResolvers(
+        ImmutableSet<ClassPath.ClassInfo> allClasses) {
+
+        Class<?> queryResolver = 
loadClass("graphql.kickstart.tools.GraphQLQueryResolver");
+        Class<?> mutationResolver = 
loadClass("graphql.kickstart.tools.GraphQLMutationResolver");
+
+        if (queryResolver == null && mutationResolver == null) {
+            log.warn("GraphQL resolver interfaces not found on classpath, 
skipping resolver scan");
+            return Collections.emptyList();
+        }
+
+        List<String> result = new ArrayList<>();
+        for (ClassPath.ClassInfo classInfo : allClasses) {
+            try {
+                Class<?> aClass = classInfo.load();
+                if (aClass.isInterface() || 
Modifier.isAbstract(aClass.getModifiers())) {
+                    continue;
+                }
+                if ((queryResolver != null && 
queryResolver.isAssignableFrom(aClass))
+                    || (mutationResolver != null && 
mutationResolver.isAssignableFrom(aClass))) {
+                    result.add(aClass.getName());
+                }
+            } catch (NoClassDefFoundError | Exception ignored) {
+            }
+        }
+        Collections.sort(result);
+        log.info("Scanned GraphQL resolvers: {} classes", result.size());
+        return result;
+    }
+
+    /**
+     * Scan GraphQL schema files (.graphqls) for type/input/enum definitions 
and match
+     * them to Java classes on the classpath by simple name.
+     * graphql-java-tools (kickstart) maps schema type names to Java class 
simple names;
+     * reflection is used to access fields/getters for field resolution.
+     */
+    private static List<String> scanGraphQLTypes(
+        ImmutableSet<ClassPath.ClassInfo> allClasses) {
+
+        // Build simple name → FQCN index for all SkyWalking classes
+        Map<String, String> simpleNameIndex = new HashMap<>();
+        for (ClassPath.ClassInfo classInfo : allClasses) {
+            simpleNameIndex.put(classInfo.getSimpleName(), 
classInfo.getName());
+        }
+
+        // Parse .graphqls files from classpath
+        Set<String> graphqlTypeNames = new HashSet<>();
+        Pattern typePattern = 
Pattern.compile("^\\s*(type|input|enum)\\s+(\\w+)");
+
+        String[] schemaFiles = listGraphQLSchemaFiles();
+        for (String schemaFile : schemaFiles) {
+            try (InputStream is = 
Precompiler.class.getClassLoader().getResourceAsStream(schemaFile);
+                 BufferedReader reader = new BufferedReader(new 
InputStreamReader(is, StandardCharsets.UTF_8))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    Matcher m = typePattern.matcher(line);
+                    if (m.find()) {
+                        graphqlTypeNames.add(m.group(2));
+                    }
+                }
+            } catch (Exception e) {
+                log.warn("Failed to parse GraphQL schema file: {}", 
schemaFile, e);
+            }
+        }
+
+        // Remove root types that are resolvers, not DTOs
+        graphqlTypeNames.remove("Query");
+        graphqlTypeNames.remove("Mutation");
+        graphqlTypeNames.remove("Subscription");
+
+        // Match schema type names to Java classes
+        List<String> result = new ArrayList<>();
+        Set<String> unmatched = new HashSet<>();
+        for (String typeName : graphqlTypeNames) {
+            String fqcn = simpleNameIndex.get(typeName);
+            if (fqcn != null) {
+                result.add(fqcn);
+            } else {
+                unmatched.add(typeName);
+            }
+        }
+        Collections.sort(result);
+        if (!unmatched.isEmpty()) {
+            log.info("GraphQL types not matched to Java classes (may be 
scalars or external): {}",
+                unmatched.stream().sorted().collect(Collectors.joining(", ")));
+        }
+        log.info("Scanned GraphQL types: {} matched from {} schema types",
+            result.size(), graphqlTypeNames.size());
+        return result;
+    }
+
+    /**
+     * List all query-protocol/*.graphqls files from the classpath.
+     * Scans both filesystem directories and JAR entries since the schema files
+     * are packaged inside the query-graphql-plugin JAR.
+     */
+    private static String[] listGraphQLSchemaFiles() {
+        Set<String> files = new HashSet<>();
+        try {
+            java.util.Enumeration<java.net.URL> urls =
+                
Precompiler.class.getClassLoader().getResources("query-protocol");
+            while (urls.hasMoreElements()) {
+                java.net.URL url = urls.nextElement();
+                if ("file".equals(url.getProtocol())) {
+                    // Filesystem directory
+                    File dir = new File(url.toURI());
+                    File[] children = dir.listFiles();
+                    if (children != null) {
+                        for (File f : children) {
+                            if (f.getName().endsWith(".graphqls")) {
+                                files.add("query-protocol/" + f.getName());
+                            }
+                        }
+                    }
+                } else if ("jar".equals(url.getProtocol())) {
+                    // JAR entry: jar:file:/path/to/jar.jar!/query-protocol
+                    String jarPath = url.getPath();
+                    String jarFile = jarPath.substring(5, 
jarPath.indexOf('!'));
+                    try (java.util.jar.JarFile jar = new 
java.util.jar.JarFile(jarFile)) {
+                        java.util.Enumeration<java.util.jar.JarEntry> entries 
= jar.entries();
+                        while (entries.hasMoreElements()) {
+                            String name = entries.nextElement().getName();
+                            if (name.startsWith("query-protocol/") && 
name.endsWith(".graphqls")) {
+                                files.add(name);
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.warn("Failed to enumerate query-protocol/ directory", e);
+        }
+        if (files.isEmpty()) {
+            log.warn("No .graphqls files found in query-protocol/");
+        }
+        return files.toArray(new String[0]);
+    }
+
+    private static Class<?> loadClass(String fqcn) {
+        try {
+            return Class.forName(fqcn);
+        } catch (ClassNotFoundException | NoClassDefFoundError ignored) {
+        }
+        return null;
+    }
+
     /**
      * Serialize MAL config data (Rules and MeterConfigs) as JSON for runtime 
loaders.
      * At runtime, replacement loader classes deserialize from these JSON 
files instead
@@ -921,6 +1193,34 @@ public class Precompiler {
             }
         }
 
+        // Armeria HTTP handlers — full access (Armeria reflects on 
@Post/@Get/@Path method annotations)
+        String[] httpHandlerManifests = {
+            "ArmeriaHandlers.txt", "GraphQLResolvers.txt", "GraphQLTypes.txt"
+        };
+        for (String manifest : httpHandlerManifests) {
+            Path file = annotationScanDir.resolve(manifest);
+            if (Files.exists(file)) {
+                for (String className : readClassNames(file)) {
+                    entries.add(fullAccessEntry(className));
+                }
+            }
+        }
+
+        // Config POJOs deserialized by Jackson/SnakeYAML at runtime — full 
access
+        String[] configPojos = {
+            "org.apache.skywalking.oap.log.analyzer.provider.LALConfigs",
+            "org.apache.skywalking.oap.log.analyzer.provider.LALConfig",
+            
"org.apache.skywalking.oap.server.analyzer.provider.meter.config.MeterConfig",
+            "org.apache.skywalking.oap.meter.analyzer.prometheus.rule.Rule",
+            
"org.apache.skywalking.oap.meter.analyzer.prometheus.rule.MetricsRule",
+            
"org.apache.skywalking.oap.server.core.management.ui.menu.UIMenuInitializer$MenuData",
+            
"org.apache.skywalking.oap.server.core.management.ui.menu.UIMenuItemSetting",
+            
"org.apache.skywalking.oap.server.receiver.telegraf.provider.handler.pojo.TelegrafData"
+        };
+        for (String className : configPojos) {
+            entries.add(fullAccessEntry(className));
+        }
+
         // MeterFunction manifest — key=value format, full access (MeterSystem 
inspects annotations)
         Path meterFunctionFile = 
annotationScanDir.resolve("MeterFunction.txt");
         if (Files.exists(meterFunctionFile)) {
diff --git a/oap-graalvm-native/src/main/assembly/native-distribution.xml 
b/oap-graalvm-native/src/main/assembly/native-distribution.xml
index 0eefddd..b02f65f 100644
--- a/oap-graalvm-native/src/main/assembly/native-distribution.xml
+++ b/oap-graalvm-native/src/main/assembly/native-distribution.xml
@@ -45,12 +45,19 @@
                 <include>application.yml</include>
             </includes>
         </fileSet>
+        <!-- config/ — native-specific log4j2.xml (console-only, avoids 
RollingFile reflection chain) -->
+        <fileSet>
+            <directory>${project.basedir}/src/main/resources</directory>
+            <outputDirectory>config</outputDirectory>
+            <includes>
+                <include>log4j2.xml</include>
+            </includes>
+        </fileSet>
         <!-- config/ — production configs from dist-material (same as upstream 
apm-dist) -->
         <fileSet>
             
<directory>${project.basedir}/../skywalking/dist-material</directory>
             <outputDirectory>config</outputDirectory>
             <includes>
-                <include>log4j2.xml</include>
                 <include>alarm-settings.yml</include>
             </includes>
         </fileSet>
diff --git 
a/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/log4j2-reflect-config.json
 
b/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/log4j2-reflect-config.json
new file mode 100644
index 0000000..d8e4c4d
--- /dev/null
+++ 
b/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/log4j2-reflect-config.json
@@ -0,0 +1,86 @@
+[
+  {
+    "name": 
"org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor",
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": 
"org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor",
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": 
"org.apache.logging.log4j.core.config.plugins.visitors.PluginConfigurationVisitor",
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": 
"org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor",
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": 
"org.apache.logging.log4j.core.config.plugins.visitors.PluginNodeVisitor",
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": 
"org.apache.logging.log4j.core.config.plugins.visitors.PluginValueVisitor",
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.config.LoggerConfig",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.config.LoggerConfig$RootLogger",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.config.AppenderRef",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.appender.ConsoleAppender",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.appender.ConsoleAppender$Builder",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": 
"org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender$Builder",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.appender.AbstractAppender$Builder",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.filter.AbstractFilterable$Builder",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.layout.PatternLayout",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  },
+  {
+    "name": "org.apache.logging.log4j.core.layout.PatternLayout$Builder",
+    "allDeclaredFields": true,
+    "allDeclaredMethods": true,
+    "allDeclaredConstructors": true
+  }
+]
diff --git 
a/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/native-image.properties
 
b/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/native-image.properties
new file mode 100644
index 0000000..7c14aeb
--- /dev/null
+++ 
b/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/native-image.properties
@@ -0,0 +1 @@
+Args = -H:ReflectionConfigurationResources=${.}/log4j2-reflect-config.json
diff --git 
a/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/reflect-config.json
 
b/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/reflect-config.json
deleted file mode 100644
index 2da0b03..0000000
--- 
a/oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native/reflect-config.json
+++ /dev/null
@@ -1,44 +0,0 @@
-[
-  {
-    "name": "org.apache.skywalking.oap.log.analyzer.provider.LALConfigs",
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true,
-    "allDeclaredConstructors": true
-  },
-  {
-    "name": "org.apache.skywalking.oap.log.analyzer.provider.LALConfig",
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true,
-    "allDeclaredConstructors": true
-  },
-  {
-    "name": 
"org.apache.skywalking.oap.server.analyzer.provider.meter.config.MeterConfig",
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true,
-    "allDeclaredConstructors": true
-  },
-  {
-    "name": "org.apache.skywalking.oap.meter.analyzer.prometheus.rule.Rule",
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true,
-    "allDeclaredConstructors": true
-  },
-  {
-    "name": 
"org.apache.skywalking.oap.meter.analyzer.prometheus.rule.MetricsRule",
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true,
-    "allDeclaredConstructors": true
-  },
-  {
-    "name": 
"org.apache.skywalking.oap.server.core.management.ui.menu.UIMenuInitializer$MenuData",
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true,
-    "allDeclaredConstructors": true
-  },
-  {
-    "name": 
"org.apache.skywalking.oap.server.core.management.ui.menu.UIMenuItemSetting",
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true,
-    "allDeclaredConstructors": true
-  }
-]
diff --git a/oap-graalvm-native/src/main/resources/log4j2.xml 
b/oap-graalvm-native/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..5be847d
--- /dev/null
+++ b/oap-graalvm-native/src/main/resources/log4j2.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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
+  ~
+  ~     http://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.
+  ~
+  -->
+
+<!--
+  Log4j2 configuration for GraalVM native image.
+  Uses console-only logging to avoid RollingFileAppender's deep reflection 
chain
+  (file date pattern converters, rollover strategies, etc.) which requires
+  extensive native-image reflection metadata.
+  For container/native deployments, stdout logging is collected by the 
container runtime.
+-->
+<Configuration status="WARN">
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout>
+                <pattern>%d - %c - %L [%t] %-5p %x - %m%n</pattern>
+            </PatternLayout>
+        </Console>
+    </Appenders>
+    <Loggers>
+        <logger name="org.apache.zookeeper" level="INFO"/>
+        <logger name="io.grpc.netty" level="INFO"/>
+        <Root level="INFO">
+            <AppenderRef ref="Console"/>
+        </Root>
+    </Loggers>
+</Configuration>

Reply via email to