Repository: apache/skywalking (language: python)

Issue:
[Feature] Support OTLP Native Logging
### Search before asking

- [X] I had searched in the [issues](https://github.com/apache/skywalking/issues?q=is%3Aissue) and found no similar feature requirement.


### Description

SkyWalking has OTLP log support through SkyWalking exporter, ref https://skywalking.apache.org/docs/main/next/en/setup/backend/log-otlp/

We should begin to consider whether we could have gRPC/HTTP native OTLP logging support as well.

### Use case

It should work similarly to the MAL engine. Various formats of logging would be transferred as the native format and pushed into the LAL engine.

### Related issues

_No response_

### Are you willing to submit a PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct)



Discussion / hints:
`receiver-otel` module may face some name confusing, as `enabledHandlers` and `enabledOtelRules` are actually only metrics oriented. Ref from a question, https://github.com/apache/skywalking/discussions/10747

Fix patch:
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index bacc6d4f8ce6..3b95fca657e1 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -68,6 +68,9 @@
 * Add Jeysey icon.
 * Fix: set endpoint and instance selectors with url parameters correctly.
 * Bump up dependencies versions icons-vue 1.1.4, element-plus 2.1.0, nanoid 3.3.6, postcss 8.4.23
+* Add OpenTelemetry log protocol support.
+* [Breaking Change] Configuration key `enabledOtelRules` is renamed to `enabledOtelMetricsRules` and
+  the corresponding environment variable is renamed to `SW_OTEL_RECEIVER_ENABLED_OTEL_METRICS_RULES`.
 
 #### Documentation
 
diff --git a/docs/en/setup/backend/backend-telemetry.md b/docs/en/setup/backend/backend-telemetry.md
index a3d242091f50..b549d318b04c 100644
--- a/docs/en/setup/backend/backend-telemetry.md
+++ b/docs/en/setup/backend/backend-telemetry.md
@@ -58,7 +58,7 @@ Set this up following these steps:
   ```
   SW_TELEMETRY=prometheus
   SW_OTEL_RECEIVER=default
-  SW_OTEL_RECEIVER_ENABLED_OTEL_RULES=oap
+  SW_OTEL_RECEIVER_ENABLED_OTEL_METRICS_RULES=oap
   ```
 
   Here is an example to install by Apache SkyWalking Kubernetes:
@@ -76,7 +76,7 @@ Set this up following these steps:
                --set oap.ports.prometheus-port=1234 \ # <<< Expose self observability metrics port
                --set oap.env.SW_TELEMETRY=prometheus \
                --set oap.env.SW_OTEL_RECEIVER=default \ # <<< Enable Otel receiver
-               --set oap.env.SW_OTEL_RECEIVER_ENABLED_OTEL_RULES=oap # <<< Add oap analyzer for Otel metrics
+               --set oap.env.SW_OTEL_RECEIVER_ENABLED_OTEL_METRICS_RULES=oap # <<< Add oap analyzer for Otel metrics
   ```
 2. Set up OpenTelemetry Collector and config a scrape job:
 ``` yaml
diff --git a/docs/en/setup/backend/configuration-vocabulary.md b/docs/en/setup/backend/configuration-vocabulary.md
index b92b23f9a69f..35beba6b1c6d 100644
--- a/docs/en/setup/backend/configuration-vocabulary.md
+++ b/docs/en/setup/backend/configuration-vocabulary.md
@@ -190,7 +190,7 @@ The Configuration Vocabulary lists all available configurations provided by `app
 | -                       | -             | k8sServiceNameRule                                                                                                                                                       | `k8sServiceNameRule` allows you to customize the service name in ALS via Kubernetes metadata. The available variables are `pod` and `service`. E.g. you can use `${service.metadata.name}-${pod.metadata.labels.version}` to append the version number to the service name. Note that when using environment variables to pass this configuration, use single quotes(`''`) to avoid being evaluated by the shell.                                                          | -                                                 |                                                                                              |
 | receiver-otel           | default       | A receiver for analyzing metrics data from OpenTelemetry.                                                                                                                | -                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | -                                                 |                                                                                              |
 | -                       | -             | enabledHandlers                                                                                                                                                          | Enabled handlers for otel.                                                                                                                                                                                                                                                                                                                                                                                                                                                 | SW_OTEL_RECEIVER_ENABLED_HANDLERS                 | -                                                                                            |
-| -                       | -             | enabledOtelRules                                                                                                                                                         | Enabled metric rules for OTLP handler.                                                                                                                                                                                                                                                                                                                                                                                                                                     | SW_OTEL_RECEIVER_ENABLED_OTEL_RULES                 | -                                                                                            |
+| -                       | -             | enabledOtelMetricsRules                                                                                                                                                         | Enabled metric rules for OTLP handler.                                                                                                                                                                                                                                                                                                                                                                                                                                     | SW_OTEL_RECEIVER_ENABLED_OTEL_METRICS_RULES                 | -                                                                                            |
 | receiver-zipkin         | default       | A receiver for Zipkin traces.                                                                                                                                            | -                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | -                                                 |                                                                                              |
 | -                       | -             | sampleRate                                                                                                                                                         | The sample rate precision is 1/10000, should be between 0 and 10000                                                                                                                                                                                                                                                                                                                                                                                                        | SW_ZIPKIN_SAMPLE_RATE             | 10000                                                                                        |
 | -                       | -             | searchableTracesTags                                                                                                                                                     | Defines a set of span tag keys which are searchable. Multiple values are separated by commas. The max length of key=value should be less than 256 or will be dropped.                                                                                                                                                                                                                                                                                                      | SW_ZIPKIN_SEARCHABLE_TAG_KEYS                     | http.method                                                                                  |
diff --git a/docs/en/setup/backend/log-otlp.md b/docs/en/setup/backend/log-otlp.md
index c6dcdee9d336..562e14d20ca0 100644
--- a/docs/en/setup/backend/log-otlp.md
+++ b/docs/en/setup/backend/log-otlp.md
@@ -1,4 +1,46 @@
 # OpenTelemetry Logging Format
 
-OpenTelemetry Collector supports to transport the logs to SkyWalking OAP.
-Read the doc on [Skywalking Exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/skywalkingexporter) for a detailed guide.
+SkyWalking can receive logs exported from OpenTelemetry collector, the data flow is:
+
+```mermaid
+graph LR
+    B[OpenTelemetry SDK 1]
+    C[FluentBit/FluentD, etc.]
+    K[Other sources that OpenTelemetry supports ...]
+    D[OpenTelemetry Collector]
+    E[SkyWalking OAP Server]
+    B --> D
+    C --> D
+    K --> D
+    D -- exporter --> E
+```
+
+where the `exporter` can be one of the following:
+
+- [OpenTelemetry SkyWalking Exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/5133f4ccd69fa40d016c5b7f2198fb6ac61007b4/exporter/skywalkingexporter).
+  An exporter that transforms the logs to SkyWalking format before sending them to SkyWalking OAP. Read the doc in the
+  aforementioned link for a detailed guide.
+- [OpenTelemetry OTLP Exporter](#opentelemetry-otlp-exporter). An exporter that sends the logs to SkyWalking OAP in OTLP
+  format, and SkyWalking OAP is responsible for transforming the data format.
+
+## OpenTelemetry OTLP Exporter
+
+By using this exporter, you can send any log data to SkyWalking OAP as long as the data is in OTLP format, no matter
+where the data is generated.
+
+To enable this exporter, make sure the `receiver-otel` is enabled and the `otlp-logs` value is in
+the `receiver-otel/default/enabledHandlers` configuration section:
+
+```yaml
+receiver-otel:
+  selector: ${SW_OTEL_RECEIVER:default}
+  default:
+    enabledHandlers: ${SW_OTEL_RECEIVER_ENABLED_HANDLERS:"otlp-metrics,otlp-logs"}
+```
+
+Also, because most of the language SDKs of OpenTelemetry do not support logging feature (yet) or the logging feature is
+experimental, it's your responsibility to make sure the reported log data contains the following attributes, otherwise
+SkyWalking is not able to consume them:
+
+- `service.name`: the name of the service that generates the log data, OpenTelemetry Java SDK (experimental) has this
+  attribute set, if you're using other SDK or agent, please check the corresponding doc.
diff --git a/docs/en/setup/backend/opentelemetry-receiver.md b/docs/en/setup/backend/opentelemetry-receiver.md
index f84089acc1fd..b6e30f9602c5 100644
--- a/docs/en/setup/backend/opentelemetry-receiver.md
+++ b/docs/en/setup/backend/opentelemetry-receiver.md
@@ -19,7 +19,7 @@ receiver-otel:
   selector: ${SW_OTEL_RECEIVER:default}
   default:
     enabledHandlers: ${SW_OTEL_RECEIVER_ENABLED_HANDLERS:"otlp"}
-    enabledOtelRules: ${SW_OTEL_RECEIVER_ENABLED_OTEL_RULES:"istio-controlplane"}
+    enabledOtelMetricsRules: ${SW_OTEL_RECEIVER_ENABLED_OTEL_METRICS_RULES:"istio-controlplane"}
 ```
 
 The receiver adds label with key `node_identifier_host_name` to the collected data samples,
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java
index babc65114832..d6fd2b128d3e 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java
@@ -18,10 +18,11 @@
 
 package org.apache.skywalking.oap.server.core.analysis;
 
+import org.apache.skywalking.oap.server.core.UnexpectedException;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
-import org.apache.skywalking.oap.server.core.UnexpectedException;
 
 /**
  * Layer represents an abstract framework in computer science, such as Operating System(OS_LINUX layer), Kubernetes(k8s
@@ -220,7 +221,7 @@ public static Layer valueOf(int value) {
     public static Layer nameOf(String name) {
         Layer layer = DICTIONARY_NAME.get(name);
         if (layer == null) {
-            throw new UnexpectedException("Unknown Layer name");
+            return UNDEFINED;
         }
         return layer;
     }
diff --git a/oap-server/server-receiver-plugin/otel-receiver-plugin/pom.xml b/oap-server/server-receiver-plugin/otel-receiver-plugin/pom.xml
index 212913443374..39ea0d547026 100644
--- a/oap-server/server-receiver-plugin/otel-receiver-plugin/pom.xml
+++ b/oap-server/server-receiver-plugin/otel-receiver-plugin/pom.xml
@@ -44,5 +44,10 @@
             <artifactId>meter-analyzer</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>log-analyzer</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/HandlerInitializationException.java b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/HandlerInitializationException.java
deleted file mode 100644
index 46a5cb7d6209..000000000000
--- a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/HandlerInitializationException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- *
- */
-
-package org.apache.skywalking.oap.server.receiver.otel;
-
-import org.apache.skywalking.oap.server.library.module.ModuleStartException;
-
-public class HandlerInitializationException extends ModuleStartException {
-    public HandlerInitializationException(String message) {
-        super(message);
-    }
-
-    public HandlerInitializationException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/OtelMetricReceiverConfig.java b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/OtelMetricReceiverConfig.java
index fc099b3a79ae..40da30faf012 100644
--- a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/OtelMetricReceiverConfig.java
+++ b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/OtelMetricReceiverConfig.java
@@ -18,55 +18,25 @@
 
 package org.apache.skywalking.oap.server.receiver.otel;
 
-import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
 import lombok.Getter;
+import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.skywalking.oap.server.library.util.StringUtil;
 import org.apache.skywalking.oap.server.library.module.ModuleConfig;
 
+import java.util.List;
+
 @Slf4j
 public class OtelMetricReceiverConfig extends ModuleConfig {
 
+    @Setter
     private String enabledHandlers;
 
     @Getter
-    private String enabledOtelRules;
+    private String enabledOtelMetricsRules;
 
     public List<String> getEnabledHandlers() {
-        return split(enabledHandlers);
-    }
-
-    List<String> getEnabledRulesFrom(String handler) {
-        Field f;
-        try {
-            f = this.getClass().getDeclaredField(String.format("enabled%sRules", StringUtils.capitalize(handler)));
-        } catch (NoSuchFieldException e) {
-            if (log.isDebugEnabled()) {
-                log.debug("failed to get disabled rule field of {}", handler, e);
-            }
-            return Collections.emptyList();
-        }
-        f.setAccessible(true);
-        try {
-            return split(f.get(this));
-        } catch (IllegalAccessException e) {
-            log.warn("failed to access disabled rule list of {}", handler, e);
-            return Collections.emptyList();
-        }
-    }
-
-    private List<String> split(Object str) {
-        return Arrays.stream(Optional.ofNullable(str).orElse("").toString()
-            .split(","))
-            .map(String::trim)
-            .filter(StringUtil::isNotEmpty)
-            .collect(Collectors.toList());
+        return Splitter.on(",").trimResults().omitEmptyStrings().splitToList(Strings.nullToEmpty(enabledHandlers));
     }
-
 }
diff --git a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/OtelMetricReceiverProvider.java b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/OtelMetricReceiverProvider.java
index e3fecb867b9b..0ba59525d980 100644
--- a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/OtelMetricReceiverProvider.java
+++ b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/OtelMetricReceiverProvider.java
@@ -22,6 +22,7 @@
 import org.apache.skywalking.oap.server.library.module.ModuleProvider;
 import org.apache.skywalking.oap.server.library.module.ModuleStartException;
 import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
+import org.apache.skywalking.oap.server.receiver.otel.otlp.OpenTelemetryLogHandler;
 import org.apache.skywalking.oap.server.receiver.otel.otlp.OpenTelemetryMetricHandler;
 import org.apache.skywalking.oap.server.receiver.otel.otlp.OpenTelemetryMetricRequestProcessor;
 import org.apache.skywalking.oap.server.receiver.sharing.server.SharingServerModule;
@@ -70,11 +71,15 @@ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
         registerServiceImplementation(OpenTelemetryMetricRequestProcessor.class, metricRequestProcessor);
         final List<String> enabledHandlers = config.getEnabledHandlers();
         List<Handler> handlers = new ArrayList<>();
-        final OpenTelemetryMetricHandler openTelemetryMetricHandler = new OpenTelemetryMetricHandler(
-            getManager(), metricRequestProcessor);
+
+        final var openTelemetryMetricHandler = new OpenTelemetryMetricHandler(getManager(), metricRequestProcessor);
         if (enabledHandlers.contains(openTelemetryMetricHandler.type())) {
             handlers.add(openTelemetryMetricHandler);
         }
+        final var openTelemetryLogHandler = new OpenTelemetryLogHandler(getManager());
+        if (enabledHandlers.contains(openTelemetryLogHandler.type())) {
+            handlers.add(openTelemetryLogHandler);
+        }
         this.handlers = handlers;
     }
 
diff --git a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryLogHandler.java b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryLogHandler.java
new file mode 100644
index 000000000000..3d6c6bc9db62
--- /dev/null
+++ b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryLogHandler.java
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.otel.otlp;
+
+import com.google.common.base.Strings;
+import io.grpc.stub.StreamObserver;
+import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest;
+import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse;
+import io.opentelemetry.proto.collector.logs.v1.LogsServiceGrpc;
+import io.opentelemetry.proto.common.v1.KeyValue;
+import io.opentelemetry.proto.logs.v1.LogRecord;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.apm.network.common.v3.KeyStringValuePair;
+import org.apache.skywalking.apm.network.logging.v3.LogData;
+import org.apache.skywalking.apm.network.logging.v3.LogDataBody;
+import org.apache.skywalking.apm.network.logging.v3.LogTags;
+import org.apache.skywalking.apm.network.logging.v3.TextLog;
+import org.apache.skywalking.oap.log.analyzer.module.LogAnalyzerModule;
+import org.apache.skywalking.oap.log.analyzer.provider.log.ILogAnalyzerService;
+import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import org.apache.skywalking.oap.server.receiver.otel.Handler;
+import org.apache.skywalking.oap.server.receiver.sharing.server.SharingServerModule;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.toMap;
+
+@Slf4j
+@RequiredArgsConstructor
+public class OpenTelemetryLogHandler
+    extends LogsServiceGrpc.LogsServiceImplBase
+    implements Handler {
+    private final ModuleManager manager;
+
+    private ILogAnalyzerService logAnalyzerService;
+
+    @Override
+    public String type() {
+        return "otlp-logs";
+    }
+
+    @Override
+    public void active() throws ModuleStartException {
+        GRPCHandlerRegister grpcHandlerRegister = manager.find(SharingServerModule.NAME)
+                                                         .provider()
+                                                         .getService(GRPCHandlerRegister.class);
+        grpcHandlerRegister.addHandler(this);
+    }
+
+    @Override
+    public void export(ExportLogsServiceRequest request, StreamObserver<ExportLogsServiceResponse> responseObserver) {
+        request.getResourceLogsList().forEach(resourceLogs -> {
+            final var resource = resourceLogs.getResource();
+            final var attributes = resource
+                .getAttributesList().stream()
+                .map(it -> Map.entry(it.getKey(), buildTagValue(it)))
+                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+            final var service = attributes.get("service.name");
+            if (Strings.isNullOrEmpty(service)) {
+                log.warn("No service name found in resource attributes, discarding the log");
+                return;
+            }
+            final var layer = attributes.getOrDefault("service.layer", "");
+            final var serviceInstance = attributes.getOrDefault("service.instance", "");
+
+            resourceLogs
+                .getScopeLogsList()
+                .stream()
+                .flatMap(it -> it.getLogRecordsList().stream())
+                .forEach(
+                    logRecord ->
+                        doAnalysisQuietly(service, layer, serviceInstance, logRecord));
+            responseObserver.onNext(ExportLogsServiceResponse.getDefaultInstance());
+            responseObserver.onCompleted();
+        });
+    }
+
+    private void doAnalysisQuietly(String service, String layer, String serviceInstance, LogRecord logRecord) {
+        try {
+            logAnalyzerService().doAnalysis(
+                LogData
+                    .newBuilder()
+                    .setService(service)
+                    .setServiceInstance(serviceInstance)
+                    .setTimestamp(logRecord.getTimeUnixNano() / 1_000_000)
+                    .setTags(buildTags(logRecord))
+                    .setBody(buildBody(logRecord))
+                    .setLayer(layer),
+                null);
+        } catch (Exception e) {
+            log.error("Failed to analyze logs", e);
+        }
+    }
+
+    private static LogDataBody buildBody(LogRecord logRecord) {
+        return LogDataBody.newBuilder().setText(
+            TextLog.newBuilder().setText(
+                logRecord.getBody().getStringValue()
+            ).build()
+        ).build();
+    }
+
+    private LogTags buildTags(LogRecord logRecord) {
+        return LogTags.newBuilder().addAllData(
+            logRecord
+                .getAttributesList()
+                .stream()
+                .collect(toMap(KeyValue::getKey, this::buildTagValue))
+                .entrySet()
+                .stream()
+                .map(it -> KeyStringValuePair
+                    .newBuilder()
+                    .setKey(it.getKey())
+                    .setValue(it.getValue())
+                    .build())
+                .collect(Collectors.toList())
+        ).build();
+    }
+
+    private String buildTagValue(KeyValue it) {
+        final var value = it.getValue();
+        return value.hasStringValue() ? value.getStringValue() :
+            value.hasIntValue() ? String.valueOf(value.getIntValue()) :
+                value.hasDoubleValue() ? String.valueOf(value.getDoubleValue()) :
+                    value.hasBoolValue() ? String.valueOf(value.getBoolValue()) :
+                        value.hasArrayValue() ? value.getArrayValue().toString() :
+                            "";
+    }
+
+    private ILogAnalyzerService logAnalyzerService() {
+        if (logAnalyzerService == null) {
+            logAnalyzerService =
+                manager.find(LogAnalyzerModule.NAME)
+                       .provider()
+                       .getService(ILogAnalyzerService.class);
+        }
+        return logAnalyzerService;
+    }
+}
diff --git a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryMetricHandler.java b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryMetricHandler.java
index 1edbe79654a0..edc794122174 100644
--- a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryMetricHandler.java
+++ b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryMetricHandler.java
@@ -41,7 +41,7 @@ public class OpenTelemetryMetricHandler
 
     @Override
     public String type() {
-        return "otlp";
+        return "otlp-metrics";
     }
 
     @Override
diff --git a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryMetricRequestProcessor.java b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryMetricRequestProcessor.java
index 4d3adbbe0787..a32805b2d7e3 100644
--- a/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryMetricRequestProcessor.java
+++ b/oap-server/server-receiver-plugin/otel-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/otel/otlp/OpenTelemetryMetricRequestProcessor.java
@@ -24,14 +24,6 @@
 import io.opentelemetry.proto.metrics.v1.Sum;
 import io.opentelemetry.proto.metrics.v1.SummaryDataPoint;
 import io.vavr.Function1;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Stream;
-
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.skywalking.oap.meter.analyzer.MetricConvert;
@@ -50,6 +42,13 @@
 import org.apache.skywalking.oap.server.library.util.prometheus.metrics.Summary;
 import org.apache.skywalking.oap.server.receiver.otel.OtelMetricReceiverConfig;
 
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
 import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA;
 import static io.opentelemetry.proto.metrics.v1.AggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED;
 import static java.util.stream.Collectors.toList;
@@ -112,7 +111,8 @@ public void start() throws ModuleStartException {
         final List<String> enabledRules =
             Splitter.on(",")
                     .omitEmptyStrings()
-                    .splitToList(config.getEnabledOtelRules());
+                    .trimResults()
+                    .splitToList(config.getEnabledOtelMetricsRules());
         final List<Rule> rules;
         try {
             rules = Rules.loadRules("otel-rules", enabledRules);
diff --git a/oap-server/server-starter/src/main/resources/application.yml b/oap-server/server-starter/src/main/resources/application.yml
index aaaa25a52d8c..dd95e74ff122 100644
--- a/oap-server/server-starter/src/main/resources/application.yml
+++ b/oap-server/server-starter/src/main/resources/application.yml
@@ -331,8 +331,8 @@ receiver-meter:
 receiver-otel:
   selector: ${SW_OTEL_RECEIVER:default}
   default:
-    enabledHandlers: ${SW_OTEL_RECEIVER_ENABLED_HANDLERS:"otlp"}
-    enabledOtelRules: ${SW_OTEL_RECEIVER_ENABLED_OTEL_RULES:"apisix,k8s/*,istio-controlplane,vm,mysql/*,postgresql/*,oap,aws-eks/*,windows,aws-s3/*,aws-dynamodb/*,aws-gateway/*,redis/*,elasticsearch/*,rabbitmq/*"}
+    enabledHandlers: ${SW_OTEL_RECEIVER_ENABLED_HANDLERS:"otlp-metrics,otlp-logs"}
+    enabledOtelMetricsRules: ${SW_OTEL_RECEIVER_ENABLED_OTEL_RULES:"apisix,k8s/*,istio-controlplane,vm,mysql/*,postgresql/*,oap,aws-eks/*,windows,aws-s3/*,aws-dynamodb/*,aws-gateway/*,redis/*,elasticsearch/*,rabbitmq/*"}
 
 receiver-zipkin:
   selector: ${SW_RECEIVER_ZIPKIN:-}


Test patch:
diff --git a/test/e2e-v2/cases/vm/prometheus-node-exporter/docker-compose.yml b/test/e2e-v2/cases/vm/prometheus-node-exporter/docker-compose.yml
index c55dd17efe7b..4acac23fa2d3 100644
--- a/test/e2e-v2/cases/vm/prometheus-node-exporter/docker-compose.yml
+++ b/test/e2e-v2/cases/vm/prometheus-node-exporter/docker-compose.yml
@@ -22,7 +22,7 @@ services:
       service: oap
     environment:
       SW_OTEL_RECEIVER: default
-      SW_OTEL_RECEIVER_ENABLED_OTEL_RULES: vm
+      SW_OTEL_RECEIVER_ENABLED_OTEL_METRICS_RULES: vm
     ports:
       - 12800