├── global.json ├── tools └── packages.config ├── ISSUE_TEMPLATE.md ├── NuGet.config ├── version.props ├── sandbox ├── MetricsInfluxDBSandbox │ ├── appsettings.json │ ├── MetricsInfluxDBSandbox.csproj.DotSettings │ ├── MetricsInfluxDBSandbox.csproj │ ├── ApplicationsMetricsRegistry.cs │ └── Host.cs └── MetricsInfluxDBSandboxMvc │ ├── appsettings.Development.json │ ├── JustForTesting │ ├── TestController.cs │ ├── FileController.cs │ ├── RandomExceptionController.cs │ ├── RandomStatusCodeController.cs │ ├── ToleratingController.cs │ ├── FrustratingController.cs │ ├── SlaTestController.cs │ └── SatisfyingController.cs │ ├── SandboxMetricsRegistry.cs │ ├── Properties │ └── launchSettings.json │ ├── Controllers │ ├── RandomValuesForTesting.cs │ ├── RandomClientIdForTesting.cs │ ├── RandomBufferGenerator.cs │ ├── ServiceCollectionExtensions.cs │ ├── SimpleConsoleMetricsReporter.cs │ ├── RequestDurationForApdexTesting.cs │ └── AppBuilderExtensions.cs │ ├── Startup.cs │ ├── appsettings.json │ ├── MetricsInfluxDBSandboxMvc.csproj │ ├── Registry.cs │ └── Host.cs ├── src ├── Directory.Build.props ├── App.Metrics.Formatters.InfluxDB │ ├── App.Metrics.Formatters.InfluxDB.csproj.DotSettings │ ├── App.Metrics.Formatters.InfluxDB.csproj │ ├── Internal │ │ ├── InfluxDBFormatterConstants.cs │ │ ├── ILineProtocolPoint.cs │ │ ├── LineProtocolPoints.cs │ │ ├── LineProtocolPointSingleValue.cs │ │ ├── LineProtocolPointMultipleValues.cs │ │ ├── LineProtocolPointBase.cs │ │ ├── LineProtocolPointLegacy.cs │ │ └── LineProtocolSyntax.cs │ ├── MetricsInfluxDBLineProtocolOptions.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── MetricsInfluxDBLineProtocolOutputFormatter.cs │ ├── Builder │ │ └── MetricsInfluxDbLineProtocolFormatterBuilder.cs │ └── MetricSnapshotInfluxDBLineProtocolWriter.cs └── App.Metrics.Reporting.InfluxDB │ ├── App.Metrics.Reporting.InfluxDB.csproj.DotSettings │ ├── Client │ ├── Constants.cs │ ├── ILineProtocolClient.cs │ ├── HttpPolicy.cs │ ├── LineProtocolWriteResult.cs │ └── DefaultLineProtocolClient.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── App.Metrics.Reporting.InfluxDB.csproj │ ├── MetricsReportingInfluxDBOptions.cs │ ├── InfluxDbMetricsReporter.cs │ ├── RetentionPolicyOptions.cs │ ├── InfluxDBOptions.cs │ └── Builder │ └── MetricsInfluxDbReporterBuilder.cs ├── benchmarks ├── App.Metrics.InfluxDB.Benchmarks │ ├── Support │ │ ├── BenchmarksAssemblyMarker.cs │ │ └── NullTextWriter.cs │ ├── Config │ │ └── DefaultConfig.cs │ ├── App.Metrics.InfluxDB.Benchmarks.csproj │ └── BenchmarkDotNetBenchmarks │ │ └── Formatters │ │ └── MetricSnapshotInfluxDbLineProtocolWriterBenchmark.cs └── App.Metrics.InfluxDB.Benchmarks.Runner │ ├── Program.cs │ ├── App.Metrics.InfluxDB.Benchmarks.Runner.csproj │ └── BenchmarkDotNet.Artifacts │ └── results │ └── App.Metrics.InfluxDB.Benchmarks.BenchmarkDotNetBenchmarks.Formatters.MetricSnapshotInfluxDbLineProtocolWriterBenchmark-report-github.md ├── GitReleaseManager.yaml ├── test ├── App.Metrics.Reporting.InfluxDB.Facts │ ├── App.Metrics.Reporting.InfluxDB.Facts.csproj │ ├── TestClock.cs │ ├── InfluxDBSettingsTests.cs │ ├── LineProtocolSyntaxTests.cs │ ├── RetentionPolicyOptionsTests.cs │ ├── LineProtocolPointTests.cs │ └── DefaultLineProtocolClientTests.cs └── Directory.Build.props ├── stylecop.json ├── app-metrics.licenseheader ├── PULL_REQUEST_TEMPLATE.md ├── .travis.yml ├── appveyor.yml ├── .gitignore ├── .gitattributes ├── AppMetrics.ruleset ├── README.md ├── InfluxDB.sln └── LICENSE /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ "src", "test", "sandbox" ], 3 | "sdk": { 4 | "version": "2.1.301" 5 | } 6 | } -------------------------------------------------------------------------------- /tools/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Before logging a new issue, please have a quick read through the [contribution guidlines](https://github.com/alhardy/AppMetrics/blob/master/CONTRIBUTING.md). 2 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3.0.0 5 | 6 | 7 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandbox/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "MetricsOptions": { 3 | "DefaultContextLabel": "MetricsInfluxDBSandbox", 4 | "GlobalTags": { "env": "stage" }, 5 | "Enabled": true, 6 | "ReportingEnabled": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /benchmarks/App.Metrics.InfluxDB.Benchmarks/Support/BenchmarksAssemblyMarker.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | namespace App.Metrics.InfluxDB.Benchmarks.Support 6 | { 7 | public class BenchmarksAssemblyMarker 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandbox/MetricsInfluxDBSandbox.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | CSharp71 -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/App.Metrics.Formatters.InfluxDB.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | CSharp71 -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/App.Metrics.Reporting.InfluxDB.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | CSharp71 -------------------------------------------------------------------------------- /GitReleaseManager.yaml: -------------------------------------------------------------------------------- 1 | create: 2 | include-footer: false 3 | footer-heading: 4 | footer-content: 5 | footer-includes-milestone: false 6 | milestone-replace-text: 7 | export: 8 | include-created-date-in-title: false 9 | created-date-string-format: 10 | perform-regex-removal: false 11 | regex-text: 12 | multiline-regex: false 13 | issue-labels-include: 14 | - bug 15 | - new feature 16 | - enhancement 17 | - breaking change 18 | - documentation 19 | - refactoring 20 | issue-labels-exclude: 21 | - wontfix 22 | - duplicate -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/JustForTesting/TestController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace MetricsInfluxDBSandboxMvc.JustForTesting 8 | { 9 | [Route("api/[controller]")] 10 | public class TestController : Controller 11 | { 12 | [HttpGet] 13 | public IActionResult Get() 14 | { 15 | return Ok(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /test/App.Metrics.Reporting.InfluxDB.Facts/App.Metrics.Reporting.InfluxDB.Facts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTest) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/Client/Constants.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | 7 | namespace App.Metrics.Reporting.InfluxDB.Client 8 | { 9 | internal static class Constants 10 | { 11 | public static readonly TimeSpan DefaultBackoffPeriod = TimeSpan.FromSeconds(30); 12 | public static readonly int DefaultFailuresBeforeBackoff = 3; 13 | public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30); 14 | } 15 | } -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/Client/ILineProtocolClient.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace App.Metrics.Reporting.InfluxDB.Client 11 | { 12 | public interface ILineProtocolClient 13 | { 14 | Task WriteAsync( 15 | Stream payload, 16 | CancellationToken cancellationToken = default(CancellationToken)); 17 | } 18 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/App.Metrics.Formatters.InfluxDB.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | App Metrics Formatting, formatting metrics data to InfluxDB formats e.g. Line Protocol 5 | netstandard1.6 6 | $(TargetFrameworks);net452 7 | appmetrics;influxdb 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System.Runtime.CompilerServices; 6 | 7 | [assembly: InternalsVisibleTo("App.Metrics.Reporting.InfluxDB.Facts, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")] -------------------------------------------------------------------------------- /stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // ACTION REQUIRED: This file was automatically added to your project, but it 3 | // will not take effect until additional steps are taken to enable it. See the 4 | // following page for additional information: 5 | // 6 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 7 | "$schema": 8 | "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 9 | "settings": { 10 | "documentationRules": { 11 | "companyName": "App Metrics Contributors", 12 | "xmlHeader": true 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /app-metrics.licenseheader: -------------------------------------------------------------------------------- 1 | extensions: designer.cs generated.cs 2 | extensions: .cs .cpp .h 3 | // Copyright (c) Allan hardy. All rights reserved. 4 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 5 | 6 | extensions: .cshtml 7 | @* 8 | // Copyright (c) Allan hardy. All rights reserved. 9 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 10 | *@ 11 | 12 | extensions: .xml .config .xsd 13 | -------------------------------------------------------------------------------- /benchmarks/App.Metrics.InfluxDB.Benchmarks/Config/DefaultConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using BenchmarkDotNet.Configs; 6 | using BenchmarkDotNet.Diagnosers; 7 | using BenchmarkDotNet.Exporters; 8 | using BenchmarkDotNet.Jobs; 9 | 10 | namespace App.Metrics.InfluxDB.Benchmarks.Config 11 | { 12 | public class DefaultConfig : ManualConfig 13 | { 14 | public DefaultConfig() 15 | { 16 | Add(Job.Core); 17 | Add(MarkdownExporter.GitHub); 18 | Add(new MemoryDiagnoser()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/Client/HttpPolicy.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | 7 | namespace App.Metrics.Reporting.InfluxDB.Client 8 | { 9 | public class HttpPolicy 10 | { 11 | public HttpPolicy() 12 | { 13 | FailuresBeforeBackoff = Constants.DefaultFailuresBeforeBackoff; 14 | BackoffPeriod = Constants.DefaultBackoffPeriod; 15 | Timeout = Constants.DefaultTimeout; 16 | } 17 | 18 | public TimeSpan BackoffPeriod { get; set; } 19 | 20 | public int FailuresBeforeBackoff { get; set; } 21 | 22 | public TimeSpan Timeout { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /benchmarks/App.Metrics.InfluxDB.Benchmarks/Support/NullTextWriter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | using System.Text; 8 | 9 | namespace App.Metrics.InfluxDB.Benchmarks.Support 10 | { 11 | /// 12 | /// This textwriter discards all values. 13 | /// 14 | public class NullTextWriter : TextWriter 15 | { 16 | public NullTextWriter() 17 | { 18 | } 19 | 20 | public NullTextWriter(IFormatProvider formatProvider) 21 | : base(formatProvider) 22 | { 23 | } 24 | 25 | public override Encoding Encoding => Encoding.UTF8; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/JustForTesting/FileController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Mvc; 8 | 9 | namespace MetricsInfluxDBSandboxMvc.JustForTesting 10 | { 11 | [Route("api/[controller]")] 12 | public class FileController : Controller 13 | { 14 | [HttpPost] 15 | public IActionResult Post(IFormFile file) 16 | { 17 | return Created(new Uri("http://localhost"), 0); 18 | } 19 | 20 | [HttpPut] 21 | public IActionResult Put(IFormFile file) 22 | { 23 | return Ok(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/Client/LineProtocolWriteResult.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | namespace App.Metrics.Reporting.InfluxDB.Client 6 | { 7 | public struct LineProtocolWriteResult 8 | { 9 | public LineProtocolWriteResult(bool success) 10 | { 11 | Success = success; 12 | ErrorMessage = null; 13 | } 14 | 15 | public LineProtocolWriteResult(bool success, string errorMessage) 16 | { 17 | Success = success; 18 | ErrorMessage = errorMessage; 19 | } 20 | 21 | public string ErrorMessage { get; } 22 | 23 | public bool Success { get; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Internal/InfluxDBFormatterConstants.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | 7 | namespace App.Metrics.Formatters.InfluxDB.Internal 8 | { 9 | public static class InfluxDbFormatterConstants 10 | { 11 | public class LineProtocol 12 | { 13 | public static readonly Func MetricNameFormatter = 14 | (metricContext, metricName) => string.IsNullOrWhiteSpace(metricContext) 15 | ? $"{metricName}".Replace(' ', '_').ToLowerInvariant() 16 | : $"{metricContext}__{metricName}".Replace(' ', '_').ToLowerInvariant(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/SandboxMetricsRegistry.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using App.Metrics.Timer; 6 | 7 | namespace MetricsInfluxDBSandboxMvc 8 | { 9 | public static class SandboxMetricsRegistry 10 | { 11 | public const string ContextName = "Sandbox"; 12 | 13 | public static readonly TimerOptions DatabaseTimer = new TimerOptions 14 | { 15 | Context = ContextName, 16 | Name = "Database Call" 17 | }; 18 | } 19 | } -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:2222/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "launchUrl": "metrics", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "MetricsInfluxDBSandboxMvc": { 20 | "commandName": "Project", 21 | "launchBrowser": false, 22 | "launchUrl": "metrics", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | }, 26 | "applicationUrl": "http://localhost:1111" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/MetricsInfluxDBLineProtocolOptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.Formatters.InfluxDB.Internal; 7 | 8 | namespace App.Metrics.Formatters.InfluxDB 9 | { 10 | /// 11 | /// Provides programmatic configuration for InfluxDB's LineProtocole format in the App Metrics framework. 12 | /// 13 | public class MetricsInfluxDbLineProtocolOptions 14 | { 15 | public MetricsInfluxDbLineProtocolOptions() 16 | { 17 | MetricNameFormatter = InfluxDbFormatterConstants.LineProtocol.MetricNameFormatter; 18 | } 19 | 20 | public Func MetricNameFormatter { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for helping out :+1: 2 | 3 | Before submitting a pull request, please have a quick read through the [contribution guidlines](https://github.com/alhardy/AppMetrics/blob/master/CONTRIBUTING.md) and provide the following information, where appropriate replace the `[ ]` with a `[X]` 4 | 5 | ### The issue or feature being addressed 6 | 7 | - link to the issue/feature using it's github issue number #number (if an issue/feature does not exist, please create it first) 8 | 9 | ### Details on the issue fix or feature implementation 10 | 11 | - provide some details here 12 | 13 | 14 | ### Confirm the following 15 | 16 | - [ ] I have ensured that I have merged the latest changes from the dev branch 17 | - [ ] I have successfully run a [local build](https://github.com/alhardy/AppMetrics#how-to-build) 18 | - [ ] I have included unit tests for the issue/feature 19 | - [ ] I have included the github issue number in my commits -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/JustForTesting/RandomExceptionController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using MetricsInfluxDBSandboxMvc.Controllers; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace MetricsInfluxDBSandboxMvc.JustForTesting 9 | { 10 | [Route("api/[controller]")] 11 | public class RandomExceptionController : Controller 12 | { 13 | private readonly RandomValuesForTesting _randomValuesForTesting; 14 | 15 | public RandomExceptionController(RandomValuesForTesting randomValuesForTesting) 16 | { 17 | _randomValuesForTesting = randomValuesForTesting; 18 | } 19 | 20 | [HttpGet] 21 | public void Get() 22 | { 23 | throw _randomValuesForTesting.NextException(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/App.Metrics.Reporting.InfluxDB.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Provides InfluxDB reporting capability to App Metrics 5 | netstandard1.6 6 | $(TargetFrameworks);net452 7 | appmetrics;reporting;influxdb 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/JustForTesting/RandomStatusCodeController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using MetricsInfluxDBSandboxMvc.Controllers; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace MetricsInfluxDBSandboxMvc.JustForTesting 9 | { 10 | [Route("api/[controller]")] 11 | public class RandomStatusCodeController : Controller 12 | { 13 | private readonly RandomValuesForTesting _randomValuesForTesting; 14 | 15 | public RandomStatusCodeController(RandomValuesForTesting randomValuesForTesting) 16 | { 17 | _randomValuesForTesting = randomValuesForTesting; 18 | } 19 | 20 | [HttpGet] 21 | public IActionResult Get() 22 | { 23 | return StatusCode(_randomValuesForTesting.NextStatusCode()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /benchmarks/App.Metrics.InfluxDB.Benchmarks.Runner/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Reflection; 7 | using App.Metrics.InfluxDB.Benchmarks.Support; 8 | using BenchmarkDotNet.Running; 9 | 10 | namespace App.Metrics.InfluxDB.Benchmarks.Runner 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | ConsoleKeyInfo keyInfo; 17 | 18 | do 19 | { 20 | BenchmarkSwitcher.FromAssembly(typeof(BenchmarksAssemblyMarker).GetTypeInfo().Assembly).Run(args); 21 | 22 | Console.WriteLine("Press ESC to quit, otherwise any key to continue..."); 23 | 24 | keyInfo = Console.ReadKey(true); 25 | } 26 | while (keyInfo.Key != ConsoleKey.Escape); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Controllers/RandomValuesForTesting.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | 8 | namespace MetricsInfluxDBSandboxMvc.Controllers 9 | { 10 | public class RandomValuesForTesting 11 | { 12 | private static readonly List Exceptions = 13 | new List { new ArgumentException(), new ArithmeticException(), new InvalidCastException(), new NullReferenceException() }; 14 | 15 | private static readonly Random Rnd = new Random(); 16 | private static readonly List StatusCodes = new List { 200, 401, 401, 404, 403, 500, 500, 500 }; 17 | 18 | public Func NextException => () => Exceptions[Rnd.Next(0, Exceptions.Count - 1)]; 19 | 20 | public Func NextStatusCode => () => StatusCodes[Rnd.Next(0, StatusCodes.Count)]; 21 | } 22 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | email: 3 | recipients: 4 | - al_hardy@live.com.au 5 | 6 | language: csharp 7 | dotnet: 2.0.0-preview1-005977 8 | os: 9 | # - osx 10 | - linux 11 | before_script: 12 | - chmod a+x ./build.sh 13 | script: 14 | - ./build.sh 15 | 16 | # .NET CLI require Ubuntu 14.04 17 | sudo: required 18 | dist: trusty 19 | env: 20 | global: 21 | - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 22 | - DOTNET_CLI_TELEMETRY_OPTOUT: 1 23 | addons: 24 | apt: 25 | packages: 26 | - gettext 27 | - libcurl4-openssl-dev 28 | - libicu-dev 29 | - libssl-dev 30 | - libunwind8 31 | 32 | # .NET CLI require OSX 10.10 33 | # osx_image: xcode7.1 34 | # before_install: 35 | # - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi 36 | 37 | mono: 38 | - 4.6.0 39 | 40 | cache: 41 | directories: 42 | - src/packages 43 | - tools -------------------------------------------------------------------------------- /test/App.Metrics.Reporting.InfluxDB.Facts/TestClock.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Globalization; 7 | 8 | namespace App.Metrics.Reporting.InfluxDB.Facts 9 | { 10 | public sealed class TestClock : IClock 11 | { 12 | public event EventHandler Advanced; 13 | 14 | public long Nanoseconds { get; private set; } 15 | 16 | public long Seconds => TimeUnit.Nanoseconds.ToSeconds(Nanoseconds); 17 | 18 | public DateTime UtcDateTime => new DateTime(Nanoseconds / 100L, DateTimeKind.Utc); 19 | 20 | public void Advance(TimeUnit unit, long value) 21 | { 22 | Nanoseconds += unit.ToNanoseconds(value); 23 | Advanced?.Invoke(this, EventArgs.Empty); 24 | } 25 | 26 | public string FormatTimestamp(DateTime timestamp) { return timestamp.ToString("yyyy-MM-ddTHH:mm:ss.ffffK", CultureInfo.InvariantCulture); } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | netcoreapp2.1 6 | $(DeveloperBuildTest) 7 | $(StandardTest);netcoreapp2.1 8 | $(StandardTest);net461 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Controllers/RandomClientIdForTesting.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Security.Claims; 8 | using Microsoft.AspNetCore.Http; 9 | 10 | namespace MetricsInfluxDBSandboxMvc.Controllers 11 | { 12 | public static class RandomClientIdForTesting 13 | { 14 | private static readonly Random Rnd = new Random(); 15 | 16 | public static void SetTheFakeClaimsPrincipal(HttpContext context) 17 | { 18 | context.User = 19 | new ClaimsPrincipal( 20 | new List 21 | { 22 | new ClaimsIdentity( 23 | new[] 24 | { 25 | new Claim("client_id", $"client-{Rnd.Next(1, 10)}") 26 | }) 27 | }); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/JustForTesting/ToleratingController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System.Threading.Tasks; 6 | using MetricsInfluxDBSandboxMvc.Controllers; 7 | using Microsoft.AspNetCore.Mvc; 8 | 9 | namespace MetricsInfluxDBSandboxMvc.JustForTesting 10 | { 11 | [Route("api/[controller]")] 12 | public class ToleratingController : Controller 13 | { 14 | private readonly RequestDurationForApdexTesting _durationForApdexTesting; 15 | 16 | public ToleratingController(RequestDurationForApdexTesting durationForApdexTesting) 17 | { 18 | _durationForApdexTesting = durationForApdexTesting; 19 | } 20 | 21 | [HttpGet] 22 | public async Task Get() 23 | { 24 | var duration = _durationForApdexTesting.NextToleratingDuration; 25 | 26 | await Task.Delay(duration, HttpContext.RequestAborted); 27 | 28 | return duration; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/JustForTesting/FrustratingController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System.Threading.Tasks; 6 | using MetricsInfluxDBSandboxMvc.Controllers; 7 | using Microsoft.AspNetCore.Mvc; 8 | 9 | namespace MetricsInfluxDBSandboxMvc.JustForTesting 10 | { 11 | [Route("api/[controller]")] 12 | public class FrustratingController : Controller 13 | { 14 | private readonly RequestDurationForApdexTesting _durationForApdexTesting; 15 | 16 | public FrustratingController(RequestDurationForApdexTesting durationForApdexTesting) 17 | { 18 | _durationForApdexTesting = durationForApdexTesting; 19 | } 20 | 21 | [HttpGet] 22 | public async Task Get() 23 | { 24 | var duration = _durationForApdexTesting.NextFrustratingDuration; 25 | 26 | await Task.Delay(duration, HttpContext.RequestAborted); 27 | 28 | return duration; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /test/App.Metrics.Reporting.InfluxDB.Facts/InfluxDBSettingsTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using FluentAssertions; 7 | using Xunit; 8 | 9 | namespace App.Metrics.Reporting.InfluxDB.Facts 10 | { 11 | // ReSharper disable InconsistentNaming 12 | public class InfluxDbSettingsTests 13 | // ReSharper restore InconsistentNaming 14 | { 15 | [Fact] 16 | public void Can_generate_influx_write_endpoint() 17 | { 18 | var settings = new InfluxDbOptions 19 | { 20 | Database = "testdb", 21 | BaseUri = new Uri("http://localhost"), 22 | RetentionPolicy = "defaultrp", 23 | Consistenency = "consistency" 24 | }; 25 | 26 | settings.Endpoint.Should().Be("write?db=testdb&rp=defaultrp&consistency=consistency"); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Internal/ILineProtocolPoint.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System.IO; 6 | 7 | namespace App.Metrics.Formatters.InfluxDB.Internal 8 | { 9 | /// 10 | /// Defines a point (a ligne), which can be written in Line Protocol format. 11 | /// 12 | internal interface ILineProtocolPoint 13 | { 14 | /// 15 | /// Write this point as a line protocol item. 16 | /// 17 | /// Text writer to write the line to. 18 | /// 19 | /// true to let the point write the timestamp by itself, false to not write the timestamp at the end of the row. 20 | /// You will have to write the timestamp by yourself of let the server receive the line and use its own timer as a timestamp. 21 | /// 22 | void Write(TextWriter textWriter, bool writeTimestamp = true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Startup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | 11 | namespace MetricsInfluxDBSandboxMvc 12 | { 13 | public class Startup 14 | { 15 | private const bool HaveAppRunSampleRequests = true; 16 | 17 | public Startup(IConfiguration configuration) { Configuration = configuration; } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | public void Configure(IApplicationBuilder app, IApplicationLifetime lifetime) 22 | { 23 | app.UseTestStuff(lifetime, HaveAppRunSampleRequests); 24 | 25 | app.UseMvc(); 26 | } 27 | 28 | public void ConfigureServices(IServiceCollection services) 29 | { 30 | services.AddTestStuff(); 31 | 32 | services.AddMvc().AddMetrics(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /benchmarks/App.Metrics.InfluxDB.Benchmarks/App.Metrics.InfluxDB.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | netcoreapp2.1 7 | $(TargetFrameworks);net461 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/JustForTesting/SlaTestController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using App.Metrics; 9 | using Microsoft.AspNetCore.Mvc; 10 | 11 | namespace MetricsInfluxDBSandboxMvc.JustForTesting 12 | { 13 | [Route("api/[controller]")] 14 | public class SlaTestController : Controller 15 | { 16 | private static readonly Random Rnd = new Random(); 17 | private readonly IMetrics _metrics; 18 | 19 | public SlaTestController(IMetrics metrics) { _metrics = metrics; } 20 | 21 | [HttpGet] 22 | [Route("timer")] 23 | public async Task GetTimer() 24 | { 25 | using (_metrics.Measure.Timer.Time(SandboxMetricsRegistry.DatabaseTimer, new MetricTags("client_id", HttpContext.User.Claims.First(c => c.Type == "client_id").Value))) 26 | { 27 | await Task.Delay(Rnd.Next(350), HttpContext.RequestAborted); 28 | } 29 | 30 | return Ok(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /benchmarks/App.Metrics.InfluxDB.Benchmarks.Runner/App.Metrics.InfluxDB.Benchmarks.Runner.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | netcoreapp2.1 7 | $(TargetFrameworks);net461 8 | exe 9 | true 10 | true 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "MetricsOptions": { 3 | "DefaultContextLabel": "MetricsInfluxDBSandboxMvc", 4 | "Enabled": true 5 | }, 6 | "MetricsWebTrackingOptions": { 7 | "ApdexTrackingEnabled": true, 8 | "ApdexTSeconds": 0.1, 9 | "IgnoredHttpStatusCodes": [ 404 ], 10 | "IgnoredRoutesRegexPatterns": [], 11 | "OAuth2TrackingEnabled": true 12 | }, 13 | "MetricEndpointsOptions": { 14 | "MetricsEndpointEnabled": true, 15 | "MetricsTextEndpointEnabled": true, 16 | "PingEndpointEnabled": true, 17 | "EnvironmentInfoEndpointEnabled": true 18 | }, 19 | "MetricsReportingInfluxDbOptions": { 20 | "InfluxDb": { 21 | "BaseUri": "http://127.0.0.1:32768", 22 | "Database": "appmetricssandbox" 23 | //"Consistenency": "", 24 | //"UserName": "", 25 | //"Password": "", 26 | //"RetentionPolicy": "" 27 | }, 28 | "HttpPolicy": { 29 | "BackoffPeriod": "0:0:30", 30 | "FailuresBeforeBackoff": 5, 31 | "Timeout": "0:0:40" 32 | }, 33 | "ReportInterval": "0:0:1" 34 | }, 35 | "Logging": { 36 | "IncludeScopes": false, 37 | "LogLevel": { 38 | "Default": "Warning" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandbox/MetricsInfluxDBSandbox.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | netcoreapp2.1;net461 7 | exe 8 | latest 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Always 29 | PreserveNewest 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Internal/LineProtocolPoints.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | 9 | namespace App.Metrics.Formatters.InfluxDB.Internal 10 | { 11 | internal class LineProtocolPoints 12 | { 13 | private readonly List _points = new List(); 14 | 15 | public void Add(ILineProtocolPoint point) 16 | { 17 | if (point == null) 18 | { 19 | return; 20 | } 21 | 22 | _points.Add(point); 23 | } 24 | 25 | public void Write(TextWriter textWriter, bool writeTimestamp = true) 26 | { 27 | if (textWriter == null) 28 | { 29 | return; 30 | } 31 | 32 | var points = _points.ToList(); 33 | 34 | foreach (var point in points) 35 | { 36 | point.Write(textWriter, writeTimestamp); 37 | textWriter.Write('\n'); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Controllers/RandomBufferGenerator.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | 7 | namespace MetricsInfluxDBSandboxMvc.Controllers 8 | { 9 | public class RandomBufferGenerator 10 | { 11 | private readonly int _maxBufferSize; 12 | private readonly Random _random = new Random(); 13 | private readonly byte[] _seedBuffer; 14 | 15 | public RandomBufferGenerator(int maxBufferSize) 16 | { 17 | _maxBufferSize = maxBufferSize; 18 | _seedBuffer = new byte[maxBufferSize]; 19 | 20 | _random.NextBytes(_seedBuffer); 21 | } 22 | 23 | public byte[] GenerateBufferFromSeed() 24 | { 25 | var size = _random.Next(_maxBufferSize); 26 | var randomWindow = _random.Next(0, size); 27 | var buffer = new byte[size]; 28 | 29 | Buffer.BlockCopy(_seedBuffer, randomWindow, buffer, 0, size - randomWindow); 30 | Buffer.BlockCopy(_seedBuffer, 0, buffer, size - randomWindow, randomWindow); 31 | 32 | return buffer; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2017 3 | init: 4 | - git config --global core.autocrlf input 5 | environment: 6 | COVERALLS_REPO_TOKEN: 7 | secure: OCt+hLolAEzMwIL35182sjpOu9GcwPRUkPHBsCdGSMnoIiWByMrcoDsVIqciM0C3 8 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 9 | DOTNET_CLI_TELEMETRY_OPTOUT: true 10 | CoverWith: OpenCover 11 | PreReleaseSuffix: alpha 12 | GitUser: 13 | secure: zAdAOUUof3XGDsdOBRYg7J7wZS44iL4VjI/MVGw+JnU= 14 | GitOwner: 15 | secure: n6W5JZ7Q/xfZC7b2k3+ORA== 16 | GitPassword: 17 | secure: fHangGPAk14u3V1eP3kg5EdekQoKdOsKC0F/A1mvh9IODHCZ92FGjfWb0U2aQ8bu 18 | build_script: 19 | - ps: .\build.ps1 -Target AppVeyor 20 | test: off 21 | artifacts: 22 | - path: artifacts/packages/*.nupkg 23 | - path: artifacts/coverage/*.xml 24 | - path: artifacts/test-results/*.trx 25 | - path: artifacts/resharper-reports/*.xml 26 | - path: artifacts/resharper-reports/*.html 27 | - path: artifacts/coverage/coverage.dcvr 28 | deploy: 29 | - provider: NuGet 30 | server: https://www.myget.org/F/appmetrics/api/v2/package 31 | api_key: 32 | secure: gIAiACgNj+JzXyLLTe3rLxZyrAB9RpC8Lw81xEjdOLXqotprqEwGiFWRipEqkpps 33 | skip_symbols: true 34 | symbol_server: https://www.myget.org/F/appmetrics/symbol 35 | skip_commits: 36 | files: 37 | - '**/*.md' -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Controllers/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.AspNetCore.Tracking; 7 | using MetricsInfluxDBSandboxMvc.Controllers; 8 | using Microsoft.Extensions.Options; 9 | 10 | // ReSharper disable CheckNamespace 11 | namespace Microsoft.Extensions.DependencyInjection 12 | // ReSharper restore CheckNamespace 13 | { 14 | public static class ServiceCollectionExtensions 15 | { 16 | public static IServiceCollection AddTestStuff(this IServiceCollection services) 17 | { 18 | services.AddTransient>( 19 | serviceProvider => { return apdexTSeconds => new RequestDurationForApdexTesting(apdexTSeconds); }); 20 | 21 | services.AddSingleton(); 22 | 23 | services.AddTransient( 24 | serviceProvider => 25 | { 26 | var optionsAccessor = serviceProvider.GetRequiredService>(); 27 | return new RequestDurationForApdexTesting(optionsAccessor.Value.ApdexTSeconds); 28 | }); 29 | 30 | return services; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System.Runtime.CompilerServices; 6 | 7 | [assembly: InternalsVisibleTo("App.Metrics.InfluxDB.Benchmarks, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")] 8 | [assembly: InternalsVisibleTo("App.Metrics.Reporting.InfluxDB, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")] 9 | [assembly: InternalsVisibleTo("App.Metrics.Reporting.InfluxDB.Facts, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")] -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/MetricsInfluxDBSandboxMvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | exe 7 | netcoreapp2.1;net461 8 | latest 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Always 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Registry.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using App.Metrics; 6 | using App.Metrics.Histogram; 7 | 8 | namespace MetricsInfluxDBSandboxMvc 9 | { 10 | public static class Registry 11 | { 12 | #pragma warning disable SA1401 // Fields must be private 13 | 14 | public static HistogramOptions One = new HistogramOptions 15 | { 16 | Name = "test1", 17 | MeasurementUnit = Unit.Bytes, 18 | Context = "test" 19 | }; 20 | 21 | public static HistogramOptions Three = new HistogramOptions 22 | { 23 | Name = "test3", 24 | MeasurementUnit = Unit.Bytes, 25 | Context = "test" 26 | }; 27 | 28 | public static HistogramOptions Two = new HistogramOptions 29 | { 30 | Name = "test2", 31 | MeasurementUnit = Unit.Bytes, 32 | Context = "test" 33 | }; 34 | #pragma warning restore SA1401 // Fields must be private 35 | } 36 | } -------------------------------------------------------------------------------- /benchmarks/App.Metrics.InfluxDB.Benchmarks.Runner/BenchmarkDotNet.Artifacts/results/App.Metrics.InfluxDB.Benchmarks.BenchmarkDotNetBenchmarks.Formatters.MetricSnapshotInfluxDbLineProtocolWriterBenchmark-report-github.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17763.292 (1809/October2018Update/Redstone5) 4 | Intel Core i7-6700 CPU 3.40GHz (Skylake), 1 CPU, 8 logical and 4 physical cores 5 | .NET Core SDK=2.1.301 6 | [Host] : .NET Core 2.1.7 (CoreCLR 4.6.27129.04, CoreFX 4.6.27129.04), 64bit RyuJIT 7 | Core : .NET Core 2.1.7 (CoreCLR 4.6.27129.04, CoreFX 4.6.27129.04), 64bit RyuJIT 8 | 9 | Job=Core Runtime=Core 10 | 11 | ``` 12 | | Method | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | 13 | |-------------------------------- |---------:|----------:|----------:|---------:|------:|--------:|------------:|------------:|------------:|--------------------:| 14 | | WriteSingleValuePoint_Legacy | 558.0 ns | 11.067 ns | 25.206 ns | 549.2 ns | 1.00 | 0.00 | 0.1478 | - | - | 624 B | 15 | | WriteSingleValuePoint | 288.3 ns | 5.704 ns | 8.881 ns | 286.4 ns | 0.51 | 0.03 | 0.0725 | - | - | 304 B | 16 | | | | | | | | | | | | | 17 | | WriteMultipleValuesPoint_Legacy | 991.9 ns | 19.413 ns | 31.348 ns | 984.6 ns | 1.00 | 0.00 | 0.2012 | - | - | 848 B | 18 | | WriteMultipleValuesPoint | 352.8 ns | 6.631 ns | 6.202 ns | 353.1 ns | 0.36 | 0.01 | 0.0873 | - | - | 368 B | 19 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Controllers/SimpleConsoleMetricsReporter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using App.Metrics; 11 | using App.Metrics.Filters; 12 | using App.Metrics.Formatters; 13 | using App.Metrics.Formatters.Ascii; 14 | using App.Metrics.Reporting; 15 | 16 | namespace MetricsInfluxDBSandboxMvc.Controllers 17 | { 18 | public class SimpleConsoleMetricsReporter : IReportMetrics 19 | { 20 | private readonly IMetricsOutputFormatter _defaultMetricsOutputFormatter = new MetricsTextOutputFormatter(); 21 | 22 | /// 23 | public IFilterMetrics Filter { get; set; } 24 | 25 | /// 26 | public TimeSpan FlushInterval { get; set; } 27 | 28 | /// 29 | public IMetricsOutputFormatter Formatter { get; set; } 30 | 31 | /// 32 | public async Task FlushAsync(MetricsDataValueSource metricsData, CancellationToken cancellationToken = default) 33 | { 34 | Console.WriteLine("Metrics Report"); 35 | Console.WriteLine("-------------------------------------------"); 36 | 37 | using (var stream = new MemoryStream()) 38 | { 39 | await _defaultMetricsOutputFormatter.WriteAsync(stream, metricsData, cancellationToken); 40 | 41 | var output = Encoding.UTF8.GetString(stream.ToArray()); 42 | 43 | Console.WriteLine(output); 44 | } 45 | 46 | return true; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/JustForTesting/SatisfyingController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using App.Metrics; 9 | using MetricsInfluxDBSandboxMvc.Controllers; 10 | using Microsoft.AspNetCore.Mvc; 11 | 12 | namespace MetricsInfluxDBSandboxMvc.JustForTesting 13 | { 14 | [Route("api/[controller]")] 15 | public class SatisfyingController : Controller 16 | { 17 | private readonly RequestDurationForApdexTesting _durationForApdexTesting; 18 | 19 | public static Random Rnd { get; } = new Random(); 20 | 21 | private readonly IMetrics _metrics; 22 | 23 | public SatisfyingController(IMetrics metrics, RequestDurationForApdexTesting durationForApdexTesting) 24 | { 25 | _metrics = metrics ?? throw new ArgumentNullException(nameof(metrics)); 26 | _durationForApdexTesting = durationForApdexTesting; 27 | } 28 | 29 | [HttpGet] 30 | public async Task Get() 31 | { 32 | var duration = _durationForApdexTesting.NextSatisfiedDuration; 33 | 34 | foreach (var i in Enumerable.Range(1, 3)) 35 | { 36 | var tags = new MetricTags($"key{i}", $"value{i}"); 37 | 38 | _metrics.Measure.Histogram.Update(Registry.One, tags, Rnd.Next(1, 500)); 39 | _metrics.Measure.Histogram.Update(Registry.Two, tags, Rnd.Next(1, 500)); 40 | _metrics.Measure.Histogram.Update(Registry.Three, tags, Rnd.Next(1, 500)); 41 | } 42 | 43 | await Task.Delay(duration, HttpContext.RequestAborted); 44 | 45 | return duration; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #OS junk files 2 | [Tt]humbs.db 3 | *.DS_Store 4 | *.orig 5 | 6 | #Visual Studio files 7 | *.[Oo]bj 8 | *.user 9 | *.aps 10 | *.pch 11 | *.vspscc 12 | *.vssscc 13 | *_i.c 14 | *_p.c 15 | *.ncb 16 | *.suo 17 | *.tlb 18 | *.tlh 19 | *.bak 20 | *.[Cc]ache 21 | *.ilk 22 | *.log 23 | *.lib 24 | *.sbr 25 | *.sdf 26 | *.opensdf 27 | *.unsuccessfulbuild 28 | ipch/ 29 | [Oo]bj/ 30 | [Bb]in 31 | Ankh.NoLoad 32 | *.vsmdi 33 | *.lock.json 34 | 35 | #Tooling 36 | .idea 37 | _ReSharper*/ 38 | *.resharper 39 | [Tt]est[Rr]esult* 40 | *.sass-cache 41 | 42 | #Subversion files 43 | .svn 44 | 45 | # Office Temp Files 46 | ~$* 47 | 48 | #ncrunch 49 | *ncrunch* 50 | *crunch*.local.xml 51 | 52 | #Test files 53 | *.testsettings 54 | 55 | #Build 56 | BuildOutput 57 | BuildOutput/* 58 | WebPublish 59 | WebPublish/* 60 | 61 | #Cake 62 | tools/* 63 | !tools/packages.config 64 | 65 | .vs 66 | 67 | # visual studio database projects 68 | *.dbmdl 69 | logs/* 70 | .vs/* 71 | *.project.lock.json 72 | 73 | #Temp folders 74 | temp/ 75 | 76 | #Tools and packages 77 | .nuget 78 | .nuget/* 79 | artifacts 80 | artifacts/* 81 | packages 82 | packages/* 83 | *.lock.json 84 | 85 | #BenchMarkDotNet 86 | BDN.Generated 87 | BDN.Generated/* 88 | scripts/BenchMarkDotNet.Artifacts 89 | scripts/BenchMarkDotNet.Artifacts/* 90 | **/BenchMarkDotNet.Artifacts/* 91 | !/benchmarks/App.Metrics.InfluxDB.Benchmarks.Runner/BenchMarkDotNet.Artifacts/* 92 | /benchmarks/App.Metrics.InfluxDB.Benchmarks.Runner/BenchMarkDotNet.Artifacts/*.log 93 | /benchmarks/App.Metrics.InfluxDB.Benchmarks.Runner/BenchMarkDotNet.Artifacts/results/*.csv 94 | /benchmarks/App.Metrics.InfluxDB.Benchmarks.Runner/BenchMarkDotNet.Artifacts/results/*.html 95 | 96 | #NDepend 97 | *.ndproj 98 | NDependOut/* 99 | 100 | #DotCover 101 | Session/* 102 | Session.html 103 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Controllers/RequestDurationForApdexTesting.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | 7 | namespace MetricsInfluxDBSandboxMvc.Controllers 8 | { 9 | public class RequestDurationForApdexTesting 10 | { 11 | private const int MaxRequestDurationFactor = 1000; 12 | private const int MinRequestDuration = 25; 13 | private static readonly Random Rnd = new Random(); 14 | 15 | public RequestDurationForApdexTesting(double apdexTSeconds) 16 | { 17 | SatisfiedMinMilliseconds = MinRequestDuration; 18 | SatisfiedMaxMilliseconds = (int)(apdexTSeconds * 1000); 19 | 20 | ToleratingMinMilliseconds = SatisfiedMaxMilliseconds + 1; 21 | ToleratingMaxMilliseconds = 4 * SatisfiedMaxMilliseconds; 22 | 23 | FrustratingMinMilliseconds = ToleratingMaxMilliseconds + 1; 24 | FrustratingMaxMilliseconds = ToleratingMaxMilliseconds + MaxRequestDurationFactor; 25 | } 26 | 27 | public int FrustratingMaxMilliseconds { get; } 28 | 29 | public int FrustratingMinMilliseconds { get; } 30 | 31 | public int NextFrustratingDuration => Rnd.Next(FrustratingMinMilliseconds, FrustratingMaxMilliseconds); 32 | 33 | public int NextSatisfiedDuration => Rnd.Next(SatisfiedMinMilliseconds, SatisfiedMaxMilliseconds); 34 | 35 | public int NextToleratingDuration => Rnd.Next(ToleratingMinMilliseconds, ToleratingMaxMilliseconds); 36 | 37 | public int SatisfiedMaxMilliseconds { get; } 38 | 39 | public int SatisfiedMinMilliseconds { get; } 40 | 41 | public int ToleratingMaxMilliseconds { get; } 42 | 43 | public int ToleratingMinMilliseconds { get; } 44 | } 45 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Internal/LineProtocolPointSingleValue.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | 8 | namespace App.Metrics.Formatters.InfluxDB.Internal 9 | { 10 | /// 11 | /// Represents a line protocol point with a single field. 12 | /// 13 | internal class LineProtocolPointSingleValue : LineProtocolPointBase, ILineProtocolPoint 14 | { 15 | public LineProtocolPointSingleValue( 16 | string measurement, 17 | string fieldName, 18 | object fieldValue, 19 | MetricTags tags, 20 | DateTime? utcTimestamp = null) 21 | : base(measurement, tags, utcTimestamp) 22 | { 23 | if (string.IsNullOrEmpty(fieldName)) 24 | { 25 | throw new ArgumentException("Field name must be specified and be non-empty"); 26 | } 27 | 28 | FieldName = fieldName; 29 | FieldValue = fieldValue; 30 | } 31 | 32 | public string FieldName { get; } 33 | 34 | public object FieldValue { get; } 35 | 36 | public void Write(TextWriter textWriter, bool writeTimestamp = true) 37 | { 38 | if (textWriter == null) 39 | { 40 | throw new ArgumentNullException(nameof(textWriter)); 41 | } 42 | 43 | WriteCommon(textWriter); 44 | 45 | textWriter.Write(' '); 46 | textWriter.Write(LineProtocolSyntax.EscapeName(FieldName)); 47 | textWriter.Write('='); 48 | textWriter.Write(LineProtocolSyntax.FormatValue(FieldValue)); 49 | 50 | if (!writeTimestamp) 51 | { 52 | return; 53 | } 54 | 55 | WriteTimestamp(textWriter); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandbox/ApplicationsMetricsRegistry.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using App.Metrics.Apdex; 6 | using App.Metrics.Counter; 7 | using App.Metrics.Gauge; 8 | using App.Metrics.Histogram; 9 | using App.Metrics.Meter; 10 | using App.Metrics.Timer; 11 | 12 | namespace MetricsInfluxDBSandbox 13 | { 14 | public static class ApplicationsMetricsRegistry 15 | { 16 | public static ApdexOptions ApdexOne => new ApdexOptions 17 | { 18 | Name = "apdex_one" 19 | }; 20 | 21 | public static CounterOptions CounterOne => new CounterOptions 22 | { 23 | Name = "counter_one" 24 | }; 25 | 26 | public static GaugeOptions GaugeOne => new GaugeOptions 27 | { 28 | Name = "gauge_one" 29 | }; 30 | 31 | public static HistogramOptions HistogramOne => new HistogramOptions 32 | { 33 | Name = "histogram_one" 34 | }; 35 | 36 | public static MeterOptions MeterOne => new MeterOptions 37 | { 38 | Name = "meter_one" 39 | }; 40 | 41 | public static TimerOptions TimerOne => new TimerOptions 42 | { 43 | Name = "timer_one" 44 | }; 45 | } 46 | } -------------------------------------------------------------------------------- /test/App.Metrics.Reporting.InfluxDB.Facts/LineProtocolSyntaxTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.Formatters.InfluxDB.Internal; 7 | using FluentAssertions; 8 | using Xunit; 9 | 10 | namespace App.Metrics.Reporting.InfluxDB.Facts 11 | { 12 | public class LineProtocolSyntaxTests 13 | { 14 | [Theory] 15 | [InlineData("test=", "test\\=")] 16 | [InlineData("test ", "test\\ ")] 17 | [InlineData("test,", "test\\,")] 18 | [InlineData("te=st", "te\\=st")] 19 | [InlineData("te st", "te\\ st")] 20 | [InlineData("te,st", "te\\,st")] 21 | public void Can_escape_name(string nameOrKey, string expected) 22 | { 23 | LineProtocolSyntax.EscapeName(nameOrKey).Should().Be(expected); 24 | } 25 | 26 | [Fact] 27 | public void Can_format_timespan() 28 | { 29 | var value = TimeSpan.FromMinutes(1); 30 | 31 | LineProtocolSyntax.FormatValue(value).Should().Be("60000"); 32 | } 33 | 34 | [Fact] 35 | public void Can_format_timestamp() 36 | { 37 | var dateTime = new DateTime(2017, 01, 01, 1, 1, 1, DateTimeKind.Utc); 38 | LineProtocolSyntax.FormatTimestamp(dateTime).Should().Be("1483232461000000000"); 39 | } 40 | 41 | [Theory] 42 | [InlineData(1, "1i")] 43 | [InlineData((sbyte)1, "1i")] 44 | [InlineData((byte)1, "1i")] 45 | [InlineData((short)1, "1i")] 46 | [InlineData((ushort)1, "1i")] 47 | [InlineData((uint)1, "1i")] 48 | [InlineData((long)1, "1i")] 49 | [InlineData((ulong)1, "1i")] 50 | [InlineData((float)1, "1")] 51 | [InlineData((double)1, "1")] 52 | [InlineData(true, "t")] 53 | [InlineData(false, "f")] 54 | public void Can_format_value(object value, string expected) 55 | { 56 | LineProtocolSyntax.FormatValue(value).Should().Be(expected); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /test/App.Metrics.Reporting.InfluxDB.Facts/RetentionPolicyOptionsTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using Xunit; 7 | 8 | namespace App.Metrics.Reporting.InfluxDB.Facts 9 | { 10 | public class RetentionPolicyOptionsTests 11 | { 12 | [Fact] 13 | public void TryApplyDefaultRetentionPolicy() 14 | { 15 | RetentionPolicyOptions options = new RetentionPolicyOptions(); 16 | 17 | bool result = options.TryApply(out var clause); 18 | 19 | Assert.False(result); 20 | Assert.Null(clause); 21 | } 22 | 23 | [Theory] 24 | [InlineData(null,null,null,null,false,null)] 25 | [InlineData(65, null, null, null, true, "WITH DURATION 65m")] 26 | [InlineData(null, 3, null, null, true, "WITH REPLICATION 3")] 27 | [InlineData(null, null, 60, null, true, "WITH SHARD DURATION 60m")] 28 | [InlineData(null, null, null, "any Name", true, "WITH NAME \"any%20Name\"")] 29 | [InlineData(65, 3, 60, "any Name", true, "WITH DURATION 65m REPLICATION 3 SHARD DURATION 60m NAME \"any%20Name\"")] 30 | 31 | public void TryApplyRetentionPolicy( 32 | int? duration, 33 | int? replication, 34 | int? shardDuration, 35 | string name, 36 | bool expected, 37 | string expectedClause) 38 | { 39 | RetentionPolicyOptions options = 40 | new RetentionPolicyOptions 41 | { 42 | Name = name, 43 | Duration = duration.HasValue ? TimeSpan.FromMinutes(duration.Value) : (TimeSpan?)null , 44 | Replication = replication, 45 | ShardDuration = shardDuration.HasValue ? TimeSpan.FromMinutes(shardDuration.Value) : (TimeSpan?)null 46 | }; 47 | 48 | bool result = options.TryApply(out var clause); 49 | 50 | Assert.Equal(expected, result); 51 | Assert.Equal(expectedClause, clause); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Internal/LineProtocolPointMultipleValues.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | 10 | namespace App.Metrics.Formatters.InfluxDB.Internal 11 | { 12 | /// 13 | /// Represents a line procol point with multiple fields. 14 | /// 15 | internal class LineProtocolPointMultipleValues : LineProtocolPointBase, ILineProtocolPoint 16 | { 17 | public LineProtocolPointMultipleValues( 18 | string measurement, 19 | IEnumerable fieldsNames, 20 | IEnumerable fieldsValues, 21 | MetricTags tags, 22 | DateTime? utcTimestamp = null) 23 | : base(measurement, tags, utcTimestamp) 24 | { 25 | if (fieldsNames == null || !fieldsNames.Any()) 26 | { 27 | throw new ArgumentException("At least one field must be specified"); 28 | } 29 | 30 | if (fieldsNames.Any(f => string.IsNullOrEmpty(f))) 31 | { 32 | throw new ArgumentException("Fields must have non-empty names"); 33 | } 34 | 35 | FieldsNames = fieldsNames; 36 | FieldsValues = fieldsValues; 37 | } 38 | 39 | public IEnumerable FieldsNames { get; } 40 | 41 | public IEnumerable FieldsValues { get; } 42 | 43 | public void Write(TextWriter textWriter, bool writeTimestamp = true) 44 | { 45 | if (textWriter == null) 46 | { 47 | throw new ArgumentNullException(nameof(textWriter)); 48 | } 49 | 50 | WriteCommon(textWriter); 51 | 52 | var fieldDelim = ' '; 53 | 54 | using (var nameEnumerator = FieldsNames.GetEnumerator()) 55 | using (var valueEnumerator = FieldsValues.GetEnumerator()) 56 | { 57 | while (nameEnumerator.MoveNext() && valueEnumerator.MoveNext()) 58 | { 59 | var name = nameEnumerator.Current; 60 | var value = valueEnumerator.Current; 61 | 62 | textWriter.Write(fieldDelim); 63 | fieldDelim = ','; 64 | textWriter.Write(LineProtocolSyntax.EscapeName(name)); 65 | textWriter.Write('='); 66 | textWriter.Write(LineProtocolSyntax.FormatValue(value)); 67 | } 68 | } 69 | 70 | if (!writeTimestamp) 71 | { 72 | return; 73 | } 74 | 75 | WriteTimestamp(textWriter); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/MetricsInfluxDBLineProtocolOutputFormatter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | #if !NETSTANDARD1_6 10 | using App.Metrics.Internal; 11 | #endif 12 | using App.Metrics.Serialization; 13 | 14 | namespace App.Metrics.Formatters.InfluxDB 15 | { 16 | public class MetricsInfluxDbLineProtocolOutputFormatter : IMetricsOutputFormatter 17 | { 18 | private readonly MetricsInfluxDbLineProtocolOptions _options; 19 | 20 | public MetricsInfluxDbLineProtocolOutputFormatter() 21 | { 22 | _options = new MetricsInfluxDbLineProtocolOptions(); 23 | } 24 | 25 | public MetricsInfluxDbLineProtocolOutputFormatter(MetricFields metricFields) 26 | { 27 | _options = new MetricsInfluxDbLineProtocolOptions(); 28 | MetricFields = metricFields; 29 | } 30 | 31 | public MetricsInfluxDbLineProtocolOutputFormatter(MetricsInfluxDbLineProtocolOptions options) 32 | { 33 | _options = options ?? throw new ArgumentNullException(nameof(options)); 34 | } 35 | 36 | public MetricsInfluxDbLineProtocolOutputFormatter(MetricsInfluxDbLineProtocolOptions options, MetricFields metricFields) 37 | { 38 | _options = options ?? throw new ArgumentNullException(nameof(options)); 39 | MetricFields = metricFields; 40 | } 41 | 42 | /// 43 | public MetricsMediaTypeValue MediaType => new MetricsMediaTypeValue("text", "vnd.appmetrics.metrics.influxdb", "v1", "plain"); 44 | 45 | /// 46 | public MetricFields MetricFields { get; set; } 47 | 48 | /// 49 | public Task WriteAsync( 50 | Stream output, 51 | MetricsDataValueSource metricsData, 52 | CancellationToken cancellationToken = default) 53 | { 54 | if (output == null) 55 | { 56 | throw new ArgumentNullException(nameof(output)); 57 | } 58 | 59 | var serializer = new MetricSnapshotSerializer(); 60 | 61 | using (var streamWriter = new StreamWriter(output)) 62 | { 63 | using (var textWriter = new MetricSnapshotInfluxDbLineProtocolWriter( 64 | streamWriter, 65 | _options.MetricNameFormatter)) 66 | { 67 | serializer.Serialize(textWriter, metricsData, MetricFields); 68 | } 69 | } 70 | 71 | #if !NETSTANDARD1_6 72 | return AppMetricsTaskHelper.CompletedTask(); 73 | #else 74 | return Task.CompletedTask; 75 | #endif 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Internal/LineProtocolPointBase.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | 8 | namespace App.Metrics.Formatters.InfluxDB.Internal 9 | { 10 | /// 11 | /// Base class for a which takes care of common properties (, and ). 12 | /// 13 | internal abstract class LineProtocolPointBase 14 | { 15 | public LineProtocolPointBase(string measurement, MetricTags tags, DateTime? utcTimestamp) 16 | { 17 | if (string.IsNullOrEmpty(measurement)) 18 | { 19 | throw new ArgumentException("A measurement name must be specified"); 20 | } 21 | 22 | if (utcTimestamp != null && utcTimestamp.Value.Kind != DateTimeKind.Utc) 23 | { 24 | throw new ArgumentException("Timestamps must be specified as UTC"); 25 | } 26 | 27 | Measurement = measurement; 28 | Tags = tags; 29 | UtcTimestamp = utcTimestamp; 30 | } 31 | 32 | public string Measurement { get; } 33 | 34 | public MetricTags Tags { get; } 35 | 36 | public DateTime? UtcTimestamp { get; } 37 | 38 | /// 39 | /// Writes the common properties of the line procol points, which includes writing the measurement name and the different tags. 40 | /// 41 | /// Writer to write the values to. 42 | protected void WriteCommon(TextWriter textWriter) 43 | { 44 | textWriter.Write(LineProtocolSyntax.EscapeName(Measurement)); 45 | 46 | if (Tags.Count > 0) 47 | { 48 | for (var i = 0; i < Tags.Count; i++) 49 | { 50 | textWriter.Write(','); 51 | textWriter.Write(LineProtocolSyntax.EscapeName(Tags.Keys[i])); 52 | textWriter.Write('='); 53 | textWriter.Write(LineProtocolSyntax.EscapeName(Tags.Values[i])); 54 | } 55 | } 56 | } 57 | 58 | /// 59 | /// Writes the timestamp using the most precise unit. 60 | /// 61 | /// Writer to write the values to. 62 | protected void WriteTimestamp(TextWriter textWriter) 63 | { 64 | textWriter.Write(' '); 65 | 66 | if (UtcTimestamp == null) 67 | { 68 | textWriter.Write(LineProtocolSyntax.FormatTimestamp(DateTime.UtcNow)); 69 | return; 70 | } 71 | 72 | textWriter.Write(LineProtocolSyntax.FormatTimestamp(UtcTimestamp.Value)); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/MetricsReportingInfluxDBOptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.Filters; 7 | using App.Metrics.Formatters; 8 | using App.Metrics.Reporting.InfluxDB.Client; 9 | 10 | namespace App.Metrics.Reporting.InfluxDB 11 | { 12 | /// 13 | /// Provides programmatic configuration for InfluxDB Reporting in the App Metrics framework. 14 | /// 15 | public class MetricsReportingInfluxDbOptions 16 | { 17 | public MetricsReportingInfluxDbOptions() 18 | { 19 | FlushInterval = TimeSpan.FromSeconds(10); 20 | HttpPolicy = new HttpPolicy 21 | { 22 | FailuresBeforeBackoff = Constants.DefaultFailuresBeforeBackoff, 23 | BackoffPeriod = Constants.DefaultBackoffPeriod, 24 | Timeout = Constants.DefaultTimeout 25 | }; 26 | InfluxDb = new InfluxDbOptions(); 27 | } 28 | 29 | /// 30 | /// Gets or sets the to use for just this reporter. 31 | /// 32 | /// 33 | /// The to use for this reporter. 34 | /// 35 | public IFilterMetrics Filter { get; set; } 36 | 37 | /// 38 | /// Gets or sets the HTTP policy settings which allows circuit breaker configuration to be adjusted 39 | /// 40 | /// 41 | /// The HTTP policy. 42 | /// 43 | public HttpPolicy HttpPolicy { get; set; } 44 | 45 | /// 46 | /// Gets or sets the available options for InfluxDB connectivity. 47 | /// 48 | /// 49 | /// The . 50 | /// 51 | public InfluxDbOptions InfluxDb { get; set; } 52 | 53 | /// 54 | /// Gets or sets the used to write metrics. 55 | /// 56 | /// 57 | /// The used to write metrics. 58 | /// 59 | public IMetricsOutputFormatter MetricsOutputFormatter { get; set; } 60 | 61 | /// 62 | /// Gets or sets the flush metrics interval 63 | /// 64 | /// 65 | /// This will apply to all configured reporters unless overriden by a specific reporters 66 | /// options. 67 | /// 68 | /// 69 | /// The to wait between reporting metrics 70 | /// 71 | public TimeSpan FlushInterval { get; set; } 72 | } 73 | } -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/InfluxDbMetricsReporter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using App.Metrics.Filters; 11 | using App.Metrics.Formatters; 12 | using App.Metrics.Formatters.InfluxDB; 13 | using App.Metrics.Logging; 14 | using App.Metrics.Reporting.InfluxDB.Client; 15 | 16 | namespace App.Metrics.Reporting.InfluxDB 17 | { 18 | public class InfluxDbMetricsReporter : IReportMetrics 19 | { 20 | private static readonly ILog Logger = LogProvider.For(); 21 | private readonly IMetricsOutputFormatter _defaultMetricsOutputFormatter = new MetricsInfluxDbLineProtocolOutputFormatter(); 22 | private readonly ILineProtocolClient _lineProtocolClient; 23 | 24 | public InfluxDbMetricsReporter( 25 | MetricsReportingInfluxDbOptions options, 26 | ILineProtocolClient lineProtocolClient) 27 | { 28 | if (options == null) 29 | { 30 | throw new ArgumentNullException(nameof(options)); 31 | } 32 | 33 | if (options.FlushInterval < TimeSpan.Zero) 34 | { 35 | throw new InvalidOperationException($"{nameof(MetricsReportingInfluxDbOptions.FlushInterval)} must not be less than zero"); 36 | } 37 | 38 | _lineProtocolClient = lineProtocolClient ?? throw new ArgumentNullException(nameof(lineProtocolClient)); 39 | 40 | Formatter = options.MetricsOutputFormatter ?? _defaultMetricsOutputFormatter; 41 | 42 | FlushInterval = options.FlushInterval > TimeSpan.Zero 43 | ? options.FlushInterval 44 | : AppMetricsConstants.Reporting.DefaultFlushInterval; 45 | 46 | Filter = options.Filter; 47 | 48 | Logger.Debug($"Using Metrics Reporter {this}. Url: {options.InfluxDb.BaseUri + options.InfluxDb.Endpoint} FlushInterval: {FlushInterval}"); 49 | } 50 | 51 | /// 52 | public IFilterMetrics Filter { get; set; } 53 | 54 | /// 55 | public TimeSpan FlushInterval { get; set; } 56 | 57 | /// 58 | public IMetricsOutputFormatter Formatter { get; set; } 59 | 60 | /// 61 | public async Task FlushAsync(MetricsDataValueSource metricsData, CancellationToken cancellationToken = default) 62 | { 63 | Logger.Trace("Flushing metrics snapshot"); 64 | 65 | LineProtocolWriteResult result; 66 | 67 | using (var stream = new MemoryStream()) 68 | { 69 | await Formatter.WriteAsync(stream, metricsData, cancellationToken); 70 | stream.Position = 0; 71 | result = await _lineProtocolClient.WriteAsync(stream, cancellationToken); 72 | } 73 | 74 | if (result.Success) 75 | { 76 | Logger.Trace("Flushed metrics snapshot"); 77 | return true; 78 | } 79 | 80 | Logger.Error(result.ErrorMessage); 81 | 82 | return false; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Host.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | using App.Metrics; 8 | using App.Metrics.AspNetCore; 9 | using App.Metrics.AspNetCore.Health; 10 | using App.Metrics.Extensions.Configuration; 11 | using App.Metrics.Formatters.InfluxDB; 12 | using App.Metrics.Health; 13 | using App.Metrics.Reporting.InfluxDB; 14 | using Microsoft.AspNetCore; 15 | using Microsoft.AspNetCore.Hosting; 16 | using Microsoft.Extensions.Configuration; 17 | using Serilog; 18 | using Serilog.Events; 19 | 20 | namespace MetricsInfluxDBSandboxMvc 21 | { 22 | public static class Host 23 | { 24 | public static IWebHost BuildWebHost(string[] args) 25 | { 26 | ConfigureLogging(); 27 | 28 | var configuration = new ConfigurationBuilder() 29 | .SetBasePath(Directory.GetCurrentDirectory()) 30 | .AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true) 31 | .Build(); 32 | 33 | var influxOptions = new MetricsReportingInfluxDbOptions(); 34 | configuration.GetSection(nameof(MetricsReportingInfluxDbOptions)).Bind(influxOptions); 35 | influxOptions.MetricsOutputFormatter = new MetricsInfluxDbLineProtocolOutputFormatter(); 36 | 37 | var metrics = AppMetrics.CreateDefaultBuilder() 38 | .Configuration.ReadFrom(configuration) 39 | .Report.ToInfluxDb(influxOptions) 40 | .Build(); 41 | 42 | return WebHost.CreateDefaultBuilder(args) 43 | .ConfigureMetrics(metrics) 44 | .ConfigureHealthWithDefaults( 45 | (context, builder) => 46 | { 47 | builder.OutputHealth.AsPlainText() 48 | .OutputHealth.AsJson() 49 | .HealthChecks.AddCheck("check 1", () => new ValueTask(HealthCheckResult.Healthy())) 50 | .HealthChecks.AddCheck("check 2", () => new ValueTask(HealthCheckResult.Degraded())) 51 | .HealthChecks.AddCheck("check 3", () => new ValueTask(HealthCheckResult.Unhealthy())) 52 | .Report.ToMetrics(metrics); 53 | }) 54 | .UseHealth() 55 | .UseMetrics() 56 | .UseSerilog() 57 | .UseStartup() 58 | .Build(); 59 | } 60 | 61 | public static void Main(string[] args) { BuildWebHost(args).Run(); } 62 | 63 | private static void ConfigureLogging() 64 | { 65 | Log.Logger = new LoggerConfiguration() 66 | .MinimumLevel.Verbose() 67 | .MinimumLevel.Override("Microsoft", LogEventLevel.Verbose) 68 | .WriteTo.LiterateConsole() 69 | .WriteTo.Seq("http://localhost:5341") 70 | .CreateLogger(); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/RetentionPolicyOptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Text; 7 | 8 | namespace App.Metrics.Reporting.InfluxDB 9 | { 10 | /// 11 | /// Single retention policy associated with the created database. If you do not specify one of the properties, the relevant behavior defaults to the autogen retention policy settings. 12 | /// 13 | public class RetentionPolicyOptions 14 | { 15 | /// 16 | /// Gets or sets the Duration determines how long InfluxDB keeps the data. 17 | /// 18 | /// 19 | /// The retention policy duration. The minimum duration for a retention policy is one hour. 20 | /// 21 | public TimeSpan? Duration { get; set; } 22 | 23 | /// 24 | /// Gets or sets the Name determines policy name. 25 | /// 26 | /// 27 | /// The retention policy name. 28 | /// 29 | public string Name { get; set; } 30 | 31 | /// 32 | /// Gets or sets the Replication determines how many independent copies of each point are stored in the cluster. Replication factors do not serve a purpose with single node instances. 33 | /// 34 | /// 35 | /// The retention policy replication. Number of data nodes. 36 | /// 37 | public int? Replication { get; set; } 38 | 39 | /// 40 | /// Gets or sets the ShardDuration determines the time range covered by a shard group. 41 | /// 42 | /// 43 | /// The retention policy shard duration. The minimum allowable SHARD GROUP DURATION is 1h 44 | /// 45 | public TimeSpan? ShardDuration { get; set; } 46 | 47 | public bool TryApply(out string clause) 48 | { 49 | clause = null; 50 | bool hasName = !string.IsNullOrWhiteSpace(Name); 51 | bool hasNonDefaultValues = Duration.HasValue || Replication.HasValue || ShardDuration.HasValue || hasName; 52 | StringBuilder stringBuilder = new StringBuilder("WITH", 100); 53 | 54 | if (hasNonDefaultValues) 55 | { 56 | if (Duration.HasValue) 57 | { 58 | stringBuilder.Append(" DURATION "); 59 | stringBuilder.Append((int)Math.Floor(Duration.Value.TotalMinutes)); 60 | stringBuilder.Append("m"); 61 | } 62 | 63 | if (Replication.HasValue) 64 | { 65 | stringBuilder.Append(" REPLICATION "); 66 | stringBuilder.Append(Replication.Value); 67 | } 68 | 69 | if (ShardDuration.HasValue) 70 | { 71 | stringBuilder.Append(" SHARD DURATION "); 72 | stringBuilder.Append((int)Math.Floor(ShardDuration.Value.TotalMinutes)); 73 | stringBuilder.Append("m"); 74 | } 75 | 76 | if (hasName) 77 | { 78 | stringBuilder.Append(" NAME \""); 79 | stringBuilder.Append(Uri.EscapeDataString(Name)); 80 | stringBuilder.Append("\""); 81 | } 82 | 83 | clause = stringBuilder.ToString(); 84 | } 85 | 86 | return hasNonDefaultValues; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Internal/LineProtocolPointLegacy.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | 10 | namespace App.Metrics.Formatters.InfluxDB.Internal 11 | { 12 | /// 13 | /// This class is not used anymore and is here only for baseline benchmarks. 14 | /// 15 | [Obsolete("This class is not used anymore and should be replaced by LineProtocolMultipleValues or LineProtocolSingleValue when appropriate")] 16 | internal class LineProtocolPointLegacy : ILineProtocolPoint 17 | { 18 | public LineProtocolPointLegacy( 19 | string measurement, 20 | IEnumerable> fields, 21 | MetricTags tags, 22 | DateTime? utcTimestamp = null) 23 | { 24 | if (string.IsNullOrEmpty(measurement)) 25 | { 26 | throw new ArgumentException("A measurement name must be specified"); 27 | } 28 | 29 | if (fields == null || !fields.Any()) 30 | { 31 | throw new ArgumentException("At least one field must be specified"); 32 | } 33 | 34 | if (fields.Any(f => string.IsNullOrEmpty(f.Key))) 35 | { 36 | throw new ArgumentException("Fields must have non-empty names"); 37 | } 38 | 39 | if (utcTimestamp != null && utcTimestamp.Value.Kind != DateTimeKind.Utc) 40 | { 41 | throw new ArgumentException("Timestamps must be specified as UTC"); 42 | } 43 | 44 | Measurement = measurement; 45 | Fields = fields; 46 | Tags = tags; 47 | UtcTimestamp = utcTimestamp; 48 | } 49 | 50 | public IEnumerable> Fields { get; } 51 | 52 | public string Measurement { get; } 53 | 54 | public MetricTags Tags { get; } 55 | 56 | public DateTime? UtcTimestamp { get; } 57 | 58 | public void Write(TextWriter textWriter, bool writeTimestamp = true) 59 | { 60 | if (textWriter == null) 61 | { 62 | throw new ArgumentNullException(nameof(textWriter)); 63 | } 64 | 65 | textWriter.Write(LineProtocolSyntax.EscapeName(Measurement)); 66 | 67 | if (Tags.Count > 0) 68 | { 69 | for (var i = 0; i < Tags.Count; i++) 70 | { 71 | textWriter.Write(','); 72 | textWriter.Write(LineProtocolSyntax.EscapeName(Tags.Keys[i])); 73 | textWriter.Write('='); 74 | textWriter.Write(LineProtocolSyntax.EscapeName(Tags.Values[i])); 75 | } 76 | } 77 | 78 | var fieldDelim = ' '; 79 | 80 | foreach (var f in Fields) 81 | { 82 | textWriter.Write(fieldDelim); 83 | fieldDelim = ','; 84 | textWriter.Write(LineProtocolSyntax.EscapeName(f.Key)); 85 | textWriter.Write('='); 86 | textWriter.Write(LineProtocolSyntax.FormatValue(f.Value)); 87 | } 88 | 89 | if (!writeTimestamp) 90 | { 91 | return; 92 | } 93 | 94 | textWriter.Write(' '); 95 | 96 | if (UtcTimestamp == null) 97 | { 98 | textWriter.Write(LineProtocolSyntax.FormatTimestamp(DateTime.UtcNow)); 99 | return; 100 | } 101 | 102 | textWriter.Write(LineProtocolSyntax.FormatTimestamp(UtcTimestamp.Value)); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Internal/LineProtocolSyntax.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Globalization; 8 | 9 | namespace App.Metrics.Formatters.InfluxDB.Internal 10 | { 11 | internal class LineProtocolSyntax 12 | { 13 | private static readonly DateTime Origin = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 14 | 15 | private static readonly Dictionary> Formatters = new Dictionary> 16 | { 17 | { typeof(sbyte), FormatInteger }, 18 | { typeof(byte), FormatInteger }, 19 | { typeof(short), FormatInteger }, 20 | { typeof(ushort), FormatInteger }, 21 | { typeof(int), FormatInteger }, 22 | { typeof(uint), FormatInteger }, 23 | { typeof(long), FormatInteger }, 24 | { typeof(ulong), FormatInteger }, 25 | { typeof(float), FormatFloat }, 26 | { typeof(double), FormatFloat }, 27 | { typeof(decimal), FormatFloat }, 28 | { typeof(bool), FormatBoolean }, 29 | { typeof(TimeSpan), FormatTimespan } 30 | }; 31 | 32 | public static string EscapeName(string nameOrKey) 33 | { 34 | if (nameOrKey == null) 35 | { 36 | throw new ArgumentNullException(nameof(nameOrKey)); 37 | } 38 | 39 | return nameOrKey 40 | .Replace("=", "\\=") 41 | .Replace(" ", "\\ ") 42 | .Replace(",", "\\,"); 43 | } 44 | 45 | public static string FormatTimestamp(DateTime utcTimestamp) 46 | { 47 | var t = utcTimestamp - Origin; 48 | return (t.Ticks * 100L).ToString(CultureInfo.InvariantCulture); 49 | } 50 | 51 | public static string FormatValue(object value) 52 | { 53 | var v = value ?? string.Empty; 54 | Func format; 55 | 56 | return Formatters.TryGetValue(v.GetType(), out format) 57 | ? format(v) 58 | : FormatString(v.ToString()); 59 | } 60 | 61 | private static string FormatBoolean(object b) { return (bool)b ? "t" : "f"; } 62 | 63 | private static string FormatFloat(object f) { return ((IFormattable)f).ToString(null, CultureInfo.InvariantCulture); } 64 | 65 | private static string FormatInteger(object i) { return ((IFormattable)i).ToString(null, CultureInfo.InvariantCulture) + "i"; } 66 | 67 | private static string FormatString(string s) { return "\"" + s.Replace("\"", "\\\"") + "\""; } 68 | 69 | private static string FormatTimespan(object ts) { return ((TimeSpan)ts).TotalMilliseconds.ToString(CultureInfo.InvariantCulture); } 70 | } 71 | } -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/InfluxDBOptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | 7 | namespace App.Metrics.Reporting.InfluxDB 8 | { 9 | /// 10 | /// Provides programmatic configuration for InfluxDB in the App Metrics framework. 11 | /// 12 | public class InfluxDbOptions 13 | { 14 | public InfluxDbOptions() { CreateDataBaseIfNotExists = true; } 15 | 16 | /// 17 | /// Gets or sets the number of InfluxDB notes that must confirm the write 18 | /// 19 | /// 20 | /// The InfluxDB node write consistency. 21 | /// 22 | public string Consistenency { get; set; } 23 | 24 | /// 25 | /// Gets formatted endpoint for writes to InfluxDB 26 | /// 27 | /// 28 | /// The InfluxDB endpoint for writes. 29 | /// 30 | public string Endpoint 31 | { 32 | get 33 | { 34 | if (string.IsNullOrWhiteSpace(Database)) 35 | { 36 | return null; 37 | } 38 | 39 | var endpoint = $"write?db={Uri.EscapeDataString(Database)}"; 40 | 41 | if (!string.IsNullOrWhiteSpace(RetentionPolicy)) 42 | { 43 | endpoint += $"&rp={Uri.EscapeDataString(RetentionPolicy)}"; 44 | } 45 | 46 | if (!string.IsNullOrWhiteSpace(Consistenency)) 47 | { 48 | endpoint += $"&consistency={Uri.EscapeDataString(Consistenency)}"; 49 | } 50 | 51 | return endpoint; 52 | } 53 | } 54 | 55 | /// 56 | /// Gets or sets the base URI of the InfluxDB API. 57 | /// 58 | /// 59 | /// The base URI of the InfluxDB API where metrics are flushed. 60 | /// 61 | public Uri BaseUri { get; set; } 62 | 63 | /// 64 | /// Gets or sets the InfluxDB database name used to report metrics. 65 | /// 66 | /// 67 | /// The InfluxDB database name where metrics are flushed. 68 | /// 69 | public string Database { get; set; } 70 | 71 | /// 72 | /// Gets or sets the InfluxDB database password. 73 | /// 74 | /// 75 | /// The InfluxDB database password. 76 | /// 77 | public string Password { get; set; } 78 | 79 | /// 80 | /// Gets or sets the InfluxDB database's retention policy to target. 81 | /// 82 | /// 83 | /// The InfluxDB database's retention policy to target. 84 | /// 85 | public string RetentionPolicy { get; set; } 86 | 87 | /// 88 | /// Gets or sets the InfluxDB database username. 89 | /// 90 | /// 91 | /// The InfluxDB database username. 92 | /// 93 | public string UserName { get; set; } 94 | 95 | /// 96 | /// Gets or sets a value indicating whether or not to attempt to create the specified database if it does not exist 97 | /// 98 | /// 99 | /// The flag indicating whether or not to create the specifried database if it does not exist 100 | /// 101 | public bool CreateDataBaseIfNotExists { get; set; } 102 | 103 | /// 104 | /// Gets or sets the InfluxDB retention policy options used during an attempt to create the specified database if it does not exist. 105 | /// 106 | /// 107 | /// The InfluxDB retention policy options . 108 | /// 109 | public RetentionPolicyOptions CreateDatabaseRetentionPolicy { get; set; } 110 | } 111 | } -------------------------------------------------------------------------------- /benchmarks/App.Metrics.InfluxDB.Benchmarks/BenchmarkDotNetBenchmarks/Formatters/MetricSnapshotInfluxDbLineProtocolWriterBenchmark.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using App.Metrics.Formatters.InfluxDB; 9 | using BenchmarkDotNet.Attributes; 10 | using BenchmarkDotNet.Configs; 11 | 12 | namespace App.Metrics.InfluxDB.Benchmarks.BenchmarkDotNetBenchmarks.Formatters 13 | { 14 | /// 15 | /// This benchmark examines the way allocates written values. 16 | /// 17 | [Config(typeof(Config.DefaultConfig))] 18 | [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] 19 | public class MetricSnapshotInfluxDbLineProtocolWriterBenchmark 20 | { 21 | private WritePointData[] pointData; 22 | private WritePointsData[] pointsData; 23 | 24 | [GlobalSetup] 25 | public void Setup() 26 | { 27 | var tags = new MetricTags(new[] { "app", "host" }, new[] { "my_app", "my_host" }); 28 | 29 | // generate 500x points 30 | pointData = new WritePointData[500]; 31 | for (int i = 0; i < 500; i++) 32 | { 33 | pointData[i] = new WritePointData { Context = "context", Name = "name", Field = "field", Tags = tags, Value = 50d, Timestamp = DateTime.UtcNow }; 34 | } 35 | 36 | pointsData = new WritePointsData[500]; 37 | for (int i = 0; i < 500; i++) 38 | { 39 | pointsData[i] = new WritePointsData { Context = "context", Name = "name", Tags = tags, Columns = new[] { "col_1", "col_2" }, Values = new object[] { 50d, 45d }, Timestamp = DateTime.UtcNow }; 40 | } 41 | } 42 | 43 | [BenchmarkCategory("SingleValue")] 44 | [Benchmark(Baseline = true)] 45 | [Obsolete("Legacy baseline benchs")] 46 | public void WriteSingleValuePoint_Legacy() 47 | { 48 | var writer = new MetricSnapshotInfluxDbLineProtocolWriter(TextWriter.Null); 49 | var p = pointData[0]; 50 | writer.WriteLegacy(p.Context, p.Name, p.Field, p.Value, p.Tags, p.Timestamp); 51 | } 52 | 53 | [BenchmarkCategory("SingleValue")] 54 | [Benchmark] 55 | public void WriteSingleValuePoint() 56 | { 57 | var writer = new MetricSnapshotInfluxDbLineProtocolWriter(TextWriter.Null); 58 | var p = pointData[0]; 59 | writer.Write(p.Context, p.Name, p.Field, p.Value, p.Tags, p.Timestamp); 60 | } 61 | 62 | [BenchmarkCategory("MultipleValues")] 63 | [Benchmark(Baseline = true)] 64 | [Obsolete("Legacy baseline benchs")] 65 | public void WriteMultipleValuesPoint_Legacy() 66 | { 67 | var writer = new MetricSnapshotInfluxDbLineProtocolWriter(TextWriter.Null); 68 | var p = pointsData[0]; 69 | writer.WriteLegacy(p.Context, p.Name, p.Columns, p.Values, p.Tags, p.Timestamp); 70 | } 71 | 72 | [BenchmarkCategory("MultipleValues")] 73 | [Benchmark] 74 | public void WriteMultipleValuesPoint() 75 | { 76 | var writer = new MetricSnapshotInfluxDbLineProtocolWriter(TextWriter.Null); 77 | var p = pointsData[0]; 78 | writer.Write(p.Context, p.Name, p.Columns, p.Values, p.Tags, p.Timestamp); 79 | } 80 | 81 | private class WritePointData 82 | { 83 | public string Context { get; set; } 84 | 85 | public string Name { get; set; } 86 | 87 | public string Field { get; set; } 88 | 89 | public object Value { get; set; } 90 | 91 | public MetricTags Tags { get; set; } 92 | 93 | public DateTime Timestamp { get; set; } 94 | } 95 | 96 | private class WritePointsData 97 | { 98 | public string Context { get; set; } 99 | 100 | public string Name { get; set; } 101 | 102 | public IEnumerable Columns { get; set; } 103 | 104 | public IEnumerable Values { get; set; } 105 | 106 | public MetricTags Tags { get; set; } 107 | 108 | public DateTime Timestamp { get; set; } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandboxMvc/Controllers/AppBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using App.Metrics.Scheduling; 9 | using MetricsInfluxDBSandboxMvc.Controllers; 10 | using Microsoft.AspNetCore.Hosting; 11 | 12 | // ReSharper disable CheckNamespace 13 | namespace Microsoft.AspNetCore.Builder 14 | // ReSharper restore CheckNamespace 15 | { 16 | public static class AppBuilderExtensions 17 | { 18 | private const int ApdexSamplesInterval = 2; 19 | private const int GetEndpointSuccessInterval = 1; 20 | private const int PutAndPostRequestsInterval = 6; 21 | private const int RandomSamplesInterval = 10; 22 | private const int SlaEndpointsInterval = 2; 23 | private static readonly HttpClient HttpClient = new HttpClient { BaseAddress = new Uri("http://localhost:1111/") }; 24 | 25 | public static IApplicationBuilder UseTestStuff( 26 | this IApplicationBuilder app, 27 | IApplicationLifetime lifetime, 28 | bool runSampleRequestsFromApp) 29 | { 30 | app.Use( 31 | (context, next) => 32 | { 33 | RandomClientIdForTesting.SetTheFakeClaimsPrincipal(context); 34 | return next(); 35 | }); 36 | 37 | var token = lifetime.ApplicationStopping; 38 | 39 | if (runSampleRequestsFromApp) 40 | { 41 | var apdexSamples = new AppMetricsTaskScheduler( 42 | TimeSpan.FromSeconds(ApdexSamplesInterval), 43 | () => 44 | { 45 | var satisfied = HttpClient.GetAsync("api/satisfying", token); 46 | var tolerating = HttpClient.GetAsync("api/tolerating", token); 47 | var frustrating = HttpClient.GetAsync("api/frustrating", token); 48 | 49 | return Task.WhenAll(satisfied, tolerating, frustrating); 50 | }); 51 | 52 | apdexSamples.Start(); 53 | 54 | var randomErrorSamples = new AppMetricsTaskScheduler( 55 | TimeSpan.FromSeconds(RandomSamplesInterval), 56 | () => 57 | { 58 | var randomStatusCode = HttpClient.GetAsync("api/randomstatuscode", token); 59 | var randomException = HttpClient.GetAsync("api/randomexception", token); 60 | 61 | return Task.WhenAll(randomStatusCode, randomException); 62 | }); 63 | 64 | randomErrorSamples.Start(); 65 | 66 | var testSamples = new AppMetricsTaskScheduler( 67 | TimeSpan.FromSeconds(GetEndpointSuccessInterval), 68 | () => HttpClient.GetAsync("api/test", token)); 69 | 70 | testSamples.Start(); 71 | 72 | var slaSamples = new AppMetricsTaskScheduler( 73 | TimeSpan.FromSeconds(SlaEndpointsInterval), 74 | () => HttpClient.GetAsync("api/slatest/timer", token)); 75 | 76 | slaSamples.Start(); 77 | 78 | var randomBufferGenerator = new RandomBufferGenerator(50000); 79 | var postPutSamples = new AppMetricsTaskScheduler( 80 | TimeSpan.FromSeconds(PutAndPostRequestsInterval), 81 | () => 82 | { 83 | var putBytes = new ByteArrayContent(randomBufferGenerator.GenerateBufferFromSeed()); 84 | var putFormData = new MultipartFormDataContent { { putBytes, "put-file", "rnd-put" } }; 85 | var putRequest = HttpClient.PutAsync("api/file", putFormData, token); 86 | 87 | var postBytes = new ByteArrayContent(randomBufferGenerator.GenerateBufferFromSeed()); 88 | var postFormData = new MultipartFormDataContent { { postBytes, "post-file", "rnd-post" } }; 89 | var postRequest = HttpClient.PostAsync("api/file", postFormData, token); 90 | 91 | return Task.WhenAll(putRequest, postRequest); 92 | }); 93 | 94 | postPutSamples.Start(); 95 | } 96 | 97 | return app; 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/Builder/MetricsInfluxDbLineProtocolFormatterBuilder.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.Formatters.InfluxDB; 7 | 8 | // ReSharper disable CheckNamespace 9 | namespace App.Metrics 10 | // ReSharper restore CheckNamespace 11 | { 12 | public static class MetricsInfluxDbLineProtocolFormatterBuilder 13 | { 14 | /// 15 | /// Add the allowing metrics to optionally be reported to 16 | /// InfluxDB using the LineProtocol. 17 | /// 18 | /// s 19 | /// The used to configure InfluxDB Lineprotocol formatting 20 | /// options. 21 | /// 22 | /// The InfluxDB LineProtocol formatting options to use. 23 | /// The metric fields to report as well as thier names. 24 | /// 25 | /// An that can be used to further configure App Metrics. 26 | /// 27 | public static IMetricsBuilder AsInfluxDbLineProtocol( 28 | this IMetricsOutputFormattingBuilder metricFormattingBuilder, 29 | Action setupAction, 30 | MetricFields fields = null) 31 | { 32 | if (metricFormattingBuilder == null) 33 | { 34 | throw new ArgumentNullException(nameof(metricFormattingBuilder)); 35 | } 36 | 37 | var options = new MetricsInfluxDbLineProtocolOptions(); 38 | 39 | setupAction?.Invoke(options); 40 | 41 | var formatter = new MetricsInfluxDbLineProtocolOutputFormatter(options, fields); 42 | 43 | return metricFormattingBuilder.Using(formatter, false); 44 | } 45 | 46 | /// 47 | /// Add the allowing metrics to optionally be reported to 48 | /// InfluxDB using the LineProtocol. 49 | /// 50 | /// s 51 | /// The used to configure InfluxDB Lineprotocol formatting 52 | /// options. 53 | /// 54 | /// The InfluxDB LineProtocol formatting options to use. 55 | /// The metric fields to report as well as thier names. 56 | /// 57 | /// An that can be used to further configure App Metrics. 58 | /// 59 | public static IMetricsBuilder AsInfluxDbLineProtocol( 60 | this IMetricsOutputFormattingBuilder metricFormattingBuilder, 61 | MetricsInfluxDbLineProtocolOptions options, 62 | MetricFields fields = null) 63 | { 64 | if (metricFormattingBuilder == null) 65 | { 66 | throw new ArgumentNullException(nameof(metricFormattingBuilder)); 67 | } 68 | 69 | var formatter = new MetricsInfluxDbLineProtocolOutputFormatter(options, fields); 70 | 71 | return metricFormattingBuilder.Using(formatter, false); 72 | } 73 | 74 | /// 75 | /// Add the allowing metrics to optionally be reported to 76 | /// InfluxDB using the LineProtocol. 77 | /// 78 | /// s 79 | /// The used to configure InfluxDB Lineprotocol formatting 80 | /// options. 81 | /// 82 | /// The metric fields to report as well as thier names. 83 | /// 84 | /// An that can be used to further configure App Metrics. 85 | /// 86 | public static IMetricsBuilder AsInfluxDbLineProtocol( 87 | this IMetricsOutputFormattingBuilder metricFormattingBuilder, 88 | MetricFields fields = null) 89 | { 90 | if (metricFormattingBuilder == null) 91 | { 92 | throw new ArgumentNullException(nameof(metricFormattingBuilder)); 93 | } 94 | 95 | var formatter = new MetricsInfluxDbLineProtocolOutputFormatter(fields); 96 | 97 | return metricFormattingBuilder.Using(formatter, false); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/App.Metrics.Formatters.InfluxDB/MetricSnapshotInfluxDBLineProtocolWriter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using App.Metrics.Formatters.InfluxDB.Internal; 10 | using App.Metrics.Serialization; 11 | 12 | namespace App.Metrics.Formatters.InfluxDB 13 | { 14 | public class MetricSnapshotInfluxDbLineProtocolWriter : IMetricSnapshotWriter 15 | { 16 | private readonly TextWriter _textWriter; 17 | private readonly Func _metricNameFormatter; 18 | private readonly LineProtocolPoints _points; 19 | 20 | public MetricSnapshotInfluxDbLineProtocolWriter( 21 | TextWriter textWriter, 22 | Func metricNameFormatter = null) 23 | { 24 | _textWriter = textWriter ?? throw new ArgumentNullException(nameof(textWriter)); 25 | _points = new LineProtocolPoints(); 26 | if (metricNameFormatter == null) 27 | { 28 | _metricNameFormatter = (metricContext, metricName) => string.IsNullOrWhiteSpace(metricContext) 29 | ? metricName 30 | : $"[{metricContext}] {metricName}"; 31 | } 32 | else 33 | { 34 | _metricNameFormatter = metricNameFormatter; 35 | } 36 | } 37 | 38 | /// 39 | public void Write(string context, string name, string field, object value, MetricTags tags, DateTime timestamp) 40 | { 41 | var measurement = _metricNameFormatter(context, name); 42 | 43 | _points.Add(new LineProtocolPointSingleValue(measurement, field, value, tags, timestamp)); 44 | } 45 | 46 | /// 47 | public void Write(string context, string name, IEnumerable columns, IEnumerable values, MetricTags tags, DateTime timestamp) 48 | { 49 | var measurement = _metricNameFormatter(context, name); 50 | 51 | _points.Add(new LineProtocolPointMultipleValues(measurement, columns, values, tags, timestamp)); 52 | } 53 | 54 | /// 55 | public void Dispose() 56 | { 57 | Dispose(true); 58 | } 59 | 60 | /// 61 | /// Writes the specific metrics and tags 62 | /// 63 | /// The metric's context 64 | /// The name of the metric 65 | /// The label for the metric value 66 | /// The value of the metrics 67 | /// The metric's tags 68 | /// The timestamp of the metrics snapshot 69 | [Obsolete("This method is not used anymore and is here only for baseline benchmarks. It will be removed in future versions")] 70 | internal void WriteLegacy(string context, string name, string field, object value, MetricTags tags, DateTime timestamp) 71 | { 72 | var measurement = _metricNameFormatter(context, name); 73 | 74 | _points.Add(new LineProtocolPointLegacy(measurement, new Dictionary { { field, value } }, tags, timestamp)); 75 | } 76 | 77 | /// 78 | /// Writes the specific metrics and tags. 79 | /// 80 | /// The metric's context 81 | /// The name of the metric 82 | /// The metric names 83 | /// The corresponding metrics values 84 | /// The metric's tags 85 | /// The timestamp of the metrics snapshot 86 | [Obsolete("This method is not used anymore and is here only for baseline benchmarks. It will be removed in future versions")] 87 | internal void WriteLegacy(string context, string name, IEnumerable columns, IEnumerable values, MetricTags tags, DateTime timestamp) 88 | { 89 | var fields = columns.Zip(values, (column, data) => new { column, data }).ToDictionary(pair => pair.column, pair => pair.data); 90 | 91 | var measurement = _metricNameFormatter(context, name); 92 | 93 | _points.Add(new LineProtocolPointLegacy(measurement, fields, tags, timestamp)); 94 | } 95 | 96 | /// 97 | /// Releases unmanaged and - optionally - managed resources. 98 | /// 99 | /// true to release both managed and unmanaged resources; false to release only unmanaged resources. 100 | protected virtual void Dispose(bool disposing) 101 | { 102 | if (disposing) 103 | { 104 | _points.Write(_textWriter); 105 | #if !NETSTANDARD1_6 106 | _textWriter?.Close(); 107 | #endif 108 | _textWriter?.Dispose(); 109 | } 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /AppMetrics.ruleset: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/Client/DefaultLineProtocolClient.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | using System.Net; 8 | using System.Net.Http; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using App.Metrics.Logging; 13 | 14 | namespace App.Metrics.Reporting.InfluxDB.Client 15 | { 16 | public class DefaultLineProtocolClient : ILineProtocolClient 17 | { 18 | private static readonly ILog Logger = LogProvider.For(); 19 | 20 | private static long _backOffTicks; 21 | private static long _failureAttempts; 22 | private static long _failuresBeforeBackoff; 23 | private static TimeSpan _backOffPeriod; 24 | 25 | private readonly HttpClient _httpClient; 26 | private readonly InfluxDbOptions _influxDbOptions; 27 | 28 | public DefaultLineProtocolClient( 29 | InfluxDbOptions influxDbOptions, 30 | HttpPolicy httpPolicy, 31 | HttpClient httpClient) 32 | { 33 | _influxDbOptions = influxDbOptions ?? throw new ArgumentNullException(nameof(influxDbOptions)); 34 | _httpClient = httpClient; 35 | _backOffPeriod = httpPolicy?.BackoffPeriod ?? throw new ArgumentNullException(nameof(httpPolicy)); 36 | _failuresBeforeBackoff = httpPolicy.FailuresBeforeBackoff; 37 | _failureAttempts = 0; 38 | } 39 | 40 | public async Task WriteAsync( 41 | Stream payload, 42 | CancellationToken cancellationToken = default) 43 | { 44 | if (payload == null) 45 | { 46 | return new LineProtocolWriteResult(true); 47 | } 48 | 49 | if (NeedToBackoff()) 50 | { 51 | return new LineProtocolWriteResult(false, "Too many failures in writing to InfluxDB, Circuit Opened"); 52 | } 53 | 54 | try 55 | { 56 | var content = new StreamContent(payload); 57 | 58 | var response = await _httpClient.PostAsync(_influxDbOptions.Endpoint, content, cancellationToken); 59 | 60 | if (response.StatusCode == HttpStatusCode.NotFound && _influxDbOptions.CreateDataBaseIfNotExists) 61 | { 62 | await TryCreateDatabase(cancellationToken); 63 | 64 | response = await _httpClient.PostAsync(_influxDbOptions.Endpoint, content, cancellationToken); 65 | } 66 | 67 | if (!response.IsSuccessStatusCode) 68 | { 69 | Interlocked.Increment(ref _failureAttempts); 70 | 71 | var errorMessage = $"Failed to write to InfluxDB - StatusCode: {response.StatusCode} Reason: {response.ReasonPhrase}"; 72 | Logger.Error(errorMessage); 73 | 74 | return new LineProtocolWriteResult(false, errorMessage); 75 | } 76 | 77 | Logger.Trace("Successful write to InfluxDB"); 78 | 79 | return new LineProtocolWriteResult(true); 80 | } 81 | catch (Exception ex) 82 | { 83 | Interlocked.Increment(ref _failureAttempts); 84 | Logger.Error(ex, "Failed to write to InfluxDB"); 85 | return new LineProtocolWriteResult(false, ex.ToString()); 86 | } 87 | } 88 | 89 | private async Task TryCreateDatabase(CancellationToken cancellationToken = default) 90 | { 91 | try 92 | { 93 | Logger.Trace($"Attempting to create InfluxDB Database '{_influxDbOptions.Database}'"); 94 | 95 | var content = new StringContent(string.Empty, Encoding.UTF8); 96 | 97 | string query = $"query?q=CREATE DATABASE \"{Uri.EscapeDataString(_influxDbOptions.Database)}\""; 98 | 99 | if (_influxDbOptions.CreateDatabaseRetentionPolicy != null && 100 | _influxDbOptions.CreateDatabaseRetentionPolicy.TryApply(out string withClause)) 101 | { 102 | query = query + " " + withClause; 103 | } 104 | 105 | var response = await _httpClient.PostAsync(query, content, cancellationToken); 106 | 107 | if (!response.IsSuccessStatusCode) 108 | { 109 | var errorMessage = $"Failed to create InfluxDB Database '{_influxDbOptions.Database}' - StatusCode: {response.StatusCode} Reason: {response.ReasonPhrase}"; 110 | Logger.Error(errorMessage); 111 | 112 | return new LineProtocolWriteResult(false, errorMessage); 113 | } 114 | 115 | Logger.Trace($"Successfully created InfluxDB Database '{_influxDbOptions.Database}'"); 116 | 117 | return new LineProtocolWriteResult(true); 118 | } 119 | catch (Exception ex) 120 | { 121 | Logger.Error(ex, $"Failed to create InfluxDB Database'{_influxDbOptions.Database}'"); 122 | return new LineProtocolWriteResult(false, ex.ToString()); 123 | } 124 | } 125 | 126 | private bool NeedToBackoff() 127 | { 128 | if (Interlocked.Read(ref _failureAttempts) < _failuresBeforeBackoff) 129 | { 130 | return false; 131 | } 132 | 133 | Logger.Error($"InfluxDB write backoff for {_backOffPeriod.Seconds} secs"); 134 | 135 | if (Interlocked.Read(ref _backOffTicks) == 0) 136 | { 137 | Interlocked.Exchange(ref _backOffTicks, DateTime.UtcNow.Add(_backOffPeriod).Ticks); 138 | } 139 | 140 | if (DateTime.UtcNow.Ticks <= Interlocked.Read(ref _backOffTicks)) 141 | { 142 | return true; 143 | } 144 | 145 | Interlocked.Exchange(ref _failureAttempts, 0); 146 | Interlocked.Exchange(ref _backOffTicks, 0); 147 | 148 | return false; 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /test/App.Metrics.Reporting.InfluxDB.Facts/LineProtocolPointTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using App.Metrics.Formatters.InfluxDB.Internal; 9 | using FluentAssertions; 10 | using Xunit; 11 | 12 | namespace App.Metrics.Reporting.InfluxDB.Facts 13 | { 14 | public class LineProtocolPointTests 15 | { 16 | [Fact] 17 | public void At_least_one_field_is_required_for_multiple_values() 18 | { 19 | var fields = new List(); 20 | var values = new List(); 21 | Action action = () => 22 | { 23 | var unused = new LineProtocolPointMultipleValues("measurement", fields, values, MetricTags.Empty); 24 | }; 25 | 26 | action.Should().Throw(); 27 | } 28 | 29 | [Fact] 30 | public void At_least_one_field_is_required_for_single_value() 31 | { 32 | var fieldName = string.Empty; 33 | var fieldValue = "value"; 34 | Action action = () => 35 | { 36 | var unused = new LineProtocolPointSingleValue("measurement", fieldName, fieldValue, MetricTags.Empty); 37 | }; 38 | 39 | action.Should().Throw(); 40 | } 41 | 42 | [Fact] 43 | public void Can_format_payload_correctly() 44 | { 45 | var textWriter = new StringWriter(); 46 | var fieldName = "key"; 47 | var fieldValue = "value"; 48 | var timestamp = new DateTime(2017, 1, 1, 1, 1, 1, DateTimeKind.Utc); 49 | var point = new LineProtocolPointSingleValue("measurement", fieldName, fieldValue, MetricTags.Empty, timestamp); 50 | 51 | point.Write(textWriter); 52 | 53 | textWriter.ToString().Should().Be("measurement key=\"value\" 1483232461000000000"); 54 | } 55 | 56 | [Fact] 57 | public void Can_format_payload_correctly_without_providing_timestamp() 58 | { 59 | var textWriter = new StringWriter(); 60 | var fieldName = "key"; 61 | var fieldValue = "value"; 62 | var point = new LineProtocolPointSingleValue("measurement", fieldName, fieldValue, MetricTags.Empty); 63 | 64 | point.Write(textWriter, false); 65 | 66 | textWriter.ToString().Should().Be("measurement key=\"value\""); 67 | } 68 | 69 | [Fact] 70 | public void Can_format_payload_with_multiple_fields_correctly() 71 | { 72 | var textWriter = new StringWriter(); 73 | var fieldsNames = new[] { "field1key", "field2key", "field3key" }; 74 | var fieldsValues = new object[] { "field1value", 2, false }; 75 | var timestamp = new DateTime(2017, 1, 1, 1, 1, 1, DateTimeKind.Utc); 76 | var point = new LineProtocolPointMultipleValues("measurement", fieldsNames, fieldsValues, MetricTags.Empty, timestamp); 77 | 78 | point.Write(textWriter); 79 | 80 | textWriter.ToString().Should().Be("measurement field1key=\"field1value\",field2key=2i,field3key=f 1483232461000000000"); 81 | } 82 | 83 | [Fact] 84 | public void Can_format_payload_with_tags_correctly() 85 | { 86 | var textWriter = new StringWriter(); 87 | var fieldName = "key"; 88 | var fieldValue = "value"; 89 | var tags = new MetricTags("tagkey", "tagvalue"); 90 | var timestamp = new DateTime(2017, 1, 1, 1, 1, 1, DateTimeKind.Utc); 91 | var point = new LineProtocolPointSingleValue("measurement", fieldName, fieldValue, tags, timestamp); 92 | 93 | point.Write(textWriter); 94 | 95 | textWriter.ToString().Should().Be("measurement,tagkey=tagvalue key=\"value\" 1483232461000000000"); 96 | } 97 | 98 | [Fact] 99 | public void Field_key_cannot_be_empty_for_single_value() 100 | { 101 | var fieldName = string.Empty; 102 | var fieldValue = "value"; 103 | Action action = () => 104 | { 105 | var unused = new LineProtocolPointSingleValue("measurement", fieldName, fieldValue, MetricTags.Empty); 106 | }; 107 | 108 | action.Should().Throw(); 109 | } 110 | 111 | [Fact] 112 | public void Field_key_cannot_be_empty_for_multiple_values() 113 | { 114 | var fieldsNames = new[] { string.Empty }; 115 | var fieldsValues = new object[] { "values" }; 116 | Action action = () => 117 | { 118 | var unused = new LineProtocolPointMultipleValues("measurement", fieldsNames, fieldsValues, MetricTags.Empty); 119 | }; 120 | 121 | action.Should().Throw(); 122 | } 123 | 124 | [Fact] 125 | public void Measurement_is_required() 126 | { 127 | var fieldName = "key"; 128 | var fieldValue = "value"; 129 | Action action = () => 130 | { 131 | var unused = new LineProtocolPointSingleValue(string.Empty, fieldName, fieldValue, MetricTags.Empty); 132 | }; 133 | 134 | action.Should().Throw(); 135 | } 136 | 137 | [Theory] 138 | [InlineData(DateTimeKind.Unspecified, false)] 139 | [InlineData(DateTimeKind.Local, false)] 140 | [InlineData(DateTimeKind.Utc, true)] 141 | public void Time_stamp_should_be_utc(DateTimeKind dateTimeKind, bool expected) 142 | { 143 | var fieldName = "key"; 144 | var fieldValue = "value"; 145 | var timestamp = new DateTime(2017, 1, 1, 1, 1, 1, dateTimeKind); 146 | 147 | Action action = () => 148 | { 149 | var unused = new LineProtocolPointSingleValue("measurement", fieldName, fieldValue, MetricTags.Empty, timestamp); 150 | }; 151 | 152 | if (!expected) 153 | { 154 | action.Should().Throw(); 155 | } 156 | else 157 | { 158 | action.Should().NotThrow(); 159 | } 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /sandbox/MetricsInfluxDBSandbox/Host.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using App.Metrics; 12 | using App.Metrics.Reporting; 13 | using Microsoft.Extensions.Configuration; 14 | using Serilog; 15 | using static System.Console; 16 | 17 | namespace MetricsInfluxDBSandbox 18 | { 19 | public static class Host 20 | { 21 | private const string InfluxDbDatabase = "metricsinfluxdbsandboxconsole"; 22 | private const string InfluxDbUri = "http://127.0.0.1:32768"; 23 | private static readonly bool FilterMetricFields = false; 24 | private static readonly Random Rnd = new Random(); 25 | 26 | private static IConfigurationRoot Configuration { get; set; } 27 | 28 | private static IMetricsRoot Metrics { get; set; } 29 | 30 | private static IRunMetricsReports Reporter { get; set; } 31 | 32 | public static async Task Main() 33 | { 34 | Init(); 35 | 36 | var cancellationTokenSource = new CancellationTokenSource(); 37 | 38 | await WriteMetricsAsync(cancellationTokenSource); 39 | 40 | PressAnyKeyToContinue(); 41 | 42 | await RunUntilEscAsync( 43 | TimeSpan.FromSeconds(5), 44 | cancellationTokenSource, 45 | async () => 46 | { 47 | Clear(); 48 | RecordMetrics(); 49 | await Task.WhenAll(Reporter.RunAllAsync(cancellationTokenSource.Token)); 50 | }); 51 | } 52 | 53 | private static void PressAnyKeyToContinue() 54 | { 55 | WriteLine(); 56 | BackgroundColor = ConsoleColor.White; 57 | ForegroundColor = ConsoleColor.Blue; 58 | WriteLine("Press any key to continue..."); 59 | ResetColor(); 60 | ReadKey(); 61 | Clear(); 62 | } 63 | 64 | private static async Task WriteMetricsAsync(CancellationTokenSource cancellationTokenSource) 65 | { 66 | foreach (var unused in Enumerable.Range(0, 10)) 67 | { 68 | RecordMetrics(); 69 | } 70 | 71 | var metricsData = Metrics.Snapshot.Get(); 72 | 73 | WriteLine("Metrics Formatters"); 74 | WriteLine("-------------------------------------------"); 75 | 76 | foreach (var formatter in Metrics.OutputMetricsFormatters) 77 | { 78 | WriteLine($"Formatter: {formatter.GetType().FullName}"); 79 | WriteLine("-------------------------------------------"); 80 | 81 | using (var stream = new MemoryStream()) 82 | { 83 | await formatter.WriteAsync(stream, metricsData, cancellationTokenSource.Token); 84 | 85 | var result = Encoding.UTF8.GetString(stream.ToArray()); 86 | 87 | WriteLine(result); 88 | } 89 | } 90 | } 91 | 92 | private static void RecordMetrics() 93 | { 94 | Metrics.Measure.Counter.Increment(ApplicationsMetricsRegistry.CounterOne); 95 | Metrics.Measure.Gauge.SetValue(ApplicationsMetricsRegistry.GaugeOne, Rnd.Next(0, 100)); 96 | Metrics.Measure.Histogram.Update(ApplicationsMetricsRegistry.HistogramOne, Rnd.Next(0, 100)); 97 | Metrics.Measure.Meter.Mark(ApplicationsMetricsRegistry.MeterOne, Rnd.Next(0, 100)); 98 | 99 | using (Metrics.Measure.Timer.Time(ApplicationsMetricsRegistry.TimerOne)) 100 | { 101 | Thread.Sleep(Rnd.Next(0, 100)); 102 | } 103 | 104 | using (Metrics.Measure.Apdex.Track(ApplicationsMetricsRegistry.ApdexOne)) 105 | { 106 | Thread.Sleep(Rnd.Next(0, 100)); 107 | } 108 | } 109 | 110 | private static void Init() 111 | { 112 | var configurationBuilder = new ConfigurationBuilder() 113 | .SetBasePath(Directory.GetCurrentDirectory()) 114 | .AddJsonFile("appsettings.json"); 115 | 116 | Configuration = configurationBuilder.Build(); 117 | 118 | Log.Logger = new LoggerConfiguration() 119 | .MinimumLevel.Verbose() 120 | .WriteTo.LiterateConsole() 121 | .WriteTo.Seq("http://localhost:5341") 122 | .CreateLogger(); 123 | 124 | var metricsConfigSection = Configuration.GetSection(nameof(MetricsOptions)); 125 | 126 | Metrics = new MetricsBuilder() 127 | .Configuration.Configure(metricsConfigSection.AsEnumerable()) 128 | .MetricFields.Configure( 129 | fields => 130 | { 131 | if (FilterMetricFields) 132 | { 133 | fields.Meter.OnlyInclude(MeterFields.Rate1M); 134 | fields.Apdex.Exclude(); 135 | fields.Counter.OnlyInclude(CounterFields.Value); 136 | fields.Gauge.Exclude(); 137 | fields.Histogram.Exclude(); 138 | } 139 | }) 140 | // Adds LineProtocolFormatter with default options, can override fields reported to influx using fields => fields... 141 | .Report.ToInfluxDb(InfluxDbUri, InfluxDbDatabase, TimeSpan.FromSeconds(5)) 142 | .Build(); 143 | 144 | Reporter = Metrics.ReportRunner; 145 | } 146 | 147 | private static async Task RunUntilEscAsync(TimeSpan delayBetweenRun, CancellationTokenSource cancellationTokenSource, Func action) 148 | { 149 | WriteLine("Press ESC to stop"); 150 | 151 | while (true) 152 | { 153 | while (!KeyAvailable) 154 | { 155 | await action(); 156 | 157 | Thread.Sleep(delayBetweenRun); 158 | } 159 | 160 | while (KeyAvailable) 161 | { 162 | var key = ReadKey(false).Key; 163 | 164 | if (key == ConsoleKey.Escape) 165 | { 166 | cancellationTokenSource.Cancel(); 167 | return; 168 | } 169 | } 170 | } 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repo has been archived, source code and issues moved to [AppMetrics](https://github.com/AppMetrics/AppMetrics) 2 | 3 | # App Metrics InfluxDB App Metrics 4 | [![Official Site](https://img.shields.io/badge/site-appmetrics-blue.svg?style=flat-square)](http://app-metrics.io/reporting/influxdb.html) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) 5 | 6 | ## What is it? 7 | 8 | This repo contains InfluxDB extension packages to [App Metrics](https://github.com/AppMetrics/AppMetrics). 9 | 10 | ## Latest Builds, Packages & Repo Stats 11 | 12 | |Branch|AppVeyor|Travis|Coverage| 13 | |------|:--------:|:--------:|:--------:| 14 | |dev|[![AppVeyor](https://img.shields.io/appveyor/ci/alhardy/influxdb/dev.svg?style=flat-square&label=appveyor%20build)](https://ci.appveyor.com/project/alhardy/influxdb/branch/dev)|[![Travis](https://img.shields.io/travis/alhardy/InfluxDB/dev.svg?style=flat-square&label=travis%20build)](https://travis-ci.org/alhardy/InfluxDB)|[![Coveralls](https://img.shields.io/coveralls/AppMetrics/InfluxDB/dev.svg?style=flat-square)](https://coveralls.io/github/AppMetrics/InfluxDB?branch=dev) 15 | |master|[![AppVeyor](https://img.shields.io/appveyor/ci/alhardy/influxdb/master.svg?style=flat-square&label=appveyor%20build)](https://ci.appveyor.com/project/alhardy/influxdb/branch/master)| [![Travis](https://img.shields.io/travis/alhardy/InfluxDB/master.svg?style=flat-square&label=travis%20build)](https://travis-ci.org/alhardy/InfluxDB)| [![Coveralls](https://img.shields.io/coveralls/AppMetrics/InfluxDB/master.svg?style=flat-square)](https://coveralls.io/github/AppMetrics/InfluxDB?branch=master)| 16 | 17 | |Package|Dev Release|PreRelease|Latest Release| 18 | |------|:--------:|:--------:|:--------:| 19 | |App.Metrics.Reporting.InfluxDB|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.Reporting.InfluxDB.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.Reporting.InfluxDB)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.Reporting.InfluxDB.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Reporting.InfluxDB/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.Reporting.InfluxDB.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Reporting.InfluxDB/) 20 | |App.Metrics.Formatters.InfluxDB|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.Formatters.InfluxDB.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.Formatters.InfluxDB)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.Formatters.InfluxDB.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Formatters.InfluxDB/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.Formatters.InfluxDB.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Formatters.InfluxDB/) 21 | 22 | #### Grafana/InfluxDB Web Monitoring 23 | 24 | ![Grafana/InfluxDB Generic Web Dashboard Demo](https://github.com/AppMetrics/AppMetrics.DocFx/blob/master/images/generic_grafana_dashboard_demo.gif) 25 | 26 | > Grab the dashboard [here](https://grafana.com/dashboards/2125) 27 | 28 | #### Grafana/InfluxDB OAuth2 Client Monitoring on a Web API 29 | 30 | ![Grafana/InfluxDB Generic OAuth2 Web Dashboard Demo](https://github.com/AppMetrics/AppMetrics.DocFx/blob/master/images/generic_grafana_oauth2_dashboard_demo.gif) 31 | 32 | > Grab the dashboard [here](https://grafana.com/dashboards/2137) 33 | 34 | ### Grafana/InfluxDB Web Application Setup 35 | 36 | - Download and install [InfluxDB](https://docs.influxdata.com/influxdb/v1.2/introduction/installation/). *Runs well on Windows using* `Bash on Windows on Ubuntu` 37 | - Create a new [InfluxDB Database](https://docs.influxdata.com/influxdb/v1.2/introduction/getting_started/). *Keep note of this for configuring the InfluxDB reporter in your web application and configuring the InfluxDB Datasource in Grafana* 38 | - Download and install [Grafana](https://grafana.com/grafana/download), then create a new [InfluxDB Datasource](http://docs.grafana.org/features/datasources/influxdb/) pointing the the Database just created and [import](http://docs.grafana.org/reference/export_import/#importing-a-dashboard) App.Metrics [web dashboard](https://grafana.com/dashboards/2125) 39 | - See the [docs](https://www.app-metrics.io/reporting/reporters/influx-data/) on configurating InfluxDB reporting. 40 | - Run your app and Grafana at visit `http://localhost:3000` 41 | 42 | **There is also a more detailed step-by-step guide [here](https://al-hardy.blog/2017/04/28/asp-net-core-monitoring-with-influxdb-grafana/)** 43 | 44 | ## How to build 45 | 46 | [AppVeyor](https://ci.appveyor.com/project/alhardy/influxdb/branch/master) and [Travis CI](https://travis-ci.org/alhardy/InfluxDB) builds are triggered on commits and PRs to `dev` and `master` branches. 47 | 48 | See the following for build arguments and running locally. 49 | 50 | |Configuration|Description|Default|Environment|Required| 51 | |------|--------|:--------:|:--------:|:--------:| 52 | |BuildConfiguration|The configuration to run the build, **Debug** or **Release** |*Release*|All|Optional| 53 | |PreReleaseSuffix|The pre-release suffix for versioning nuget package artifacts e.g. `beta`|*ci*|All|Optional| 54 | |CoverWith|**DotCover** or **OpenCover** to calculate and report code coverage, **None** to skip. When not **None**, a coverage file and html report will be generated at `./artifacts/coverage`|*OpenCover*|Windows Only|Optional| 55 | |SkipCodeInspect|**false** to run ReSharper code inspect and report results, **true** to skip. When **true**, the code inspection html report and xml output will be generated at `./artifacts/resharper-reports`|*false*|Windows Only|Optional| 56 | |BuildNumber|The build number to use for pre-release versions|*0*|All|Optional| 57 | |LinkSources|[Source link](https://github.com/ctaggart/SourceLink) support allows source code to be downloaded on demand while debugging|*true*|All|Optional| 58 | 59 | 60 | ### Windows 61 | 62 | Run `build.ps1` from the repositories root directory. 63 | 64 | ``` 65 | .\build.ps1' 66 | ``` 67 | 68 | **With Arguments** 69 | 70 | ``` 71 | .\build.ps1 --ScriptArgs '-BuildConfiguration=Release -PreReleaseSuffix=beta -CoverWith=OpenCover -SkipCodeInspect=false -BuildNumber=1' 72 | ``` 73 | 74 | ### Linux & OSX 75 | 76 | Run `build.sh` from the repositories root directory. Code Coverage reports are now supported on Linux and OSX, it will be skipped running in these environments. 77 | 78 | ``` 79 | .\build.sh' 80 | ``` 81 | 82 | **With Arguments** 83 | 84 | ``` 85 | .\build.sh --ScriptArgs '-BuildConfiguration=Release -PreReleaseSuffix=beta -BuildNumber=1' 86 | ``` 87 | 88 | ## Contributing 89 | 90 | See the [contribution guidlines](https://github.com/alhardy/AppMetrics/blob/master/CONTRIBUTING.md) in the [main repo](https://github.com/alhardy/AppMetrics) for details. 91 | 92 | ## Acknowledgements 93 | 94 | * [InfluxDB](https://www.influxdata.com/time-series-platform/influxdb/) 95 | * [DocFX](https://dotnet.github.io/docfx/) 96 | * [Fluent Assertions](http://www.fluentassertions.com/) 97 | * [XUnit](https://xunit.github.io/) 98 | * [StyleCopAnalyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) 99 | * [Cake](https://github.com/cake-build/cake) 100 | * [OpenCover](https://github.com/OpenCover/opencover) 101 | 102 | ***Thanks for providing free open source licensing*** 103 | 104 | * [Jetbrains](https://www.jetbrains.com/dotnet/) 105 | * [AppVeyor](https://www.appveyor.com/) 106 | * [Travis CI](https://travis-ci.org/) 107 | * [Coveralls](https://coveralls.io/) 108 | 109 | ## License 110 | 111 | This library is release under Apache 2.0 License ( see LICENSE ) Copyright (c) 2016 Allan Hardy 112 | -------------------------------------------------------------------------------- /InfluxDB.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2D805782-756E-4C98-B22E-F502BEE95318}" 7 | ProjectSection(SolutionItems) = preProject 8 | src\Directory.Build.props = src\Directory.Build.props 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{31A4DDB1-952E-4EED-96EF-29C669279A86}" 12 | ProjectSection(SolutionItems) = preProject 13 | .travis.yml = .travis.yml 14 | AppMetrics.ruleset = AppMetrics.ruleset 15 | appveyor.yml = appveyor.yml 16 | global.json = global.json 17 | stylecop.json = stylecop.json 18 | version.props = version.props 19 | EndProjectSection 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{DD2E4647-7125-4CE3-9BA5-B45A26113A31}" 22 | ProjectSection(SolutionItems) = preProject 23 | test\Directory.Build.props = test\Directory.Build.props 24 | EndProjectSection 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cake", "Cake", "{E1A943F7-5493-4B64-A448-3E9EEA582E20}" 27 | ProjectSection(SolutionItems) = preProject 28 | build.cake = build.cake 29 | build.ps1 = build.ps1 30 | build.sh = build.sh 31 | build_travis.sh = build_travis.sh 32 | tools\packages.config = tools\packages.config 33 | EndProjectSection 34 | EndProject 35 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Git", "Git", "{4199989A-27B6-4A6A-99C2-1AF6B01DA5EB}" 36 | ProjectSection(SolutionItems) = preProject 37 | GitReleaseManager.yaml = GitReleaseManager.yaml 38 | ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md 39 | PULL_REQUEST_TEMPLATE.md = PULL_REQUEST_TEMPLATE.md 40 | README.md = README.md 41 | EndProjectSection 42 | EndProject 43 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.Reporting.InfluxDB", "src\App.Metrics.Reporting.InfluxDB\App.Metrics.Reporting.InfluxDB.csproj", "{F07C2053-2AD0-4FA0-8265-761FCE289F59}" 44 | EndProject 45 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.Reporting.InfluxDB.Facts", "test\App.Metrics.Reporting.InfluxDB.Facts\App.Metrics.Reporting.InfluxDB.Facts.csproj", "{84005F55-AF0E-40F4-A596-129E19C418CE}" 46 | EndProject 47 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "visualization", "visualization", "{5BC3B2AF-AFC7-4C92-BD1E-D347E83B2497}" 48 | EndProject 49 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "grafana-dashboards", "grafana-dashboards", "{44E50731-4AE5-456F-A84C-CE3C4C977097}" 50 | ProjectSection(SolutionItems) = preProject 51 | visualization\grafana-dashbaords\App.Metrics.Sandbox-InfluxDB-GenericWeb.json = visualization\grafana-dashbaords\App.Metrics.Sandbox-InfluxDB-GenericWeb.json 52 | visualization\grafana-dashbaords\App.Metrics.Sandbox-InfluxDB-GenericWebOAuth2.json = visualization\grafana-dashbaords\App.Metrics.Sandbox-InfluxDB-GenericWebOAuth2.json 53 | EndProjectSection 54 | EndProject 55 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{B0E330F2-942D-47DC-940D-692E01459A06}" 56 | EndProject 57 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.Formatters.InfluxDB", "src\App.Metrics.Formatters.InfluxDB\App.Metrics.Formatters.InfluxDB.csproj", "{A1659468-1D7F-4DAB-8487-4E14DC9D92BB}" 58 | EndProject 59 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{28CE67A4-2662-43AA-B42E-B3C8F33DF31B}" 60 | ProjectSection(SolutionItems) = preProject 61 | build\common.props = build\common.props 62 | build\dependencies.props = build\dependencies.props 63 | EndProjectSection 64 | EndProject 65 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MetricsInfluxDBSandboxMvc", "sandbox\MetricsInfluxDBSandboxMvc\MetricsInfluxDBSandboxMvc.csproj", "{0D6738DF-4035-4701-B5F9-6F1FD5A7FC81}" 66 | EndProject 67 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MetricsInfluxDBSandbox", "sandbox\MetricsInfluxDBSandbox\MetricsInfluxDBSandbox.csproj", "{3D1B0A38-F839-446A-BF1B-2DB9C4C3EDEE}" 68 | EndProject 69 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{8C50C564-6978-481F-9A0D-7762F12CAC52}" 70 | EndProject 71 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.InfluxDB.Benchmarks", "benchmarks\App.Metrics.InfluxDB.Benchmarks\App.Metrics.InfluxDB.Benchmarks.csproj", "{69301D2B-01F6-4B08-8618-66B956348964}" 72 | EndProject 73 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App.Metrics.InfluxDB.Benchmarks.Runner", "benchmarks\App.Metrics.InfluxDB.Benchmarks.Runner\App.Metrics.InfluxDB.Benchmarks.Runner.csproj", "{854B3A85-C144-4B42-B177-6C5B0D250F9E}" 74 | EndProject 75 | Global 76 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 77 | Debug|Any CPU = Debug|Any CPU 78 | Release|Any CPU = Release|Any CPU 79 | EndGlobalSection 80 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 81 | {F07C2053-2AD0-4FA0-8265-761FCE289F59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 82 | {F07C2053-2AD0-4FA0-8265-761FCE289F59}.Debug|Any CPU.Build.0 = Debug|Any CPU 83 | {F07C2053-2AD0-4FA0-8265-761FCE289F59}.Release|Any CPU.ActiveCfg = Release|Any CPU 84 | {F07C2053-2AD0-4FA0-8265-761FCE289F59}.Release|Any CPU.Build.0 = Release|Any CPU 85 | {84005F55-AF0E-40F4-A596-129E19C418CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 86 | {84005F55-AF0E-40F4-A596-129E19C418CE}.Debug|Any CPU.Build.0 = Debug|Any CPU 87 | {84005F55-AF0E-40F4-A596-129E19C418CE}.Release|Any CPU.ActiveCfg = Release|Any CPU 88 | {84005F55-AF0E-40F4-A596-129E19C418CE}.Release|Any CPU.Build.0 = Release|Any CPU 89 | {A1659468-1D7F-4DAB-8487-4E14DC9D92BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 90 | {A1659468-1D7F-4DAB-8487-4E14DC9D92BB}.Debug|Any CPU.Build.0 = Debug|Any CPU 91 | {A1659468-1D7F-4DAB-8487-4E14DC9D92BB}.Release|Any CPU.ActiveCfg = Release|Any CPU 92 | {A1659468-1D7F-4DAB-8487-4E14DC9D92BB}.Release|Any CPU.Build.0 = Release|Any CPU 93 | {0D6738DF-4035-4701-B5F9-6F1FD5A7FC81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 94 | {0D6738DF-4035-4701-B5F9-6F1FD5A7FC81}.Debug|Any CPU.Build.0 = Debug|Any CPU 95 | {0D6738DF-4035-4701-B5F9-6F1FD5A7FC81}.Release|Any CPU.ActiveCfg = Release|Any CPU 96 | {0D6738DF-4035-4701-B5F9-6F1FD5A7FC81}.Release|Any CPU.Build.0 = Release|Any CPU 97 | {3D1B0A38-F839-446A-BF1B-2DB9C4C3EDEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 98 | {3D1B0A38-F839-446A-BF1B-2DB9C4C3EDEE}.Debug|Any CPU.Build.0 = Debug|Any CPU 99 | {3D1B0A38-F839-446A-BF1B-2DB9C4C3EDEE}.Release|Any CPU.ActiveCfg = Release|Any CPU 100 | {3D1B0A38-F839-446A-BF1B-2DB9C4C3EDEE}.Release|Any CPU.Build.0 = Release|Any CPU 101 | {69301D2B-01F6-4B08-8618-66B956348964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 102 | {69301D2B-01F6-4B08-8618-66B956348964}.Debug|Any CPU.Build.0 = Debug|Any CPU 103 | {69301D2B-01F6-4B08-8618-66B956348964}.Release|Any CPU.ActiveCfg = Release|Any CPU 104 | {69301D2B-01F6-4B08-8618-66B956348964}.Release|Any CPU.Build.0 = Release|Any CPU 105 | {854B3A85-C144-4B42-B177-6C5B0D250F9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 106 | {854B3A85-C144-4B42-B177-6C5B0D250F9E}.Debug|Any CPU.Build.0 = Debug|Any CPU 107 | {854B3A85-C144-4B42-B177-6C5B0D250F9E}.Release|Any CPU.ActiveCfg = Release|Any CPU 108 | {854B3A85-C144-4B42-B177-6C5B0D250F9E}.Release|Any CPU.Build.0 = Release|Any CPU 109 | EndGlobalSection 110 | GlobalSection(SolutionProperties) = preSolution 111 | HideSolutionNode = FALSE 112 | EndGlobalSection 113 | GlobalSection(NestedProjects) = preSolution 114 | {E1A943F7-5493-4B64-A448-3E9EEA582E20} = {31A4DDB1-952E-4EED-96EF-29C669279A86} 115 | {4199989A-27B6-4A6A-99C2-1AF6B01DA5EB} = {31A4DDB1-952E-4EED-96EF-29C669279A86} 116 | {F07C2053-2AD0-4FA0-8265-761FCE289F59} = {2D805782-756E-4C98-B22E-F502BEE95318} 117 | {84005F55-AF0E-40F4-A596-129E19C418CE} = {DD2E4647-7125-4CE3-9BA5-B45A26113A31} 118 | {5BC3B2AF-AFC7-4C92-BD1E-D347E83B2497} = {31A4DDB1-952E-4EED-96EF-29C669279A86} 119 | {44E50731-4AE5-456F-A84C-CE3C4C977097} = {5BC3B2AF-AFC7-4C92-BD1E-D347E83B2497} 120 | {A1659468-1D7F-4DAB-8487-4E14DC9D92BB} = {2D805782-756E-4C98-B22E-F502BEE95318} 121 | {28CE67A4-2662-43AA-B42E-B3C8F33DF31B} = {31A4DDB1-952E-4EED-96EF-29C669279A86} 122 | {0D6738DF-4035-4701-B5F9-6F1FD5A7FC81} = {B0E330F2-942D-47DC-940D-692E01459A06} 123 | {3D1B0A38-F839-446A-BF1B-2DB9C4C3EDEE} = {B0E330F2-942D-47DC-940D-692E01459A06} 124 | {69301D2B-01F6-4B08-8618-66B956348964} = {8C50C564-6978-481F-9A0D-7762F12CAC52} 125 | {854B3A85-C144-4B42-B177-6C5B0D250F9E} = {8C50C564-6978-481F-9A0D-7762F12CAC52} 126 | EndGlobalSection 127 | GlobalSection(ExtensibilityGlobals) = postSolution 128 | SolutionGuid = {B2CAE199-E006-4654-A849-86DDD09BBAA8} 129 | EndGlobalSection 130 | EndGlobal 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/App.Metrics.Reporting.InfluxDB/Builder/MetricsInfluxDbReporterBuilder.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Net.Http; 7 | using System.Net.Http.Headers; 8 | using System.Text; 9 | using App.Metrics.Builder; 10 | using App.Metrics.Formatters; 11 | using App.Metrics.Formatters.InfluxDB; 12 | using App.Metrics.Reporting.InfluxDB; 13 | using App.Metrics.Reporting.InfluxDB.Client; 14 | 15 | // ReSharper disable CheckNamespace 16 | namespace App.Metrics 17 | // ReSharper restore CheckNamespace 18 | { 19 | /// 20 | /// Builder for configuring metrics InfluxDB reporting using an 21 | /// . 22 | /// 23 | public static class MetricsInfluxDbReporterBuilder 24 | { 25 | /// 26 | /// Add the allowing metrics to be reported to InfluxDB. 27 | /// 28 | /// 29 | /// The used to configure metrics reporters. 30 | /// 31 | /// The InfluxDB reporting options to use. 32 | /// 33 | /// An that can be used to further configure App Metrics. 34 | /// 35 | public static IMetricsBuilder ToInfluxDb( 36 | this IMetricsReportingBuilder metricReporterProviderBuilder, 37 | MetricsReportingInfluxDbOptions options) 38 | { 39 | if (metricReporterProviderBuilder == null) 40 | { 41 | throw new ArgumentNullException(nameof(metricReporterProviderBuilder)); 42 | } 43 | 44 | var httpClient = CreateClient(options.InfluxDb, options.HttpPolicy); 45 | var reporter = new InfluxDbMetricsReporter(options, httpClient); 46 | 47 | return metricReporterProviderBuilder.Using(reporter); 48 | } 49 | 50 | /// 51 | /// Add the allowing metrics to be reported to InfluxDB. 52 | /// 53 | /// 54 | /// The used to configure metrics reporters. 55 | /// 56 | /// The InfluxDB reporting options to use. 57 | /// 58 | /// An that can be used to further configure App Metrics. 59 | /// 60 | public static IMetricsBuilder ToInfluxDb( 61 | this IMetricsReportingBuilder metricReporterProviderBuilder, 62 | Action setupAction) 63 | { 64 | if (metricReporterProviderBuilder == null) 65 | { 66 | throw new ArgumentNullException(nameof(metricReporterProviderBuilder)); 67 | } 68 | 69 | var options = new MetricsReportingInfluxDbOptions(); 70 | 71 | setupAction?.Invoke(options); 72 | 73 | var httpClient = CreateClient(options.InfluxDb, options.HttpPolicy); 74 | var reporter = new InfluxDbMetricsReporter(options, httpClient); 75 | 76 | return metricReporterProviderBuilder.Using(reporter); 77 | } 78 | 79 | /// 80 | /// Add the allowing metrics to be reported to InfluxDB. 81 | /// 82 | /// 83 | /// The used to configure metrics reporters. 84 | /// 85 | /// The base url where InfluxDB is hosted. 86 | /// The InfluxDB where metrics should be flushed. 87 | /// The metric fields to report as well as thier names. 88 | /// The setup action to configure the to use. 89 | /// 90 | /// An that can be used to further configure App Metrics. 91 | /// 92 | public static IMetricsBuilder ToInfluxDb( 93 | this IMetricsReportingBuilder metricReporterProviderBuilder, 94 | string url, 95 | string database, 96 | Action fieldsSetup = null, 97 | Action lineProtocolOptionsSetup = null) 98 | { 99 | if (metricReporterProviderBuilder == null) 100 | { 101 | throw new ArgumentNullException(nameof(metricReporterProviderBuilder)); 102 | } 103 | 104 | if (url == null) 105 | { 106 | throw new ArgumentNullException(nameof(url)); 107 | } 108 | 109 | if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) 110 | { 111 | throw new InvalidOperationException($"{nameof(url)} must be a valid absolute URI"); 112 | } 113 | 114 | var lineProtocolOptions = new MetricsInfluxDbLineProtocolOptions(); 115 | 116 | lineProtocolOptionsSetup?.Invoke(lineProtocolOptions); 117 | 118 | IMetricsOutputFormatter formatter; 119 | MetricFields fields = null; 120 | 121 | if (fieldsSetup == null) 122 | { 123 | formatter = new MetricsInfluxDbLineProtocolOutputFormatter(lineProtocolOptions); 124 | } 125 | else 126 | { 127 | fields = new MetricFields(); 128 | fieldsSetup.Invoke(fields); 129 | formatter = new MetricsInfluxDbLineProtocolOutputFormatter(lineProtocolOptions, fields); 130 | } 131 | 132 | var options = new MetricsReportingInfluxDbOptions 133 | { 134 | InfluxDb = 135 | { 136 | BaseUri = uri, 137 | Database = database 138 | }, 139 | MetricsOutputFormatter = formatter 140 | }; 141 | 142 | var httpClient = CreateClient(options.InfluxDb, options.HttpPolicy); 143 | var reporter = new InfluxDbMetricsReporter(options, httpClient); 144 | 145 | var builder = metricReporterProviderBuilder.Using(reporter); 146 | builder.OutputMetrics.AsInfluxDbLineProtocol(lineProtocolOptions, fields); 147 | 148 | return builder; 149 | } 150 | 151 | /// 152 | /// Add the allowing metrics to be reported to InfluxDB. 153 | /// 154 | /// 155 | /// The used to configure metrics reporters. 156 | /// 157 | /// The base url where InfluxDB is hosted. 158 | /// The InfluxDB where metrics should be flushed. 159 | /// 160 | /// The interval used if intended to schedule metrics 161 | /// reporting. 162 | /// 163 | /// The metric fields to report as well as thier names. 164 | /// The setup action to configure the to use. 165 | /// 166 | /// An that can be used to further configure App Metrics. 167 | /// 168 | public static IMetricsBuilder ToInfluxDb( 169 | this IMetricsReportingBuilder metricReporterProviderBuilder, 170 | string url, 171 | string database, 172 | TimeSpan flushInterval, 173 | Action fieldsSetup = null, 174 | Action lineProtocolOptionsSetup = null) 175 | { 176 | if (metricReporterProviderBuilder == null) 177 | { 178 | throw new ArgumentNullException(nameof(metricReporterProviderBuilder)); 179 | } 180 | 181 | if (url == null) 182 | { 183 | throw new ArgumentNullException(nameof(url)); 184 | } 185 | 186 | if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) 187 | { 188 | throw new InvalidOperationException($"{nameof(url)} must be a valid absolute URI"); 189 | } 190 | 191 | var lineProtocolOptions = new MetricsInfluxDbLineProtocolOptions(); 192 | 193 | lineProtocolOptionsSetup?.Invoke(lineProtocolOptions); 194 | 195 | IMetricsOutputFormatter formatter; 196 | MetricFields fields = null; 197 | 198 | if (fieldsSetup == null) 199 | { 200 | formatter = new MetricsInfluxDbLineProtocolOutputFormatter(lineProtocolOptions); 201 | } 202 | else 203 | { 204 | fields = new MetricFields(); 205 | fieldsSetup.Invoke(fields); 206 | formatter = new MetricsInfluxDbLineProtocolOutputFormatter(lineProtocolOptions, fields); 207 | } 208 | 209 | var options = new MetricsReportingInfluxDbOptions 210 | { 211 | FlushInterval = flushInterval, 212 | InfluxDb = 213 | { 214 | BaseUri = uri, 215 | Database = database 216 | }, 217 | MetricsOutputFormatter = formatter 218 | }; 219 | 220 | var httpClient = CreateClient(options.InfluxDb, options.HttpPolicy); 221 | var reporter = new InfluxDbMetricsReporter(options, httpClient); 222 | 223 | var builder = metricReporterProviderBuilder.Using(reporter); 224 | 225 | builder.OutputMetrics.AsInfluxDbLineProtocol(lineProtocolOptions, fields); 226 | 227 | return builder; 228 | } 229 | 230 | internal static ILineProtocolClient CreateClient( 231 | InfluxDbOptions influxDbOptions, 232 | HttpPolicy httpPolicy, 233 | HttpMessageHandler httpMessageHandler = null) 234 | { 235 | var httpClient = httpMessageHandler == null 236 | ? new HttpClient() 237 | : new HttpClient(httpMessageHandler); 238 | 239 | httpClient.BaseAddress = influxDbOptions.BaseUri; 240 | httpClient.Timeout = httpPolicy.Timeout; 241 | 242 | if (string.IsNullOrWhiteSpace(influxDbOptions.UserName) || string.IsNullOrWhiteSpace(influxDbOptions.Password)) 243 | { 244 | return new DefaultLineProtocolClient( 245 | influxDbOptions, 246 | httpPolicy, 247 | httpClient); 248 | } 249 | 250 | var byteArray = Encoding.ASCII.GetBytes($"{influxDbOptions.UserName}:{influxDbOptions.Password}"); 251 | httpClient.BaseAddress = influxDbOptions.BaseUri; 252 | httpClient.Timeout = httpPolicy.Timeout; 253 | httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); 254 | 255 | return new DefaultLineProtocolClient( 256 | influxDbOptions, 257 | httpPolicy, 258 | httpClient); 259 | } 260 | } 261 | } -------------------------------------------------------------------------------- /test/App.Metrics.Reporting.InfluxDB.Facts/DefaultLineProtocolClientTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) App Metrics Contributors. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Text; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using App.Metrics.Reporting.InfluxDB.Client; 14 | using FluentAssertions; 15 | using Moq; 16 | using Moq.Protected; 17 | using Xunit; 18 | 19 | namespace App.Metrics.Reporting.InfluxDB.Facts 20 | { 21 | public class DefaultLineProtocolClientTests 22 | { 23 | private static readonly string Payload = "test__test_counter,mtype=counter,unit=none value=1i 1483232461000000000\n"; 24 | 25 | [Fact] 26 | public async Task Can_write_payload_successfully() 27 | { 28 | // Arrange 29 | var httpMessageHandlerMock = new Mock(); 30 | httpMessageHandlerMock.Protected().Setup>( 31 | "SendAsync", 32 | ItExpr.IsAny(), 33 | ItExpr.IsAny()).Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))); 34 | 35 | var settings = new InfluxDbOptions 36 | { 37 | BaseUri = new Uri("http://localhost"), 38 | Database = "influx" 39 | }; 40 | var policy = new HttpPolicy(); 41 | var influxClient = MetricsInfluxDbReporterBuilder.CreateClient(settings, policy, httpMessageHandlerMock.Object); 42 | 43 | // Act 44 | LineProtocolWriteResult response; 45 | using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(Payload))) 46 | { 47 | response = await influxClient.WriteAsync(payload, CancellationToken.None); 48 | } 49 | 50 | // Assert 51 | response.Success.Should().BeTrue(); 52 | } 53 | 54 | [Fact] 55 | public async Task Can_write_payload_successfully_with_creds() 56 | { 57 | // Arrange 58 | var httpMessageHandlerMock = new Mock(); 59 | httpMessageHandlerMock.Protected().Setup>( 60 | "SendAsync", 61 | ItExpr.IsAny(), 62 | ItExpr.IsAny()).Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))); 63 | 64 | var settings = new InfluxDbOptions 65 | { 66 | BaseUri = new Uri("http://localhost"), 67 | Database = "influx", 68 | UserName = "admin", 69 | Password = "password" 70 | }; 71 | 72 | var policy = new HttpPolicy(); 73 | var influxClient = MetricsInfluxDbReporterBuilder.CreateClient(settings, policy, httpMessageHandlerMock.Object); 74 | 75 | // Act 76 | LineProtocolWriteResult response; 77 | using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(Payload))) 78 | { 79 | response = await influxClient.WriteAsync(payload, CancellationToken.None); 80 | } 81 | 82 | // Assert 83 | response.Success.Should().BeTrue(); 84 | } 85 | 86 | [Fact] 87 | public void Http_policy_is_required() 88 | { 89 | // Arrange 90 | Action action = () => 91 | { 92 | var settings = new InfluxDbOptions 93 | { 94 | BaseUri = new Uri("http://localhost"), 95 | Database = "influx" 96 | }; 97 | 98 | // Act 99 | var unused = new DefaultLineProtocolClient(settings, null, new HttpClient()); 100 | }; 101 | 102 | // Assert 103 | action.Should().Throw(); 104 | } 105 | 106 | [Fact] 107 | public void Influxdb_settings_are_required() 108 | { 109 | // Arrange 110 | Action action = () => 111 | { 112 | // Act 113 | var unused = new DefaultLineProtocolClient(null, new HttpPolicy(), new HttpClient()); 114 | }; 115 | 116 | // Assert 117 | action.Should().Throw(); 118 | } 119 | 120 | [Fact] 121 | public async Task Should_back_off_when_reached_max_failures() 122 | { 123 | // Arrange 124 | var httpMessageHandlerMock = new Mock(); 125 | httpMessageHandlerMock.Protected().Setup>( 126 | "SendAsync", 127 | ItExpr.IsAny(), 128 | ItExpr.IsAny()). 129 | Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.BadRequest))); 130 | var policy = new HttpPolicy { FailuresBeforeBackoff = 3, BackoffPeriod = TimeSpan.FromMinutes(1) }; 131 | var settings = new InfluxDbOptions 132 | { 133 | BaseUri = new Uri("http://localhost"), 134 | Database = "influx" 135 | }; 136 | var influxClient = MetricsInfluxDbReporterBuilder.CreateClient(settings, policy, httpMessageHandlerMock.Object); 137 | 138 | // Act 139 | foreach (var attempt in Enumerable.Range(0, 10)) 140 | { 141 | using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(Payload))) 142 | { 143 | await influxClient.WriteAsync(payload, CancellationToken.None); 144 | } 145 | 146 | // ReSharper disable ConvertIfStatementToConditionalTernaryExpression 147 | if (attempt <= policy.FailuresBeforeBackoff) 148 | { 149 | // ReSharper restore ConvertIfStatementToConditionalTernaryExpression 150 | // Assert 151 | httpMessageHandlerMock.Protected().Verify>( 152 | "SendAsync", 153 | Times.AtLeastOnce(), 154 | ItExpr.IsAny(), 155 | ItExpr.IsAny()); 156 | } 157 | else 158 | { 159 | // Assert 160 | httpMessageHandlerMock.Protected().Verify>( 161 | "SendAsync", 162 | Times.AtMost(6), // TODO: Starting failing when running all tests with 2.0.0 upgrade, should be 3 163 | ItExpr.IsAny(), 164 | ItExpr.IsAny()); 165 | } 166 | } 167 | } 168 | 169 | [Fact] 170 | public async Task Should_back_off_when_reached_max_failures_then_retry_after_backoff_period() 171 | { 172 | var httpMessageHandlerMock = new Mock(); 173 | httpMessageHandlerMock.Protected().Setup>( 174 | "SendAsync", 175 | ItExpr.IsAny(), 176 | ItExpr.IsAny()). 177 | Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.BadRequest))); 178 | var policy = new HttpPolicy { FailuresBeforeBackoff = 3, BackoffPeriod = TimeSpan.FromSeconds(1) }; 179 | var settings = new InfluxDbOptions 180 | { 181 | BaseUri = new Uri("http://localhost"), 182 | Database = "influx" 183 | }; 184 | var influxClient = MetricsInfluxDbReporterBuilder.CreateClient(settings, policy, httpMessageHandlerMock.Object); 185 | 186 | foreach (var attempt in Enumerable.Range(0, 10)) 187 | { 188 | using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(Payload))) 189 | { 190 | await influxClient.WriteAsync(payload, CancellationToken.None); 191 | } 192 | 193 | if (attempt <= policy.FailuresBeforeBackoff) 194 | { 195 | httpMessageHandlerMock.Protected().Verify>( 196 | "SendAsync", 197 | Times.AtLeastOnce(), 198 | ItExpr.IsAny(), 199 | ItExpr.IsAny()); 200 | } 201 | else 202 | { 203 | httpMessageHandlerMock.Protected().Verify>( 204 | "SendAsync", 205 | Times.AtMost(3), 206 | ItExpr.IsAny(), 207 | ItExpr.IsAny()); 208 | } 209 | } 210 | 211 | await Task.Delay(policy.BackoffPeriod); 212 | 213 | httpMessageHandlerMock = new Mock(); 214 | httpMessageHandlerMock.Protected().Setup>( 215 | "SendAsync", 216 | ItExpr.IsAny(), 217 | ItExpr.IsAny()).Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))); 218 | 219 | influxClient = MetricsInfluxDbReporterBuilder.CreateClient(settings, policy, httpMessageHandlerMock.Object); 220 | 221 | LineProtocolWriteResult response; 222 | using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(Payload))) 223 | { 224 | response = await influxClient.WriteAsync(payload, CancellationToken.None); 225 | } 226 | 227 | response.Success.Should().BeTrue(); 228 | } 229 | 230 | [Fact] 231 | public async Task Can_create_database_without_retention_policy() 232 | { 233 | // Arrange 234 | var httpMessageHandlerMock = new Mock(); 235 | 236 | httpMessageHandlerMock.Protected().Setup>( 237 | "SendAsync", 238 | ItExpr.IsAny(), 239 | ItExpr.IsAny()).Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound))); 240 | 241 | var settings = new InfluxDbOptions 242 | { 243 | BaseUri = new Uri("http://localhost"), 244 | Database = "influx", 245 | CreateDataBaseIfNotExists = true 246 | }; 247 | 248 | var policy = new HttpPolicy(); 249 | var influxClient = MetricsInfluxDbReporterBuilder.CreateClient(settings, policy, httpMessageHandlerMock.Object); 250 | 251 | // Act 252 | using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(Payload))) 253 | { 254 | await influxClient.WriteAsync(payload, CancellationToken.None); 255 | } 256 | 257 | httpMessageHandlerMock.Protected().Verify>( 258 | "SendAsync", 259 | Times.Exactly(1), 260 | ItExpr.Is(message => message.RequestUri.ToString().EndsWith("CREATE DATABASE \"influx\"")), 261 | ItExpr.IsAny()); 262 | } 263 | 264 | [Fact] 265 | public async Task Can_create_database_with_retention_policy() 266 | { 267 | // Arrange 268 | var httpMessageHandlerMock = new Mock(); 269 | 270 | httpMessageHandlerMock.Protected().Setup>( 271 | "SendAsync", 272 | ItExpr.IsAny(), 273 | ItExpr.IsAny()).Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound))); 274 | 275 | var settings = new InfluxDbOptions 276 | { 277 | BaseUri = new Uri("http://localhost"), 278 | Database = "influx", 279 | CreateDataBaseIfNotExists = true, 280 | CreateDatabaseRetentionPolicy = new RetentionPolicyOptions { Duration = TimeSpan.FromMinutes(70) } 281 | }; 282 | 283 | var policy = new HttpPolicy(); 284 | var influxClient = MetricsInfluxDbReporterBuilder.CreateClient(settings, policy, httpMessageHandlerMock.Object); 285 | 286 | // Act 287 | using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(Payload))) 288 | { 289 | await influxClient.WriteAsync(payload, CancellationToken.None); 290 | } 291 | 292 | httpMessageHandlerMock.Protected().Verify>( 293 | "SendAsync", 294 | Times.Exactly(1), 295 | ItExpr.Is(message => message.RequestUri.ToString().EndsWith("CREATE DATABASE \"influx\" WITH DURATION 70m")), 296 | ItExpr.IsAny()); 297 | } 298 | } 299 | } --------------------------------------------------------------------------------