Problem/Motivation

To solve #2842222: D7 Plain text fields incorrectly migrated to D8 as Text (formatted), we'd like to log a message to the migration message table.

Proposed resolution

Create a process plugin that logs a value. It should have a fluid interface and return the same value passed into it. It should have a single config parameter to configure the log level.

Remaining tasks

Source values of the plugin is the log or error message.

User interface changes

API changes

Data model changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

heddn created an issue. See original summary.

heddn’s picture

vasi’s picture

Here's a first stab at this, which someone else can try to finish. It even supports substituting text!

TODO:

* Better docs
* Figure out whether we want to log unconditionally (and use skip_on_empty to not log), or have an implicit condition (eg: if source is some value).
* Figure out how we want substitution to work. I chose to make the entire Row source available for substitution, as well as other things in a 'context' section. But that's pretty complex!
* Figure out where we should log to. Choices include:
* The dblog
* The IdMap message. This allows us to associate the message with a particular row, which is nice! But I think we can have only one message per row.
* The MigrateMessageInterface. I think this is what's shown in the UI. But there's no easy way to get it from a process plugin, so we'll need to build a way. Maybe with a new interface that MigrateExecutable can implement?
* Write tests


namespace Drupal\migrate\Plugin\migrate\process;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Logs a message about a migration, and then passes through the value.
 *
 * Available configuration keys:
 * - level: The message severity.
 * - message: The message, with placeholders.
 *
 * Examples:
 *
 * @code
 * process:
 *   show_notice:
 *     - plugin: log
 *       level: notice
 *       message: 'Item imported!'
 * @endcode
 *
 * @MigrateProcessPlugin(
 *   id = "log"
 * )
 */
class LogMessage extends ProcessPluginBase implements ContainerFactoryPluginInterface {

  /**
   * The logger.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;



  /**
   * EntityExists constructor.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Psr\Log\LoggerInterface $logger
   *   The logger.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->logger = $logger;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('logger.factory')->get('migrate')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    // TODO: Use the current MigrateMessage?
    // TODO: Log to the migrate message table?
    $process = empty($this->configuration['context']) ? []
      : $this->configuration['context'];
    $contextRow = clone $row;
    $migrate_executable->processRow($contextRow, $process);
    $contextItems = $contextRow->getDestination() + $contextRow->getSource();
    $context = [];
    foreach ($contextItems as $key => $value) {
      $context['@' . $key] = $value;
    }
    $this->logger->log(
      $this->configuration['level'],
      $this->configuration['message'],
      $context
    );

    // Return original value.
    return $value;
  }

}
cosmicdreams’s picture

That's really great work @vasi. I had an opportunity to talk to Lucas about this issue and it sounded like instead of using the system's logger to log these we want to use the MigrateExecutable's saveMessage() method. So like the patch I just attached. What do you think?

rakesh.gectcr’s picture

Status: Active » Needs review
rakesh.gectcr’s picture

Issue tags: +Needs tests

However, we needed add tests.

rakesh.gectcr’s picture

Had a light discussion with Mike,

Removed the constructor and create function. Because we are not using either of it. Looks like a simple straight forward one.

Tried rolling the test as well.

rakesh.gectcr’s picture

Oops new line is missing.

rakesh.gectcr’s picture

Issue tags: +Baltimore2017
ohthehugemanatee’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

Looks good to me. Also, lol at "buffalo" as the destination. :)

heddn’s picture

I think we need to be migrating 8 buffaloes. But that is just a small bikeshed, so +1 on RTBC.

vasi’s picture

If we log messages this way, will the messages be seen in the Migrate UI? That's the imagined use-case, so we should verify that.

ohthehugemanatee’s picture

If migrate UI doesn't read messages from the migrate log, that's migrate UI's problem. This patch correctly tests that the message makes it to the migrate log, which is the scope of this issue.

heddn’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs manual testing

@vasi, I think both ways of logging go to the same location. I'm not sure though if anything get's surfaced to the UI or not. Adding a tag for some manual testing. Part of the point of using this process plugin was in hopes we could throw errors at the user that would surface to the client in the web UI. If that isn't possible with the approach we are using, then we might need to log the errors differently?

heddn’s picture

Priority: Normal » Critical

Marking this critical since it is a pre-task for #2842222: D7 Plain text fields incorrectly migrated to D8 as Text (formatted), which is also critical.

catch’s picture

Does the logging actually block that issue, or is this something that could be worked on in parallel?

heddn’s picture

No, it actually blocks the other issue. We need to log an error when a couple things occur. This would let us do that.

cilefen’s picture

Could you expound on how the need for logging blocks? I read the comments on the blocked issue but it isn't clear to me.

rakesh.gectcr’s picture

@heddn,

After applying the patch#8

I am trying to do the manual testing, Trying to migrate users from a csv file , which is following (migrate_source_csv)

ID,name,email,firstname,lastname,Status,roles,logs
1,rakesh,rakesh.james@gmail.com,Rakesh,James,1,administrator,log1
2,james,james.rakesh@gmail.com,James,Rakesh,1,authenticated,log2
3,dummy,dummy@gmail.com,Dummy,User,1,testuser,log3

Here is yml file

langcode: en
status: true
dependencies: {  }
id: demo_user_migration
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags: null
migration_group: null
label: 'User migration from CSV'
source:
  plugin: csv
  path: /Users/rakeshjames/Sites/drupal/sites/default/files/demousers_migrate.csv
  header_row_count: 1
  keys:
    - ID
process:
  name: name
  mail: email
  roles: roles
  field_last_name: firstname
  field_first_name: lastname
  status: Status
  test:
    plugin: log
    source: logs
destination:
  plugin: 'entity:user'
migration_dependencies:
  required: {  }
  optional: {  }

which is migrating all the rows. and gives the following status

Group: Default (default)      Status  Total  Imported  Unprocessed  Last imported       
 demo_user_migration           Idle    3      3         0            2017-05-16 20:52:48 

I am trying to find the log, not able to find in the migration table. Am I missing something on the

test:
    plugin: log
    source: logs

?
I know its a stupidity to ask, still Where can I find the logs? :(

heddn’s picture

re #16/#18, I've updated the IS of the parent. One of the things we are doing is loudly logging an error to the migrating user if the fallback text filter doesn't exist.

re #19: to see log messages, run drush mmsg <migration_name>

But based on the fact that you didn't see anything spit out to the console when running the migration, this didn't run as we were hoping/expecting. It should throw something more visible to the screen.

catch’s picture

I understand that logging could/will block the ability to show a message to the user in the case of an error condition, but does it block anything else in that issue?

rakesh.gectcr’s picture

Status: Needs work » Needs review

@heddn

Cool, its working,

It was my bad, I was not aware of how to check the log, When i am checking

I am getting the following log message.

~  (8.4.x) $ drush mmsg demo_user_migration
 source_ids_hash                             level   message  
 7ad742edb7e866caa78ced1e4455d2e9cbd8adb207  1       log1     
 4e7c323d21b4e67732e755                                       
 2d3ec2b0c547e819346e6ae03f881fd9f5c978ff3c  1       log2     
 be29dfb807d40735e53703                                       
 12a042f72cad9a2a8c7715df0c7695d762975f0687  1       log3     
 d87f5d480725dae1432a6f

I uploaded a testing video in youtube, Please check the following link
Migration log process plugin manual testing

mikeryan’s picture

Assigned: Unassigned » mikeryan
mikeryan’s picture

Assigned: mikeryan » Unassigned
Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs manual testing
FileSize
83.38 KB

I modified d6_user.yml to log a message:

source:
  plugin: d6_user
  constants:
    log_message: 'The username of this user is '
process:
  ...
  formatted_message:
    plugin: concat
    source:
      - constants/log_message
      - name
  name:
    plugin: log
    source: '@formatted_message'

When running the upgrade process through the UI, the messages displayed as expected:

Code looks good, let's unblock the text format issue...

catch’s picture

The impression I got from #2842222: D7 Plain text fields incorrectly migrated to D8 as Text (formatted) was more of a validation step/drupal_set_message() warning before the migration happens, this looks like it will just be logged after the migration runs?

heddn’s picture

Issue tags: +Migrate critical

We were never planning to do a pre-audit in the parent issue. The logging here is only tell the end user that the fall back text formatter doesn't exist.

  • catch committed 0bff624 on 8.4.x
    Issue #2872812 by rakesh.gectcr, cosmicdreams, mikeryan: Create process...

  • catch committed 3899b98 on 8.3.x
    Issue #2872812 by rakesh.gectcr, cosmicdreams, mikeryan: Create process...

  • catch committed 95059ed on 8.4.x
    Issue #2872812 by rakesh.gectcr, cosmicdreams, mikeryan: Create process...

  • catch committed 4a427a4 on 8.3.x
    Issue #2872812 by rakesh.gectcr, cosmicdreams, mikeryan: Create process...
catch’s picture

Version: 8.4.x-dev » 8.3.x-dev
Status: Reviewed & tested by the community » Fixed

Hmm I guess if we skip the table that's also going to end up as a warning, but that feels like something that we can resolve in the main issue, so committed/pushed to 8.4.x and cherry-picked to 8.3.x

Made the following fixes on commit:

diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php
index d861d6d..bb5d04d 100644
--- a/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php
+++ b/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php
@@ -19,7 +19,7 @@ class LogTest extends KernelTestBase {
   public static $modules = ['migrate'];
 
   /**
-   * Test the Log plugin
+   * Test the Log plugin.
    */
   public function testLog() {
     $plugin = \Drupal::service('plugin.manager.migrate.process')
@@ -28,7 +28,7 @@ public function testLog() {
     $row = new Row();
     $log_message = "Testing the log message";
 
-    //Ensure the log is getting saved
+    // Ensure the log is getting saved.
     $saved_message = $plugin->transform($log_message, $executable, $row, 'buffalo');
     $this->assertSame($log_message, $saved_message);
   }
cosmicdreams’s picture

awesome!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.