이 문서는 .NET 6.0 이상 ✔️ .NET Framework 4.6.1 이상에 적용됩니다 ✔️.
계측된 코드는 숫자 측정값을 기록할 수 있지만, 모니터링에 유용한 메트릭을 만들려면 일반적으로 측정값을 집계, 전송 및 저장해야 합니다. 데이터를 모아서 전송하고 저장하는 과정을 컬렉션이라고 합니다. 이 자습서에서는 메트릭 수집에 대한 몇 가지 예를 보여 줍니다.
- OpenTelemetry 및 Prometheus를 사용하여 Grafana에서 메트릭 채우기
-
dotnet-counters
를 사용하여 실시간으로 메트릭 보기 - 기본 .NET MeterListener API를 사용하여 사용자 지정 컬렉션 도구 만들기
사용자 지정 메트릭 계측 및 옵션에 대한 자세한 내용은 메트릭 API 비교를 참조하세요.
필수 조건
- .NET 6.0 SDK 이상
예제 앱 만들기
메트릭을 수집하려면 먼저 측정값을 생성해야 합니다. 이 자습서에서는 기본 메트릭 계측이 있는 앱을 만듭니다. .NET 런타임에는 다양한 메트릭도 기본 제공되어 있습니다. API를 사용하여 System.Diagnostics.Metrics.Meter 새 메트릭을 만드는 방법에 대한 자세한 내용은 계측 자습서를 참조하세요.
dotnet new console -o metric-instr
cd metric-instr
dotnet add package System.Diagnostics.DiagnosticSource
Program.cs
의 내용을 다음 코드로 바꿉니다.
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hats-sold");
static void Main(string[] args)
{
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
}
위의 코드는 임의 간격 및 임의 시간에 모자 판매를 시뮬레이션합니다.
dotnet-counters를 사용하여 메트릭 보기
dotnet-counters는 필요에 따라 .NET Core 앱에 대한 라이브 메트릭을 볼 수 있는 명령줄 도구입니다. 설정이 필요하지 않으므로 임시 조사나 메트릭 계측이 작동하는지 확인하는 데 유용합니다. System.Diagnostics.Metrics 기반 API 및 EventCounters 모두에서 작동합니다.
dotnet-counters 도구가 설치되지 않은 경우 다음 명령을 실행합니다.
dotnet tool update -g dotnet-counters
예제 앱이 실행되는 동안 dotnet-counters를 시작합니다. 다음 명령은 dotnet-counters
미터의 모든 메트릭을 모니터링하는 예를 HatCo.HatStore
보여줍니다. 미터 이름은 대/소문자를 구분합니다. 샘플 앱의 이름을 metric-instr.exe으로 대체하십시오.
dotnet-counters monitor -n metric-instr HatCo.HatStore
다음과 비슷한 출력이 표시됩니다.
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.HatStore]
hats-sold (Count / 1 sec) 4
dotnet-counters
는 다른 메트릭 집합을 사용하여 실행하여 .NET 런타임의 일부 기본 제공 계측을 확인할 수 있습니다.
dotnet-counters monitor -n metric-instr
다음과 비슷한 출력이 표시됩니다.
Press p to pause, r to resume, q to quit.
Status: Running
[System.Runtime]
% Time in GC since last GC (%) 0
Allocation Rate (B / 1 sec) 8,168
CPU Usage (%) 0
Exception Count (Count / 1 sec) 0
GC Heap Size (MB) 2
Gen 0 GC Count (Count / 1 sec) 0
Gen 0 Size (B) 2,216,256
Gen 1 GC Count (Count / 1 sec) 0
Gen 1 Size (B) 423,392
Gen 2 GC Count (Count / 1 sec) 0
Gen 2 Size (B) 203,248
LOH Size (B) 933,216
Monitor Lock Contention Count (Count / 1 sec) 0
Number of Active Timers 1
Number of Assemblies Loaded 39
ThreadPool Completed Work Item Count (Count / 1 sec) 0
ThreadPool Queue Length 0
ThreadPool Thread Count 3
Working Set (MB) 30
자세한 내용은 dotnet-counters를 참조하세요. .NET의 메트릭에 대한 자세한 내용은 기본 제공 메트릭을 참조하세요.
OpenTelemetry 및 Prometheus를 사용하여 Grafana에서 메트릭 보기
개요
- Cloud Native Computing Foundation에서 지원하는 공급업체 중립적 오픈 소스 프로젝트입니다.
- 클라우드 기반 소프트웨어에 대한 원격 분석 생성 및 수집을 표준화합니다.
- .NET 메트릭 API를 사용하여 .NET에서 작동합니다.
- Azure Monitor 및 많은 APM 공급업체의 보증을 받았습니다.
이 자습서에서는 OSS Prometheus 및 Grafana 프로젝트를 사용하여 OpenTelemetry 메트릭에 사용할 수 있는 통합 중 하나를 보여 줍니다. 메트릭 데이터 흐름:
.NET 메트릭 API는 예제 앱의 측정값을 기록합니다.
앱에서 실행되는 OpenTelemetry 라이브러리는 측정값을 집계합니다.
Prometheus exporter 라이브러리는 HTTP 메트릭 엔드포인트를 통해 집계된 데이터를 사용할 수 있게 합니다. 'Exporter'는 OpenTelemetry가 공급업체별 백 엔드에 원격 분석을 전송하는 라이브러리를 지칭하는 용어입니다.
Prometheus 서버:
- 메트릭의 엔드포인트를 폴링합니다.
- 데이터를 읽습니다.
- 장기간 지속성을 위해 데이터를 데이터베이스에 저장합니다. Prometheus는 엔드포인트를 스크래핑하는 것으로 데이터를 읽고 저장하는 것을 말합니다.
- 다른 컴퓨터에서 실행 가능
Grafana 서버:
- Prometheus에 저장된 데이터를 쿼리하여 웹 기반 모니터링 대시보드에 표시합니다.
- 다른 컴퓨터에서 실행할 수 있습니다.
OpenTelemetry의 Prometheus 내보내기를 사용하도록 예제 앱 구성
예제 앱에 OpenTelemetry Prometheus 내보내기 참조를 추가합니다.
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
비고
이 자습서에서는 작성 시 사용할 수 있는 OpenTelemetry의 Prometheus 지원의 시험판 빌드를 사용합니다.
OpenTelemetry 구성으로 Program.cs
을(를) 업데이트하십시오.
using OpenTelemetry;
using OpenTelemetry.Metrics;
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(
name: "hats-sold",
unit: "Hats",
description: "The number of hats sold in our store");
static void Main(string[] args)
{
using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter("HatCo.HatStore")
.AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { "http://localhost:9184/" })
.Build();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0,1000));
}
}
}
앞의 코드에서 다음을 수행합니다.
-
AddMeter("HatCo.HatStore")
는 앱에 정의된 미터에서 수집한 모든 메트릭을 전송하도록 OpenTelemetry를 구성합니다. -
AddPrometheusHttpListener
는 OpenTelemetry를 다음과 같이 구성합니다.- Prometheus의 메트릭 엔드포인트를 포트
9184
에 노출하십시오. - HttpListener를 사용합니다.
- Prometheus의 메트릭 엔드포인트를 포트
OpenTelemetry 구성 옵션에 대한 자세한 내용은 OpenTelemetry 설명서를 참조하세요. OpenTelemetry 설명서에는 ASP.NET 앱에 대한 호스팅 옵션이 표시됩니다.
측정값을 수집할 수 있도록 앱을 실행하고 실행 상태로 둡니다.
dotnet run
Prometheus 설정 및 구성
Prometheus 첫 번째 단계에 따라 Prometheus 서버를 설정하고 작동 중인지 확인합니다.
Prometheus가 예제 앱이 노출하는 메트릭 엔드포인트를 스크랩하도록 prometheus.yml 구성 파일을 수정합니다.
scrape_configs
섹션에 다음 강조 표시된 텍스트를 추가합니다.
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ["localhost:9090"]
- job_name: 'OpenTelemetryTest'
scrape_interval: 1s # poll very quickly for a more responsive demo
static_configs:
- targets: ['localhost:9184']
Prometheus를 시작하세요
구성을 다시 로드하거나 Prometheus 서버를 다시 시작합니다.
Prometheus 웹 포털의 상태>대상 페이지에서 OpenTelemetryTest가 UP 상태인지 확인합니다.
Prometheus 웹 포털의 그래프 페이지에서 표현식 텍스트 상자에
hats
을(를) 입력하고,hats_sold_Hats
그래프 탭에서을 선택하면, Prometheus는 예제 앱에서 내보내는 "hats-sold" 카운터의 증가하는 값을 표시합니다.
이전 이미지에서 그래프 시간은 5분인 5m로 설정됩니다.
Prometheus 서버가 예제 앱을 오랫동안 스크래핑하지 않은 경우 데이터가 누적되기를 기다려야 할 수 있습니다.
Grafana 대시보드에 메트릭 표시
표준 지침에 따라 Grafana를 설치하고 Prometheus 데이터 원본에 연결합니다.
Grafana 웹 포털의 + 왼쪽 도구 모음에서 아이콘을 클릭하여 Grafana 대시보드를 만든 다음, 대시보드를 선택합니다. 표시되는 대시보드 편집기에서 Title 입력 상자에 Hats Sold/Sec를 입력하고 PromQL 식 필드에 rate(hats_sold[5m])를 입력합니다.
적용을 클릭하여 새 대시보드를 저장하고 봅니다.
]
.NET MeterListener API를 사용하여 사용자 지정 컬렉션 도구 만들기
.NET MeterListener API를 사용하면 사용자 지정 인프로세스 논리를 작성하여 System.Diagnostics.Metrics.Meter에서 기록하는 측정 데이터를 관찰할 수 있습니다. 이전 EventCounters 계측과 호환되는 사용자 지정 논리를 만드는 방법에 대한 지침은 EventCounters를 참조하세요.
Program.cs
코드를 수정하여 MeterListener를 사용하도록 합니다.
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(
name: "hats-sold",
unit: "Hats",
description: "The number of hats sold in our store");
static void Main(string[] args)
{
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
// Start the meterListener, enabling InstrumentPublished callbacks.
meterListener.Start();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
static void OnMeasurementRecorded<T>(
Instrument instrument,
T measurement,
ReadOnlySpan<KeyValuePair<string, object?>> tags,
object? state)
{
Console.WriteLine($"{instrument.Name} recorded measurement {measurement}");
}
}
다음 출력은 각 측정값에 대한 사용자 지정 콜백이 있는 앱의 출력을 보여줍니다.
> dotnet run
Press any key to exit
hats-sold recorded measurement 978
hats-sold recorded measurement 775
hats-sold recorded measurement 666
hats-sold recorded measurement 66
hats-sold recorded measurement 914
hats-sold recorded measurement 912
...
샘플 코드 설명
이 섹션의 코드 조각은 이전 샘플에서 제공됩니다.
다음 강조 표시된 코드에서 측정값을 MeterListener 수신하기 위해 인스턴스가 만들어집니다.
using
키워드는 Dispose
가 범위를 벗어날 때 meterListener
가 호출되도록 합니다.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
다음 강조 표시된 코드는 리스너가 어떤 계측기에서 측정값을 수신할지 설정합니다. InstrumentPublished 는 앱 내에서 새 계측을 만들 때 호출되는 대리자입니다.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
대리자는 문서를 검토하여 구독 여부를 결정할 수 있습니다. 예를 들어 대리자는 이름, 계량기 또는 기타 공용 속성을 확인할 수 있습니다. EnableMeasurementEvents 를 사용하면 지정된 계측기에서 측정값을 수신할 수 있습니다. 다른 방법으로 계측에 대한 참조를 가져오는 코드:
- 일반적으로 수행되지 않습니다.
- 언제든지 참조를 사용하여
EnableMeasurementEvents()
호출할 수 있습니다.
계측에서 측정값을 받을 때 호출되는 대리자는 다음을 호출 SetMeasurementEventCallback하여 구성됩니다.
meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
// Start the meterListener, enabling InstrumentPublished callbacks.
meterListener.Start();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
static void OnMeasurementRecorded<T>(
Instrument instrument,
T measurement,
ReadOnlySpan<KeyValuePair<string, object?>> tags,
object? state)
{
Console.WriteLine($"{instrument.Name} recorded measurement {measurement}");
}
제네릭 매개 변수는 콜백에서 수신되는 측정 데이터 형식을 제어합니다. 예를 들어, Counter<int>
는 int
측정값을 생성하고, Counter<double>
는 double
측정값을 생성합니다. 악기는 byte
, short
, int
, long
, float
, double
, 및 decimal
유형으로 만들 수 있습니다. 모든 데이터 형식이 필요하지 않다는 시나리오별 지식이 없는 한 모든 데이터 형식에 대한 콜백을 등록하는 것이 좋습니다. 다른 제네릭 인수를 사용하여 반복적으로 호출하는 SetMeasurementEventCallback
것은 약간 특이한 것처럼 보일 수 있습니다. 이 API는 성능 오버헤드가 낮은 측정값(일반적으로 몇 나노초)을 수신할 수 있도록 MeterListener
이러한 방식으로 설계되었습니다.
MeterListener.EnableMeasurementEvents
호출되면 개체를 state
매개 변수 중 하나로 제공할 수 있습니다.
state
개체는 임의입니다. 해당 호출에서 상태 개체를 제공하는 경우 해당 계측과 함께 저장되고 콜백의 state
매개 변수로 반환됩니다. 편의성과 성능 최적화를 위한 것입니다. 종종 청취자는 다음을 해야하는 경우가 있습니다.
- 메모리에 측정값을 저장하는 각 계측에 대한 개체를 만듭니다.
- 해당 측정값에 대한 계산을 수행하는 코드가 있어야 합니다.
또는 계측기에서 스토리지 개체로 매핑되는 Dictionary
를 만들고, 각 측정 시 이를 조회하십시오.
Dictionary
를 사용하는 것이 state
에서 액세스하는 것보다 훨씬 느립니다.
meterListener.Start();
위의 코드는 콜백을 MeterListener
사용하도록 설정하는 것을 시작하는 코드입니다.
InstrumentPublished
대리자는 프로세스의 모든 기존 Instrument에 대해 호출됩니다. 새로 생성된 Instrument 객체는 또한 InstrumentPublished
를 호출하도록 트리거됩니다.
using MeterListener meterListener = new MeterListener();
앱이 수신 대기를 완료하면 수신기를 삭제하면 콜백 흐름이 중지되고 수신기 개체에 대한 내부 참조가 해제됩니다. 변수가 범위를 벗어날 때 using
가 호출되도록 하는 키워드는 meterListener
을 선언할 때 사용됩니다.
Dispose
는 새 콜백을 시작하지 않을 것임만을 약속한다는 점을 유의하세요. 콜백은 서로 다른 스레드에서 발생하므로 반환 호출 Dispose
후에도 진행 중인 콜백이 있을 수 있습니다.
콜백의 특정 코드 영역이 현재 실행되고 있지 않고 나중에 실행되지 않도록 하려면 스레드 동기화를 추가해야 합니다.
Dispose
는 다음과 같은 이유로 기본적으로 동기화를 포함하지 않습니다.
- 동기화는 모든 측정 콜백에 성능 오버헤드를 추가합니다.
-
MeterListener
는 고성능에 민감한 API로 설계되었습니다.
.NET