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

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new bab67a10d0 Issue apache#2340 [Bug]: Beam - WriteToLog never logs when 
running via Beam-Direct (#8127)
bab67a10d0 is described below

commit bab67a10d01b76e6f93f30dde735d50fc87c1b04
Author: Sergio Ramazzina <[email protected]>
AuthorDate: Thu Aug 27 16:33:42 2026 +0200

    Issue apache#2340 [Bug]: Beam - WriteToLog never logs when running via 
Beam-Direct (#8127)
    
    - The converter reloaded the run configuration from the metadata, so the 
runtime
      log-level variable was missing and the level fell back to MINIMAL, 
swallowing
      BASIC-level messages: default to BASIC instead.
    - The per-transform pipeline's log channel was attached to a detached 
logging
      object, so its messages never reached the parent pipeline log: attach it 
to the
      parent Beam pipeline log channel when present, falling back to a 
standalone
      logging object otherwise.
    - Added a Beam Direct unit test and a Beam Direct integration test covering 
the
      Write to log transform.
    
    Fixes #2340
---
 .../beam_directrunner/0013-write-to-log.hpl        | 100 +++++++++++++++++
 .../beam_directrunner/main-0013-write-to-log.hwf   | 124 +++++++++++++++++++++
 plugins/engines/beam/pom.xml                       |   6 +
 .../hop/beam/core/transform/TransformFn.java       |  15 ++-
 .../HopPipelineMetaToBeamPipelineConverter.java    |   9 +-
 .../hop/beam/transform/WriteToLogPipelineTest.java |  85 ++++++++++++++
 .../apache/hop/beam/util/BeamPipelineMetaUtil.java |  44 ++++++++
 7 files changed, 379 insertions(+), 4 deletions(-)

diff --git a/integration-tests/beam_directrunner/0013-write-to-log.hpl 
b/integration-tests/beam_directrunner/0013-write-to-log.hpl
new file mode 100644
index 0000000000..628ccae7c1
--- /dev/null
+++ b/integration-tests/beam_directrunner/0013-write-to-log.hpl
@@ -0,0 +1,100 @@
+<?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.
+
+-->
+<pipeline>
+  <info>
+    <capture_transform_performance>N</capture_transform_performance>
+    
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
+    
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
+    <pipeline_type>Normal</pipeline_type>
+    <pipeline_status>-1</pipeline_status>
+    <parameters/>
+    <name>New pipeline</name>
+    <name_sync_with_filename>Y</name_sync_with_filename>
+    <created_user>-</created_user>
+    <modified_user>-</modified_user>
+    <created_date>2026/08/27 14:54:21.077</created_date>
+    <modified_date>2026/08/27 14:54:21.077</modified_date>
+  </info>
+  <transform>
+    <type>RowGenerator</type>
+    <name>Generate rows</name>
+    <never_ending>N</never_ending>
+    <interval_in_ms>5000</interval_in_ms>
+    <row_time_field>now</row_time_field>
+    <last_time_field>FiveSecondsAgo</last_time_field>
+    <limit>1</limit>
+    <fields>
+      <field>
+        <name>foo</name>
+        <type>String</type>
+        <format/>
+        <length>-1</length>
+        <precision>-1</precision>
+        <currency/>
+        <decimal/>
+        <group/>
+        <nullif>Hello Apache Hop!</nullif>
+        <set_empty_string>N</set_empty_string>
+      </field>
+    </fields>
+    <distribute>Y</distribute>
+    <copies>1</copies>
+    <GUI>
+      <xloc>224</xloc>
+      <yloc>160</yloc>
+    </GUI>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+  </transform>
+  <transform>
+    <type>WriteToLog</type>
+    <name>Write to log</name>
+    <displayHeader>Y</displayHeader>
+    <limitRows>N</limitRows>
+    <limitRowsNumber>0</limitRowsNumber>
+    <logmessage/>
+    <loglevel>Basic</loglevel>
+    <fields/>
+    <distribute>Y</distribute>
+    <copies>1</copies>
+    <GUI>
+      <xloc>480</xloc>
+      <yloc>160</yloc>
+    </GUI>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+  </transform>
+  <order>
+    <hop>
+      <from>Generate rows</from>
+      <to>Write to log</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <notepads/>
+  <attributes/>
+  <transform_error_handling/>
+</pipeline>
diff --git a/integration-tests/beam_directrunner/main-0013-write-to-log.hwf 
b/integration-tests/beam_directrunner/main-0013-write-to-log.hwf
new file mode 100644
index 0000000000..14f104fa60
--- /dev/null
+++ b/integration-tests/beam_directrunner/main-0013-write-to-log.hwf
@@ -0,0 +1,124 @@
+<?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.
+
+-->
+<workflow>
+  <name>main-0013-write-to-log</name>
+  <name_sync_with_filename>Y</name_sync_with_filename>
+  <created_user>-</created_user>
+  <modified_user>-</modified_user>
+  <created_date>2026/08/27 15:06:07.411</created_date>
+  <modified_date>2026/08/27 15:06:07.411</modified_date>
+  <parameters/>
+  <actions>
+    <action>
+      <repeat>N</repeat>
+      <schedulerType>0</schedulerType>
+      <intervalSeconds>0</intervalSeconds>
+      <intervalMinutes>60</intervalMinutes>
+      <DayOfMonth>1</DayOfMonth>
+      <weekDay>1</weekDay>
+      <minutes>0</minutes>
+      <hour>12</hour>
+      <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+      <name>Start</name>
+      <description/>
+      <type>SPECIAL</type>
+      <attributes/>
+      <xloc>96</xloc>
+      <yloc>176</yloc>
+      <parallel>N</parallel>
+      <attributes_hac/>
+    </action>
+    <action>
+      <filename>${PROJECT_HOME}/0013-write-to-log.hpl</filename>
+      <params_from_previous>N</params_from_previous>
+      <exec_per_row>N</exec_per_row>
+      <clear_rows>N</clear_rows>
+      <clear_files>N</clear_files>
+      <create_parent_folder>N</create_parent_folder>
+      <set_logfile>N</set_logfile>
+      <set_append_logfile>N</set_append_logfile>
+      <logfile/>
+      <logext/>
+      <add_date>N</add_date>
+      <add_time>N</add_time>
+      <loglevel>Basic</loglevel>
+      <wait_until_finished>Y</wait_until_finished>
+      <wait_timeout/>
+      <parameters>
+        <pass_all_parameters>Y</pass_all_parameters>
+      </parameters>
+      <run_configuration>local</run_configuration>
+      <name>0013-write-to-log.hpl</name>
+      <type>PIPELINE</type>
+      <attributes/>
+      <xloc>272</xloc>
+      <yloc>176</yloc>
+      <parallel>N</parallel>
+      <attributes_hac/>
+    </action>
+    <action>
+      <script>
+var txt = previous_result.getLogText();
+
+
+var ok = true;
+
+var expectedValues = [ 
+   "foo = Hello Apache Hop!"
+ ];
+
+for (var i = 0 ; i&lt;expectedValues.length ; i++) {
+  var expectedValue = expectedValues[i];
+  if ( !txt.contains(expectedValue)) {
+    ok = false;
+    log.logError("Value logged as '" + expectedValue + "' was not logged at 
least once");
+  }
+}
+
+ok;</script>
+      <name>Check log</name>
+      <description/>
+      <type>EVAL</type>
+      <attributes/>
+      <xloc>464</xloc>
+      <yloc>176</yloc>
+      <parallel>N</parallel>
+      <attributes_hac/>
+    </action>
+  </actions>
+  <hops>
+    <hop>
+      <from>Start</from>
+      <to>0013-write-to-log.hpl</to>
+      <evaluation>Y</evaluation>
+      <unconditional>Y</unconditional>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>0013-write-to-log.hpl</from>
+      <to>Check log</to>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+      <enabled>Y</enabled>
+    </hop>
+  </hops>
+  <notepads/>
+  <attributes/>
+</workflow>
diff --git a/plugins/engines/beam/pom.xml b/plugins/engines/beam/pom.xml
index fc6f029a09..215a417533 100644
--- a/plugins/engines/beam/pom.xml
+++ b/plugins/engines/beam/pom.xml
@@ -1643,6 +1643,12 @@
             <version>${project.version}</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.hop</groupId>
+            <artifactId>hop-transform-writetolog</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.kafka</groupId>
             <artifactId>kafka-clients</artifactId>
diff --git 
a/plugins/engines/beam/src/main/java/org/apache/hop/beam/core/transform/TransformFn.java
 
b/plugins/engines/beam/src/main/java/org/apache/hop/beam/core/transform/TransformFn.java
index 4347ca35eb..3500aa7895 100644
--- 
a/plugins/engines/beam/src/main/java/org/apache/hop/beam/core/transform/TransformFn.java
+++ 
b/plugins/engines/beam/src/main/java/org/apache/hop/beam/core/transform/TransformFn.java
@@ -38,7 +38,9 @@ import 
org.apache.hop.beam.engines.HopPipelineExecutionOptions;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.exception.HopRuntimeException;
 import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.core.logging.ILoggingObject;
 import org.apache.hop.core.logging.LoggingObject;
+import org.apache.hop.core.logging.LoggingRegistry;
 import org.apache.hop.core.metadata.SerializableMetadataProvider;
 import org.apache.hop.core.plugins.PluginRegistry;
 import org.apache.hop.core.plugins.TransformPluginType;
@@ -334,9 +336,16 @@ public class TransformFn extends TransformBaseFn {
 
     // Create the transformation...
     //
-    pipeline =
-        new LocalPipelineEngine(
-            pipelineMeta, variables, new 
LoggingObject("apache-beam-transform"));
+    // Attach the per-transform pipeline's log channel to the parent Beam 
pipeline channel so that
+    // messages logged by the wrapped transform (e.g. the "Write to log" 
transform) show up in the
+    // pipeline log instead of a detached channel. Fall back to a standalone 
logging object when the
+    // parent isn't in the local registry (e.g. on a distributed runner 
running in another JVM).
+    ILoggingObject parentLoggingObject =
+        LoggingRegistry.getInstance().getLoggingObject(parentLogChannelId);
+    if (parentLoggingObject == null) {
+      parentLoggingObject = new LoggingObject("apache-beam-transform");
+    }
+    pipeline = new LocalPipelineEngine(pipelineMeta, variables, 
parentLoggingObject);
     pipeline.setLogLevel(
         
context.getPipelineOptions().as(HopPipelineExecutionOptions.class).getLogLevel());
     pipeline.setMetadataProvider(pipelineMeta.getMetadataProvider());
diff --git 
a/plugins/engines/beam/src/main/java/org/apache/hop/beam/pipeline/HopPipelineMetaToBeamPipelineConverter.java
 
b/plugins/engines/beam/src/main/java/org/apache/hop/beam/pipeline/HopPipelineMetaToBeamPipelineConverter.java
index d1048c7fda..edf25b0bac 100644
--- 
a/plugins/engines/beam/src/main/java/org/apache/hop/beam/pipeline/HopPipelineMetaToBeamPipelineConverter.java
+++ 
b/plugins/engines/beam/src/main/java/org/apache/hop/beam/pipeline/HopPipelineMetaToBeamPipelineConverter.java
@@ -292,6 +292,13 @@ public class HopPipelineMetaToBeamPipelineConverter {
 
     pipelineOptions.setJobName(sanitizeJobName(pipelineMeta.getName()));
 
+    // The log level for the per-transform pipelines executed inside the Beam 
workers.
+    // The engine sets this as a runtime variable on its live run 
configuration, but this converter
+    // reloads a fresh copy of the run configuration from the metadata, so 
that runtime variable is
+    // not present here. When it is absent we default to BASIC (the documented 
default of
+    // HopPipelineExecutionOptions and the behaviour of the local pipeline 
engine); defaulting to a
+    // lower level such as MINIMAL would silently swallow BASIC-level 
transform logging (e.g. the
+    // "Write to log" transform) when running on Beam.
     pipelineOptions
         .as(HopPipelineExecutionOptions.class)
         .setLogLevel(
@@ -299,7 +306,7 @@ public class HopPipelineMetaToBeamPipelineConverter {
                 Const.NVL(
                     pipelineRunConfiguration.getVariable(
                         BeamConst.STRING_LOCAL_PIPELINE_FLAG_LOG_LEVEL),
-                    "MINIMAL")));
+                    LogLevel.BASIC.getCode())));
 
     pipelineOptions.setRunner(runnerClass);
   }
diff --git 
a/plugins/engines/beam/src/test/java/org/apache/hop/beam/transform/WriteToLogPipelineTest.java
 
b/plugins/engines/beam/src/test/java/org/apache/hop/beam/transform/WriteToLogPipelineTest.java
new file mode 100644
index 0000000000..690d520990
--- /dev/null
+++ 
b/plugins/engines/beam/src/test/java/org/apache/hop/beam/transform/WriteToLogPipelineTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.hop.beam.transform;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.hop.beam.util.BeamPipelineMetaUtil;
+import org.apache.hop.core.logging.HopLogStore;
+import org.apache.hop.pipeline.Pipeline;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.engine.EngineMetrics;
+import org.apache.hop.pipeline.engine.IEngineComponent;
+import org.apache.hop.pipeline.engine.IPipelineEngine;
+import org.apache.hop.pipeline.engine.PipelineEngineFactory;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Reproduction test for issue #2340: the "Write to log" transform produces no 
visible output in the
+ * pipeline log when the pipeline is executed on the Beam Direct runner (it 
works fine on the local
+ * runner).
+ */
+class WriteToLogPipelineTest extends PipelineTestBase {
+
+  private static final String MARKER = "HOP2340WRITETOLOGMARKER";
+
+  @Test
+  void testWriteToLogAppearsInPipelineLogOnBeamDirect() throws Exception {
+
+    PipelineMeta pipelineMeta =
+        BeamPipelineMetaUtil.generateWriteToLogPipelineMeta(
+            "beam-write-to-log", "INPUT", "WriteToLog", MARKER, 
metadataProvider);
+    pipelineMeta.lookupReferencesAfterLoading();
+
+    IPipelineEngine<PipelineMeta> pipeline =
+        PipelineEngineFactory.createPipelineEngine(
+            variables, NAME_RUN_CONFIG, metadataProvider, pipelineMeta);
+    pipeline.execute();
+    pipeline.waitUntilFinished();
+
+    // Guard: make sure rows actually flowed through the WriteToLog transform, 
so an empty-input
+    // regression can never masquerade as the #2340 bug.
+    //
+    EngineMetrics metrics = pipeline.getEngineMetrics();
+    Long writeToLogRead = null;
+    for (IEngineComponent component : metrics.getComponents()) {
+      if ("WriteToLog".equals(component.getName())) {
+        writeToLogRead = metrics.getComponentMetric(component, 
Pipeline.METRIC_READ);
+      }
+    }
+    if (writeToLogRead != null) {
+      assertTrue(
+          writeToLogRead > 0,
+          "WriteToLog should have read rows on the Beam Direct runner (read="
+              + writeToLogRead
+              + ")");
+    }
+
+    // What the user actually sees for this run: the log scoped to the 
pipeline's own log channel.
+    //
+    String pipelineLog =
+        HopLogStore.getAppender().getBuffer(pipeline.getLogChannelId(), 
true).toString();
+
+    assertTrue(
+        pipelineLog.contains(MARKER),
+        "The WriteToLog message '"
+            + MARKER
+            + "' should appear in the pipeline log when running on the Beam 
Direct runner, "
+            + "but it was absent (issue #2340).");
+  }
+}
diff --git 
a/plugins/engines/beam/src/test/java/org/apache/hop/beam/util/BeamPipelineMetaUtil.java
 
b/plugins/engines/beam/src/test/java/org/apache/hop/beam/util/BeamPipelineMetaUtil.java
index 576ee52cba..be48485839 100644
--- 
a/plugins/engines/beam/src/test/java/org/apache/hop/beam/util/BeamPipelineMetaUtil.java
+++ 
b/plugins/engines/beam/src/test/java/org/apache/hop/beam/util/BeamPipelineMetaUtil.java
@@ -24,6 +24,7 @@ import org.apache.hop.beam.transform.PipelineTestBase;
 import org.apache.hop.beam.transforms.io.BeamInputMeta;
 import org.apache.hop.beam.transforms.io.BeamOutputMeta;
 import org.apache.hop.core.Condition;
+import org.apache.hop.core.logging.LogLevel;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.ValueMetaAndData;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
@@ -42,6 +43,7 @@ import 
org.apache.hop.pipeline.transforms.mergejoin.MergeJoinMeta;
 import org.apache.hop.pipeline.transforms.streamlookup.StreamLookupMeta;
 import org.apache.hop.pipeline.transforms.switchcase.SwitchCaseMeta;
 import org.apache.hop.pipeline.transforms.switchcase.SwitchCaseTarget;
+import org.apache.hop.pipeline.transforms.writetolog.WriteToLogMeta;
 
 public class BeamPipelineMetaUtil {
 
@@ -93,6 +95,48 @@ public class BeamPipelineMetaUtil {
     return pipelineMeta;
   }
 
+  public static PipelineMeta generateWriteToLogPipelineMeta(
+      String pipelineName,
+      String inputTransformName,
+      String writeToLogTransformName,
+      String logMessage,
+      IHopMetadataProvider metadataProvider)
+      throws Exception {
+
+    IHopMetadataSerializer<FileDefinition> serializer =
+        metadataProvider.getSerializer(FileDefinition.class);
+    FileDefinition customerFileDefinition = 
createCustomersInputFileDefinition();
+    serializer.save(customerFileDefinition);
+
+    PipelineMeta pipelineMeta = new PipelineMeta();
+    pipelineMeta.setName(pipelineName);
+    pipelineMeta.setMetadataProvider(metadataProvider);
+
+    // Input transform reading the customers file
+    //
+    BeamInputMeta beamInputMeta = new BeamInputMeta();
+    beamInputMeta.setInputLocation(PipelineTestBase.INPUT_CUSTOMERS_FILE);
+    beamInputMeta.setFileDefinitionName(customerFileDefinition.getName());
+    TransformMeta beamInputTransformMeta = new 
TransformMeta(inputTransformName, beamInputMeta);
+    beamInputTransformMeta.setTransformPluginId("BeamInput");
+    pipelineMeta.addTransform(beamInputTransformMeta);
+
+    // Write to log transform, logging a distinctive message at BASIC level
+    //
+    WriteToLogMeta writeToLogMeta = new WriteToLogMeta();
+    writeToLogMeta.setLogLevel(LogLevel.BASIC);
+    writeToLogMeta.setDisplayHeader(true);
+    writeToLogMeta.setLogMessage(logMessage);
+    TransformMeta writeToLogTransformMeta =
+        new TransformMeta(writeToLogTransformName, writeToLogMeta);
+    writeToLogTransformMeta.setTransformPluginId("WriteToLog");
+    pipelineMeta.addTransform(writeToLogTransformMeta);
+    pipelineMeta.addPipelineHop(
+        new PipelineHopMeta(beamInputTransformMeta, writeToLogTransformMeta));
+
+    return pipelineMeta;
+  }
+
   public static PipelineMeta generateBeamGroupByPipelineMeta(
       String transname,
       String inputTransformName,

Reply via email to