(лайфхак) - полу-автоматическое обновление Номеров Строк В ЛОГАХ (для поиска багов в коде)

 

Итак, в процессе программирования я столкнулся с потребностью "писать номера строк" в Логах ! чтобы читая Журнал Логов - ориентироваться не только на показатели Логов, а и видеть: НОМЕР СТРОКИ особо-важных Логов !!!

Сперва это делал тупо-глупо ВРУЧНУЮ, например:

Print("DEBUG START (строка 2828): ExecuteInformFromPanel()");


...но потом перебирать 3500+ строк кода и заменять все устаревшие номера строк на новые - согласитесь - ДОЛГО и НЕУДОБНО !!!

Тогда , будучи пользователем ЧатаGPT я его начал телебонить - КАК по маркерам в текстовой строки - сделать АВТО-ЗАМЕНУ старого номера строки на новый ?!

ЧатаGPT честно ответил что АВТОМАТИЧЕСКИ этого сделать НЕЛЬЗЯ !
Но в Notepad++ есть возможность написать скрипт, который будет искать "шаблон" (например: "строка 1234") и менять <старое число > на <новое число > (т.е. обновлять номера строк) !

...порекомендовал поставить плагин "NppExec" , написал с десяток скриптов ... но увы - всё оказалось сложно и неработало ... потом сменили кирилицу (строка) на латиницу (stroka) но и это НЕ ПОМОГЛО !!!

Потом Искусственный Импровизатор предложил: можно попробовать сделать РУЧНОЙ ПОИСК/ЗАМЕНА ... и дал ВОЛШЕБНУЮ комбинацию для поиска "шаблона" и замены старого номера строки на новый !!!

... в сухом остатке после теста - пишу годный Лайфхак на случай если кому-то вдруг тоже надо (!?!)

Шаг 1 - подготовка:

1)      Сперва весь наш код из MetaEditor4-5 загоняем в Notedad++ и генерим номера строк В НАЧАЛЕ строки:
Ctrl+A – выделить всё !
ALT+C – Текст для вставки (задаём "пробел" - это даст пробел между номером строки и строкой) –> Ок


Ctrl+A – выделить всё !
ALT+C – Число для вставки : Исходное число: 1, Увеличение на: 1 –> Ок
(после каждой операции – ПЕРЕВЫДЕЛЯТЬ ВЕСЬ текст для корреткной генерации).

 Шаг 2 - работа по обновлению строк ВНУТРИ вашего текста:

2)      … после номерации строк: через Поиск/Замену выполняем п.4 (ниже),

3)      После перенумерации номеров строк В ЛОГАХ – перед возвращением кода в MetaEditor -->> обязательно удаляем номера строк В НАЧАЛЕ строк (чтобы компилятор кода не ругался на пустые числа в начале строк):

  •  ставим курсор В НАЧАЛО всего Блокнота в строку 1 !
  •  потом Alt+Shift+стрелками – делаем ВЕРТИКАЛЬНОЕ выделение всех чисел В НАЧАЛЕ строки и удаляем их (чтобы компилятор кода не ругался на пустые числа в начале строк) !

(!)  Основной процесс по обновлению номеров строк в Логах:

4) Один проход “обновить  любые  стр.N” 

Ctrl+H (Поиск / Замена):

Поиск (+поставить галочку "Регуляр.выражен."):

^(\s*)(\d+)([^\r\n]*?\bстр\.)\d+([^\r\n]*)(\R?)

 

Заменить на:

\1\2\3\2\4\5

 

Это:

·         сохраняет ведущие пробелы \1,
·         сохраняет номер слева \2,
·         меняет число после str. на \2,
·         сохраняет конец строки \5 (чтобы не было склеек).

 ​результат:


Было:
616 Print(StringFormat("PPB (стр.720): возврат отложен до ПОСЛЕ скриншота (oldScalePtPerBar=%s oldPointsPerBar=%.5f)",
стало:
616 Print(StringFormat("PPB (стр. 616): возврат отложен до ПОСЛЕ скриншота (oldScalePtPerBar=%s oldPointsPerBar=%.5f)",




Теперь номерация Логов стала проще и быстрее !

Пользуйтесь, если кому-то надо ! )
 
Vitaliy Kostrubko:



Теперь номерация Логов стала проще и быстрее !

Пользуйтесь, если кому-то надо ! )


 

 
Vitaliy Kostrubko:
Пользуйтесь, если кому-то надо ! )
А зачем?

Чем __LINE__ не устраивает?
 
Sergey Gridnev #:
А зачем?

Чем __LINE__ не устраивает?
... знал-бы раньше - не писал ! ))
Благодарю! тоже полезно!


Итого имеем уже 2 способа нумеровать логи:
* простой,
* и сложный ))))
 
Sergey Gridnev #:
__LINE__

Проверил ...

PrintFormat("(__LINE__) MEAS rot=900 max_tw=%d max_th=%d line_h=%d n=%d shift_y=%d => w_final=%d h_final=%d",
                  max_tw, max_th, line_h, n, shift_y, w_final, h_final);

Результат: 

2026.01.13 16:11:49.476    (EURUSD,M5)  (__LINE__) MEAS rot=900 max_tw=685 max_th=13 line_h=14 n=10 shift_y=70 => w_final=203 h_final=260


... но в другом варианте:

 Print(__LINE__);
      PrintFormat("(__LINE__) MEAS rot=900 max_tw=%d max_th=%d line_h=%d n=%d shift_y=%d => w_final=%d h_final=%d",
                  max_tw, max_th, line_h, n, shift_y, w_final, h_final);

всё получилось:

2026.01.13 16:15:02.898   (EURUSD,M5)   416
2026.01.13 16:15:02.898 (EURUSD,M5)     (__LINE__) MEAS rot=900 max_tw=646 max_th=13 line_h=14 n=4 shift_y=70 => w_final=131 h_final=260

... т.е. "416" - это действительно НОМЕР СТРОКИ !
... но тогда возникает вопрос:

"КАК номеровать" логи - чтобы номер строки был именно в строке с логом ?! (особенно для "PrintFormat")

... Нашёл ответ ...

PrintFormat("(стр.%d): MEAS rot=900 max_tw=%d max_th=%d line_h=%d n=%d shift_y=%d => w_final=%d h_final=%d",
            __LINE__, max_tw, max_th, line_h, n, shift_y, w_final, h_final);

Результат: 2026.01.13 16:30:57.628  (EURUSD,M5)	(стр.423): MEAS rot=900 max_tw=685 max_th=13 line_h=14 n=10 shift_y=70 => w_final=203 h_final=260
Так  __LINE__  выводится как число через  %d  (а не как текст в кавычках), и получается ровно префикс  (число):  перед логом.

 
Vitaliy Kostrubko #:
"КАК номеровать" логи - чтобы номер строки был именно в строке с логом ?! (особенно для "PrintFormat")
Научиться пользоваться форматной строкой.
 
Vitaliy Kostrubko #:
"КАК номеровать" логи - чтобы номер строки был именно в строке с логом ?! (особенно для "PrintFormat")

Можно и так:

PrintFormat("("__LINE__") MEAS rot=900 max_tw=%d max_th=%d line_h=%d n=%d shift_y=%d => w_final=%d h_final=%d",
                  max_tw, max_th, line_h, n, shift_y, w_final, h_final);

Если код разбит на достаточно компактные функции, то может быть удобнее выводить не номер строки кода, а название функции или метода с помощью макроса __FUNCTION__:

PrintFormat(__FUNCTION__" | Scale = %.2f", coeff);

Или использовать их вместе:

PrintFormat(__FUNCTION__" ("__LINE__") | Scale = %.2f", coeff);
 
Yuriy Bykov #:

Можно и так:

Если код разбит на достаточно компактные функции, то может быть удобнее выводить не номер строки кода, а название функции или метода с помощью макроса __FUNCTION__:

Или использовать их вместе:


... Как вариант - ДА, благодарю за комбо !)
... но порой бывает так, что функция находится в середине кода, а её вызов происходит в "конце" кода, и там могут быть "собрание логов" с разных функций, типа:
--------- Сборная инфа : ---------
* вызвали №1 ...
* позиция объекта 'такая-то'
... и т.д.
----------------------------------

поэтому __FUNCTION__ придётся пользоваться или "очень умело", или что-то в случае "сборных логов" -->> в комментах сразу (для себя) писать название функций которые в итоге и отображают в "сборных логах" свои результаты!

Например в разных функциях я пишу НЕ "Print..." , а :

AddToLog(StringFormat("...текст..." , переменные , значения которых нужно показать в "Сборном логе"));

и потом есть специальная функция: 

//+------------------------------------------------------------------+
//| AddToLog - Добавление строки в лог                               |
//+------------------------------------------------------------------+
void AddToLog(string text)
  {
   int size = ArraySize(logLines);
   ArrayResize(logLines, size + 1);
   logLines[size] = text;
// НЕ выводим в Print - только в массив для метки на скрине
  }

// ← НОВОЕ: Функция для вывода в журнал
void PrintToLog(string text)
  {
   Print(text);
  }

(которая собирает логи со всего кода, где-бы они нибыли, чтобы потом в функции "вызова" - отобразить логи (с разных частей кода) сразу в одном Блоке : "сборный лог") :

// ========== ВЫВОДИМ ДЕТАЛЬНЫЙ ЛОГ В ЖУРНАЛ ========== //<<-- строка 2848
   PrintToLog("==========================================");
   PrintToLog(">>> ПАРАМЕТРЫ ДЛЯ СКРИНЕРА (из Панели):");
   PrintToLog("==========================================");
   PrintToLog(StringFormat("g_PanelScreenWidthMode   = %s", g_PanelScreenWidthMode)); //<<-- из Функции #4
   PrintToLog(StringFormat("g_PanelBarsForScreenshot = %d", bars_count_param));//<<-- из Функции # 25
   PrintToLog(StringFormat("g_PanelScalePointsPerBar = %d", scale_param));//<<-- из Функции # 38
   PrintToLog(StringFormat("g_PanelUseBorders        = %s", g_PanelUseBorders ? "TRUE" : "FALSE"));
   PrintToLog("==========================================");
   PrintToLog(StringFormat("Коррекция Окно/Ratio     = %d px", g_PanelWindowWidthCorrection)); //<<-- из Функции #55
   PrintToLog(StringFormat("Коррекция Границы        = %d px", g_PanelBordersWidthCorrection)); //<<-- из Функции #56
   PrintToLog("==========================================");
   PrintToLog(">>> СКРИНЕР ВЫЗВАН ИЗ ПАНЕЛИ <<<");
   PrintToLog("==========================================");
   if(g_BtnSignalState)
      PrintToLog(">>> СИГНАЛ <<<");
   PrintToLog(mode_text);
   PrintToLog(StringFormat("Размеры: %d x %d px (коррекция +%d)", width_px, height_px, correction));
   PrintToLog(StringFormat("Баров в скрине: %d", actual_bars));
   if(scale_param > 0)
      PrintToLog(StringFormat("Масштаб: %d п/бар (ЗАДАН ВРУЧНУЮ)", scale_param)); //<<-- из Функции #74
   else
      PrintToLog("Масштаб: с графика (автоматический)"); //<<-- из Функции # 75
   PrintToLog(StringFormat("Папка: %s", symbol_folder)); //<<-- из Функции # 88
   PrintToLog("==========================================");

... и в логах вижу финальный результат:

==========================================
>>> ПАРАМЕТРЫ ДЛЯ СКРИНЕРА (из Панели):
==========================================
g_PanelScreenWidthMode   = RATIO_WINDOW
g_PanelBarsForScreenshot = 0
g_PanelScalePointsPerBar = 0.00000
g_PanelUseBorders        = FALSE
==========================================
Коррекция Окно/Ratio     = 150 px
Коррекция Границы        = 150 px
==========================================
>>> СКРИНЕР ВЫЗВАН ИЗ ПАНЕЛИ <<<
==========================================
Режим: 'Окно'
Размеры: 1795 x 924 px (коррекция +150)
Баров в скрине: 412
Масштаб: с графика (автоматический) (стр.3349)
Папка: Screenshots\EURUSD
==========================================

... как видно - такая "сборка лого" намного компактнее, чем отображать все Логи "каждый в моменте вызова" со ссылкой на строку "функции откуда вызвался Лог", п.ч. тогда будет перемешка всех логов, и среди пачки нумерованных логов не сразу найдёшь нужную информацию ! ...


По сути - Ваше предложение на счёт применения : PrintFormat(__FUNCTION__" ("__LINE__")
интересное , и даже в моей "сборке логов" оно МОЖЕТ найти применение ! ... я подумаю над этим! 
Спасибо за идею ! )

 

Мне кажется, вы с ИИ-помощником немного перемудрили. Если судить по тому коду, что вы показали, то видно следующее:

  • функция AddToLog() не используется нигде, поэтому может быть удалена;
  • функция PrintToLog() не делает ничего, кроме вызова функции Print(), поэтому может быть удалена и заменена вызовом функции Print()
  • вызов функции Print(StringFormat("строка с параметрами", [значения параметров, ...])) эквивалентен вызову PrintFormat("строка с параметрами", [значения параметров, ...]).
 
Yuriy Bykov #:
функция AddToLog() не используется нигде, поэтому может быть удалена;

... на самом деле - всё зависит от "поставленной задачи" !

Если мы разрабатываем , например - простой индикатор ZZ, то нам важно видеть ГДЕ "барахлит" (в какой строке кода согласно логам), поэтому AddToLog тут не подходит, достаточно обычного Print / PrintFormat с номерацией строк кода (__LINE__),

а в моём случае (просто уталю ваше любопытство о том - ГДЕ применялась функция AddToLog) - нужно было действительно делать "засечки логирования" в РАЗНЫХ фрагментах кода, и потом собирать "разрозненные" логи и показывать их в одном узком блоке - для наглядности картины - ЧТО происходило внутри кода в разных процессах ! (именно в разных параллельных ПРОЦЕССАХ) ! и именно для таких вот целей - функция AddToLog как раз пригодилась очень великолепно !
... Кстати именно AddToLog () помогла выявить много "причин" которые давали "некорректные работы", так как мы в одном блоке логов прекрасно видели РАЗНЫЕ результаты, и их легко было сравнить и проанализировать, чем искать те-же данные среди сотен разрозненны строк логов ! 

Так-что  AddToLog () - это "дело вкуса" и поставленной задачи ))

 
Vitaliy Kostrubko #:

... на самом деле - всё зависит от "поставленной задачи" !

Если мы разрабатываем , например - простой индикатор ZZ, то нам важно видеть ГДЕ "барахлит" (в какой строке кода согласно логам), поэтому AddToLog тут не подходит, достаточно обычного Print / PrintFormat с номерацией строк кода (__LINE__),

а в моём случае (просто уталю ваше любопытство о том - ГДЕ применялась функция AddToLog) - нужно было действительно делать "засечки логирования" в РАЗНЫХ фрагментах кода, и потом собирать "разрозненные" логи и показывать их в одном узком блоке - для наглядности картины - ЧТО происходило внутри кода в разных процессах ! (именно в разных параллельных ПРОЦЕССАХ) ! и именно для таких вот целей - функция AddToLog как раз пригодилась очень великолепно !
... Кстати именно AddToLog () помогла выявить много "причин" которые давали "некорректные работы", так как мы в одном блоке логов прекрасно видели РАЗНЫЕ результаты, и их легко было сравнить и проанализировать, чем искать те-же данные среди сотен разрозненны строк логов ! 

Так-что  AddToLog () - это "дело вкуса" и поставленной задачи ))

про дело вкуса лучше забыть в программировании, легче будет чужое читать и свое показывать. Лучше придерживаться стандартов