Skip to content

Logging

Logging is essential for anyone using the Shopware Migration Assistant. In case of failure, it enables users to find out why part of their data might be missing. Most of the logging takes place in the Converter classes each time they detect missing required values. Also, every exception will create a log entry automatically.

We use LogEntry objects for our logging, so it's easier to group logs/errors of the same type and get the corresponding amount. Here is an example of how the logging works in the CustomerConverter:

php
<?php declare(strict_types=1);

abstract class CustomerConverter extends ShopwareConverter
{
    /* ... */

    public function convert(
            array $data,
            Context $context,
            MigrationContextInterface $migrationContext
        ): ConvertStruct
    {
        $this->generateChecksum($data);
        $oldData = $data;
        $this->runId = $migrationContext->getRunUuid();

        $fields = $this->checkForEmptyRequiredDataFields($data, $this->requiredDataFieldKeys);

        if (!empty($fields)) {
            $this->loggingService->addLogEntry(new EmptyNecessaryFieldRunLog(
                $this->runId,
                DefaultEntities::CUSTOMER,
                $data['id'],
                implode(',', $fields)
            ));

            return new ConvertStruct(null, $oldData);
        }

        /* ... */
    }

    /* ... */
}

You can get the LoggingService from the service container. Use the addLogEntry method with a compatible instance of LogEntryInterface and save the logging later with saveLogging:

php
<?php declare(strict_types=1);

interface LoggingServiceInterface
{
    public function addLogEntry(LogEntryInterface $logEntry): void;

    public function saveLogging(Context $context): void;
}

Look at the already existing classes, which implement the LogEntryInterface to find one that fits your needs, just like the EmptyNecessaryFieldRunLog in the CustomerConverter example above. All the general LogEntry classes are located under the following namespace SwagMigrationAssistant\Migration\Logging\Log.

To create a custom LogEntry make sure you at least implement the LogEntryInterface or, if your log happens during a running migration, you can also extend your LogEntry by the BaseRunLogEntry.

php
<?php declare(strict_types=1);

namespace SwagMigrationAssistant\Migration\Logging\Log;

class EmptyNecessaryFieldRunLog extends BaseRunLogEntry
{
    private string $emptyField;

    public function __construct(string $runId, string $entity, string $sourceId, string $emptyField)
    {
        parent::__construct($runId, $entity, $sourceId);
        $this->emptyField = $emptyField;
    }

    public function getCode(): string
    {
        $entity = $this->getEntity();
        if ($entity === null) {
            return 'SWAG_MIGRATION_EMPTY_NECESSARY_FIELD';
        }

        return sprintf('SWAG_MIGRATION_EMPTY_NECESSARY_FIELD_%s', mb_strtoupper($entity));
    }

    public function getLevel(): string
    {
        return self::LOG_LEVEL_WARNING;
    }

    public function getTitle(): string
    {
        $entity = $this->getEntity();
        if ($entity === null) {
            return 'The entity has one or more empty necessary fields';
        }

        return sprintf('The %s entity has one or more empty necessary fields', $entity);
    }

    public function getParameters(): array
    {
        return [
            'entity' => $this->getEntity(),
            'sourceId' => $this->getSourceId(),
            'emptyField' => $this->emptyField,
        ];
    }

    public function getDescription(): string
    {
        $args = $this->getParameters();

        return sprintf(
            'The %s entity with the source id %s does not have the necessary data for the field(s): %s',
            $args['entity'],
            $args['sourceId'],
            $args['emptyField']
        );
    }

    public function getTitleSnippet(): string
    {
        return sprintf('%s.%s.title', $this->getSnippetRoot(), 'SWAG_MIGRATION__SHOPWARE_EMPTY_NECESSARY_DATA_FIELDS');
    }

    public function getDescriptionSnippet(): string
    {
        return sprintf('%s.%s.description', $this->getSnippetRoot(), 'SWAG_MIGRATION__SHOPWARE_EMPTY_NECESSARY_DATA_FIELDS');
    }
}

The important part here is the getCode method. It should not contain any details, otherwise, grouping won't work properly. Also, keep in mind to specify the English title and description in the respective getTitle and getDescription methods. Create corresponding snippets with the same content for both the getTitleSnippet and getDescriptionSnippet methods.

The English text is used in the international log file. Instead, snippets are used all over in the Administration to inform or guide the user. Parameters for the description should be returned by the getParameters method so the English description and snippets can both use them.