Создание плагина для отправки логов (событийной информации) - MONQDL/agent-docs GitHub Wiki
Это пошаговое руководство детально описывает как создать Monq Agent плагин для пересылки логов (событий) в Monq. В качестве примера, этот тестовый плагин будет получать системную информацию - имя компьютера на котором запущен плагин и отправлять это имя в коллектор логов (событий). Для пересылки будут использоваться HTTP POST запросы к точке приема логов в Monq.
- Тестовый поток данных, куда вы можете добавлять задания;
- Установленный Monq Agent версии 1.4.0 или новее. Чтобы узнать версию агента выполните консольную команду
monq-agent --version
. Актуальную версию можно установить из раздела Агенты -> Установка. - Monq Agent должен быть подключен к вашему потоку данных;
- Установленная платформа Visual Studio или Visual Studio Code с установленным пакетом C# Dev Kit;
- Пакет SDK для .NET 7.
Создайте новый проект библиотеки классов с названием SystemInfoPlugin
, как описано на странице Создание нового проекта плагина.
В файле проекта SystemInfoPlugin.csproj
укажите название плагина и версию.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0</Version>
<AssemblyName>SystemInfoPlugin</AssemblyName>
…
</PropertyGroup>
Добавьте в проект NuGet пакеты Monq.Plugins.Abstractions
и Microsoft.Extensions.Http
, как описано на странице NuGet пакеты необходимые для работы плагина. Откройте файл проекта SystemInfoPlugin.csproj
и исключите оба этих пакета из публикации (выставите свойства Private = false и ExcludeAssets = runtime).
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0">
<Private>false</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="Monq.Plugins.Abstractions" Version="2.0.0">
<Private>false</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
Создайте новый класс PluginTaskStrategy
, который будет выполнять задание и имплементируйте в нем интерфейс Monq.Plugins.Abstractions.IPluginTaskStrategy
. Этот интерфейс содержит только метод Run
который вызывается Monq Agent'ом при получении соответствующего задания.
public class PluginTaskStrategy : IPluginTaskStrategy
{
public Task<IDictionary<string, object?>> Run(
IDictionary<string, object?> variables,
IEnumerable<string> securedVariables,
CancellationToken cancellationToken)
{
// Здесь будет выполняться задача полученная от агента.
}
}
Параметр variables
в методе Run
содержит значения параметров с которыми было запущено задание. Эти параметры будут нужны нам далее для отправки событий. Самый удобный способ извлечь эти значения, это создать отдельный класс TaskConfig
который будет содержать эти параметры задания.
public class TaskConfig
{
public string BaseUri { get; set; }
public long UserspaceId { get; set; }
public string StreamKey { get; set; }
}
Далее сконвертируйте параметр variables
метода Run
в TaskConfig
используя метод расширения Monq.Plugins.Abstractions.Extensions.ToObject<T>(…)
var config = variables.ToObject<TaskConfig>();
Далее выполните валидацию класса конфигурации и убедитесь что все значения его свойств конвертировались верно и хранят допустимые значения.
public Task<IDictionary<string, object?>> Run(…)
{
var config = variables.ToObject<TaskConfig>();
ValidateConfig(config);
// Здесь будет выполняться задача полученная от агента.
}
void ValidateConfig(TaskConfig config)
{
if (string.IsNullOrWhiteSpace(config.StreamKey)
|| string.IsNullOrWhiteSpace(config.BaseUri)
|| config.UserspaceId == 0)
throw new PluginNotConfiguredException();
}
Note
За подробной информацией о формировании параметра variables
обратитесь к разделу Передача плагину параметров из YAML скрипта задания.
Создайте класс SystemInformation
который будет содержать системную информацию (имя компьютера).
public class SystemInformation
{
public string Name { get; set; }
}
В классе PluginTaskStrategy
создайте новый метод GetSystemInformation
для получения этой системной информации и вызовите его в методе Run
.
public Task<IDictionary<string, object?>> Run(…)
{
var config = variables.ToObject<TaskConfig>();
ValidateConfig(config);
var sysInfo = GetSystemInformation();
// Здесь будет выполняться задача полученная от агента.
}
SystemInformation GetSystemInformation()
{
return new SystemInformation()
{
Name = Environment.MachineName
};
}
Для пересылки логов будут использоваться HTTP POST запросы к точке приема логов в Monq.
Создайте новый интерфейс сервиса IStreamDataCollectorApiHttpService
который будет описывать метод пересылки логов в коллектор логов:
public interface IStreamDataCollectorApiHttpService
{
public Task PushEvents(string baseUri, string streamKey, long userspaceId, IList events);
}
Далее создайте новый класс сервиса StreamDataCollectorApiHttpService
в котором имплементируйте интерфейс IStreamDataCollectorApiHttpService
.
public class StreamDataCollectorApiHttpService : IStreamDataCollectorApiHttpService
{
public async Task PushEvents(string baseUri, string streamKey, long userspaceId, IList events)
{
// Здесь будет выполняться пересылка событий.
}
}
Для отправки HTTP POST запроса нужен экземпляр класса HttpClient
. Удобней всего его получить из абстрактyой фабрики IHttpClientFactory
, которая зарегистрирована в DI контейнере агента, следующим образом.
readonly IHttpClientFactory _httpClientFactory;
public StreamDataCollectorApiHttpService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
Далее можно обращаться к полю _httpClientFactory
для создания HttpClient
. Создание и настройка http клиента выглядит следующим образом.
const string UserspaceHeader = "X-Smon-Userspace-Id";
public async Task PushEvents(string baseUri, string streamKey, long userspaceId, IList events)
{
using var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(baseUri);
client.DefaultRequestHeaders.TryAddWithoutValidation(UserspaceHeader, userspaceId.ToString());
// Здесь будет выполняться пересылка событий.
}
Note
Константа UserspaceHeader
это обязательный заголовок запроса, описанный в разделе Параметры заголовка запроса API потоков данных. Он должен содержать идентификатор пользовательского пространства к которому относится поток данных, куда требуется отправить данные.
Для пересылки событий (events
параметр) их надо предварительно сконвертировать в JSON строку. В данном примере для конвертации используется пространство имен System.Text.Json
.
using System.Text.Json;
using System.Text.Json.Serialization;
…
static readonly JsonSerializerOptions _serializerOptions = new()
{
WriteIndented = true,
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
public async Task PushEvents(string baseUri, string streamKey, long userspaceId, IList events)
{
…
var json = JsonSerializer.Serialize(events, _serializerOptions);
// Здесь будет выполняться пересылка событий.
}
Теперь выполните POST запрос к точке приема логов в Monq.
const string StreamDataApiUri = "api/public/cl/v1/stream-data";
…
public async Task PushEvents(string baseUri, string streamKey, long userspaceId, IList events)
{
…
var json = JsonSerializer.Serialize(events, _serializerOptions);
var data = new StringContent(json, Encoding.UTF8, Application.Json);
var requestUri = $"{StreamDataApiUri}?streamKey={streamKey}";
var response = await client.PostAsync(requestUri, data, new CancellationTokenSource(_defaultTimeout).Token);
response.EnsureSuccessStatusCode();
}
Note
Константа StreamDataApiUri
это относительный адрес точки приема логов описанный в статье API - Потоки данных. В качестве параметра запроса необходимо указать ключ потока данных куда требуется отправить данные.
Сервис пересылки логов готов, теперь его надо использовать в методе Run
класса PluginTaskStrategy
.
public class PluginTaskStrategy : IPluginTaskStrategy
{
readonly IStreamDataCollectorApiHttpService _streamDataCollectorApiHttpService;
public PluginTaskStrategy(IStreamDataCollectorApiHttpService streamDataCollectorApiHttpService)
{
_streamDataCollectorApiHttpService = streamDataCollectorApiHttpService;
}
public async Task<IDictionary<string, object?>> Run(…)
{
…
var sysInfo = GetSystemInformation();
await _streamDataCollectorApiHttpService.PushEvents(config.BaseUri, config.StreamKey, config.UserspaceId, new[] { sysInfo });
return new Dictionary<string, object?>();
}
В данном плагине не требуется хранить данные между запусками, потому в методе Run
возвращаем пустой словарь.
Добавьте новый класс PluginTaskBootstrap
, который будет выполнять регистрацию сервисов необходимых для выполнения задания и имплементируйте в нем интерфейс Monq.Plugins.Abstractions.IPluginTaskBootstrap
. Этот интерфейс предназначен для регистрации зависимостей в DI контейнере, прежде всего зависимости от класса PluginTaskStrategy
созданного на шаге 3.
public class PluginTaskBootstrap : IPluginTaskBootstrap
{
const string Name = "System Information Plugin";
const string Command = "SystemInfoPlugin";
public PluginTask PluginTask => new(Name, Command, typeof(PluginTaskStrategy));
public void RegisterServiceProvider(IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<PluginTaskStrategy>();
services.AddScoped<IStreamDataCollectorApiHttpService, StreamDataCollectorApiHttpService>();
}
}
Код плагина полностью готов. Осталось только собрать и запустить.
Для дальнейшего использования плагина необходимо собрать проект со всеми зависимостями. Этот процесс детально описан на странице Сборка проекта плагина.
Перейдите в директорию с плагинами Monq Agent, создайте там новую директорию с названием плагина и скопируйте все файлы плагина (файлы из ~/publish директории). Таким образом, если директория с плагинами расположена по пути c:\monq-agent\plugins\
, файлы плагина надо положить в директорию c:\monq-agent\plugins\*customPlugin*
.
Note
Директория с плагинами указывается в конфигурационном файле агента. По умолчанию, это файл monq-agent.conf который находится в директории с Monq Agent. В этом файле находим свойство CSharpPath
которое и содержит требуемый путь.
Далее запустите Monq Agent выполнив консольную команду monq-agent start
. Если агент уже запущен, его надо остановить и запустить заново. Если все предыдущие шаги были выполнены успешно, в консоли вы увидите название своего плагина среди остальных загруженных плагинов:
c:\monq-agent>monq-agent start
Loading plugins from c:\monq-agent\plugins\...
[Http Plugin v1.3.3.0] was loaded.
[System Information Plugin v1.0.0.0] was loaded.
2 plugins was loaded.
Плагин готов к работе.
Перейдите в ваш тестовый поток данных в Monq. Далее перейдите на вкладку конфигурация потока и создайте новое задание с шаблоном Default template. В сценарии задания введите следующий yaml скрипт.
name: System Info Gathering Task
jobs:
- name: System Info Gathering Job
steps:
- plugin: SystemInfoPlugin
with:
streamKey: $.vars.stream.key
Note
В поле 'plugin' надо указывать название команды (константа Command
) из шага 8.
За подробной информацией о том как параметры задания передаются в метод Run
обратитесь к разделу Передача плагину параметров из YAML скрипта задания.
В поле Агент (Agent) выберите метку вашего агента.
Сохраните это задание и запустите.
Убедитесь что поток запущен.
После запуска задания в консоли Monq Agent должны появиться следующие записи:
[2023-11-29 13:31:48 +03:00 INF] Task received 11a978eb-bb95-456c-b486-81a4e0914b84 - System Info Gathering Task. Owner: Stream 3400.
[2023-11-29 13:31:48 +03:00 INF] ["HTTP POST https://…/api/public/cl/v1/stream-data?streamKey=8d8f8de4-fc5b-454f-95ae-1b4c869800ca"] Start processing HTTP request POST https://…/api/public/cl/v1/stream-data?streamKey=8d8f8de4-fc5b-454f-95ae-1b4c869800ca
[2023-11-29 13:31:48 +03:00 INF] ["HTTP POST https://…/api/public/cl/v1/stream-data?streamKey=8d8f8de4-fc5b-454f-95ae-1b4c869800ca"] Sending HTTP request POST https://…/api/public/cl/v1/stream-data?streamKey=8d8f8de4-fc5b-454f-95ae-1b4c869800ca
[2023-11-29 13:31:48 +03:00 INF] ["HTTP POST https://…/api/public/cl/v1/stream-data?streamKey=8d8f8de4-fc5b-454f-95ae-1b4c869800ca"] Received HTTP response headers after 194.0119ms - 200
[2023-11-29 13:31:48 +03:00 INF] ["HTTP POST https://…/api/public/cl/v1/stream-data?streamKey=8d8f8de4-fc5b-454f-95ae-1b4c869800ca"] End processing HTTP request after 201.415ms - 200
[2023-11-29 13:31:48 +03:00 INF] Agent task 11a978eb-bb95-456c-b486-81a4e0914b84 was completed with result: Ok. Details:
Первая запись информирует что агент получил задание. Следующие записи показывают состояние POST запроса к точке приема логов. Последняя запись показывает что задание выполнено успешно. Далее перейдите на страницу Логи и события (первичные данные). Выберите там ваш тестовый поток и посмотрите событие которое там пришло. Оно должно иметь следующий вид в табличном представлении:
Поле | Значение |
---|---|
_stream.name | Название вашего тестового потока |
@timestamp | 29.11.2023 10:31:48.185 |
source.Name | Название вашего компьютера |