Технологический журнал 1С 8.3 пишет на диск то, чего нет в журнале регистрации: запросы к СУБД с длительностью и строкой кода, исключения со стеком, ожидания управляемых блокировок, серверные вызовы. Его включают, когда база тормозит «иногда», ошибка у пользователя не оставляет следов или документы падают по таймауту блокировки. Вся настройка технологического журнала 1С — один файл logcfg.xml: ниже — куда его положить, готовые шаблоны, ротация и разбор логов. Общий порядок поиска тормозов — в статье «1С тормозит и зависает».
Технологический журнал сервера 1С и клиента настраивается одинаково: файл читает каждый процесс платформы — rphost, rmngr, ragent, клиентские приложения, модуль веб-сервера — в каталоге conf своей установки. Таких каталогов два:
| Каталог conf | Windows, 64-разрядная платформа | Linux |
|---|---|---|
| Общий для всех версий | C:\Program Files\1cv8\conf | /opt/1cv8/conf |
| Конкретной версии | C:\Program Files\1cv8\8.3.27.xxxx\bin\conf | /opt/1cv8/x86_64/8.3.27.xxxx/conf |
В каталоге версии обычно лежит только conf.cfg: его параметр ConfLocation указывает на общий каталог. Кладите logcfg.xml туда — после обновления платформы журнал продолжит писаться. Если ConfLocation ведёт в другое место, кладите файл по этому пути. У 32-разрядной платформы корень — C:\Program Files (x86)\1cv8, а в старых Linux-установках из deb- и rpm-пакетов каталог /opt/1C/v8.3/x86_64/conf создают вручную.
Какой компьютер настраивать, зависит от того, где выполняется код:
| Что расследуем | Кто пишет журнал | Куда положить logcfg.xml |
|---|---|---|
| Клиент-серверная база: запросы, блокировки, серверный код | rphost, rmngr, ragent | на каждый рабочий сервер кластера |
| Клиентский код, ожидание ответа сервера | 1cv8c, 1cv8 | компьютер пользователя или терминальный сервер |
| Файловая база | клиентское приложение: в нём выполняется и серверный код | компьютер пользователя |
| Публикация на веб-сервере | процесс Apache или IIS с модулем 1С | веб-сервер |
Каталог из location создайте заранее и дайте право записи учётной записи процесса: службе сервера 1С (по умолчанию USR1CV8 в Windows, usr1cv8 в Linux), пользователю — для клиентского журнала, службе Apache или пулу IIS — для веб-сервера. Перезапускать сервер 1С не нужно: процессы сами перечитывают logcfg.xml и в течение минуты начинают или прекращают запись. Признак, что журнал пишется, — подкаталоги вида rphost_4120 в каталоге журнала.
# Windows, PowerShell от администратора: каталог журнала с правом записи для службы 1С
New-Item -ItemType Directory -Force D:\techlog | Out-Null
icacls D:\techlog /grant "USR1CV8:(OI)(CI)M"
Copy-Item .\logcfg.xml "C:\Program Files\1cv8\conf\"
# Linux: то же для usr1cv8; в шаблонах location="/var/log/1c/techlog/sql" и т.п.
sudo mkdir -p /var/log/1c/techlog
sudo chown -R usr1cv8:grp1cv8 /var/log/1c/techlog
sudo cp logcfg.xml /opt/1cv8/conf/
Каждый шаблон — законченный logcfg.xml. Чтобы собирать несколько вещей сразу, перенесите элементы <log> в один <config>, у каждого — свой каталог. Вместо trade подставьте имя базы из консоли кластера.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<!-- Запросы к СУБД дольше 1 секунды в базе trade -->
<log location="D:\techlog\sql" history="24">
<event>
<eq property="Name" value="DBMSSQL"/>
<eq property="p:processName" value="trade"/>
<ge property="Durationus" value="1000000"/>
</event>
<event>
<eq property="Name" value="DBPOSTGRS"/>
<eq property="p:processName" value="trade"/>
<ge property="Durationus" value="1000000"/>
</event>
<property name="all"/>
</log>
<!-- Планы запросов: нагрузка на сервер, включайте на время разбора -->
<plansql/>
</config>
Разбор ключевых строк:
xmlns="http://v8.1c.ru/v8/tech-log" — обязательное пространство имён; location — каталог, history — сколько часов хранить файлы.<event> объединяются через «И», разные <event> — через «ИЛИ». Кроме eq и ge есть ne, gt, lt, le и like (шаблон, % — любые символы).p:processName — имя базы в кластере, а не имя базы данных в СУБД.Durationus — микросекунды: 1000000 — одна секунда. В старых примерах встречается Duration в десятитысячных долях секунды, не смешивайте их.<property name="all"/> — все свойства события, включая Sql и Context.<plansql/> пишет план запроса в planSQLText и нагружает сервер — включайте только на время разбора.В файловой базе запросы пишет событие DBV8DBENG на компьютере пользователя.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<!-- Исключения базы trade: класс, текст ошибки и стек кода -->
<log location="D:\techlog\excp" history="72">
<event>
<eq property="Name" value="EXCP"/>
<eq property="p:processName" value="trade"/>
</event>
<property name="all"/>
</log>
</config>
В записи — класс ошибки (Exception), текст (Descr) и стек (Context). Попадают и ошибки, перехваченные в Попытка…Исключение, поэтому событий больше, чем жалоб. Шаблон для случая «у пользователя ошибка, а в журнале регистрации пусто»: запись находят по времени и Usr.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<!-- Таймауты, взаимоблокировки и ожидания управляемых блокировок дольше 1 с -->
<log location="D:\techlog\locks" history="48">
<event>
<eq property="Name" value="TTIMEOUT"/>
</event>
<event>
<eq property="Name" value="TDEADLOCK"/>
</event>
<event>
<eq property="Name" value="TLOCK"/>
<eq property="p:processName" value="trade"/>
<ge property="Durationus" value="1000000"/>
</event>
<property name="all"/>
</log>
</config>
TTIMEOUT (истекло ожидание блокировки) и TDEADLOCK (взаимоблокировка) случаются редко, их можно не выключать. TLOCK пишется на каждую управляемую блокировку, поэтому шаблон берёт только ожидания дольше секунды: длительность TLOCK включает время ожидания. Как найти, кто держал данные, — в статье «Конфликт блокировок 1С».
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<!-- Серверные вызовы дольше 5 секунд: что вызвали, память, процессор -->
<log location="D:\techlog\calls" history="24">
<event>
<eq property="Name" value="CALL"/>
<eq property="p:processName" value="trade"/>
<ge property="Durationus" value="5000000"/>
</event>
<property name="all"/>
</log>
</config>
CALL — входящий вызов на сервере, например серверной процедуры с клиента: в записи есть Context, память, процессорное время и объём переданных данных. SCALL — исходящий вызов: на компьютере пользователя он показывает, сколько клиент ждал сервер вместе с сетью.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<!-- Компьютер пользователя: вызовы сервера дольше 1 с и ошибки клиента -->
<log location="C:\techlog\client" history="24">
<event>
<eq property="Name" value="SCALL"/>
<ge property="Durationus" value="1000000"/>
</event>
<event>
<eq property="Name" value="EXCP"/>
</event>
<property name="all"/>
</log>
</config>
Если клиентский SCALL намного длиннее серверного CALL того же сеанса, время уходит на сеть, а не на код.
Чтобы настроить технологический журнал без ошибок в кодировке и единицах, шаблон удобно собирать кодом — например, во внешней обработке администратора. ЗаписьXML сама экранирует значения и пишет объявление с UTF-8.
// Модуль объекта внешней обработки «Разбор технологического журнала».
// Текст logcfg.xml: события одной базы не короче порога (ПорогМс = 0 - без порога).
// Пример: Текст = ТекстНастройкиЖурнала("D:\techlog\sql", "trade", 1000);
Функция ТекстНастройкиЖурнала(Каталог, ИмяБазы = "", ПорогМс = 0, ЧасовХранения = 24,
События = "DBMSSQL,DBPOSTGRS") Экспорт
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку(Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина, Ложь, " "));
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("config");
Запись.ЗаписатьСоответствиеПространстваИмен("", "http://v8.1c.ru/v8/tech-log");
Запись.ЗаписатьНачалоЭлемента("log");
Запись.ЗаписатьАтрибут("location", Каталог);
Запись.ЗаписатьАтрибут("history", XMLСтрока(ЧасовХранения));
Для Каждого ИмяСобытия Из СтрРазделить(События, ", ", Ложь) Цикл
Запись.ЗаписатьНачалоЭлемента("event");
ЗаписатьУсловие(Запись, "eq", "Name", ВРег(ИмяСобытия));
Если Не ПустаяСтрока(ИмяБазы) Тогда
ЗаписатьУсловие(Запись, "eq", "p:processName", ИмяБазы);
КонецЕсли;
Если ПорогМс > 0 Тогда // Durationus - микросекунды
ЗаписатьУсловие(Запись, "ge", "Durationus", XMLСтрока(ПорогМс * 1000));
КонецЕсли;
Запись.ЗаписатьКонецЭлемента(); // event
КонецЦикла;
Запись.ЗаписатьНачалоЭлемента("property");
Запись.ЗаписатьАтрибут("name", "all");
Запись.ЗаписатьКонецЭлемента(); // property
Запись.ЗаписатьКонецЭлемента(); // log
Запись.ЗаписатьКонецЭлемента(); // config
Возврат Запись.Закрыть();
КонецФункции
// Условие отбора вида <eq property="Name" value="DBMSSQL"/>.
Процедура ЗаписатьУсловие(Запись, Операция, ИмяСвойства, Значение)
Запись.ЗаписатьНачалоЭлемента(Операция);
Запись.ЗаписатьАтрибут("property", ИмяСвойства);
Запись.ЗаписатьАтрибут("value", Значение);
Запись.ЗаписатьКонецЭлемента();
КонецПроцедуры
Результат сохраните в logcfg.xml в кодировке UTF-8.
Каждый процесс пишет в свой подкаталог <процесс>_<PID> файлы по часам с именем ггммддчч.log: 26101014.log — 10 октября 2026 года, с 14:00 до 15:00. Файлы старше history часов платформа удаляет сама, так что на диске лежит примерно «объём за час × history».
DBMSSQL без порога или TLOCK без условий на нагруженной базе могут давать гигабайты в час.history с запасом на разбор, а нужные часы копируйте в сторону.Главный риск — забытый logcfg.xml с широким отбором, который съедает диск. Проверку размера повесьте на регламентное задание — оно проверит диск того сервера, где выполнилось.
// Серверный общий модуль ТехЖурналКонтроль: метод регламентного задания.
// Предупреждает в журнале регистрации, если каталог журнала разросся.
Процедура ПроверитьРазмерЖурнала(Каталог = "D:\techlog", ЛимитМБ = 20480) Экспорт
Размер = РазмерЖурнала(Каталог);
Если Размер.МБ > ЛимитМБ Тогда
ЗаписьЖурналаРегистрации("ТехнологическийЖурнал.Размер", УровеньЖурналаРегистрации.Предупреждение, , ,
СтрШаблон("Каталог %1: %2 МБ в %3 файлах, самый старый изменён %4. Проверьте logcfg.xml и history.",
Каталог, Размер.МБ, Размер.Файлов, Размер.СамыйСтарый));
КонецЕсли;
КонецПроцедуры
// Сколько места занимают файлы *.log в каталоге и подкаталогах процессов.
Функция РазмерЖурнала(Каталог) Экспорт
Результат = Новый Структура("Файлов, МБ, СамыйСтарый", 0, 0, Неопределено);
Байт = 0;
Для Каждого Файл Из НайтиФайлы(Каталог, "*.log", Истина) Цикл
Попытка
Байт = Байт + Файл.Размер();
Изменен = Файл.ПолучитьВремяИзменения();
Исключение
Продолжить; // файл удалён по history во время обхода
КонецПопытки;
Если Результат.СамыйСтарый = Неопределено Или Изменен < Результат.СамыйСтарый Тогда
Результат.СамыйСтарый = Изменен;
КонецЕсли;
Результат.Файлов = Результат.Файлов + 1;
КонецЦикла;
Результат.МБ = Окр(Байт / 1048576, 1);
Возврат Результат;
КонецФункции
Файл текстовый, в UTF-8. Каждое событие начинается с новой строки со времени, а значения в кавычках — текст запроса, стек — могут занимать несколько строк. Пример записи (сокращён, имена условные):
46:02.123004-1520345,DBMSSQL,5,process=rphost,p:processName=trade,OSThread=5120,t:clientID=27,t:applicationName=1CV8C,t:computerName=pc-12,t:connectID=311,SessionID=48,Usr=Кладовщик,Trans=1,dbpid=73,Sql='SELECT T1._IDRRef, T1._Date_Time FROM dbo._Document123 T1 WHERE T1._Date_Time >= @P1',Rows=1840,Context='ОбщийМодуль.ОбменССайтомСервер.Модуль : 120 : ЗагрузитьЗаказы(Пакет);
ОбщийМодуль.ОбменССайтомСервер.Модуль : 214 : Выборка = Запрос.Выполнить().Выбрать();'
| Фрагмент | Что значит |
|---|---|
46:02.123004 | минуты, секунды и микросекунды; дата и час — в имени файла |
-1520345 | длительность в микросекундах, здесь 1,52 с |
DBMSSQL,5 | имя события и уровень |
p:processName, Usr, SessionID | база в кластере, пользователь, номер сеанса |
t:computerName, t:applicationName | компьютер и тип приложения: 1CV8C — тонкий клиент, BackgroundJob — фоновое задание |
Sql, Rows | текст запроса к СУБД и число строк результата |
Context | стек кода 1С; последняя строка — место, откуда выполнен запрос |
Свойства по событиям:
| Событие | Что это | На что смотреть |
|---|---|---|
| DBMSSQL, DBPOSTGRS, DBV8DBENG | запрос к MS SQL, PostgreSQL, файловой базе | Sql, Rows, RowsAffected, planSQLText |
| EXCP | исключение | Exception — класс, Descr — текст ошибки |
| TLOCK | установка управляемой блокировки | Regions — пространства блокировок, WaitConnections — чьё соединение ждали |
| TTIMEOUT | истекло время ожидания блокировки | WaitConnections |
| TDEADLOCK | взаимоблокировка | DeadlockConnectionIntersections |
| CALL | входящий вызов на сервере | Memory, MemoryPeak, CpuTime, InBytes, OutBytes |
| SCALL | исходящий вызов | IName, MName — интерфейс и метод |
Для первого взгляда хватает строк-заголовков: в них есть длительность и имя события. На Windows-сервере:
# 20 самых долгих запросов к СУБД: секунды, файл и номер строки
$dir = 'D:\techlog\sql'
Get-ChildItem $dir -Recurse -Filter *.log |
Select-String -Encoding UTF8 -Pattern '^\d\d:\d\d\.\d+-(\d+),(DBMSSQL|DBPOSTGRS|DBV8DBENG),' |
ForEach-Object {
[pscustomobject]@{
Sec = [math]::Round([long]$_.Matches[0].Groups[1].Value / 1e6, 2)
File = $_.Path
Line = $_.LineNumber
}
} | Sort-Object Sec -Descending | Select-Object -First 20 | Format-Table -AutoSize
# Запись целиком: 40 строк, начиная с найденной (здесь Line = 1235)
Get-Content 'D:\techlog\sql\rphost_4120\26101014.log' -Encoding UTF8 |
Select-Object -Skip 1234 -First 40
# Какие исключения чаще всего: EXCP по классу ошибки
Get-ChildItem 'D:\techlog\excp' -Recurse -Filter *.log |
Select-String -Encoding UTF8 -Pattern ',EXCP,.*?Exception=([^,]+)' |
Group-Object { $_.Matches[0].Groups[1].Value } -NoElement |
Sort-Object Count -Descending | Select-Object -First 15
На Linux многострочные записи сначала склеивают, потом сортируют по длительности:
# Linux: склеить многострочные записи и вывести 20 самых долгих запросов к СУБД
cd /var/log/1c/techlog/sql
LC_ALL=C awk -v ev='DBMSSQL|DBPOSTGRS' '
function flush() { if (keep) print rec; keep = 0 }
FNR == 1 { sub(/^\357\273\277/, "") } # BOM в начале файла
/^[0-9]+:[0-9]+\.[0-9]+-[0-9]+,/ { # начало новой записи
flush()
split($0, f, ",") # f[1] = время-длительность, f[2] = событие
keep = (f[2] ~ ("^(" ev ")$"))
d = f[1]; sub(/.*-/, "", d)
rec = d "\t" FILENAME "\t" $0; next
}
{ rec = rec " " $0 } # продолжение текста запроса или стека
END { flush() }' */*.log |
sort -t "$(printf '\t')" -k1,1nr | head -20 | cut -c1-400
# Взаимоблокировки и таймауты блокировок по файлам
grep -rcE ',(TDEADLOCK|TTIMEOUT),' --include='*.log' /var/log/1c/techlog/locks | grep -v ':0$'
Нет доступа к серверу по RDP или SSH, но есть права администратора в базе — читайте журнал из 1С: серверный код видит диск сервера. Ниже — модуль объекта внешней обработки. Открывайте её через «Файл → Открыть»: в безопасном режиме чтение файлов запрещено.
// Модуль объекта обработки (продолжение). Код работает там, где создан объект:
// при вызове из серверной процедуры формы - на сервере 1С, поэтому Каталог -
// путь на сервере, тот же, что в location. Подкаталоги процессов обходятся сами.
// ИменаСобытий - "DBMSSQL,EXCP" и т.п., пустая строка - все события.
Функция ПрочитатьЖурнал(Каталог, ИменаСобытий = "", МинимумМс = 0) Экспорт
Если Не ПравоДоступа("Администрирование", Метаданные) Тогда
ВызватьИсключение "Читать технологический журнал может только администратор";
КонецЕсли;
Таблица = Новый ТаблицаЗначений;
Для Каждого Колонка Из СтрРазделить("Момент,Событие,ДлительностьМс,База,Пользователь,Контекст,Свойства,Файл", ",") Цикл
Таблица.Колонки.Добавить(Колонка);
КонецЦикла;
Отбор = Новый Соответствие;
Для Каждого Имя Из СтрРазделить(ВРег(ИменаСобытий), ", ", Ложь) Цикл
Отбор.Вставить(Имя, Истина);
КонецЦикла;
Для Каждого Файл Из НайтиФайлы(Каталог, "*.log", Истина) Цикл
Попытка
// Ложь - разделяемый режим: в файл текущего часа ещё пишет процесс сервера
Чтение = Новый ЧтениеТекста(Файл.ПолноеИмя, КодировкаТекста.UTF8, , , Ложь);
Исключение
Продолжить; // файл удалён по history, пока шёл обход
КонецПопытки;
ТекстЗаписи = "";
СтрокаФайла = Чтение.ПрочитатьСтроку();
Пока СтрокаФайла <> Неопределено Цикл
Если Лев(СтрокаФайла, 1) = Символ(65279) Тогда // BOM в начале файла
СтрокаФайла = Сред(СтрокаФайла, 2);
КонецЕсли;
Если ЭтоНачалоЗаписи(СтрокаФайла) Тогда
ДобавитьСобытие(Таблица, ТекстЗаписи, Файл, Отбор, МинимумМс);
ТекстЗаписи = СтрокаФайла;
Иначе // продолжение многострочного значения: текст запроса, стек
ТекстЗаписи = ТекстЗаписи + Символы.ПС + СтрокаФайла;
КонецЕсли;
СтрокаФайла = Чтение.ПрочитатьСтроку();
КонецЦикла;
Чтение.Закрыть();
ДобавитьСобытие(Таблица, ТекстЗаписи, Файл, Отбор, МинимумМс);
КонецЦикла;
Возврат Таблица;
КонецФункции
// Запись начинается со времени "мм:сс.мкс-длительность," (дата и час - в имени
// файла ггммддчч.log) или с полной датой "гггг-мм-ддTчч:мм:сс.мкс-длительность,".
Функция ЭтоНачалоЗаписи(СтрокаФайла)
Если Сред(СтрокаФайла, 3, 1) = ":" И Сред(СтрокаФайла, 6, 1) = "." Тогда
Возврат ТолькоЦифры(Лев(СтрокаФайла, 2) + Сред(СтрокаФайла, 4, 2));
ИначеЕсли Сред(СтрокаФайла, 5, 1) = "-" И Сред(СтрокаФайла, 11, 1) = "T" Тогда
Возврат ТолькоЦифры(Лев(СтрокаФайла, 4));
КонецЕсли;
Возврат Ложь;
КонецФункции
Процедура ДобавитьСобытие(Таблица, ТекстЗаписи, Файл, Отбор, МинимумМс)
Если ПустаяСтрока(ТекстЗаписи) Тогда
Возврат;
КонецЕсли;
// Шапка без кавычек: время-длительность,СОБЫТИЕ,уровень, дальше свойства
Запятая1 = СтрНайти(ТекстЗаписи, ",");
Запятая2 = НайтиДалее(ТекстЗаписи, ",", Запятая1 + 1);
Если Запятая1 < 2 Или Запятая2 = 0 Тогда
Возврат; // оборванная запись
КонецЕсли;
Событие = ВРег(Сред(ТекстЗаписи, Запятая1 + 1, Запятая2 - Запятая1 - 1));
Если Отбор.Количество() > 0 И Отбор[Событие] = Неопределено Тогда
Возврат;
КонецЕсли;
// Длительность - после последнего дефиса: в полном формате дефисы есть и в дате
Шапка = Лев(ТекстЗаписи, Запятая1 - 1);
Дефис = СтрНайти(Шапка, "-", НаправлениеПоиска.СКонца);
Микросекунд = Сред(Шапка, Дефис + 1);
Если Дефис = 0 Или Не ТолькоЦифры(Микросекунд) Тогда
Возврат;
КонецЕсли;
ДлительностьМс = Число(Микросекунд) / 1000;
Если ДлительностьМс < МинимумМс Тогда
Возврат;
КонецЕсли;
Запятая3 = НайтиДалее(ТекстЗаписи, ",", Запятая2 + 1);
Свойства = СвойстваЗаписи(ТекстЗаписи, ?(Запятая3 = 0, СтрДлина(ТекстЗаписи) + 1, Запятая3 + 1));
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Момент = МоментСобытия(Файл.ИмяБезРасширения, Лев(Шапка, Дефис - 1));
НоваяСтрока.Событие = Событие;
НоваяСтрока.ДлительностьМс = ДлительностьМс;
НоваяСтрока.База = Свойство(Свойства, "p:processName");
НоваяСтрока.Пользователь = Свойство(Свойства, "Usr");
НоваяСтрока.Контекст = Свойство(Свойства, "Context");
НоваяСтрока.Свойства = Свойства;
НоваяСтрока.Файл = Файл.ПолноеИмя;
КонецПроцедуры
// Свойства "Имя=Значение" через запятую. Значение в одинарных или двойных
// кавычках может содержать запятые и переводы строк, кавычка внутри удваивается.
Функция СвойстваЗаписи(ТекстЗаписи, Начало)
Свойства = Новый Соответствие;
Длина = СтрДлина(ТекстЗаписи);
Позиция = Начало;
Пока Позиция <= Длина Цикл
Равно = НайтиДалее(ТекстЗаписи, "=", Позиция);
Если Равно = 0 Тогда
Прервать;
КонецЕсли;
Имя = Сред(ТекстЗаписи, Позиция, Равно - Позиция);
Позиция = Равно + 1;
Кавычка = Сред(ТекстЗаписи, Позиция, 1);
Если Кавычка = "'" Или Кавычка = """" Тогда
Значение = "";
Позиция = Позиция + 1;
Пока Позиция <= Длина Цикл
Граница = НайтиДалее(ТекстЗаписи, Кавычка, Позиция);
Если Граница = 0 Тогда
Граница = Длина + 1; // кавычка не закрыта: берём до конца записи
КонецЕсли;
Значение = Значение + Сред(ТекстЗаписи, Позиция, Граница - Позиция);
Позиция = Граница + 1;
Если Сред(ТекстЗаписи, Позиция, 1) = Кавычка Тогда
Значение = Значение + Кавычка; // удвоенная кавычка - символ внутри значения
Позиция = Позиция + 1;
Иначе
Позиция = Позиция + 1; // пропускаем запятую после значения
Прервать;
КонецЕсли;
КонецЦикла;
Иначе
Граница = НайтиДалее(ТекстЗаписи, ",", Позиция);
Если Граница = 0 Тогда
Граница = Длина + 1;
КонецЕсли;
Значение = Сред(ТекстЗаписи, Позиция, Граница - Позиция);
Позиция = Граница + 1;
КонецЕсли;
Свойства.Вставить(Имя, Значение);
КонецЦикла;
Возврат Свойства;
КонецФункции
// Дата события с точностью до секунды: в коротком формате час - из имени файла.
Функция МоментСобытия(ИмяФайла, Время)
Если СтрДлина(Время) >= 19 И Сред(Время, 11, 1) = "T" Тогда
ДатаСтрокой = СтрЗаменить(СтрЗаменить(СтрЗаменить(Лев(Время, 19), "-", ""), "T", ""), ":", "");
ИначеЕсли СтрДлина(ИмяФайла) = 8 И ТолькоЦифры(ИмяФайла) Тогда
ДатаСтрокой = "20" + ИмяФайла + Лев(Время, 2) + Сред(Время, 4, 2);
Иначе
Возврат Неопределено;
КонецЕсли;
Попытка
Возврат Дата(ДатаСтрокой);
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции
// СтрНайти с проверкой: начальная позиция за концом строки - ошибка платформы.
Функция НайтиДалее(Текст, Подстрока, Позиция)
Если Позиция > СтрДлина(Текст) Тогда
Возврат 0;
КонецЕсли;
Возврат СтрНайти(Текст, Подстрока, , Позиция);
КонецФункции
Функция ТолькоЦифры(Текст)
Для Номер = 1 По СтрДлина(Текст) Цикл
Если СтрНайти("0123456789", Сред(Текст, Номер, 1)) = 0 Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат СтрДлина(Текст) > 0;
КонецФункции
Функция Свойство(Свойства, Имя)
Значение = Свойства[Имя];
Возврат ?(Значение = Неопределено, "", Значение);
КонецФункции
Сводка «где в коде больше всего времени уходит на запросы» — в тот же модуль:
// Где в коде больше всего времени уходит на запросы к СУБД. Группировка по
// последней строке Context - строке модуля, которая выполнила запрос.
Функция ТопКонтекстов(Каталог, Количество = 20, МинимумМс = 0) Экспорт
Итог = Новый ТаблицаЗначений;
Итог.Колонки.Добавить("МестоВКоде", Новый ОписаниеТипов("Строка"));
Итог.Колонки.Добавить("Запросов", Новый ОписаниеТипов("Число"));
Итог.Колонки.Добавить("ВсегоСек", Новый ОписаниеТипов("Число"));
Итог.Колонки.Добавить("МаксимумМс", Новый ОписаниеТипов("Число"));
Итог.Колонки.Добавить("ТекстЗапроса", Новый ОписаниеТипов("Строка"));
ПоМесту = Новый Соответствие;
События = ПрочитатьЖурнал(Каталог, "DBMSSQL,DBPOSTGRS,DBV8DBENG", МинимумМс);
Для Каждого Событие Из События Цикл
СтрокиСтека = СтрРазделить(Событие.Контекст, Символы.ПС, Ложь);
Место = ?(СтрокиСтека.Количество() = 0, "<без контекста>",
СокрЛП(СтрокиСтека[СтрокиСтека.ВГраница()]));
СтрокаИтога = ПоМесту[Место];
Если СтрокаИтога = Неопределено Тогда
СтрокаИтога = Итог.Добавить();
СтрокаИтога.МестоВКоде = Место;
ПоМесту.Вставить(Место, СтрокаИтога);
КонецЕсли;
СтрокаИтога.Запросов = СтрокаИтога.Запросов + 1;
СтрокаИтога.ВсегоСек = СтрокаИтога.ВсегоСек + Событие.ДлительностьМс / 1000;
Если Событие.ДлительностьМс > СтрокаИтога.МаксимумМс Тогда
СтрокаИтога.МаксимумМс = Событие.ДлительностьМс; // запоминаем самый долгий
СтрокаИтога.ТекстЗапроса = Лев(Свойство(Событие.Свойства, "Sql"), 3000);
КонецЕсли;
КонецЦикла;
Итог.Сортировать("ВсегоСек Убыв");
Пока Итог.Количество() > Количество Цикл
Итог.Удалить(Итог.Количество() - 1);
КонецЦикла;
Возврат Итог;
КонецФункции
Форма вызывает обе функции на сервере — с клиента файлы сервера недоступны:
// Модуль формы обработки. Реквизиты обработки: Каталог, ИмяБазы, ТекстНастройки
// (Строка); реквизит формы ТопЗапросов (ТаблицаЗначений) с колонками
// МестоВКоде, Запросов, ВсегоСек, МаксимумМс, ТекстЗапроса.
&НаКлиенте
Процедура СобратьНастройку(Команда)
СобратьНастройкуНаСервере();
КонецПроцедуры
&НаКлиенте
Процедура РазобратьЖурнал(Команда)
Если ПустаяСтрока(Объект.Каталог) Тогда
ПоказатьПредупреждение(, "Укажите каталог журнала на сервере 1С");
Возврат;
КонецЕсли;
РазобратьЖурналНаСервере();
КонецПроцедуры
&НаСервере
Процедура СобратьНастройкуНаСервере()
Обработка = РеквизитФормыВЗначение("Объект");
Объект.ТекстНастройки = Обработка.ТекстНастройкиЖурнала(Объект.Каталог, Объект.ИмяБазы, 1000);
КонецПроцедуры
&НаСервере
Процедура РазобратьЖурналНаСервере()
// Файлы читает рабочий процесс сервера: с клиента диск сервера не виден
Обработка = РеквизитФормыВЗначение("Объект");
ЗначениеВРеквизитФормы(Обработка.ТопКонтекстов(Объект.Каталог, 20), "ТопЗапросов");
КонецПроцедуры
Разбор ключевых строк:
ЧтениеТекста(..., Ложь) — разделяемый режим: в файл текущего часа ещё пишет сервер, а по умолчанию файл открывается монопольно.ЭтоНачалоЗаписи режет записи по времени в начале строки, в том числе в полном формате даты из 8.3.25.СвойстваЗаписи читает значение в кавычках до парной кавычки, удвоенная кавычка — символ внутри.НайтиДалее страхует от ошибки «Неверное значение параметра»: СтрНайти не принимает позицию за концом строки.ТопКонтекстов группирует по последней строке Context — строке, которая выполнила запрос, — и хранит SQL самого долгого. Запрос 1С из этой строки удобно повторить в консоли запросов.Все события держатся в памяти: для каталога в сотни мегабайт сначала сузьте отбор в logcfg.xml.
conf (другая версия, 32-разрядная платформа, компьютер разработчика вместо сервера) или у службы нет права записи в каталог location.Durationus со значением 1000 — это миллисекунда, а не секунда, и журнал разрастается за минуты.p:processName указано имя базы данных в СУБД, а не в кластере, или файл лежит только на одном из рабочих серверов.Удалите или переименуйте logcfg.xml: процессы перечитывают настройку сами, и через минуту запись прекратится. Собранные файлы удалите вручную — чистить их по history уже некому.
Да: добавьте в <event> условие <eq property="Usr" value="Кладовщик"/> с именем пользователя базы. Так же отбирают по компьютеру (t:computerName) и номеру сеанса (SessionID).
Да, с узкими отборами: EXCP, TTIMEOUT, TDEADLOCK и запросы дольше нескольких секунд, history на двое-трое суток. TLOCK без порога и <plansql/> включайте только на время расследования.
Журнал регистрации хранит прикладные события — кто записал документ, какая ошибка у пользователя — и открывается из 1С. Технологический журнал пишет в файлы события платформы: запросы к СУБД, блокировки, вызовы. О первом — в статье «Где в 1С журнал регистрации».
Если журнал собран, а по нему непонятно, что исправлять, или тормоза проявляются только под нагрузкой рабочей базы, разбор быстрее отдать тому, кто делал его не раз. Посмотрите услугу «1С тормозит» или разместите задачу с описанием симптомов: оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.