В наших предыдущих постах мы узнали, как создавать красивые приложения командной строки с использованием System.CommandLine и современное внедрение зависимостей. Могут возникнуть ситуации, когда вы захотите отслеживать, как используется ваше приложение. Специфика сбора данных телеметрии во многом зависит от характера вашего приложения. Помните, что нецелесообразно собирать личную информацию (PII) в общедоступных приложениях, хотя это может быть допустимо, если вы создаете внутренний инструмент для своей компании.

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

И снова мы собираемся реализовать собственное промежуточное ПО System.CommandLine, которое будет собирать информацию о текущем выполнении команды:

using System.CommandLine.Invocation;
using System.CommandLine.Parsing;

namespace System.CommandLine.Builder;

internal static class TelemetryMiddleware
{
    public static CommandLineBuilder UseTelemetry(this CommandLineBuilder builder)
    {
        return builder.AddMiddleware(async (context, next) =>
        {
            // Track command name, command arguments and username
            var commandName = GetFullCommandName(context.ParseResult);
            var commandArgs = string.Join(' ', context.ParseResult.Tokens.Select(t => t.Value));
            var userName = Environment.UserName;

            try
            {
                await next(context);

                // Track command duration
                // Mark command as successful
            }
            catch (Exception ex)
            {
                // Track command duration
                // Mark command as failed, track exception

                throw;
            }
        }, MiddlewareOrder.ExceptionHandler);
    }

    private static string GetFullCommandName(ParseResult parseResult)
    {
        var commandNames = new List<string>();
        var commandResult = parseResult.CommandResult;

        while (commandResult != null && commandResult != parseResult.RootCommandResult)
        {
            commandNames.Add(commandResult.Command.Name);
            commandResult = commandResult.Parent as CommandResult;
        }

        commandNames.Reverse();

        return string.Join(' ', commandNames);
    }
}

Не забудьте добавить новое промежуточное ПО для телеметрии в свое приложение:

var rootCommand = new RootCommand
{
    // subcommands here
};

var builder = new CommandLineBuilder(rootCommand);

builder.UseDefaults();
builder.UseTelemetry();
builder.UseDependencyInjection(services => { /* [...] */ });

return builder.Build().Invoke(args);

Выбор способа сбора телеметрии зависит от вас. Вы можете отправить его на определенную конечную точку или использовать Application Insights SDK или OpenTelemetry для автоматического создания трассировки. Последний может быть более современным методом сбора телеметрии. Трассировки OpenTelemetry могут включать иерархические интервалы, что позволяет вашему приложению отслеживать новые процессы или HTTP-вызовы и группировать их как часть одной трассировки. OpenTelemetry действительно может упростить отладку и устранение неполадок.

Наконец, подумайте о том, чтобы предложить своим пользователям возможность отказаться от сбора телеметрии. Вы можете реализовать что-то похожее на переменную среды .NET DOTNET_CLI_TELEMETRY_OPTOUT.

Первоначально опубликовано на https://anthonysimmon.com 1 августа 2023 г.