Anixe.IO.Appmetrics
4.0.0
dotnet add package Anixe.IO.Appmetrics --version 4.0.0
NuGet\Install-Package Anixe.IO.Appmetrics -Version 4.0.0
<PackageReference Include="Anixe.IO.Appmetrics" Version="4.0.0" />
paket add Anixe.IO.Appmetrics --version 4.0.0
#r "nuget: Anixe.IO.Appmetrics, 4.0.0"
// Install Anixe.IO.Appmetrics as a Cake Addin #addin nuget:?package=Anixe.IO.Appmetrics&version=4.0.0 // Install Anixe.IO.Appmetrics as a Cake Tool #tool nuget:?package=Anixe.IO.Appmetrics&version=4.0.0
Appmetrics
Anixe.IO.Appmetrics allows you to quickly integrate your asp .net core application with graylog. The module produces json object in GELF format with fields describing inbound communication (incoming HTTP request into the application) and outbound communication (any IO operation made by the application)
How to integrate
add in csproj as a package reference
<PackageReference Include="Anixe.IO.Appmetrics" Version="2.0.11" />
add section to appsettings.json
{
...
"Appmetrics" : {
"LogSender" : {
"Channel": "NLog" // External, NLog, UdpClient
},
"Middleware": { // configuration for inbound HTTP requests
"TenantFieldName": "prof", // url query parameter or routing data or http context's items
"RequestId" : {
"QueryFieldName": "rid", // url query parameter
"HeaderFieldName": "X-RequestId", // request header parameter
"AddToResponse": true // will add the value to the response header
},
"MethodIdIdFieldName": "mid", // HTTP context's item name
"IncludeMethods": [],
"ExcludeMethods": [ // omit these requests with HTTP method
"OPTIONS",
"HEAD"
],
"IncludeUrls": [],
"ExcludeUrls": [ // do not log for these incoming requests URLs to avoid measuring healthchecks
"/ping",
"$health$", //$..$ to match all urls which contains health
"/status$" // ...$ to match all urls which start with /status
]
},
"HttpClient": { // configuration for outbound HTTP requests
"TimeoutCustomMessage": "Timeout occurred", // override OperationCancelledException and TaskCancelledException with this message
"IncludeUrls": [],
"ExcludeUrls": [ // do not log for these outbound urls to avoid measuring healthchecks
"/_cluster/health"
]
},
"Defaults" : { // each message will have these defaults
"application_name": "geows-be",
"src": "geows-be", // can be something else e.g. module from the monolith (plugin name, startup). Useful in case of several different metrics outside the convention scope
"platform": "shared-svc",
"product": "geo"
}
}
}
register middleware for inbound HTTP requests and handling exceptions in DI container in Startup.cs and additional http client handler for collecting metrics from outbound HTTP request, all you need it to define HttpClient
sub-section in Appmetrics
section in appsettings.json and use HttpClientFactory for http client registration; otherwise only middleware will be registered.
public void ConfigureServices(IServiceCollection services)
{
services.UseAnixeAppmetrics(this.config, opts => // use this to provide metrics for inbound HTTP requests and unhandled exception
{
// override any option here
opts.Middleware.HostName = "my machine"; /* providing machine name is on your own */
opts.Middleware.ServerIp = "127.0.0.1"; /* providing machine name is on your own */
})
}
Register NLog target or built-in UDP sender
for NLog just set up appsettings.json
{
...
"Appmetrics" : {
"LogSender" : {
"Channel": "NLog"
},
...
}
}
then use build-in anixe-appmetrics
layout formatter and define an NLog rule with appmetrics
name
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwExceptions="true"
autoReload="false"
internalLogLevel="Warn"
internalLogFile="internal-nlog.txt">
<extensions>
<add assembly="Anixe.IO.Appmetrics"/>
</extensions>
<targets>
<target xsi:type="Console" name="appmetrics-console" layout="${anixe-appmetrics}" />
<target xsi:type="Network" address="udp://graylog.com:5558" name="appmetrics" layout="${anixe-appmetrics}" />
</targets>
<rules>
<logger name="appmetrics-console" minlevel="Debug" writeTo="appmetrics-console" final="true">
<filters defaultAction="Ignore">
<when condition="equals('${environment:ASPNETCORE_ENVIRONMENT}','Development')" action="Log"/>
</filters>
</logger>
<logger name="appmetrics" minlevel="Debug" writeTo="appmetrics" final="true">
<filters defaultAction="Ignore">
<when condition="not equals('${environment:ASPNETCORE_ENVIRONMENT}','Development')" action="Log"/>
</filters>
</logger>
</rules>
</nlog>
for build-in UdpSender just set up appsettings.json
{
...
"Appmetrics" : {
"LogSender" : {
"Channel": "UdpClient"
},
...
}
}
and expect IUdpClient
instance from DI container as a dependency. The IUdpClient
instance is a thread-safe singleton.
public class MyCustomLogger
{
private readonly IUdpClient client;
public MyCustomLogger(IUdpClient client)
{
this.client = client;
}
}
for your custom log sender just set up in appsettings.json
{
...
"Appmetrics" : {
"LogSender" : {
"Channel": "External"
},
...
}
}
and register your own IUdpClient
implementation in DI container.
And thats all, it registers default convention made from Appmetrics middleware, filter and http client delegating handler. You don't need to add any additional code.
How to get access to log message within asp .net core processing pipeline
Expect IMessage
in HttpContext.Items[nameof(IMessage)]
. This example describes a controller which uses HttpContext.Items[nameof(IMessage)]
public class LocationsController : ControllerBase
{
private readonly ILocationsService locations;
public LocationsController(ILocationsService locations)
{
this.locations = locations;
}
[Route("locations")]
[HttpGet]
public async Task<IActionResult> GetAllAsync([FromQuery]LocationsRequestModel request)
{
var mgs = this.HttpContext.Items[nameof(IMessage)] as IMessage;
var retval = await locations.FindAllAsync(request, this.HttpContext.RequestAborted);
msg.AddPair("no", retval.locations?.Count ?? 0);
Response.Headers.Add("X-Total", retval.total.GetValueOrDefault().ToString());
Response.Headers.Add("X-Total-Unfiltered", retval.total_unfiltered.GetValueOrDefault().ToString());
return Ok(retval);
}
}
How to manually prepare log message then send it outside asp .net core processing pipeline.
Expect IMetricsCollector
instance which has several logging method. Each method will send log to the graylog after method call ends. This example describes a hosted service.
public class TaskQueueHostedService : BackgroundService
{
private readonly IBackgroundTaskQueue queue;
private readonly IMetricsCollector metricsCollector;
public TaskQueueHostedService(
IBackgroundTaskQueue queue,
IMetricsCollector metricsCollector)
{
this.metricsCollector = metricsCollector;
this.queue = queue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Consts.Trace.Value.Info("Starting TaskQueueHostedService");
while (!stoppingToken.IsCancellationRequested)
{
try
{
var job = await this.queue.DequeueAsync(stoppingToken);
await this.metricsCollector.LogAsync(LogInvokeTask, (job, stoppingToken));
}
catch (Exception ex)
{
Consts.Trace.Value.Fatal(ex);
}
}
Consts.Trace.Value.Info("Finished TaskQueueHostedService");
}
private async Task LogInvokeTask(IMessage msg, ((string jobName, Func<CancellationToken, Task<int>> task) job, CancellationToken token) state)
{
var (job, token) = state;
msg.src = Consts.JobFacility;
msg.transaction_name = job.jobName;
var itemsAffected = await job.task?.Invoke(token);
msg.AddPair("no", itemsAffected);
}
}
How to integrate with Resfinity Ops
define Middleware section in Appmetrics in appsettings.json like the below:
"Middleware": {
"TenantFieldName": "profile", // this field will be displayed as contract
"RequestId": {
"QueryFieldName": "rid",
"HeaderFieldName": "rid",
"AddToResponse": true
},
"TransactionNameField": "controller",
"MethodIdIdFieldName": "mid",
...
define defaults in appsettings as in example:
"Defaults": {
"application_name": "campers_booking_api", // this will be displayed as facility
"src": "campers", // this value will be used to match with provider stats
"platform": "campers",
"product": "CAMPER" // needed to determine the correct GrayLog stream and will be displayed as product
}
optionally, you can add extra data that will be displayed Resfinity Ops:
IMessage msg = HttpContext.Items[nameof(IMessage)];
msg.AddPair("df", "2021-11-01T09:30"); // will be displayed in dates section
msg.AddPair("dt", "2021-12-01T09:30")); // will be displayed in dates section
msg.AddPair("pl", "LAX"); // will be displayed in destination section
msg.AddPair("dl", "NYC"); // will be displayed in destination section
msg.AddPair("no", 1); // will be displayed in dump in items_count property
Configuration fields explanation
- app metrics options
- app metrics middleware options
- app metrics httpclient options
- app metrics message defaults
Example log in GELF format
{
"version": "1.1",
"host": "wm-anx-macbook.local",
"level": 6,
"timestamp": 1562766590.96176,
"_application_name": "geows-be",
"_transaction_name": "locationproducts", // path in request url
"_user": "Mozilla/5.0", // User Agent
"_src": "geows-be",
"_rid": "5680338139043881971", // can be passed from Http request to chain request in one transaction flow or autogenerated if not present
"_mid": "4732771268302683729", // is always autogenerated to distinguish individual requests
"_con": "mpg", // tenant or client-id - something to detect the client
"_st": "OK", // return status (OK or ERR)
"_p": "geo", // product name, several applications can have the same product name
"_env": "development", // environment
"_platform": "shared-svc", // platform name, several products can be bound within one platform
"_host_ip": "192.168.195.81", // IP of host machine
"_rq_mth": "GET", // HTTP request method
"_rq_url": "http://localhost:5000/locationproducts?lang=de&prof=mpg&gp=IT.LI.PSA&product=gegs", // full request url
"_rs_code": 200, // HTTP response code
"_client_ip": "::1", // client IP, it also checks for X-Forwared-For ip collection
"_http__locations_geocodes__count_tt": 97.821, // outbound httpclient requests
"_http__locations_geocodes__search_tt": 52.019,
"_http__all_products_product__search_tt": 27.514,
"tx_bytes": 123, // bytes send by http client
"tx_bytes": 123, // bytes received from http client
"rx_bytes": 3625,
"_tt": 391.937, // HTTP request time taken
"_ttc": 128.025, // time taken spent awaiting for IO operation to complete
"_ttp": 263.912, // HTTP request CPU bound time taken (tt - ttc)
"short_message": "-" // - for OK, error message for ERR status
}
Example error log in GELF format
{
"version": "1.1",
"host": "wm-anx-macbook.local",
"level": 6,
"timestamp": 1562766590.96176,
"_application_name": "geows-be",
"_transaction_name": "locationproducts",
"_user": "Mozilla/5.0",
"_src": "geows-be",
"_rid": "5680338139043881971",
"_mid": "4732771268302683729",
"_con": "mpg",
"_st": "ERR",
"_p": "geo",
"_env": "development",
"_platform": "shared-svc",
"_host_ip": "192.168.195.81",
"_rq_mth": "GET",
"_rq_url": "http",
"_rs_code": 500,
"_client_ip": "::1",
"_http__locations_geocodes__count_tt": 97.821,
"_http__locations_geocodes__search_tt": 52.019,
"_http__all_products_product__search_tt": 27.514,
"_tt": 391.937,
"_ttc": 128.025,
"_ttp": 263.912,
"short_message": "Object reference not set to an instance of an object."
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Anixe.IO.Appmetrics:
Package | Downloads |
---|---|
Anixe.IO.Appmetrics.Extensions.SqlDB
Package Description |
|
Anixe.IO.Appmetrics.Extensions.Mongodb
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
4.0.0 | 2,751 | 1/8/2024 |
3.0.1 | 615 | 12/4/2023 |
3.0.0 | 3,689 | 12/20/2022 |
2.1.1 | 4,053 | 1/21/2022 |
2.1.0 | 2,824 | 8/31/2021 |
2.0.20 | 786 | 7/8/2021 |
2.0.19 | 2,874 | 3/15/2021 |
2.0.18 | 842 | 1/27/2021 |
2.0.17 | 581 | 11/17/2020 |
2.0.16 | 1,173 | 10/21/2020 |
2.0.15 | 1,838 | 7/9/2020 |
2.0.13 | 1,030 | 4/27/2020 |
2.0.12 | 617 | 2/26/2020 |
2.0.11 | 1,315 | 12/11/2019 |
2.0.10 | 1,337 | 9/5/2019 |
2.0.9 | 566 | 9/2/2019 |
2.0.8 | 2,409 | 9/2/2019 |
2.0.7 | 551 | 8/28/2019 |
2.0.6 | 356 | 8/27/2019 |
2.0.5 | 342 | 8/27/2019 |
2.0.4 | 519 | 8/27/2019 |
2.0.3 | 347 | 8/23/2019 |
2.0.2 | 554 | 7/25/2019 |
2.0.1 | 442 | 7/23/2019 |
2.0.0 | 983 | 7/22/2019 |
1.0.1 | 710 | 7/15/2019 |
1.0.0 | 805 | 7/14/2019 |
# Anixe.IO.Appmetrics CHANGELOG
## 4.0.0 - 2024-01-03
- Updated Anixe.IO from v3.1.0 to v4.0.0
- Updated NLog from v5.1.0 to v5.2.8
## 3.0.1 - 2023-12-05
- Fix poor precision of big values in custom fields of type long when sending using Gelf
- Fix typo of parameter name in IMessage.AddIO method
- Fix NullReferenceException when resolving transaction name using only HttpClient.BaseAddress without url in HttpRequestMessage
- Update Anixe.IO to 3.1.0
## 3.0.0 - 2022-12-15
- Drop support of .NET older than .NET 6
- Update NLog to 5.1.0
- Changed nullability of AppmetricsOptions.BeforeControllerAction
## 2.1.1 - 2022-01-21
- fix MethodId value was not used from httpContext (if exists) and was always generated a new one
- Update NLog to 4.7.13
## 2.1.0 - 2021-08-31
- Update NLog to 4.7.11
- Update Anixe.IO to 2.1.0
## 2.0.20 - 2021-07-08
### Added
- License information in package metadata