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

glcj pushed a commit to branch feature/merlot
in repository https://gitbox.apache.org/repos/asf/plc4x-extras.git


The following commit(s) were added to refs/heads/feature/merlot by this push:
     new 50319aa  Implement and document Loki appender for Karaf event bus 
(#649)
50319aa is described below

commit 50319aa1ae2531b303f04131687fc2a56a642f73
Author: Lerb123 <[email protected]>
AuthorDate: Wed May 20 11:50:05 2026 -0400

    Implement and document Loki appender for Karaf event bus (#649)
    
    * feat: Implement Loki appender for Karaf event bus
    
    * fix: Implement Loki appender for Karaf event bus, added comments.
    
    * feat: Functional test for Loki Appender using WireMock
    
    * feat: CI Pipeline for MerlotLokiAppender
    
    * Correction to the Maven configuration to prevent tests from being 
skipped; add .mvn
    
    * Correction to Maven configuration: local cache is disabled
    
    * feat:Kafka collector for topics on the Phoebus alarm server
---
 plc4j/tools/merlot/.mvn/maven.config               |   0
 plc4j/tools/merlot/anahata.md                      |   3 -
 plc4j/tools/merlot/merlot.iml                      |  15 --
 .../org.apache.plc4x.merlot.archiver.iml           |   8 +
 .../org.apache.plc4x.merlot.archiver/pom.xml       |  99 ++++++++--
 .../cfg/org.apache.plc4x.merlot.loki.appender.cfg  |  39 ++++
 .../merlot/archiver/impl/MerlotLokiAppender.java   | 195 ++++++++++++++++++++
 .../OSGI-INF/blueprint/archiver-service.xml        |  35 +++-
 .../java/runner/MerlotLokiAppenderRunnerTest.java  |  16 ++
 .../stepdefinition/LokiAppenderDefinition.java     |  71 +++++++
 .../src/test/resources/features/loki.feature       |   6 +
 .../src/main/feature/feature.xml                   |  10 +-
 .../merlot/org.apache.plc4x.merlot.kafka/pom.xml   |   2 +-
 ....apache.plc4x.merlot.kafka.collector-alarm.cfg} |   6 +-
 .../core/MerlotKafkaManagedServiceFactory.java     |  50 +++--
 .../impl/MerlotKafkaDecanterCollectorImpl.java     | 205 ++++++++++++++++-----
 .../resources/OSGI-INF/blueprint/kafka-service.xml |   6 +-
 plc4j/tools/merlot/org.apache.plc4x.merlot/pom.xml |   3 +-
 plc4j/tools/merlot/pom.xml                         |   1 +
 19 files changed, 661 insertions(+), 109 deletions(-)

diff --git a/plc4j/tools/merlot/.mvn/maven.config 
b/plc4j/tools/merlot/.mvn/maven.config
new file mode 100644
index 0000000..e69de29
diff --git a/plc4j/tools/merlot/anahata.md b/plc4j/tools/merlot/anahata.md
deleted file mode 100644
index 7a959db..0000000
--- a/plc4j/tools/merlot/anahata.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Anahata Project Notes
-
-This file is for Anahata AI Assistant's notes regarding the 'merlot' project.
diff --git a/plc4j/tools/merlot/merlot.iml b/plc4j/tools/merlot/merlot.iml
deleted file mode 100644
index 452f210..0000000
--- a/plc4j/tools/merlot/merlot.iml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module version="4">
-  <component name="FacetManager">
-    <facet type="web" name="Web">
-      <configuration>
-        <descriptors>
-          <deploymentDescriptor name="web.xml" 
url="file://$MODULE_DIR$/org.apache.plc4x.merlot.ui/src/main/webapp/WEB-INF/web.xml"
 />
-        </descriptors>
-        <webroots>
-          <root 
url="file://$MODULE_DIR$/org.apache.plc4x.merlot.ui/src/main/webapp" 
relative="/" />
-        </webroots>
-      </configuration>
-    </facet>
-  </component>
-</module>
\ No newline at end of file
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver.iml
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver.iml
new file mode 100644
index 0000000..359c469
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver.iml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4">
+  <component name="AdditionalModuleElements">
+    <content url="file://$MODULE_DIR$" dumb="true">
+      <sourceFolder url="file://$MODULE_DIR$/src/test/src/test/resources" 
type="java-test-resource" />
+    </content>
+  </component>
+</module>
\ No newline at end of file
diff --git a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
index 878cce8..5edea7a 100644
--- a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
+++ b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
@@ -21,10 +21,10 @@
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
-    <artifactId>merlot</artifactId>
-    <groupId>org.apache.plc4x</groupId>
-    <version>0.13.0-SNAPSHOT</version>
-  </parent>
+        <artifactId>merlot</artifactId>
+        <groupId>org.apache.plc4x</groupId>
+        <version>0.13.0-SNAPSHOT</version>
+    </parent>
 
     <groupId>org.apache.plc4x.merlot.archiver</groupId>
     <artifactId>org.apache.plc4x.merlot.archiver</artifactId>
@@ -34,6 +34,19 @@
     <name>PLC4J: Merlot :: archiver :: Historical archive </name>
     <description>decanter OSGi blueprint bundle project.</description>
 
+    <properties>
+
+        <lombok.version>1.18.46</lombok.version>
+        <junit.version>4.13.2</junit.version>
+        <junit-jupiter.version>6.0.3</junit-jupiter.version>
+        <mockito-junit-jupiter.version>5.23.0</mockito-junit-jupiter.version>
+        <wiremock.version>3.13.2</wiremock.version>
+        <wiremock-junit5.version>1.1.0</wiremock-junit5.version>
+        <junit-platform.version>6.0.3</junit-platform.version>
+        <cucumber-java.version>7.34.3</cucumber-java.version>
+        
<cucumber-junit-platform-engine.version>7.34.3</cucumber-junit-platform-engine.version>
+    </properties>
+    
     <build>
         <plugins>
             <plugin>
@@ -62,8 +75,8 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <configuration>
-                    <source>17</source>
-                    <target>17</target>
+                    <source>21</source>
+                    <target>21</target>
                     <maxmem>256M</maxmem>
                 </configuration>
             </plugin>
@@ -107,10 +120,10 @@
                                     
<file>src/main/cfg/org.apache.plc4x.merlot.collector.log-alarm.cfg</file>       
                                                                
                                     <type>cfg5</type>
                                 </artifact>                                    
                                                                                
               
-<!--                                <artifact>
-                                    
<file>src/main/cfg/org.apache.plc4x.merlot.pvcomplex.cfg</file>                 
                                                      
+                                <artifact>
+                                    
<file>src/main/cfg/org.apache.plc4x.merlot.loki.appender.cfg</file>             
                                                          
                                     <type>cfg7</type>
-                                </artifact>                                    
                                                                                
               -->
+                                </artifact>                                    
                                                                                
               
                             </artifacts>
                         </configuration>
                     </execution>
@@ -227,19 +240,19 @@
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-api</artifactId>
-            <version>5.10.3</version>
+            <version>${junit-jupiter.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-params</artifactId>
-            <version>5.10.3</version>
+            <version>${junit-jupiter.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-engine</artifactId>
-            <version>5.10.3</version>
+            <version>${junit-jupiter.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -266,15 +279,73 @@
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
-            <version>4.13.2</version>
+            <version>${junit.version}</version>
             <scope>test</scope>
             <type>jar</type>
         </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
-            <version>1.18.42</version>
+            <version>${lombok.version}</version>
             <type>jar</type>
         </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.decanter</groupId>
+            <artifactId>org.apache.karaf.decanter.api</artifactId>
+            <version>2.10.0</version>
+            <type>bundle</type>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <version>${mockito-junit-jupiter.version}</version>
+            <scope>test</scope>
+        </dependency>
+       
+        <dependency>
+            <groupId>org.wiremock</groupId>
+            <artifactId>wiremock</artifactId>
+            <version>${wiremock.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>de.mkammerer.wiremock-junit5</groupId>
+            <artifactId>wiremock-junit5</artifactId>
+            <version>${wiremock-junit5.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <version>${junit-platform.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-suite-api</artifactId>
+            <version>${junit-platform.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.cucumber</groupId>
+            <artifactId>cucumber-java</artifactId>
+            <version>${cucumber-java.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.cucumber</groupId>
+            <artifactId>cucumber-junit-platform-engine</artifactId>
+            <version>${cucumber-junit-platform-engine.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-suite-engine</artifactId>
+            <version>${junit-platform.version}</version> <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/cfg/org.apache.plc4x.merlot.loki.appender.cfg
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/cfg/org.apache.plc4x.merlot.loki.appender.cfg
new file mode 100644
index 0000000..2ca9a72
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/cfg/org.apache.plc4x.merlot.loki.appender.cfg
@@ -0,0 +1,39 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+#Server Parameters
+#loki.url: Loki server IP address and port
+#loki.username: User configured on the Loki server
+#loki.password: Password configured on the Loki server
+
+################################################################################
+#loki.url=http://127.0.0.1:3100/loki/api/v1/push
+
+# Authentication (optional)
+#loki.username=loki
+#loki.password=loki
+
+#Allowed topics (separated by commas). General topics: merlot/*, phoebus/*, 
iotdb/*, decanter/*
+#loki.topics=decanter/collect/log,decanter/collect/metric,phoebus/data/test,merlot/archiver/test
+
+# Static labels (always added)
+#loki.label.job=merlot
+#loki.label.env=dev
+#loki.label.app=plc4x-archiver
+#loki.label.logLevel=INFO
+#loki.label.component=archiver
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotLokiAppender.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotLokiAppender.java
new file mode 100644
index 0000000..4ccf410
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotLokiAppender.java
@@ -0,0 +1,195 @@
+/*
+ * 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.plc4x.merlot.archiver.impl;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.nio.charset.StandardCharsets;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MerlotLokiAppender implements EventHandler, ManagedService {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(MerlotLokiAppender.class);
+
+    private String url;
+    private String username;
+    private String password;
+    private final Set<String> allowedTopics = new HashSet<>();
+    private final Map<String, String> labels = new HashMap<>();
+
+    private HttpClient httpClient;
+
+    public MerlotLokiAppender() {
+    }
+
+    public void init() {
+        LOGGER.info("Starting the Merlot-Loki appender");
+        httpClient = HttpClient.newBuilder()
+                .connectTimeout(Duration.ofSeconds(5))
+                .build();
+    }
+
+    public void destroy() {
+        LOGGER.info("Deleting the Merlot-Loki appender module");
+        httpClient.close();
+        httpClient.shutdownNow();
+    }
+
+    //Inject HttpClient for use with WireMock
+    public void setHttpClient(HttpClient httpClient) {
+        this.httpClient = httpClient;
+    }
+
+    ////Inject Url for use with WireMock
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    @Override
+    public void handleEvent(Event event) {
+        String topic = event.getTopic();
+
+        //The topic must match those listed in the “loki.topics” property of 
the corresponding cfg file
+        if (!allowedTopics.isEmpty() && !allowedTopics.contains(topic)) {
+            return;
+        }
+        //For events without loki.label.<name> tags, general tags are assigned 
as the primary tags (cfg file)
+        Map<String, String> eventLabels = new HashMap<>(this.labels);
+
+        StringBuilder msgBuilder = new StringBuilder();
+        msgBuilder.append("[").append(topic).append("] ");
+
+        for (String prop : event.getPropertyNames()) {
+            Object value = event.getProperty(prop);
+
+            //Static tags will be sent as message content to Loki.
+            if (prop.startsWith("loki.label.")) {
+                String labelName = prop.substring("loki.label.".length());
+                eventLabels.put(labelName, value.toString());
+            } else if (!prop.equals("event.topics") && 
!prop.equals("service.id") && !prop.equals("subject")) {
+                msgBuilder.append(prop).append("=").append(value).append(" ");
+            }
+        }
+
+        sendLokiServer(msgBuilder.toString().trim(), eventLabels);
+    }
+
+    public void sendLokiServer(String message, Map<String, String> 
dynamicLabels) {
+        if (this.url == null || this.url.isEmpty()) {
+            return;
+        }
+
+        try {
+            long timeNano = System.currentTimeMillis() * 1_000_000;
+
+            //Label classifier
+            StringBuilder labelsJson = new StringBuilder("{");
+
+            StringJoiner sj = new StringJoiner(",");
+
+            dynamicLabels.forEach((k, v) -> {
+                sj.add("\"" + escapeJson(k) + "\":\"" + escapeJson(v) + "\"");
+            });
+
+            labelsJson.append(sj.toString()).append("}");
+
+            //Payload to be sent to the Loki server, with the tags from the 
event as well as those saved in the cfg file.
+            String payload = "{\"streams\": [{\"stream\": " + labelsJson + ", 
\"values\": [ [\"" + timeNano + "\", \"" + escapeJson(message) + "\"] ]}]}";
+
+            //The message must be sent as JSON
+            HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+                    .uri(URI.create(this.url))
+                    .header("Content-Type", "application/json")
+                    .POST(HttpRequest.BodyPublishers.ofString(payload, 
StandardCharsets.UTF_8));
+
+            //If the server requires authentication, it must be encrypted
+            if (this.username != null && this.password != null) {
+                String auth = this.username + ":" + this.password;
+                String encoded = 
Base64.getEncoder().encodeToString(auth.getBytes());
+                requestBuilder.header("Authorization", "Basic " + encoded);
+            }
+
+            //Send the request asynchronously to avoid blocking the Karaf 
internal bus
+            httpClient.sendAsync(requestBuilder.build(), 
HttpResponse.BodyHandlers.ofString())
+                    .thenAccept(response -> {
+                        if (response.statusCode() != 204 && 
response.statusCode() != 200) {
+                            LOGGER.error("Loki rejected (Code {} ): {}", 
response.statusCode(), response.body());
+                        }
+                    })
+                    .exceptionally(e -> {
+                        LOGGER.error("Loki Connection Error: {}", 
e.getMessage());
+                        return null;
+                    });
+
+        } catch (Exception e) {
+            LOGGER.error("Critical Error in MerlotLokiAppender: {}", 
e.getMessage());
+        }
+    }
+
+    private String escapeJson(String s) {
+        if (s == null) {
+            return "";
+        }
+        //Escape character correction
+        return s.replace("\\", "\\\\")
+                .replace("\"", "\\\"")
+                .replace("\n", "\\n")
+                .replace("\r", "")
+                .replace("\t", "\\t");
+    }
+
+    @Override
+    public void updated(Dictionary<String, ?> properties) throws 
ConfigurationException {
+        if (properties == null || properties.isEmpty()) {
+            return;
+        }
+
+        //Connection parameters
+        this.url = (String) properties.get("loki.url");
+        this.username = (String) properties.get("loki.username");
+        this.password = (String) properties.get("loki.password");
+
+        // Topic configuration
+        this.allowedTopics.clear();
+        String strTopics = (String) properties.get("loki.topics");
+        if (strTopics != null) {
+            
Arrays.stream(strTopics.split(",")).map(String::trim).forEach(this.allowedTopics::add);
+        }
+
+        // Configuration of dynamic, static labels
+        this.labels.clear();
+        Enumeration<String> keys = properties.keys();
+        while (keys.hasMoreElements()) {
+            String key = keys.nextElement();
+            if (key.startsWith("loki.label.")) {
+                String labelName = key.substring("loki.label.".length());
+                this.labels.put(labelName, properties.get(key).toString());
+            }
+        }
+    }
+}
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/resources/OSGI-INF/blueprint/archiver-service.xml
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/resources/OSGI-INF/blueprint/archiver-service.xml
index d25c314..4e1e90a 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/resources/OSGI-INF/blueprint/archiver-service.xml
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/resources/OSGI-INF/blueprint/archiver-service.xml
@@ -30,7 +30,11 @@
     <reference
         id="refEventAdminService"
         interface="org.osgi.service.event.EventAdmin" availability="mandatory" 
timeout="1200"/>  
-        
+    <reference id="refMarshaller"
+               interface="org.apache.karaf.decanter.api.marshaller.Marshaller"
+               timeout="20000"
+               availability="mandatory">
+    </reference>     
     <bean id="MerlotGPClientBean" 
           class="org.apache.plc4x.merlot.archiver.impl.MerlotGPClientImpl"
           init-method="init"  
@@ -60,7 +64,17 @@
         <argument ref="blueprintBundleContext" />
         <argument ref="MerlotGPClientBean" />        
         <argument ref="refEventAdminService" />        
-    </bean>     
+    </bean> 
+    
+    <!--Loki Appender-->   
+    <bean id="MerlotLokiAppenderBean" 
+          class="org.apache.plc4x.merlot.archiver.impl.MerlotLokiAppender"
+          init-method="init"
+          destroy-method="destroy" 
+          scope="singleton"
+          activation="eager">
+       
+    </bean>  
     
     <bean id="MerlotPvHtcCollectorImplBean" 
           
class="org.apache.plc4x.merlot.archiver.impl.MerlotPvHtcCollectorImpl" 
@@ -81,7 +95,7 @@
         <argument ref="MerlotGPClientBean" />                      
     </bean> 
      
-<!--    <bean id="MerlotPvStructureRtCollectorBean" 
+    <!--    <bean id="MerlotPvStructureRtCollectorBean" 
           
class="org.apache.plc4x.merlot.archiver.impl.MerlotPVStructureRtCollector" 
           init-method="init"  
           destroy-method="destroy"                           
@@ -189,13 +203,26 @@
         </service-properties>
     </service>  
     
+    <service ref="MerlotLokiAppenderBean" auto-export="interfaces">
+        <service-properties>
+            <entry key="service.pid" 
value="org.apache.plc4x.merlot.loki.appender"/>  
+            <entry key="event.topics">
+                <array value-type="java.lang.String">
+                    <value>merlot/*</value>
+                    <value>phoebus/*</value>
+                    <value>iotdb/*</value>
+                    <value>decanter/*</value>
+                </array>
+            </entry>
+        </service-properties>
+    </service>        
     <service ref="MerlotPvRtCollectorImplBean" auto-export="interfaces">
         <service-properties>
             <entry key="service.pid" value="org.apache.plc4x.merlot.pvrt"/>  
         </service-properties>
     </service>        
     
-<!--    <service ref="MerlotPvStructureRtCollectorBean" 
auto-export="interfaces">
+    <!--    <service ref="MerlotPvStructureRtCollectorBean" 
auto-export="interfaces">
         <service-properties>
             <entry key="service.pid" 
value="org.apache.plc4x.merlot.pvcomplex"/>  
         </service-properties>
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/runner/MerlotLokiAppenderRunnerTest.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/runner/MerlotLokiAppenderRunnerTest.java
new file mode 100644
index 0000000..6c610e5
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/runner/MerlotLokiAppenderRunnerTest.java
@@ -0,0 +1,16 @@
+package runner;
+
+import org.junit.platform.suite.api.ConfigurationParameter;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectClasspathResource;
+import org.junit.platform.suite.api.Suite;
+
+@Suite
+@IncludeEngines("cucumber")
+@SelectClasspathResource("features")
+@ConfigurationParameter(key = "cucumber.glue", value = "stepdefinition")
+@ConfigurationParameter(key = "cucumber.snippet-type", value = "camelcase")
+public class MerlotLokiAppenderRunnerTest {
+}
+
+
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/stepdefinition/LokiAppenderDefinition.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/stepdefinition/LokiAppenderDefinition.java
new file mode 100644
index 0000000..5692b1c
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/stepdefinition/LokiAppenderDefinition.java
@@ -0,0 +1,71 @@
+package stepdefinition;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import io.cucumber.java.en.Given;
+import io.cucumber.java.en.Then;
+import io.cucumber.java.en.When;
+import org.apache.plc4x.merlot.archiver.impl.MerlotLokiAppender;
+import org.apache.plc4x.merlot.archiver.impl.MerlotPvRtCollectorImpl;
+import org.slf4j.LoggerFactory;
+
+import java.net.http.HttpClient;
+import java.util.Map;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.*;
+import static com.github.tomakehurst.wiremock.client.WireMock.containing;
+import static com.github.tomakehurst.wiremock.client.WireMock.matching;
+import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
+
+public class LokiAppenderDefinition {
+    private static final org.slf4j.Logger LOGGER = 
LoggerFactory.getLogger(LokiAppenderDefinition.class);
+
+    private final MerlotLokiAppender mLoki = new MerlotLokiAppender();
+    private final WireMockServer wireMockServer = new WireMockServer(3100);
+    private final HttpClient httpClient = HttpClient.newHttpClient();
+
+    private Map<String, String> labels;
+
+
+    @Given("Labels supplied for the message {string}, {string}, {string}, 
{string}")
+    public void labelsSuppliedForTheMessage(String label1, String label2, 
String label3, String label4) {
+        mLoki.setHttpClient(httpClient);
+        LOGGER.info("Labels: {} {} {} {}", label1, label2, label3, label4);
+        //Labels
+        labels = Map.of(label1, label2, label3, label4);
+
+    }
+    @Given("Url Loki Server {string}")
+    public void url(String url) {
+        mLoki.setUrl(url);
+        wireMockServer.start();
+        WireMock.configureFor("localhost", 3100);
+    }
+
+    @When("The server returned an HTTP {int} response")
+    public void theServerReturnedAnHTTPResponse(Integer code) {
+        //Stub for POST requests to the URL /loki/api/v1/push
+        wireMockServer.stubFor(WireMock.post(urlEqualTo("/loki/api/v1/push"))
+                .willReturn(aResponse()
+                        .withStatus(code)
+                        .withBody("Successfully pushed")));
+        //Send data to the simulated “Loki Server”
+        mLoki.sendLokiServer("Test Message", labels);
+
+
+    }
+    @Then("The instance displays “Log sent”")
+    public void theInstanceDisplaysLogSent() {
+        // Verify that at least one HTTP request was received during the test
+        verify(postRequestedFor(urlEqualTo("/loki/api/v1/push"))
+                .withHeader("Content-Type", matching("application/json.*"))
+                .withRequestBody(containing("Test Message"))
+                .withRequestBody(containing("\"job\":\"test\"")));
+
+
+        //Stop server
+        wireMockServer.stop();
+        LOGGER.info("Successfully pushed");
+    }
+
+}
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/resources/features/loki.feature
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/resources/features/loki.feature
new file mode 100644
index 0000000..4f828ab
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/resources/features/loki.feature
@@ -0,0 +1,6 @@
+Feature: Sending data to the Loki server
+  Scenario: Build Message
+    Given Labels supplied for the message "job", "test", "env", "dev"
+    And Url Loki Server "http://localhost:3100/loki/api/v1/push";
+    When The server returned an HTTP 204 response
+    Then The instance displays “Log sent”
\ No newline at end of file
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.features/src/main/feature/feature.xml
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.features/src/main/feature/feature.xml
index 987c791..d249b00 100755
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.features/src/main/feature/feature.xml
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.features/src/main/feature/feature.xml
@@ -36,6 +36,7 @@
         <feature prerequisite="true">pax-web-karaf</feature>        
         
         <feature>plc4x-epics-gpclient</feature>               
+        <feature>plc4x-kafka</feature>               
         
         
<bundle>wrap:mvn:org.osgi/org.osgi.service.device/1.1.0/$Export-Package=*;version=&quot;1.1.0&quot;,!*</bundle>
   
                  
@@ -257,7 +258,7 @@
         <configfile 
finalname="/etc/org.apache.plc4x.merlot.decanter-iotdb.cfg">mvn:org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver/${project.version}/cfg</configfile>
 
         <configfile 
finalname="/etc/org.apache.plc4x.merlot.pvhtc-iotdb.cfg">mvn:org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver/${project.version}/cfg4</configfile>
         <configfile 
finalname="/etc/org.apache.plc4x.merlot.pvrt-grafana.cfg">mvn:org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver/${project.version}/cfg6</configfile>
-        <!--<configfile 
finalname="/etc/org.apache.plc4x.merlot.pvcomplex.cfg">mvn:org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver/${project.version}/cfg7</configfile>-->
+        <configfile 
finalname="/etc/org.apache.plc4x.merlot.loki.appender.cfg">mvn:org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver/${project.version}/cfg7</configfile>
         <configfile 
finalname="/etc/org.apache.plc4x.merlot.iotdb.cfg">mvn:org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver/${project.version}/cfg3</configfile>
        
         <configfile 
finalname="/etc/org.apache.plc4x.merlot.collector.log-alarm.cfg">mvn:org.apache.plc4x.merlot.archiver/org.apache.plc4x.merlot.archiver/${project.version}/cfg3</configfile>
        
     </feature>    
@@ -728,6 +729,13 @@
         <bundle 
start-level="30">mvn:org.apache.activemq/artemis-features/2.30.0</bundle>     
     </feature>  
     
+    <feature name='plc4x-kafka' description='${project.name}' 
version='${project.version}'>
+        <bundle 
start-level="10">wrap:mvn:org.apache.kafka/kafka-clients/${kafka.version}/$Bundle-SymbolicName=kafka-client&amp;Bundle-Version=${kafka.version}&amp;Bundle-ManifestVersion=&quot;2&quot;&amp;Export-Package=*;version=&quot;${kafka.version}&quot;,!*</bundle>
   
+        <bundle 
start-level="10">wrap:mvn:org.apache.karaf.decanter.collector/org.apache.karaf.decanter.collector.utils/2.12.0/$Bundle-SymbolicName=decanter-collector-utils&amp;Bundle-Version=2.12.0&amp;Bundle-ManifestVersion=&quot;2&quot;&amp;Export-Package=*;version=&quot;2.12.0&quot;,!*</bundle>
   
+        <bundle 
start-level="30">mvn:org.apache.plc4x.merlot.kafka/org.apache.plc4x.merlot.kafka/${project.version}</bundle>
   
+        <configfile 
finalname="/etc/org.apache.plc4x.merlot.kafka.collector-alarm.cfg">mvn:org.apache.plc4x.merlot.kafka/org.apache.plc4x.merlot.kafka/${project.version}/cfg</configfile>
+    </feature>  
+    
     <feature name="plc4x-iotdb" version="${project.version}"> 
         <!--<bundle 
start-level="60">mvn:org.apache.iotdb/iotdb-session/${iotdb.session.version}</bundle>-->
  
         <bundle 
start-level="60">wrap:mvn:org.apache.iotdb/iotdb-session/${iotdb.session.version}/$Bundle-SymbolicName=iotdb-session&amp;Bundle-Version=${iotdb.session.version}&amp;Bundle-ManifestVersion=&quot;2&quot;&amp;Export-Package=*;version=&quot;${iotdb.session.version}&quot;,!*</bundle>
diff --git a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/pom.xml 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/pom.xml
index c42666e..ad3e576 100644
--- a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/pom.xml
+++ b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/pom.xml
@@ -97,7 +97,7 @@
                         <configuration>
                             <artifacts>
                                 <artifact>
-                                    
<file>src/main/cfg/org.apache.plc4x.merlot.kafka.collector.cfg</file>
+                                    
<file>src/main/cfg/org.apache.plc4x.merlot.kafka.collector-alarm.cfg</file>
                                     <type>cfg</type>
                                 </artifact>
                             </artifacts>
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/cfg/org.apache.plc4x.merlot.kafka.collector.cfg
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/cfg/org.apache.plc4x.merlot.kafka.collector-alarm.cfg
similarity index 96%
rename from 
plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/cfg/org.apache.plc4x.merlot.kafka.collector.cfg
rename to 
plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/cfg/org.apache.plc4x.merlot.kafka.collector-alarm.cfg
index 15ebd09..3bc044d 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/cfg/org.apache.plc4x.merlot.kafka.collector.cfg
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/cfg/org.apache.plc4x.merlot.kafka.collector-alarm.cfg
@@ -76,10 +76,10 @@
 # For SASL, you have to configure Java System property as explained in 
http://kafka.apache.org/documentation.html#security_ssl
 
 # message type (text to use the unmarshaller or raw to not use it)
-message.type=text
+#message.type=text
 
 # Unmarshaller to use (json is recommended)
-unmarshaller.target=(dataFormat=json)
+#unmarshaller.target=(dataFormat=json)
 
 # custom fields
-fields.remove.request-size-max=remove
\ No newline at end of file
+#fields.remove.request-size-max=remove
\ No newline at end of file
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/java/org/apache/plc4x/merlot/kafka/core/MerlotKafkaManagedServiceFactory.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/java/org/apache/plc4x/merlot/kafka/core/MerlotKafkaManagedServiceFactory.java
index 5a41e00..65ed744 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/java/org/apache/plc4x/merlot/kafka/core/MerlotKafkaManagedServiceFactory.java
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/java/org/apache/plc4x/merlot/kafka/core/MerlotKafkaManagedServiceFactory.java
@@ -25,8 +25,6 @@ import 
org.apache.plc4x.merlot.kafka.api.MerlotDecanterCollector;
 import org.apache.plc4x.merlot.kafka.impl.MerlotKafkaDecanterCollectorImpl;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.cm.ManagedServiceFactory;
@@ -36,13 +34,11 @@ import org.slf4j.LoggerFactory;
 
 
 public class MerlotKafkaManagedServiceFactory implements ManagedServiceFactory 
{
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(MerlotKafkaManagedServiceFactory.class); 
-    private static String FILTER_COLLECTOR =  "(&(" + Constants.OBJECTCLASS + 
"=" + MerlotDecanterCollector.class.getName() + ")" +
-                        "(" + Constants.SERVICE_PID + "=*))";   
-    
-    private final BundleContext ctx;    
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(MerlotKafkaManagedServiceFactory.class);
+
+    private final BundleContext ctx;
     private final EventAdmin dispatcher;
-    private final Unmarshaller unmarshaller;  
+    private  Unmarshaller unmarshaller;
     private Map<String,ServiceRegistration> services = new HashMap<>();
 
     public MerlotKafkaManagedServiceFactory(BundleContext ctx, EventAdmin 
dispatcher, Unmarshaller unmarshaller) {
@@ -50,17 +46,21 @@ public class MerlotKafkaManagedServiceFactory implements 
ManagedServiceFactory {
         this.dispatcher = dispatcher;
         this.unmarshaller = unmarshaller;
     }
-    
+
     @Override
     public String getName() {
-        return "Prueba";
+        return "Merlot Kafka Managed Service Factory";
     }
 
     @Override
     public void updated(String pid, Dictionary<String, ?> properties) throws 
ConfigurationException {
-        System.out.println("Registering service: " + pid);
+        LOGGER.info("Registering service: {}", pid);
         deleted(pid);
-        MerlotKafkaDecanterCollectorImpl bundle = new 
MerlotKafkaDecanterCollectorImpl(dispatcher, unmarshaller);        
+        MerlotKafkaDecanterCollectorImpl bundle = new 
MerlotKafkaDecanterCollectorImpl(dispatcher, unmarshaller);
+
+        bundle.activate(pid, (Dictionary<String, Object>) properties);
+        bundle.init();
+        
         Hashtable<String, String> serviceProperties = new Hashtable<>();
         serviceProperties.put(Constants.SERVICE_PID, pid);
         ServiceRegistration registration = 
ctx.registerService(MerlotDecanterCollector.class, bundle, serviceProperties);
@@ -70,13 +70,25 @@ public class MerlotKafkaManagedServiceFactory implements 
ManagedServiceFactory {
     @Override
     public void deleted(String pid) {
         LOGGER.info("Removing service: " + pid);
-        if (services.containsKey(pid)) {
-            MerlotDecanterCollector bundle = (MerlotDecanterCollector) 
ctx.getService(services.get(pid).getReference());
-            bundle.destroy();
-            services.get(pid).unregister();
+        ServiceRegistration registration = services.remove(pid);
+        if (registration != null) {
+            try {
+                MerlotDecanterCollector collector = (MerlotDecanterCollector) 
ctx.getService(registration.getReference());
+                if (collector != null) {
+                    collector.destroy();
+                }
+                registration.unregister();
+            } catch (Exception e) {
+                LOGGER.error("Error al eliminar el servicio {}", pid, e);
+            }
         }
     }
-    
-    
-    
+
+    public void destroy() {
+        LOGGER.info("Destroying MerlotKafkaManagedServiceFactory, cleaning up 
{} services", services.size());
+        for (String pid : services.keySet().toArray(new String[0])) {
+            deleted(pid);
+        }
+    }
+
 }
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/java/org/apache/plc4x/merlot/kafka/impl/MerlotKafkaDecanterCollectorImpl.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/java/org/apache/plc4x/merlot/kafka/impl/MerlotKafkaDecanterCollectorImpl.java
index 3075260..c6782c2 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/java/org/apache/plc4x/merlot/kafka/impl/MerlotKafkaDecanterCollectorImpl.java
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/java/org/apache/plc4x/merlot/kafka/impl/MerlotKafkaDecanterCollectorImpl.java
@@ -24,10 +24,17 @@ import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import org.apache.kafka.clients.consumer.ConsumerRecord;
 import org.apache.kafka.clients.consumer.ConsumerRecords;
 import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.kafka.common.errors.WakeupException;
 import org.apache.karaf.decanter.api.marshaller.Unmarshaller;
 import org.apache.karaf.decanter.collector.utils.PropertiesPreparator;
 import org.apache.plc4x.merlot.kafka.api.MerlotDecanterCollector;
@@ -39,35 +46,53 @@ import org.slf4j.LoggerFactory;
 
 
 public class MerlotKafkaDecanterCollectorImpl implements 
MerlotDecanterCollector, Runnable {
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(MerlotKafkaDecanterCollectorImpl.class);  
-    
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(MerlotKafkaDecanterCollectorImpl.class);
+
     private String topic;
     private String eventAdminTopic;
-    private boolean consuming = false;
+    private volatile boolean consuming = false;
+    private final AtomicBoolean shutdownInitiated = new AtomicBoolean(false);
     private String messageType;
-    
-    private Dictionary<String, Object> properties;    
+
+    private Dictionary<String, Object> properties;
     private KafkaConsumer<String, String> consumer;
-    
+
     private final EventAdmin dispatcher;
-    private final Unmarshaller unmarshaller;
+    private  Unmarshaller unmarshaller;
+    private ExecutorService executor;
 
     public MerlotKafkaDecanterCollectorImpl(EventAdmin dispatcher, 
Unmarshaller unmarshaller) {
         this.dispatcher = dispatcher;
         this.unmarshaller = unmarshaller;
     }
-    
+
     @Override
     public void init() {
-        consuming = true;  
-        Executors.newSingleThreadExecutor().execute(this);          
+        consuming = true;
+       this.executor = Executors.newSingleThreadExecutor();
+       this.executor.execute(this);
     }
 
     @Override
     public void destroy() {
         consuming = false;
-    }    
-    
+        shutdownInitiated.set(true);
+        if (consumer != null) {
+            consumer.wakeup();
+        }
+        if (executor != null) {
+            executor.shutdown();
+            try {
+                if (!executor.awaitTermination(2, TimeUnit.SECONDS)) {
+                    executor.shutdownNow();
+                }
+            } catch (InterruptedException e) {
+                executor.shutdownNow();
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
     public void activate(String pid, Dictionary<String, Object> properties) {
         this.properties = properties;
         topic = getValue(properties, "topic", "decanter");
@@ -139,64 +164,154 @@ public class MerlotKafkaDecanterCollectorImpl implements 
MerlotDecanterCollector
 
         String sslKeystoreType = getValue(properties, "ssl.keystore.type", 
null);
         if (sslKeystoreType != null)
-            config.put("ssl.keystore.type", sslKeystoreType);  
-        
+            config.put("ssl.keystore.type", sslKeystoreType);
+
         ClassLoader originClassLoader = 
Thread.currentThread().getContextClassLoader();
         try {
             Thread.currentThread().setContextClassLoader(null);
             consumer = new KafkaConsumer<String, String>(config);
             String[] topics = topic.split(",");
-            for (String t:topics){
-                t = t.replaceAll("\\s+","");
+            for (int i = 0; i < topics.length; i++) {
+                topics[i] = topics[i].replaceAll("\\s+", "");
             }
             consumer.subscribe(Arrays.asList(topics));
         } finally {
             Thread.currentThread().setContextClassLoader(originClassLoader);
-        }                    
+        }
     }
-    
+
     @Override
     public void run() {
-        while (consuming) {
-            try {
-                consume();
-            } catch (Exception e) {
-                LOGGER.warn(e.getMessage(), e);
+        try {
+            while (consuming && !shutdownInitiated.get()) {
+                try {
+                    consume();
+                } catch (WakeupException e) {
+                } catch (Exception e) {
+                    LOGGER.info(e.getMessage(), e);
+                }
+            }
+        } finally {
+            if (consumer != null) {
+                try {
+                    consumer.close();
+                } catch (Exception e) {
+                    LOGGER.warn("Error closing Kafka consumer", e);
+                }
             }
         }
-    } 
-    
-    private void consume() throws UnsupportedEncodingException {
+    }
+
+    private void consume() {
         ConsumerRecords<String, String> records = 
consumer.poll(Duration.ofSeconds(1));
+
         if (records.isEmpty()) {
             return;
         }
+
         Map<String, Object> data = new HashMap<>();
-        data.put("type", "kafka");
-        
+        data.put("loki.label.job", "MerlotAlarmCollector");
+
         for (ConsumerRecord<String, String> record : records) {
-            String value = record.value();
-            if (messageType.equalsIgnoreCase("text")) {
-                ByteArrayInputStream is = new 
ByteArrayInputStream(value.getBytes("utf-8"));
-                data.putAll(unmarshaller.unmarshal(is));
-            } else {
-                data.put("payload", value);
+            if (!consuming) {
+                return;
             }
-        }
 
-        try {
-            PropertiesPreparator.prepare(data, properties);
-        } catch (Exception e) {
-            LOGGER.warn("Can't prepare data for the dispatcher", e);
+            //Data headers
+            String key = record.key();
+
+            //Alarm values
+            String value = record.value();
+
+            LOGGER.info("Key: {} Value: {}", key, value);
+
+            String pathPV = getPathPV(key);
+
+            //Loki paramaters
+            data.put("loki.label.topicalarm", getTopicAlarm(key));
+            data.put("alarm.pathpvname", pathPV);
+            data.put("loki.label.pvname", 
pathPV.substring(pathPV.indexOf("//") + 2));
+            data.put("loki.label.component", getComponent(key));
+            data.put("loki.label.severity", getSeverity(value));
+            data.put("alarm.value", getValueAlarm(value));
+
+
+            //Send event bus karaf
+            Event event = new Event(eventAdminTopic, data);
+            dispatcher.postEvent(event);
         }
+    }
 
-        Event event = new Event(eventAdminTopic, data);
-        dispatcher.postEvent(event);
-    }    
-    
+
+    //Initial parameters
     private String getValue(Dictionary<String, Object> config, String key, 
String defaultValue) {
         String value = (String)config.get(key);
         return (value != null) ? value :  defaultValue;
-    }    
-   
+    }
+
+
+    //Kafka message parameters
+    public static String getTopicAlarm(String keyText) {
+        if (keyText == null) return null;
+        String regex = ":/([^/]+)/";
+        Matcher matcher = Pattern.compile(regex).matcher(keyText);
+
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+
+        return null;
+    }
+    public static String getPathPV(String keyText) {
+        if (keyText == null) return null;
+
+        int indexEndProtocol = keyText.indexOf(":\\/\\/");
+        if (indexEndProtocol == -1) {
+            indexEndProtocol = keyText.indexOf("://");
+        }
+
+        if (indexEndProtocol != -1) {
+            int indexLastSlash = keyText.lastIndexOf("/", indexEndProtocol);
+
+            if (indexLastSlash != -1) {
+                return keyText.substring(indexLastSlash + 1).replace("\\/\\/", 
"//");
+            }
+        }
+        return null;
+    }
+    public static String getComponent(String keyText) {
+        if (keyText == null) return null;
+        String regex = "^[^:/]+:/[^/]+/(.+)/[a-zA-Z0-9]+:[\\\\/]{2}";
+
+        Matcher matcher = Pattern.compile(regex).matcher(keyText);
+
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+
+        return null;
+    }
+    public static String getSeverity(String valueText) {
+        if (valueText == null) return null;
+
+        String regex = "\"severity\"\\s*:\\s*\"([^\"]+)\"";
+
+        Matcher matcher = Pattern.compile(regex).matcher(valueText);
+
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+        return null;
+    }
+    public static String getValueAlarm(String valueText) {
+        if (valueText == null) return null;
+        String regex = "\"value\"\\s*:\\s*\"([^\"]+)\"";
+
+        Matcher matcher = Pattern.compile(regex).matcher(valueText);
+
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+        return null;
+    }
 }
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/resources/OSGI-INF/blueprint/kafka-service.xml
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/resources/OSGI-INF/blueprint/kafka-service.xml
index 69a97ec..125deed 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/resources/OSGI-INF/blueprint/kafka-service.xml
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.kafka/src/main/resources/OSGI-INF/blueprint/kafka-service.xml
@@ -27,11 +27,13 @@
         interface="org.apache.karaf.decanter.api.marshaller.Unmarshaller"
         timeout="20000"
         availability="mandatory">
-    </reference> 
+    </reference>
                                  
     <bean id="serviceBean" 
         
class="org.apache.plc4x.merlot.kafka.core.MerlotKafkaManagedServiceFactory"
-        scope="singleton">
+        scope="singleton"
+        destroy-method="destroy">
+
         <argument ref="blueprintBundleContext"/>
         <argument ref="refEventAdmin"/> 
         <argument ref="refMarshaller"/>                  
diff --git a/plc4j/tools/merlot/org.apache.plc4x.merlot/pom.xml 
b/plc4j/tools/merlot/org.apache.plc4x.merlot/pom.xml
index 9677963..d042bb5 100644
--- a/plc4j/tools/merlot/org.apache.plc4x.merlot/pom.xml
+++ b/plc4j/tools/merlot/org.apache.plc4x.merlot/pom.xml
@@ -201,8 +201,7 @@
                     </startupFeatures>
                     
                     <bootFeatures>
-                        <feature>org.apache.plc4x.merlot.features</feature>    
                                        
-                        <feature>decanter-appender-loki</feature>              
                              
+                        <feature>org.apache.plc4x.merlot.features</feature>   
                         <feature>wrap</feature>
                         <feature>classpath</feature>
                         <feature>shell</feature>
diff --git a/plc4j/tools/merlot/pom.xml b/plc4j/tools/merlot/pom.xml
index c101b64..3e333ea 100644
--- a/plc4j/tools/merlot/pom.xml
+++ b/plc4j/tools/merlot/pom.xml
@@ -100,6 +100,7 @@ under the License.
         <disruptor.version>4.0.0</disruptor.version>
         <maven-failsafe-plugin.version>3.5.3</maven-failsafe-plugin.version>   
 
 
+        <kafka.version>3.9.2</kafka.version>
         <!-- EPICS -->
         <epics.core.version>7.0.9</epics.core.version>
         <epics.util.version>1.0.7</epics.util.version>

Reply via email to