[Oscript] От базовой библиотеки до полного расцвета с ОСенью. Разработка базовой версии

13.01.25

Разработка - OneScript

Вместе создадим библиотеку на Oscript с нуля, шаг за шагом: от базовой структуры проекта до перевода на ОСень. Разберем структуру проекта, работу с файлом конфигурации, логирование, уведомления, консольный интерфейс и многое другое. Освоим весь цикл разработки и сделаем первый шаг к созданию собственных инструментов на Oscript!

Привет! Меня зовут Очаковский Владимир. Сегодня хочу предложить тебе пройти практику на OneScript. Мы будем вместе разрабатывать собственную библиотеку — от самых первых шагов до полноценной реализации, включая версию на библиотеке ОСень.

Мы будем работать на основе заранее подготовленного технического задания, чтобы процесс разработки был максимально приближен к реальным условиям. Это поможет нам не только разобраться в ключевых принципах, но и научиться эффективно структурировать и выполнять задачи в рамках требований.

Хочу сразу сказать, что я не являюсь экспертом в этой области. Скорее, я такой же практик, как и ты. Наша цель — вместе пройти все этапы, разложить процесс по полочкам и на практике разобраться, как все это работает.

В конце мы получим две версии библиотеки: базовая и улучшенная на основе ОСени. А еще ты найдешь практические задания, чтобы попробовать свои силы, отточить навыки и, возможно, сделать нашу библиотеку еще лучше.

Так что, если ты готов — давай начинать!

 

Перед началом

Прежде чем мы начнем, хочу отметить, что наш мастер-класс рассчитан на тех, кто уже знаком с базовыми принципами OneScript. Мы не будем подробно разбирать основы (установка, настройка, отладка), чтобы сосредоточиться на практических аспектах разработки.

Чтобы ты смог легче погрузиться в материал и получить максимум пользы, я рекомендую ознакомиться с некоторыми полезными ресурсами заранее:

  1. Мастер-класс «OneScript. От простого к сложному с Никитой Иванченко» — отличный быстрый старт для понимания структуры и возможностей языка. В этом мастер-классе шаг за шагом создается простой проект, который затем преобразуется в реализацию на основе библиотеки ОСень. Это отличный способ увидеть весь процесс перехода от базовой идеи к более сложной реализации. Нас с тобой ждет что-то подобное!
  2. Статья «Создаем свою библиотеку для OneScript»— фундаментальная статья для создания своей библиотеки.
  3. Документация OneScript: добавь её в закладки и изучи основные разделы — она станет твоим незаменимым помощником в процессе работы.

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

 

Техническое задание

Теперь тебе предстоит ознакомиться с техническим заданием, которое станет основой для разработки нашей библиотеки.

 
 Основные моменты из технического задания

Цель: Создание библиотеки для мониторинга сервера.

Основные требования:

  1. Расширяемость:
    • Модульная структура для легкого добавления новых методов.
    • Один JSON-файл для хранения конфигурации всех методов.
  2. Функционал логирования:
    • Запись событий (успехи, ошибки) с деталями в файл и вывод в консоль.
  3. Уведомления:
    • Telegram и Email как основные виды транспорта.
    • Легкость добавления новых видов транспорта.

Метод проверки дисков:

  • Настройки: список дисков, пороговое значение (в процентах).
  • Уведомления при превышении порога.
  • Вывод информации в консоль (доступное и общее место, проблемные диски).

Расширение библиотеки (возможности):

  • Мониторинг CPU и RAM.
  • Проверка состояния служб и времени отклика сервисов.
  • Проверка доступности внешних ресурсов.

Полное техническое задание доступно по ссылке.

Я подготовил общую диаграмму деятельности и простую диаграмму классов для методов проверки, чтобы облегчить проектирование начальной архитектуры библиотеки.

Диаграмма деятельности

 

 

Диаграмма классов

 

 

Для обеспечения гибкости и простоты добавления новых методов мониторинга мы будем использовать единый интерфейс для всех проверок. Это позволит легко добавлять новые проверки, не изменяя базовую логику.

На основе технического задания и предложенных диаграмм попробуй спроектировать начальную структуру библиотеки.

Исходя из наших требований, хотелось бы, чтобы приложение начинало работу следующим образом:

Монитор = Новый Монитор();  
Монитор.ЗапуститьМониторинг();

Теперь мы готовы перейти к реализации!


Версия Базовая

Версия ОСень

Практика

Материалы

Заключение


Версия Базовая

Структура проекта

Перед началом создания базовой версии нашей библиотеки, давай зафиксируем основные шаги и договоренности о её устройстве из документации:

 

 

Шаги по созданию структуры:

  1. Создание корневого каталога проекта:

    • Назовем корневой каталог monitor.
  2. Подготовка структуры папок:

    • В корне создаем подпапки:
      • Классы — для размещения классов библиотеки.
      • Модули — для реализации проверок и вспомогательных функций.
  3. Создание стартового файла:

    • В корне проекта создаем файл main.os, который станет точкой входа в нашу библиотеку.
  4. Создание ключевых компонентов:

    • На основе диаграммы классов создаем:
      • Класс Монитор.os в папке Классы.
      • Модуль ПроверкаДисков.os в папке Модули.

Промежуточная структура библиотеки:

  • monitor
    • Классы
      • Монитор.os
    • Модули
      • ПроверкаДисков.os
    • main.os

Реализация начального каркаса логики:

В main.os добавляем стартовый код и инструкцию #Использовать “.” для загрузки наших классов и методов:

#Использовать "."

Монитор = Новый Монитор();
Монитор.ЗапуститьМониторинг();

Все проверки располагаются в папке Модули и следуют единому шаблону наименования Проверка<Назначение>.os. Также наши проверки имеют единый интерфейс. ПроверкаДисков.os:

Процедура ВыполнитьПроверку() Экспорт
	
	Сообщить("Начало проверки дисков");

	Сообщить("Завершение проверки дисков");

КонецПроцедуры

Создадим модуль Мониторинг.os для набора общих методов по управлению проверками:

#Область Проверки

Функция ДоступныеПроверки() Экспорт

    Проверки = Новый Соответствие();
    Проверки.Вставить("ПроверкаДисков", ПроверкаДисков);

    Возврат Проверки; 

КонецФункции

#КонецОбласти

Добавляем реализацию метода ЗапуститьМониторинг, который запускает все проверки в Монитор.os:

Процедура ЗапуститьМониторинг() Экспорт

    Проверки = Мониторинг.ДоступныеПроверки();

    Для Каждого Проверка Из Проверки Цикл
        
        Менеджер = Проверка.Значение;
        Менеджер.ВыполнитьПроверку();

    КонецЦикла;

КонецПроцедуры

Тестирование:

  • Создаем отладочный файл (launch.json).
  • Запускаем приложение, проверяя корректность работы методов.

 

 

Теперь у нас есть базовая структура и начальный функционал библиотеки. Это станет отправной точкой для дальнейшего развития и улучшений!

Файл конфигурации

На основании технического задания и предложенных диаграмм попробуй самостоятельно представить, как должен выглядеть файл config.json. Сравни свою версию с моим описанием и подумай, насколько близки они по структуре и содержанию.

 
 config.json

Для удобства пользователей нашей библиотеки мы также создадим файл-шаблон example_config.json, который будет находиться в корне проекта. Это позволит всегда иметь под рукой пример настроек, облегчающий начальную конфигурацию.

Файл конфигурации будет включать следующие параметры:

  • Настройки логирования: путь к файлу логов.
  • Список проверок, которые нужно выполнять, с параметрами для каждой проверки (например, список дисков и пороговые значения для проверки дискового пространства).
  • Настройки уведомлений: данные для подключения к Telegram и/или электронной почте.
 
 Рекомендации по хранению паролей и токенов

Пароли, токены и другие конфиденциальные данные не должны храниться в открытом виде в файлах конфигурации или в коде. В рамках мастер-класса мы упростили этот момент для демонстрации, но в реальных проектах обязательно учитывай это и используй безопасные подходы для хранения таких данных:

  • Секреты CI/CD
    Используй встроенные механизмы управления секретами в системе CI/CD.

  • Переменные окружения (Environment Variables)
    Храни пароли и токены в переменных окружения на сервере или рабочем окружении.

  • Локальные конфигурационные файлы, исключенные из контроля версий
    Если ты вынужден использовать файлы, не забудь добавить его в .gitignore, чтобы он не попадал в репозиторий.


Перед тем как приступить к реализации работы с конфигурационным файлом, я советую тебе провести небольшое исследование. Это не только поможет лучше понять, как решать задачу, но и облегчит написание кода.

Попробуй следующий подход:

1. Проверка синтакс-помощника

Если ты еще не знаком с синтакс-помощником, самое время это исправить.

Например, для работы с JSON я нашел там метод ПрочитатьJSON. Этот метод считывает содержимое файла и преобразует его в структуры данных. Удобно и просто — то, что нужно! Вот ссылка на метод — обязательно посмотри.

2. Исследование готовых библиотек

На GitHub есть библиотека пакетов oscript-library. Часто там можно найти уже готовые инструменты.

Например, я нашел библиотеку configor. Она как раз создана для работы с конфигурационными файлами. Там уже есть методы для чтения JSON и управления настройками. Готовый интерфейс — бери и используй!

3. Анализ похожих решений в библиотеках

Еще один крутой способ — посмотреть, как похожие задачи решаются в других библиотеках. Например, я заглянул в популярный проект vanessa-runner.

В нем есть модуль ОбщиеМетоды.os, где нашел методы ПрочитатьНастройкиФайлJSON и ПрочитатьФайлJSON. Эти методы используют библиотеку json для работы с файлами. Хотя сейчас появился метод ПрочитатьJSON, этот пример все равно полезен: можно увидеть, как организована работа с настройками.

 
 Методы из ОбщиеМетоды.os

 

Что предлагаю сделать

На основе всего этого я бы предложил такой подход:

  1. Создать отдельный класс Конфигурация.os, который будет изолировать логику работы с настройками.
  2. Включить в класс публичные методы для чтения, проверки и получения значений настроек.
  3. Для работы с JSON используем метод ПрочитатьJSON — он встроенный, простой и эффективный.

Такой способ даст нам гибкость, позволит легко менять логику и добавлять новые возможности.


ПриСозданииОбъекта

Когда мы создаем объект класса, у него есть предопределенный метод ПриСозданииОбъекта, который автоматически срабатывает при инициализации.

Конфигурация = Новый Конфигурация();

Это удобно, но заставляет задуматься: где лучше разместить логику чтения конфигурационного файла?

Есть два подхода: реализовать эту логику в методе ПриСозданииОбъекта или вынести в отдельный метод Инициализировать. Давай разберемся.

 
 Логика в ПриСозданииОбъекта

Плюсы:

  • Простота использования. После создания объекта он сразу готов к работе, дополнительных вызовов не требуется.
  • Единый путь инициализации. Это уменьшает вероятность ошибок, если разработчик забудет вызвать метод Инициализировать.

Минусы:

  • "Тяжелый" конструктор. С ростом функционала объект может выполнять слишком много задач сразу (например, настройку лога, проверку файла, чтение настроек). Это усложнит отладку.
  • Сложности тестирования. Нельзя создать объект без выполнения всей логики, что может затруднить модульные тесты.
 
 Логика в отдельном методе Инициализировать

 Плюсы:

  • Простота конструктора. Весь тяжелый функционал вынесен, что делает код более читаемым и понятным.
  • Удобство тестирования. Объект можно протестировать в разных состояниях — до и после инициализации.
  • Гибкость. Метод можно вызывать с дополнительными параметрами, если потребуется адаптировать инициализацию.

Минусы:

  • Необходимость помнить. Разработчик обязан вызывать метод Инициализировать, иначе объект не будет готов к работе.
  • Немного больше кода. Каждый раз после создания объекта придется добавлять вызов метода.

Мы можем объединить преимущества обоих подходов.

  1. В методе ПриСозданииОбъекта добавляем проверку наличия файла конфигурации. Если файла нет, сразу сообщаем об ошибке, создаем шаблонный файл или используем значения по умолчанию.
  2. Логику чтения настроек из файла реализуем в методе Инициализировать. Это позволит вызывать его позже, если потребуется.

Преимущества смешанного подхода:

  1. Раннее обнаружение критических ошибок. Например, если конфигурационный файл отсутствует, мы узнаем об этом на этапе создания объекта.
  2. Гибкость. Основная логика инициализации может быть вызвана позже, с учетом дополнительных параметров или подготовки.
  3. Удобство тестирования. Можно создавать объект без полной инициализации, что особенно полезно при модульном тестировании.
Конфигурация = Новый Конфигурация();
Конфигурация.Инициализировать();

Такой подход делает наш код понятным, удобным в использовании и масштабируемым. Что скажешь?


Перед тем как приступить к реализации, давай обсудим, как лучше связать объекты Конфигурация и Монитор. Здесь есть два варианта:

  1. Создавать экземпляр класса Конфигурация внутри Монитор в методе ПриСозданииОбъекта.
  2. Передавать объект Конфигурация в Монитор в качестве параметра (через конструктор).

Я думаю, ты уже знаешь, какой вариант правильнее выбрать, но давай все равно их разберем.

Создание экземпляра класса вне конструктора и передача его в конструктор имеет несколько преимуществ по сравнению с созданием объекта непосредственно в конструкторе. Вот основные причины:

  • Повторное использование. Один экземпляр Конфигурации можно передать нескольким объектам Монитор или другим классам.
  • Гибкость. Легко подменить экземпляр Конфигурации на другую версию, если это потребуется.
  • Тестируемость. Можно передать тестовый экземпляр или мок-объект, изолируя зависимости и делая тесты проще и предсказуемее.
  • Прозрачность. Конструктор Монитор явно показывает, что он зависит от объекта Конфигурация.
  • Следование SOLID. Класс Монитор остается сфокусированным на своей задаче, а создание и настройка конфигурации передаются внешнему коду, реализуя принцип инверсии зависимостей (Dependency Injection) и упрощая управление зависимостями. Если ты не сталкивался с данными аббревиатурами ранее, то не забивай пока себе голову — мы еще столкнемся с Dependency Injection при переходе на ОСень.
Конфигурация = Новый Конфигурация();
Конфигурация.Инициализировать();

Монитор = Новый Монитор(Конфигурация);
Монитор.ЗапуститьМониторинг();

Мы готовы двигаться дальше!


Перед тем как перейти к реализации, давай визуализируем наши изменения на диаграмме классов. Это поможет нам четко понять, как взаимодействуют объекты Конфигурация и Монитор, и убедиться, что структура соответствует нашим целям.

 

 

Обрати внимание, что у класса Конфигурация появился метод Проверки(), который возвращает загруженные проверки из конфигурационного файла. 

Кроме того, у методов ВыполнитьПроверку(ПараметрыПроверки) теперь появился параметр ПараметрыПроверки. Этот параметр будет содержать настройки конкретной проверки, которые передаются из метода ПараметрыПроверки

Давай переходить к реализации!


Начнем с файла main.os:

#Использовать "."

Конфигурация = Новый Конфигурация();
Конфигурация.Инициализировать();

Монитор = Новый Монитор(Конфигурация);
Монитор.ЗапуститьМониторинг();

Добавим параметр ПараметрыПроверки в ПроверкаДисков.os:

Процедура ВыполнитьПроверку(ПараметрыПроверки) Экспорт
	
	Сообщить("Начало проверки дисков");

	Сообщить("Завершение проверки дисков");

КонецПроцедуры

Создадим класс Конфигурация.os:

Перем Настройки;

Процедура ПриСозданииОбъекта()

	ИмяФайла = ИмяФайлаПоУмолчанию();

	ФайлКонфигурации = Новый Файл(ИмяФайла);
	Если Не ФайлКонфигурации.Существует() Тогда
		ВызватьИсключение "Не найден файл конфигурации: " + ИмяФайла;
	КонецЕсли;

КонецПроцедуры

#Область ПрограммныйИнтерфейс

Процедура Инициализировать() Экспорт

	Настройки = ПрочитатьФайлJSON(ИмяФайлаПоУмолчанию());

КонецПроцедуры

Функция Проверки() Экспорт
	Возврат Настройки["Проверки"];
КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Функция ИмяФайлаПоУмолчанию()
	Возврат "config.json";
КонецФункции

Функция ПрочитатьФайлJSON(ИмяФайла)
	
	Сообщить("Читаю настройки из " + ИмяФайла);

	ЧтениеJSON = Новый ЧтениеJSON;
	ЧтениеJSON.ОткрытьФайл(ИмяФайла);

	Результат = ПрочитатьJSON(ЧтениеJSON, Истина);

	ЧтениеJSON.Закрыть();

	Возврат Результат;

КонецФункции

#КонецОбласти

Доработаем класс Монитор.os для работы с конфигурацией:

Перем _Конфигурация;

Процедура ПриСозданииОбъекта(Конфигурация)

	_Конфигурация = Конфигурация;

КонецПроцедуры

Процедура ЗапуститьМониторинг() Экспорт

	ЗагруженныеПроверки = _Конфигурация.Проверки();
	ДоступныеПроверки   = Мониторинг.ДоступныеПроверки();

	Для Каждого Проверка Из ЗагруженныеПроверки Цикл

		ИмяПроверки = Проверка.Ключ;
		ПараметрыПроверки = Проверка.Значение;

		Менеджер = ДоступныеПроверки.Получить(ИмяПроверки);
		Если Менеджер = Неопределено Тогда
			Сообщить("Не найден менеджер для проверки: " + ИмяПроверки);
			Продолжить;
		КонецЕсли;

		Если Не ПараметрыПроверки["Использовать"] Тогда
			Сообщить("Отключена проверка: " + ИмяПроверки);
			Продолжить;
		КонецЕсли;

		Менеджер.ВыполнитьПроверку(ПараметрыПроверки);

	КонецЦикла;

КонецПроцедуры

Теперь цикл обработки проверок работает не по всем доступным проверкам, а только по загруженным из конфигурации. Дополнительно мы проверяем, используется ли конкретная проверка, прежде чем вызывать ее выполнение. Это позволяет избежать лишних вызовов и обеспечивает гибкость в управлении настройками.

Тестирование:

  • В отладке убедись, что параметры передаются корректно в метод выполнения проверки:
  • Проверь корректность работы приложения в целом:

Не забудь проверить различные сценарии, например, когда нету файла конфигурации или проверка отключена.

Текущая структура библиотеки:

  • monitor
    • Классы
      • Конфигурация.os
      • Монитор.os
    • Модули
      • Мониторинг.os
      • ПроверкаДисков.os
    • main.os
    • example_config.json

Проверка дисков

Для реализации нашей первой проверки дисков мы будем работать с методом ИнформацияОДиске из синтакс-помощника. Этот метод предоставляет информацию о доступном пространстве и общем размере диска, что идеально подходит для нашей задачи.

Попробуй самостоятельно реализовать логику проверки дисков, используя этот метод.

Процедура ВыполнитьПроверку(ПараметрыПроверки) Экспорт
	
	Сообщить("Начало проверки дисков");

	Диски = ПараметрыПроверки["Диски"];
	Порог = ПараметрыПроверки["Порог"];

	Для Каждого Диск Из Диски Цикл
		ПроверитьДиск(Диск, Порог);
	КонецЦикла;

	Сообщить("Завершение проверки дисков");

КонецПроцедуры

Основная идея проверки:

  1. Получить данные о дисках.
  2. Рассчитать процент свободного места.
  3. Вывести сообщение с информацией согласно техническому заданию.

Когда закончишь свою реализацию, обязательно сравни её с моей версией.

 
 ПроверкаДисков.os

Тестирование:

  • В config.json указываем настройки для проверки ПроверкаДисков:
  • Запускаем приложение, проверяя корректность работы методов:

Логирование

Для того чтобы закрыть функциональные требования из технического задания, нам понадобится механизм логирования. Я предлагаю использовать готовую библиотеку logos. Она поддерживает параллельный вывод логов в несколько источников, что идеально подходит для нашего проекта.

Так как это первая внешняя библиотека в нашем проекте, начнём с её установки. Выполни команду для установки:

opm i logos

Для структурированного логирования мы будем использовать основной журнал с именем нашего приложения. Давай начнем с создания модуля ПараметрыПриложения.os. В нём добавим функцию, которая возвращает имя приложения:

Функция ИмяПриложения() Экспорт
    Возврат "monitor";
КонецФункции

Теперь это имя станет основой для нашего журнала логов.

Поскольку модуль Мониторинг является основным для нашего приложения, создание и настройку логов разместим именно там, чтобы она была в одном месте. Особенностью модулей в OneScript является то, что у них есть переменные и они сохраняют значение после последнего присвоения. Это удобно для инициализации единого экземпляра объекта лога. Подключим в модуле библиотеку и создадим переменную для лога:

#Использовать logos

Перем Лог;

Теперь создадим функцию, которая будет возвращать сам лог:

Функция Лог() Экспорт

	Если Лог = Неопределено Тогда
		
		Лог = Логирование.ПолучитьЛог(ПараметрыПриложения.ИмяПриложения());

	КонецЕсли;
	
	Возврат Лог;

КонецФункции

Теперь давай найдем все строки, где используется Сообщить

 

 

И заменим их на методы библиотеки logos. Вот основные методы:

  • Лог.Информация("Информация");
  • Лог.Ошибка("Ошибка");
  • Лог.Отладка("Отладка");
  • Лог.КритичнаяОшибка("Критичная ошибка");
Лог = Мониторинг.Лог();
Лог.Информация("Информация");

Для того чтобы включить отладочные логи в приложении, рекомендую изучить документацию библиотеки logos. Это поможет понять все доступные возможности настройки.

Один из вариантов — использование конфигурационного файла logos.cfg. В этом файле можно задать уровень логирования для нашего приложения. Например:

logger.monitor=DEBUG

Задание

  1. Подключи библиотеку logos в тех модулях, где используется Сообщить.
  2. Замени вызовы Сообщить на соответствующие методы библиотеки.
  3. Проверь, что лог корректно работает, и сообщения выводятся в указанный журнал.

После выполнения задания сравни свой результат с моей версией.

 

 

Давай научимся настраивать форматирование сообщений в логах. Это позволит сделать вывод более удобным и информативным. Выполним нужные шаги по установке раскладки по документации в модуле Мониторинг.os:

Функция Лог() Экспорт

	Если Лог = Неопределено Тогда
		
		Лог = Логирование.ПолучитьЛог(ПараметрыПриложения.ИмяПриложения());
		Лог.УстановитьРаскладку(ЭтотОбъект);

	КонецЕсли;
	
	Возврат Лог;

КонецФункции
#Область СлужебныйПрограммныйИнтерфейс

Функция ПолучитьФорматированноеСообщение(Знач СобытиеЛога) Экспорт
   
	Уровень = УровниЛога.НаименованиеУровня(СобытиеЛога.ПолучитьУровень());

	ФорматированноеСообщение = СтрШаблон("%1: %2 - %3",
		ТекущаяДата(),
		Уровень,
		СобытиеЛога.ПолучитьСообщение());

	Возврат ФорматированноеСообщение;
 
 КонецФункции

#КонецОбласти

Для записи логов в файл нужно донастроить наш лог. Так как имя файла лога указывается в конфигурационном файле, настройку вывода в файл мы выполним после чтения настроек. При этом важно учесть особенность библиотеки logos: если мы добавляем второй способ вывода (например, в файл), то первый способ (например, вывод в консоль) нужно явно указать, иначе он пропадет.

Изменения в Мониторинг.os:

#Область Логирование

Функция Лог() Экспорт

	Если Лог = Неопределено Тогда
		
		Лог = Логирование.ПолучитьЛог(ПараметрыПриложения.ИмяПриложения());
		Лог.УстановитьРаскладку(ЭтотОбъект);

		ВыводЛогаВКонсоль = Новый ВыводЛогаВКонсоль;
		Лог.ДобавитьСпособВывода(ВыводЛогаВКонсоль);

	КонецЕсли;
	
	Возврат Лог;

КонецФункции

Процедура ДобавитьВыводЛогаВФайл(ФайлЛога) Экспорт

	Лог = Лог();

	ВыводЛогаВФайл = Новый ВыводЛогаВФайл;
	ВыводЛогаВФайл.ОткрытьФайл(ФайлЛога, , Ложь);

	Лог.ДобавитьСпособВывода(ВыводЛогаВФайл);

КонецПроцедуры

#КонецОбласти

Изменения в Конфигурация.os:

Процедура Инициализировать() Экспорт

	Настройки = ПрочитатьФайлJSON(ИмяФайлаПоУмолчанию());

	Мониторинг.ДобавитьВыводЛогаВФайл(ФайлЛога());

КонецПроцедуры

Функция ФайлЛога() Экспорт
	Возврат Настройки["ФайлЛога"];
КонецФункции

После настройки перезапусти приложение, чтобы убедиться, что логи корректно выводятся в консоль и записываются в файл, указанного в конфигурации.

 

 

Кстати, вот пример настройки вывода лога через файл logos.cfg:

logger.monitor=DEBUG, result, console

appender.result=ВыводЛогаВФайл
appender.result.file=logs.txt

appender.console=ВыводЛогаВКонсоль

Поскольку в проекте появился модуль ПараметрыПриложения.os, перед завершением работы над логированием предлагаю сделать небольшой рефакторинг в Конфигурация.os. Это улучшит структуру кода и сделает его более модульным.

Вот что мы сделаем:

  1. Имя файла конфигурации перенесем в модуль ПараметрыПриложения:
    • В ПараметрыПриложения.os создадим функцию ИмяФайлаКонфигурации().
  2. Создадим модуль РаботаСФайлами.os для операций с файлами:
    • Вынесем всю логику, связанную с файлами, включая проверку их существования и открытие, в отдельный модуль.

Попробуй выполнить эти шаги самостоятельно, а затем сравни свой результат с моими изменениями.

Текущая структура библиотеки:

  • monitor
    • Классы
      • Конфигурация.os
      • Монитор.os
    • Модули
      • Мониторинг.os
      • ПараметрыПриложения.os
      • ПроверкаДисков.os
      • РаботаСФайлами.os
    • main.os
    • example_config.json
 
 Измененные файлы
 
 main.os
 
 Конфигурация.os
 
 Монитор.os
 
 Мониторинг.os
 
 ПараметрыПриложения.os
 
 ПроверкаДисков.os
 
 РаботаСФайлами.os

Уведомления

Мы дошли до реализации последнего крупного функционального блока — уведомления. Этот компонент позволит нашему приложению сообщать о результатах проверок через различные каналы связи.

Проектирование функционала

Сначала нужно продумать архитектуру. Обратимся к файлу config.json. В нем уже есть ключевая информация:

  • Имя транспорта — название канала связи (например, почта или телеграм).
  • Настройки транспорта — данные для подключения и настройки канала.

Также нам не обойтись без параметров уведомления - структура, включающая заголовок, тело сообщения, вложения и дополнительные параметры, которые будут использоваться при отправке.

На основании технического задания нам нужно:

  1. Реализовать отправку уведомлений через почту и телеграм.
  2. Обеспечить возможность легкого добавления новых транспортов.

Для этого за основу возьмем подход, схожий с архитектурой проверок, но адаптируем его:

  • Все транспорты будут представлены модулями.
  • Создание отдельного класса для каждого транспорта избыточно, так как мы лишь вызываем конкретную реализацию функции отправки, а дополнительные состояния и сложная логика в данном случае отсутствуют.

Для наглядности представим архитектуру функционала в виде диаграммы классов:

 

 

  1. Уведомления — основной интерфейс, управляющий транспортами и уведомлениями.
    • Метод ОтправитьУведомление определяет общую логику отправки.
    • МенеджерТранспорта отвечает за подключение конкретного модуля транспорта.
  2. УведомленияПочта и УведомленияТелеграм — модули, реализующие свои методы отправки.
  3. Уведомления<НовыйТранспорт> — пример для добавления нового транспорта. Достаточно создать новый модуль и реализовать в нем метод ОтправитьУведомление.

Будет достаточно создать новый модуль, реализующий метод отправки, без изменений основной логики и каждая реализация изолирована и легко заменяема.

Теперь, когда мы спроектировали начальную архитектуру, давай приступим к реализации. 🚀


Начальная реализация

По диаграмме классов создадим каркас методов:

Уведомления.os

#Область ПрограмммныйИнтерфейс

Процедура ОтправитьУведомление(ИмяТранспорта, НастройкиТранспорта, ПараметрыУведомления) Экспорт

КонецПроцедуры

Функция ПараметрыУведомления() Экспорт
	
	Параметры = Новый Соответствие;
	Параметры.Вставить("Тема");
	Параметры.Вставить("Текст");
	Параметры.Вставить("Вложение");
	
	Возврат Параметры;
	
КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Функция МенеджерТранспорта(ИмяТранспорта)
	
	Менеджеры = Новый Соответствие;
	Менеджеры.Вставить("Почта", УведомленияПочта);
	Менеджеры.Вставить("Телеграм", УведомленияТелеграм);
	
	Возврат Менеджеры[ИмяТранспорта];
	
КонецФункции

#КонецОбласти

УведомленияПочта.os

Процедура ОтправитьУведомление(НастройкиТранспорта, ПараметрыУведомления) Экспорт
	
	Сообщить("Отправка уведомления через почту");
	
КонецПроцедуры

УведомленияТелеграм.os

Процедура ОтправитьУведомление(НастройкиТранспорта, ПараметрыУведомления) Экспорт
	
	Сообщить("Отправка уведомления через телеграм");
	
КонецПроцедуры

Итак, нам нужно определить, где именно в нашем коде будет происходить вызов отправки уведомлений. На первый взгляд, логично разместить эту логику в методе ЗапуститьМониторинг, который уже отвечает за выполнение всех проверок и координацию работы приложения.

Монитор.ЗапуститьМониторинг();

Однако стоит задуматься, соответствует ли это подход принципу единой ответственности (SRP, Single Responsibility Principle). Если мы добавим логику отправки уведомлений в ЗапуститьМониторинг, этот метод станет перегруженным и сложным для тестирования. Кроме того, это создаст дополнительную связанность кода, что ухудшит его поддержку и расширение.

Чтобы избежать этих проблем, правильнее бу

Вступайте в нашу телеграмм-группу Инфостарт

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Инструментарий разработчика Сервера OneScript Системный администратор Программист 1С 8.3 Россия Бесплатно (free)

Библиотека для создания многопоточного TCP-сервера, а так же TCP-клиента с поддержкой SSL/TLS шифрования для экосистемы OneScript. Удобный инструмент для построения распределенных систем, высоконагруженных сервисов, систем реального времени. С низким порогом вхождения и подробной документацией с примерами.

12.01.2026    2564    ahyahy    2    

10

Инструментарий разработчика OneScript Работа с интерфейсом Программист Россия Бесплатно (free)

Представляю кроссплатформенную библиотеку для разработки приложений с текстовым пользовательским интерфейсом (TUI) для сценарного языка OneScript. Она использует модель программирования, похожую на классические Desktop GUI (например, WinForms или WPF), но целиком работает в текстовом режиме. Возможно это ностальгия по DOS временам, но в наше время это так же и повышенная скорость отрисовки интерфейса, и легкость в написании скрипта. Создавайте интуитивно понятные окна, кнопки, поля ввода и выпадающие списки. Благодаря OneScript инструмент будет доступен даже новичкам без долгого обучения.

14.11.2025    4293    ahyahy    12    

28

OneScript Мессенджеры и боты Программист Бесплатно (free)

Создаём Telegram-бота для декомпиляции 1С файлов на OneScript и фреймворке Осень. Разберём архитектуру MVC для Telegram-бота. Научимся работать с фреймворком Осень: внедрение зависимостей, аннотации, логирование. Реализуем разбор бинарных файлов (EPF, ERT, CF, CFE.). Упакуем бота в Docker-контейнер

21.08.2025    5080    untru    15    

30

DevOps и автоматизация разработки OneScript Программист Бесплатно (free)

Когда в компании используется более 500 внешних обработок для 20 различных баз, процесс их параллельной разработки превращается в борьбу. Расскажем о тернистом пути от ручных скриптов к масштабируемой DevOps-системе, позволяющей централизованно управлять внешними обработками, автоматизировать сборки, интегрироваться с таск-трекером, запускать автотесты и разворачивать окружение в пару кликов.

12.08.2025    9113    untru    13    

28

OneScript Программист 1С:Предприятие 8 Бесплатно (free)

В 2024 году главному инструменту DevOps в 1С исполнилось 10 лет. Расскажем о том, что представляет собой экосистема 1Script в 2024 году и почему её важно включить в свой рабочий процесс.

16.06.2025    9322    Evil Beaver    43    

60

Групповая разработка (Git, хранилище) EDT OneScript Программист 1С:Предприятие 8 Бесплатно (free)

В данной публикации рассматривается пример реализации скрипта, который автоматизирует получение ветки из GIT репозитория и обновление конфигурации, если разработка проекта ведется в EDT.

11.06.2025    7730    AlexF1    4    

10

WEB-интеграция OneScript Программист Стажер Бесплатно (free)

Библиотека для работы с базами MySQL на основе внешней компоненты. Для Linux и Windows, бесплатно и с открытым исходным кодом!

08.04.2025    6872    bayselonarrend    27    

50

Внешние источники данных OneScript Программист Стажер 1С:Предприятие 8 Бесплатно (free)

Библиотека для работы с базами PostgreSQL на основе внешней компоненты. Для Linux и Windows, бесплатно и с открытым исходным кодом!

20.02.2025    9498    bayselonarrend    30    

46
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. fatman78 21 13.01.25 16:42 Сейчас в теме
Отличный материал!
О каком репозитарии GitHub идет речь в просьбе поддержать репозиторий? Непонятно.
3. bayselonarrend 3209 13.01.25 16:51 Сейчас в теме
9. leobrn 712 19.01.25 14:27 Сейчас в теме
2. bayselonarrend 3209 13.01.25 16:43 Сейчас в теме
Извините)
Прикрепленные файлы:
cheburashka; +1 Ответить
4. nixel 1474 13.01.25 17:29 Сейчас в теме
Аффтар, пешы исчо!

По поводу перевода на осень - обратите внимание, что осень умеет внедрять зависимости в приватные поля класса, не обязательно использовать пластилин в конструкторе объекта и заниматься переприсваиванием параметра в поле. А если нужна какая-то дополнительная логика инициализации, то есть &ФинальныйШтрих.

А так - очень жду :)

P.S. Интересно, затроните ли вы тему мета-аннотаций. Если есть, что обсудить - стучитесь в личку в телеге или приходите в осенний чат там же.
5. nixel 1474 13.01.25 18:29 Сейчас в теме
(4) как идея - реализацию интерфейса проверок можно организовать с помощью &Приемка, как это сделано в самой осени.
14. leobrn 712 19.01.25 16:05 Сейчас в теме
15. nixel 1474 19.01.25 16:13 Сейчас в теме
(14) интересно, что выйдет быстрее - вторая часть вашей статьи или я таки наследование и проверку реализации интерфейсов в ядро осени затяну через библиотеку extends :D
16. leobrn 712 19.01.25 18:01 Сейчас в теме
(15) у меня пока ориентир вторая половина февраля)
13. leobrn 712 19.01.25 14:28 Сейчас в теме
(4) Спасибо! Постараемся оправдать ожидания)
6. o.nikolaev 217 13.01.25 20:51 Сейчас в теме
Отличный материал, мое почтение.
starik-2005; +1 Ответить
10. leobrn 712 19.01.25 14:27 Сейчас в теме
7. PaStrel 132 13.01.25 20:52 Сейчас в теме
Отлично подготовленный материал и, что становится редкостью, без грамматических ошибок.
starik-2005; +1 Ответить
11. leobrn 712 19.01.25 14:28 Сейчас в теме
8. Redinternational 86 14.01.25 15:37 Сейчас в теме
Идеальная подача материала! Однозначно плюс!
12. leobrn 712 19.01.25 14:28 Сейчас в теме
Для отправки сообщения требуется регистрация/авторизация