使用控制台检查遥测数据

尽管控制台不是检查遥测数据的建议方法,但这是一种简单而快速的入门方法。 本文介绍如何使用最小内核设置将遥测数据输出到控制台进行检查。

出口商

导出程序负责将遥测数据发送到目标。 在此处阅读有关出口商的详细信息。 在此示例中,我们使用控制台导出程序将遥测数据输出到控制台。

先决条件

  • Azure OpenAI 聊天完成部署。
  • 适用于操作系统的最新 .Net SDK

注意

语义内核可观测性尚不适用于 Java。

安装

创建新的控制台应用程序

在终端中运行以下命令,在 C# 中创建新的控制台应用程序:

dotnet new console -n TelemetryConsoleQuickstart

命令完成后,导航到新创建的项目目录。

安装所需程序包

  • 语义内核

    dotnet add package Microsoft.SemanticKernel
    
  • OpenTelemetry 控制台导出程序

    dotnet add package OpenTelemetry.Exporter.Console
    

使用语义内核创建简单应用程序

从项目目录中,使用你喜欢的编辑器打开 Program.cs 该文件。 我们将创建一个简单的应用程序,该应用程序使用语义内核向聊天完成模型发送提示。 将现有内容替换为以下代码,并填写所需的值 deploymentNameendpoint以及 apiKey

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

namespace TelemetryConsoleQuickstart
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // Telemetry setup code goes here

            IKernelBuilder builder = Kernel.CreateBuilder();
            // builder.Services.AddSingleton(loggerFactory);
            builder.AddAzureOpenAIChatCompletion(
                deploymentName: "your-deployment-name",
                endpoint: "your-azure-openai-endpoint",
                apiKey: "your-azure-openai-api-key"
            );

            Kernel kernel = builder.Build();

            var answer = await kernel.InvokePromptAsync(
                "Why is the sky blue in one sentence?"
            );

            Console.WriteLine(answer);
        }
    }
}

添加遥测

如果现在运行控制台应用,应该会看到一句话,说明天空为何为蓝色。 若要通过遥测观察内核,请将 // Telemetry setup code goes here 注释替换为以下代码:

var resourceBuilder = ResourceBuilder
    .CreateDefault()
    .AddService("TelemetryConsoleQuickstart");

// Enable model diagnostics with sensitive data.
AppContext.SetSwitch("Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnosticsSensitive", true);

using var traceProvider = Sdk.CreateTracerProviderBuilder()
    .SetResourceBuilder(resourceBuilder)
    .AddSource("Microsoft.SemanticKernel*")
    .AddConsoleExporter()
    .Build();

using var meterProvider = Sdk.CreateMeterProviderBuilder()
    .SetResourceBuilder(resourceBuilder)
    .AddMeter("Microsoft.SemanticKernel*")
    .AddConsoleExporter()
    .Build();

using var loggerFactory = LoggerFactory.Create(builder =>
{
    // Add OpenTelemetry as a logging provider
    builder.AddOpenTelemetry(options =>
    {
        options.SetResourceBuilder(resourceBuilder);
        options.AddConsoleExporter();
        // Format log messages. This is default to false.
        options.IncludeFormattedMessage = true;
        options.IncludeScopes = true;
    });
    builder.SetMinimumLevel(LogLevel.Information);
});

最后取消注释要向生成器添加记录器工厂的行 // builder.Services.AddSingleton(loggerFactory);

在上面的代码片段中,我们首先创建用于生成资源实例的资源生成器。 资源表示生成遥测数据的实体。 可以在此处阅读有关资源的详细信息。 提供程序的资源生成器是可选的。 如果未提供,则使用具有默认属性的默认资源。

接下来,我们打开包含敏感数据的诊断。 这是一项实验性功能,可用于为语义内核中的 AI 服务启用诊断。 启用此功能后,你将看到其他遥测数据,例如发送到的提示以及从 AI 模型收到的响应,这些数据被视为敏感数据。 如果不想在遥测中包含敏感数据,可以使用另一个开关 Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnostics 来启用非敏感数据的诊断,例如模型名称、操作名称和令牌用法等。

然后,我们将创建跟踪提供程序生成器和计量提供程序生成器。 提供程序负责处理遥测数据并将其管道传递给导出程序。 我们订阅 Microsoft.SemanticKernel* 源以从语义内核命名空间接收遥测数据。 我们将控制台导出程序添加到跟踪提供程序和计量提供程序。 控制台导出程序将遥测数据发送到控制台。

最后,我们将创建一个记录器工厂,并将 OpenTelemetry 添加为将日志数据发送到控制台的日志记录提供程序。 我们将最小日志级别设置为 Information 日志输出中格式化的消息和范围,并将其包含在日志输出中。 然后,记录器工厂将添加到生成器。

重要

提供程序应为单一实例,并且在整个应用程序生存期内应处于活动状态。 当应用程序关闭时,应释放提供程序。

创建新的 Python 虚拟环境

python -m venv telemetry-console-quickstart

激活虚拟环境。

telemetry-console-quickstart\Scripts\activate

安装所需程序包

pip install semantic-kernel

使用语义内核创建简单的 Python 脚本

创建新的 Python 脚本,并使用你喜欢的编辑器打开它。

New-Item -Path telemetry_console_quickstart.py -ItemType file

我们将创建一个简单的 Python 脚本,该脚本使用语义内核向聊天完成模型发送提示。 将现有内容替换为以下代码,并填写所需的值 deployment_nameendpoint以及 api_key

import asyncio
import logging

from opentelemetry._logs import set_logger_provider
from opentelemetry.metrics import set_meter_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader
from opentelemetry.sdk.metrics.view import DropAggregation, View
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
from opentelemetry.semconv.resource import ResourceAttributes
from opentelemetry.trace import set_tracer_provider

from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion


# Telemetry setup code goes here

async def main():
    # Create a kernel and add a service
    kernel = Kernel()
    kernel.add_service(AzureChatCompletion(
        api_key="your-azure-openai-api-key",
        endpoint="your-azure-openai-endpoint",
        deployment_name="your-deployment-name"
    ))

    answer = await kernel.invoke_prompt("Why is the sky blue in one sentence?")
    print(answer)


if __name__ == "__main__":
    asyncio.run(main())

添加遥测

环境变量

默认情况下,内核不会为 AI 连接器发出跨度,因为这些范围包含 gen_ai 被视为实验性的属性。 若要启用该功能,请将环境变量 SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICSSEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE 设置为 true

重要

提示和完成被视为敏感数据。 语义内核不会从 AI 连接器发出这些数据, SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE 除非环境变量设置为 truetrue设置为SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS仅发出非敏感数据,例如模型名称、操作名称和令牌用法。

在脚本所在的同一目录中创建一 .env 个名为的新文件,并添加以下内容:

SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE=true

代码

如果现在运行该脚本,应该会看到一个句子,说明天空为何为蓝色。 若要通过遥测观察内核,请将 # Telemetry setup code goes here 注释替换为以下代码:

# Create a resource to represent the service/sample
resource = Resource.create({ResourceAttributes.SERVICE_NAME: "telemetry-console-quickstart"})


def set_up_logging():
    exporter = ConsoleLogExporter()

    # Create and set a global logger provider for the application.
    logger_provider = LoggerProvider(resource=resource)
    # Log processors are initialized with an exporter which is responsible
    # for sending the telemetry data to a particular backend.
    logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
    # Sets the global default logger provider
    set_logger_provider(logger_provider)

    # Create a logging handler to write logging records, in OTLP format, to the exporter.
    handler = LoggingHandler()
    # Add filters to the handler to only process records from semantic_kernel.
    handler.addFilter(logging.Filter("semantic_kernel"))
    # Attach the handler to the root logger. `getLogger()` with no arguments returns the root logger.
    # Events from all child loggers will be processed by this handler.
    logger = logging.getLogger()
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)


def set_up_tracing():
    exporter = ConsoleSpanExporter()

    # Initialize a trace provider for the application. This is a factory for creating tracers.
    tracer_provider = TracerProvider(resource=resource)
    # Span processors are initialized with an exporter which is responsible
    # for sending the telemetry data to a particular backend.
    tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
    # Sets the global default tracer provider
    set_tracer_provider(tracer_provider)


def set_up_metrics():
    exporter = ConsoleMetricExporter()

    # Initialize a metric provider for the application. This is a factory for creating meters.
    meter_provider = MeterProvider(
        metric_readers=[PeriodicExportingMetricReader(exporter, export_interval_millis=5000)],
        resource=resource,
        views=[
            # Dropping all instrument names except for those starting with "semantic_kernel"
            View(instrument_name="*", aggregation=DropAggregation()),
            View(instrument_name="semantic_kernel*"),
        ],
    )
    # Sets the global default meter provider
    set_meter_provider(meter_provider)


# This must be done before any other telemetry calls
set_up_logging()
set_up_tracing()
set_up_metrics()

在上面的代码片段中,我们首先创建一个资源来表示服务。 资源表示生成遥测数据的实体。 可以在此处阅读有关资源的详细信息。 然后创建三个函数来设置日志记录、跟踪和指标。 每个函数都会为相应的遥测数据创建提供程序,并向提供程序添加控制台导出程序。

最后,我们调用三个函数来设置日志记录、跟踪和指标。 必须在任何其他遥测调用之前执行此操作。

注意

语义内核可观测性尚不适用于 Java。

运行

使用以下命令运行控制台应用程序:

dotnet run

使用以下命令运行 Python 脚本:

python telemetry_console_quickstart.py

注意

语义内核可观测性尚不适用于 Java。

检查遥测数据

日志记录

控制台输出中应会显示多个日志记录。 它们如下所示:

LogRecord.Timestamp:               2024-09-12T21:48:35.2295938Z
LogRecord.TraceId:                 159d3f07664838f6abdad7af6a892cfa
LogRecord.SpanId:                  ac79a006da8a6215
LogRecord.TraceFlags:              Recorded
LogRecord.CategoryName:            Microsoft.SemanticKernel.KernelFunction
LogRecord.Severity:                Info
LogRecord.SeverityText:            Information
LogRecord.FormattedMessage:        Function InvokePromptAsync_290eb9bece084b00aea46b569174feae invoking.
LogRecord.Body:                    Function {FunctionName} invoking.
LogRecord.Attributes (Key:Value):
    FunctionName: InvokePromptAsync_290eb9bece084b00aea46b569174feae
    OriginalFormat (a.k.a Body): Function {FunctionName} invoking.

Resource associated with LogRecord:
service.name: TelemetryConsoleQuickstart
service.instance.id: a637dfc9-0e83-4435-9534-fb89902e64f8
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.9.0

每个日志记录有两个部分:

  • 日志记录本身:包含生成日志记录的时间戳和命名空间、日志记录的严重性和正文以及与日志记录关联的任何属性。
  • 与日志记录关联的资源:包含有关用于生成日志记录的服务、实例和 SDK 的信息。

活动

注意

.Net 中的活动类似于 OpenTelemetry 中的跨度。 它们用于表示应用程序中的工作单元。

控制台输出中应会显示多个活动。 它们如下所示:

Activity.TraceId:            159d3f07664838f6abdad7af6a892cfa
Activity.SpanId:             8c7c79bc1036eab3
Activity.TraceFlags:         Recorded
Activity.ParentSpanId:       ac79a006da8a6215
Activity.ActivitySourceName: Microsoft.SemanticKernel.Diagnostics
Activity.DisplayName:        chat.completions gpt-4o
Activity.Kind:               Client
Activity.StartTime:          2024-09-12T21:48:35.5717463Z
Activity.Duration:           00:00:02.3992014
Activity.Tags:
    gen_ai.operation.name: chat.completions
    gen_ai.system: openai
    gen_ai.request.model: gpt-4o
    gen_ai.response.prompt_tokens: 16
    gen_ai.response.completion_tokens: 29
    gen_ai.response.finish_reason: Stop
    gen_ai.response.id: chatcmpl-A6lxz14rKuQpQibmiCpzmye6z9rxC
Activity.Events:
    gen_ai.content.prompt [9/12/2024 9:48:35 PM +00:00]
        gen_ai.prompt: [{"role": "user", "content": "Why is the sky blue in one sentence?"}]
    gen_ai.content.completion [9/12/2024 9:48:37 PM +00:00]
        gen_ai.completion: [{"role": "Assistant", "content": "The sky appears blue because shorter blue wavelengths of sunlight are scattered in all directions by the gases and particles in the Earth\u0027s atmosphere more than other colors."}]
Resource associated with Activity:
    service.name: TelemetryConsoleQuickstart
    service.instance.id: a637dfc9-0e83-4435-9534-fb89902e64f8
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.9.0

每个活动有两个部分:

  • 活动本身:包含 APM 工具用来生成跟踪、活动持续时间以及与活动关联的任何标记和事件跨度 ID 和父范围 ID。
  • 与活动关联的资源:包含有关用于生成活动的服务、实例和 SDK 的信息。

重要

要特别注意的属性是一 gen_ai开始的属性。 这些是 GenAI 语义约定中指定的属性。

指标

控制台输出中应会显示多个指标记录。 它们如下所示:

Metric Name: semantic_kernel.connectors.openai.tokens.prompt, Number of prompt tokens used, Unit: {token}, Meter: Microsoft.SemanticKernel.Connectors.OpenAI
(2024-09-12T21:48:37.9531072Z, 2024-09-12T21:48:38.0966737Z] LongSum
Value: 16

可在此处查看名称、说明、单位、时间范围、类型、指标的值以及指标所属的计量。

注意

上述指标是计数器指标。 有关指标类型的完整列表,请参阅 此处。 根据指标的类型,输出可能会有所不同。

日志

控制台输出中应会显示多个日志记录。 它们如下所示:

{
    "body": "Function SyVCcBjaULqEhItH invoking.",
    "severity_number": "<SeverityNumber.INFO: 9>",
    "severity_text": "INFO",
    "attributes": {
        "code.filepath": "C:\\tmp\\telemetry-console-quickstart\\Lib\\site-packages\\semantic_kernel\\functions\\kernel_function_log_messages.py",
        "code.function": "log_function_invoking",
        "code.lineno": 19
    },
    "dropped_attributes": 0,
    "timestamp": "2024-09-13T17:55:45.504983Z",
    "observed_timestamp": "2024-09-13T17:55:45.504983Z",
    "trace_id": "0xe23e2c10785ea61ffc9f28be19482a80",
    "span_id": "0x686bd592e27661d7",
    "trace_flags": 1,
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.27.0",
            "service.name": "telemetry-console-quickstart"
        },
        "schema_url": ""
    }
}

范围

控制台输出中应显示多个跨度。 它们如下所示:

{
    "name": "chat.completions gpt-4o",
    "context": {
        "trace_id": "0xe23e2c10785ea61ffc9f28be19482a80",
        "span_id": "0x8b20e9655610c3c9",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0x686bd592e27661d7",
    "start_time": "2024-09-13T17:55:45.515198Z",
    "end_time": "2024-09-13T17:55:46.469471Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "gen_ai.operation.name": "chat.completions",
        "gen_ai.system": "openai",
        "gen_ai.request.model": "gpt-4o",
        "gen_ai.response.id": "chatcmpl-A74oD7WGDjawnZ44SJZrj9fKrEv1B",
        "gen_ai.response.finish_reason": "FinishReason.STOP",
        "gen_ai.response.prompt_tokens": 16,
        "gen_ai.response.completion_tokens": 29
    },
    "events": [
        {
            "name": "gen_ai.content.prompt",
            "timestamp": "2024-09-13T17:55:45.515198Z",
            "attributes": {
                "gen_ai.prompt": "[{\"role\": \"user\", \"content\": \"Why is the sky blue in one sentence?\"}]"
            }
        },
        {
            "name": "gen_ai.content.completion",
            "timestamp": "2024-09-13T17:55:46.469471Z",
            "attributes": {
                "gen_ai.completion": "[{\"role\": \"assistant\", \"content\": \"The sky appears blue because shorter blue wavelengths of sunlight are scattered in all directions by the molecules and particles in the atmosphere more effectively than other colors.\"}]"
            }
        }
    ],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.27.0",
            "service.name": "telemetry-console-quickstart"
        },
        "schema_url": ""
    }
}

请注意以 gen_ai.. 开头的属性。 这些是 GenAI 语义约定中指定的属性。 它们提供有关发送到的请求和从 AI 模型接收的响应的有用信息。

指标

控制台输出中应会显示多个指标记录。 它们如下所示:

{
    "resource_metrics": [
        {
            "resource": {
                "attributes": {
                    "telemetry.sdk.language": "python",
                    "telemetry.sdk.name": "opentelemetry",
                    "telemetry.sdk.version": "1.27.0",
                    "service.name": "telemetry-console-quickstart"
                },
                "schema_url": ""
            },
            "scope_metrics": [
                {
                    "scope": {
                        "name": "semantic_kernel.functions.kernel_function",
                        "version": null,
                        "schema_url": "",
                        "attributes": null
                    },
                    "metrics": [
                        {
                            "name": "semantic_kernel.function.invocation.duration",
                            "description": "Measures the duration of a function's execution",
                            "unit": "s",
                            "data": {
                                "data_points": [
                                    {
                                        "attributes": {
                                            "semantic_kernel.function.name": "SyVCcBjaULqEhItH"
                                        },
                                        "start_time_unix_nano": 1726250146470468300,
                                        "time_unix_nano": 1726250146478526600,
                                        "count": 1,
                                        "sum": 0.9650602999900002,
                                        "bucket_counts": [
                                            0,
                                            1,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0
                                        ],
                                        "explicit_bounds": [
                                            0.0,
                                            5.0,
                                            10.0,
                                            25.0,
                                            50.0,
                                            75.0,
                                            100.0,
                                            250.0,
                                            500.0,
                                            750.0,
                                            1000.0,
                                            2500.0,
                                            5000.0,
                                            7500.0,
                                            10000.0
                                        ],
                                        "min": 0.9650602999900002,
                                        "max": 0.9650602999900002
                                    }
                                ],
                                "aggregation_temporality": 2
                            }
                        }
                    ],
                    "schema_url": ""
                }
            ],
            "schema_url": ""
        }
    ]
}

上面所示的度量值是直方图指标。 有关指标类型的完整列表,请参阅 此处

注意

语义内核可观测性尚不适用于 Java。

后续步骤

成功将遥测数据输出到控制台后,可以详细了解如何使用 APM 工具可视化和分析遥测数据。