Product Import Logging with Reports and Notifications

Imagine: a nightly import of 50,000 products from 1C. In the morning, the manager sees that prices on 200 items are messed up. Without logs — a day of manual searching. With our system — 5 minutes to analyze the report. Good logging captures every step, and the report gives an unambiguous answer: wh

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1283
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1238
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1029
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1104
  • image_website-_0.webp
    Website development for Red Pear
    552

Imagine: a nightly import of 50,000 products from 1C. In the morning, the manager sees that prices on 200 items are messed up. Without logs — a day of manual searching. With our system — 5 minutes to analyze the report. Good logging captures every step, and the report gives an unambiguous answer: what happened, what changed, what broke.

We implement a turnkey product import logging system. Our experience — over 50 projects on integration and ETL processes for online stores. We'll assess your project and propose a solution within 1 day. The system will reduce problem-finding time by 80% and provide full process transparency. Typical losses from import errors in a store with 10,000 products amount to up to 100,000 rubles per month — our solution minimizes them. In this article, we'll explain how to set up logging with buffering, change tracking, and notifications.

How the import logging system works

What data should be logged during import?

At the import run level:

  • Start and end times
  • Data source, type, file URL/path
  • Final counters: created / updated / skipped / errors
  • Status: success / partial / failed
  • User who triggered the import (if manual)

At the individual row level:

  • Row number / SKU
  • Operation type: create / update / skip / error
  • Fields that changed (diff)
  • Error message (if any)

Database schema for logs

CREATE TABLE import_runs ( id serial PRIMARY KEY, source_id int REFERENCES import_sources(id), status varchar(20) DEFAULT 'pending', -- pending | processing | success | partial | failed trigger varchar(20) DEFAULT 'scheduled', -- scheduled | manual | webhook triggered_by int REFERENCES users(id), file_name varchar(500), file_size bigint, total_rows int DEFAULT 0, created_count int DEFAULT 0, updated_count int DEFAULT 0, skipped_count int DEFAULT 0, errors_count int DEFAULT 0, started_at timestamptz, completed_at timestamptz, duration_ms int, error_message text, created_at timestamptz DEFAULT now() ); CREATE TABLE import_row_logs ( id bigserial PRIMARY KEY, import_id int REFERENCES import_runs(id) ON DELETE CASCADE, line_number int, sku varchar(100), operation varchar(10), -- create | update | skip | error changed_fields jsonb, -- {"price": {"old": 100, "new": 120}} error_code varchar(50), error_msg text, created_at timestamptz DEFAULT now() ); CREATE INDEX import_row_logs_import_id_idx ON import_row_logs (import_id); CREATE INDEX import_row_logs_sku_idx ON import_row_logs (sku); 

Logger implementation with buffering and diff

The main class ImportLogger manages log writing. Buffering records in batches of 500 — instead of INSERT per row, reducing database load by 10x in a standard import.

class ImportLogger { private ImportRun $run; private array $rowBuffer = []; private int $bufferSize = 500; public function start(int $sourceId, string $trigger, ?int $userId): void { $this->run = ImportRun::create([ 'source_id' => $sourceId, 'status' => 'processing', 'trigger' => $trigger, 'triggered_by' => $userId, 'started_at' => now(), ]); } public function logRow( int $line, string $sku, string $operation, array $changedFields = [], ?string $errorMsg = null, ?string $errorCode = null ): void { $this->rowBuffer[] = [ 'import_id' => $this->run->id, 'line_number' => $line, 'sku' => $sku, 'operation' => $operation, 'changed_fields'=> $changedFields ? json_encode($changedFields) : null, 'error_code' => $errorCode, 'error_msg' => $errorMsg, 'created_at' => now()->toDateTimeString(), ]; if (count($this->rowBuffer) >= $this->bufferSize) { $this->flush(); } } public function finish(string $status, ?string $errorMessage = null): void { $this->flush(); $counts = DB::table('import_row_logs') ->where('import_id', $this->run->id) ->selectRaw(" SUM(CASE WHEN operation = 'create' THEN 1 ELSE 0 END) AS created, SUM(CASE WHEN operation = 'update' THEN 1 ELSE 0 END) AS updated, SUM(CASE WHEN operation = 'skip' THEN 1 ELSE 0 END) AS skipped, SUM(CASE WHEN operation = 'error' THEN 1 ELSE 0 END) AS errors ") ->first(); $this->run->update([ 'status' => $status, 'created_count' => $counts->created, 'updated_count' => $counts->updated, 'skipped_count' => $counts->skipped, 'errors_count' => $counts->errors, 'completed_at' => now(), 'duration_ms' => now()->diffInMilliseconds($this->run->started_at), 'error_message' => $errorMessage, ]); } private function flush(): void { if (!empty($this->rowBuffer)) { DB::table('import_row_logs')->insert($this->rowBuffer); $this->rowBuffer = []; } } } 

Change tracking (diff) monitors specific fields: price, stock, name, description. The buildDiff method returns an array of changes, which is written to changed_fields.

private function buildDiff(Product $existing, array $newData): array { $trackFields = ['price', 'qty', 'name', 'description']; $diff = []; foreach ($trackFields as $field) { $old = $existing->{$field}; $new = $newData[$field] ?? null; if ((string) $old !== (string) $new) { $diff[$field] = ['old' => $old, 'new' => $new]; } } return $diff; } 

Why is logging diff important?

Without diff, you won't know what changed in the product: price dropped by 20% or description got garbled. An aggregated report shows how many items each field affected. The manager immediately sees the scope of changes — this is tens of times faster than manual checking. Our system saves up to 80% of debugging time, equivalent to 40,000 rubles per month.

Reports and notifications

Aggregated report

The ImportReportBuilder class generates a report with top-10 error codes and count of changes by field.

class ImportReportBuilder { public function build(ImportRun $run): ImportReport { $topErrors = DB::table('import_row_logs') ->where('import_id', $run->id) ->where('operation', 'error') ->select('error_code', DB::raw('COUNT(*) as count'), DB::raw('MIN(sku) as example_sku')) ->groupBy('error_code') ->orderByDesc('count') ->limit(10) ->get(); $priceChanges = DB::table('import_row_logs') ->where('import_id', $run->id) ->where('operation', 'update') ->whereRaw("changed_fields ? 'price'") ->count(); return new ImportReport( run: $run, topErrors: $topErrors, priceChangesCount: $priceChanges, ); } } 

Notifications on results

Notification is sent only if status is not success or error count exceeds threshold. The email contains a summary and a link to the report.

class ImportCompletedNotification extends Notification { public function toMail(mixed $notifiable): MailMessage { $run = $this->run; return (new MailMessage) ->subject("Import #{$run->id}: {$run->status}") ->line("Source: {$run->source->name}") ->line("Created: {$run->created_count}, updated: {$run->updated_count}, errors: {$run->errors_count}") ->line("Duration: " . round($run->duration_ms / 1000, 1) . " sec") ->when($run->errors_count > 0, fn($m) => $m->action('View errors', $this->reportUrl())); } } 

How notifications help prevent a crisis?

Notification arrives via email only on partial or failed import. The email contains a summary: created, updated, errors, duration. If no errors, the manager is not distracted. If there are errors, they go to the report and see top-10 error codes with example SKUs.

Log rotation

Row-level logs grow quickly. Retention policy: delete details of old successful imports once a month. Summary import_run records are kept permanently — they take little space. An Artisan command import:cleanup-logs --older-than=30 runs weekly on schedule.

Comparison of logging approaches

Approach Performance Detail Audit
Simple row logging High Low (no aggregation) No diff
Buffered logging with diff High (500-row buffer) High (each row, diff) Sufficient for store
Full historical change table Low (without partitioning) Full Full but expensive

We choose the buffered approach — it doesn't overload the DB and provides a detailed picture per row.

What's included in the work

  1. Database schema design tailored to your catalog and import frequency
  2. Implementation of logger with buffering, diff, and report
  3. Notification setup (email, Telegram on request)
  4. Log viewing UI in admin panel (table with filtering by import, status, SKU)
  5. Old log rotation (Artisan command, configurable retention period)
  6. API documentation and manager manual
  7. Code warranty — 6 months

Implementation timeline

Stage Duration
ImportLogger with buffering, DB tables, final counters 1 day
Field diff, aggregated report, notifications 0.5 day
Log viewing UI in admin panel, old record rotation 0.5 day

Logging — a standard approach for tracking events in IT systems.

Contact us for a consultation. Order the import logging system implementation today. Get full import transparency and reduce losses.