diff --git a/src/Cassandra.IntegrationTests/OpenTelemetry/OpenTelemetryTests.cs b/src/Cassandra.IntegrationTests/OpenTelemetry/OpenTelemetryTests.cs index 60463673c..8e8d61147 100644 --- a/src/Cassandra.IntegrationTests/OpenTelemetry/OpenTelemetryTests.cs +++ b/src/Cassandra.IntegrationTests/OpenTelemetry/OpenTelemetryTests.cs @@ -31,6 +31,7 @@ using Cassandra.Tests.Mapping.Pocos; using NUnit.Framework; using OpenTelemetry; +using OpenTelemetry.Metrics; using OpenTelemetry.Trace; namespace Cassandra.IntegrationTests.OpenTelemetry @@ -45,12 +46,15 @@ public class OpenTelemetryTests : SharedClusterTest private const string NodeActivityName = "Node_Request"; private readonly CopyOnReadList _exportedActivities = new CopyOnReadList(); + private readonly CopyOnReadList _exportedMetrics = new CopyOnReadList(); private readonly ActivitySource _internalActivitySource = new ActivitySource("testeActivitySource"); private DateTime _testStartDateTime; - private TracerProvider _sdk; + private MeterProvider _metricsSdk; + + private TracerProvider _tracesSdk; public OpenTelemetryTests() : base(3) { @@ -60,7 +64,12 @@ public OpenTelemetryTests() : base(3) [SetUp] public void SetUp() { - _sdk = Sdk.CreateTracerProviderBuilder() + _metricsSdk = Sdk.CreateMeterProviderBuilder() + .AddMeter(OpenTelemetrySourceName) + .AddMeter(_internalActivitySource.Name) + .AddInMemoryExporter(_exportedMetrics) + .Build(); + _tracesSdk = Sdk.CreateTracerProviderBuilder() .AddSource(OpenTelemetrySourceName) .AddSource(_internalActivitySource.Name) .AddInMemoryExporter(_exportedActivities) @@ -71,8 +80,10 @@ public void SetUp() [TearDown] public void Teardown() { - _sdk.Dispose(); + _metricsSdk.Dispose(); + _tracesSdk.Dispose(); _exportedActivities.Clear(); + _exportedMetrics.Clear(); } [Category(TestCategory.RealCluster)] @@ -82,7 +93,7 @@ public void AddOpenTelemetry_WithKeyspaceAvailable_DbOperationAndDbNameAreInclud var keyspace = "system"; var expectedActivityName = $"{SessionActivityName}({nameof(SimpleStatement)}) {keyspace}"; var expectedDbNameAttribute = keyspace; - var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation()); + var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation().WithOpenTelemetryMetrics()); var session = cluster.Connect(); var statement = new SimpleStatement("SELECT key FROM system.local"); @@ -91,19 +102,21 @@ public void AddOpenTelemetry_WithKeyspaceAvailable_DbOperationAndDbNameAreInclud RetryUntilActivities(_testStartDateTime, expectedActivityName, 1); - var activity = GetActivities(_testStartDateTime).First(x => x.DisplayName == expectedActivityName); + var activity = GetActivities(_testStartDateTime).First(x => x.DisplayName == expectedActivityName); ValidateSessionActivityAttributes(activity, typeof(SimpleStatement)); Assert.AreEqual(expectedActivityName, activity.DisplayName); Assert.AreEqual(expectedDbNameAttribute, activity.Tags.First(kvp => kvp.Key == "db.namespace").Value); + + ValidateMetrics(); } [Category(TestCategory.RealCluster)] [Test] public void AddOpenTelemetry_WithoutKeyspace_DbNameIsNotIncluded() { - var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation()); + var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation().WithOpenTelemetryMetrics()); var session = cluster.Connect(); var statement = new SimpleStatement("SELECT key FROM system.local"); @@ -117,13 +130,15 @@ public void AddOpenTelemetry_WithoutKeyspace_DbNameIsNotIncluded() Assert.AreEqual($"{SessionActivityName}({nameof(SimpleStatement)})", activity.DisplayName); Assert.IsNull(activity.Tags.FirstOrDefault(kvp => kvp.Key == "db.namespace").Value); + + ValidateMetrics(); } [Category(TestCategory.RealCluster)] [Test] public void AddOpenTelemetry_WithDefaultOptions_DbStatementIsNotIncludedAsAttribute() { - var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation()); + var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation().WithOpenTelemetryMetrics()); var session = cluster.Connect(); var statement = new SimpleStatement("SELECT key FROM system.local"); @@ -136,6 +151,8 @@ public void AddOpenTelemetry_WithDefaultOptions_DbStatementIsNotIncludedAsAttrib ValidateSessionActivityAttributes(activity, typeof(SimpleStatement)); Assert.IsNull(activity.Tags.FirstOrDefault(kvp => kvp.Key == "db.query.text").Value); + + ValidateMetrics(); } [Category(TestCategory.RealCluster)] @@ -144,7 +161,7 @@ public void AddOpenTelemetry_WithIncludeDatabaseStatementOption_DbStatementIsInc { var expectedDbStatement = "SELECT key FROM system.local"; - var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation(options => options.IncludeDatabaseStatement = true)); + var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation(options => options.IncludeDatabaseStatement = true).WithOpenTelemetryMetrics()); var session = cluster.Connect(); var statement = new SimpleStatement("SELECT key FROM system.local"); @@ -157,6 +174,8 @@ public void AddOpenTelemetry_WithIncludeDatabaseStatementOption_DbStatementIsInc ValidateSessionActivityAttributes(activity, typeof(SimpleStatement)); Assert.AreEqual(expectedDbStatement, activity.Tags.First(kvp => kvp.Key == "db.query.text").Value); + + ValidateMetrics(); } [Category(TestCategory.RealCluster)] @@ -164,7 +183,7 @@ public void AddOpenTelemetry_WithIncludeDatabaseStatementOption_DbStatementIsInc public async Task AddOpenTelemetry_ExecuteAndExecuteAsync_SessionRequestIsParentOfNodeRequest() { var localDateTime = DateTime.UtcNow; - var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation(opt => opt.IncludeDatabaseStatement = true)); + var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation(opt => opt.IncludeDatabaseStatement = true).WithOpenTelemetryMetrics()); var session = cluster.Connect(); var statement = new SimpleStatement("SELECT key FROM system.local"); @@ -204,6 +223,8 @@ await session.ExecuteAsync(statement).ContinueWith(t => ValidateNodeActivityAttributes(syncNodeActivity, typeof(SimpleStatement)); Assert.Contains(new KeyValuePair("db.query.text", "SELECT key FROM system.local"), syncSessionActivity.Tags.ToArray()); Assert.Contains(new KeyValuePair("db.query.text", "SELECT key FROM system.local"), syncNodeActivity.Tags.ToArray()); + + ValidateMetrics(); } [Category(TestCategory.RealCluster)] @@ -215,6 +236,7 @@ public async Task AddOpenTelemetry_MapperAndMapperAsync_SessionRequestIsParentOf var cluster = GetNewTemporaryCluster(b => b .WithOpenTelemetryInstrumentation(opt => opt.IncludeDatabaseStatement = true) + .WithOpenTelemetryMetrics() .WithExecutionProfiles(opts => opts .WithProfile(testProfile, profile => profile .WithConsistencyLevel(ConsistencyLevel.One)))); @@ -283,6 +305,8 @@ await mapper.InsertIfNotExistsAsync(songOne, testProfile, true, null) "db.query.text", $"INSERT INTO {keyspace}.song (Artist, Id, ReleaseDate, Title) VALUES (?, ?, ?, ?) IF NOT EXISTS"), syncSessionActivity.Tags.ToArray()); Assert.Contains(new KeyValuePair( "db.query.text", $"INSERT INTO {keyspace}.song (Artist, Id, ReleaseDate, Title) VALUES (?, ?, ?, ?) IF NOT EXISTS"), syncNodeActivity.Tags.ToArray()); + + ValidateMetrics(); } [Category(TestCategory.RealCluster)] @@ -292,7 +316,8 @@ public void AddOpenTelemetry_Linq_SessionRequestIsParentOfNodeRequest() var keyspace = TestUtils.GetUniqueKeyspaceName().ToLowerInvariant(); var cluster = GetNewTemporaryCluster(b => b - .WithOpenTelemetryInstrumentation()); + .WithOpenTelemetryInstrumentation() + .WithOpenTelemetryMetrics()); var session = cluster.Connect(); @@ -331,6 +356,8 @@ public void AddOpenTelemetry_Linq_SessionRequestIsParentOfNodeRequest() ValidateSessionActivityAttributes(syncSessionActivity, typeof(BoundStatement)); ValidateNodeActivityAttributes(syncNodeActivity, typeof(BoundStatement)); + + ValidateMetrics(); } [Test] @@ -343,7 +370,7 @@ public void AddOpenTelemetry_WhenMethodIsInvokedAfterQuery_TraceIdIsTheSameThrou using (var _ = _internalActivitySource.StartActivity(firstMethodName, ActivityKind.Internal)) { - var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation()); + var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation().WithOpenTelemetryMetrics()); var session = cluster.Connect(); session.CreateKeyspaceIfNotExists(keyspace); @@ -354,6 +381,8 @@ public void AddOpenTelemetry_WhenMethodIsInvokedAfterQuery_TraceIdIsTheSameThrou MapperMethod(session); SecondMethod(secondMethodName); + + ValidateMetrics(); } Assert_WhenMethodIsInvokedAfterQuery_TraceIdIsTheSameThroughRequest(keyspace); @@ -369,7 +398,7 @@ public async Task AddOpenTelemetry_WhenMethodIsInvokedAfterQuery_TraceIdIsTheSam var keyspace = TestUtils.GetUniqueKeyspaceName().ToLowerInvariant(); using (var _ = _internalActivitySource.StartActivity(firstMethodName, ActivityKind.Internal)) { - var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation()); + var cluster = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation().WithOpenTelemetryMetrics()); var session = await cluster.ConnectAsync().ConfigureAwait(false); session.CreateKeyspaceIfNotExists(keyspace); @@ -380,6 +409,8 @@ public async Task AddOpenTelemetry_WhenMethodIsInvokedAfterQuery_TraceIdIsTheSam await MapperMethodAsync(session).ConfigureAwait(false); SecondMethod(secondMethodName); + + ValidateMetrics(); } Assert_WhenMethodIsInvokedAfterQuery_TraceIdIsTheSameThroughRequest(keyspace); @@ -404,7 +435,8 @@ public void AddOpenTelemetry_RetryOnNextHost_ShouldProduceOneErrorAndOneValidSpa .SetReadTimeoutMillis(5000)) .WithLoadBalancingPolicy(loadBalancingPolicy) .WithRetryPolicy(TryNextHostRetryPolicy.Instance) - .WithOpenTelemetryInstrumentation(); + .WithOpenTelemetryInstrumentation() + .WithOpenTelemetryMetrics(); using (var cluster = builder.Build()) { @@ -434,6 +466,8 @@ public void AddOpenTelemetry_RetryOnNextHost_ShouldProduceOneErrorAndOneValidSpa Assert.AreEqual(sessionActivity.SpanId, errorNodeActivity.ParentSpanId); Assert.AreEqual(expectedErrorDescription, errorNodeActivity.StatusDescription); Assert.True(validNodeActivity.StartTimeUtc > errorNodeActivity.StartTimeUtc); + + ValidateMetrics(); } } } @@ -454,7 +488,8 @@ public void AddOpenTelemetry_WithSpeculativeExecutionOnSameNode_ShouldProduceVal .SetReadTimeoutMillis(5000)) .WithLoadBalancingPolicy(loadBalancingPolicy) .WithSpeculativeExecutionPolicy(new ConstantSpeculativeExecutionPolicy(500, 2)) - .WithOpenTelemetryInstrumentation(); + .WithOpenTelemetryInstrumentation() + .WithOpenTelemetryMetrics(); using (var cluster = builder.Build()) { @@ -495,6 +530,8 @@ public void AddOpenTelemetry_WithSpeculativeExecutionOnSameNode_ShouldProduceVal Assert.AreEqual(2, abortedActivities.Count()); Assert.True(successActivities.First().StartTimeUtc > abortedActivities.ElementAt(0).StartTimeUtc); Assert.True(successActivities.First().StartTimeUtc > abortedActivities.ElementAt(1).StartTimeUtc); + + ValidateMetrics(); } } } @@ -505,7 +542,7 @@ public void AddOpenTelemetry_WithPaginationOnQuery_ShouldMultipleSpansForTheSame { using (var activity = _internalActivitySource.StartActivity("Paging", ActivityKind.Internal)) { - var session = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation()).Connect(); + var session = GetNewTemporaryCluster(b => b.WithOpenTelemetryInstrumentation().WithOpenTelemetryMetrics()).Connect(); session.CreateKeyspaceIfNotExists(KeyspaceName, null, false); @@ -537,6 +574,8 @@ public void AddOpenTelemetry_WithPaginationOnQuery_ShouldMultipleSpansForTheSame Assert.AreEqual(firstActivity.TraceId, lastActivity.TraceId); Assert.AreNotEqual(firstActivity.SpanId, lastActivity.SpanId); + + ValidateMetrics(); } } @@ -547,7 +586,8 @@ public async Task AddOpenTelemetry_Batch_ExpectedStatement() var keyspace = TestUtils.GetUniqueKeyspaceName().ToLowerInvariant(); var cluster = GetNewTemporaryCluster(b => b - .WithOpenTelemetryInstrumentation(opt => opt.IncludeDatabaseStatement = true)); + .WithOpenTelemetryInstrumentation(opt => opt.IncludeDatabaseStatement = true) + .WithOpenTelemetryMetrics()); var session = cluster.Connect(); @@ -603,6 +643,8 @@ public async Task AddOpenTelemetry_Batch_ExpectedStatement() $"INSERT INTO {keyspace}.song (Artist, Id, ReleaseDate, Title) VALUES (?, ?, ?, ?)"; Assert.Contains(new KeyValuePair("db.query.text", expectedStatement), syncSessionActivity.Tags.ToArray()); Assert.Contains(new KeyValuePair("db.query.text", expectedStatement), syncNodeActivity.Tags.ToArray()); + + ValidateMetrics(); } [Category(TestCategory.RealCluster)] @@ -615,6 +657,7 @@ public async Task AddOpenTelemetry_Mapper_ExpectedPrepareActivities(bool prepare var cluster = GetNewTemporaryCluster(b => b .WithOpenTelemetryInstrumentation(opt => opt.IncludeDatabaseStatement = true) + .WithOpenTelemetryMetrics() .WithQueryOptions(new QueryOptions().SetPrepareOnAllHosts(prepareOnAllHosts))); var session = cluster.Connect(); @@ -714,6 +757,8 @@ public async Task AddOpenTelemetry_Mapper_ExpectedPrepareActivities(bool prepare : typeof(BoundStatement)); } } + + ValidateMetrics(); } private void Assert_WhenMethodIsInvokedAfterQuery_TraceIdIsTheSameThroughRequest(string keyspace) @@ -859,6 +904,12 @@ PRIMARY KEY (id) session.Execute(createTableQuery); } + private void ValidateMetrics() + { + var metrics = _exportedMetrics.Where(m => m.Name.StartsWith("cassandra.")).ToList(); + Assert.True(metrics.Count > 0, "Count = {0}", metrics.Count); + } + private IEnumerable GetActivities(DateTime from) { return _exportedActivities.Where(x => x.StartTimeUtc >= from); diff --git a/src/Extensions/Cassandra.OpenTelemetry/BuilderExtensions.cs b/src/Extensions/Cassandra.OpenTelemetry/BuilderExtensions.cs index 1e829b3f1..7b43137a9 100644 --- a/src/Extensions/Cassandra.OpenTelemetry/BuilderExtensions.cs +++ b/src/Extensions/Cassandra.OpenTelemetry/BuilderExtensions.cs @@ -15,6 +15,8 @@ // using System; +using Cassandra.Metrics; +using Cassandra.OpenTelemetry.Metrics; namespace Cassandra.OpenTelemetry { @@ -48,5 +50,34 @@ public static Builder WithOpenTelemetryInstrumentation(this Builder builder, Act return builder.WithRequestTracker(new OpenTelemetryRequestTracker(instrumentationOptions)); } + + /// + /// Adds OpenTelemetry metrics to the . + /// + /// The . + /// Returning Cassandra builder. + public static Builder WithOpenTelemetryMetrics(this Builder builder) + { + return builder.WithOpenTelemetryMetrics(null); + } + + /// + /// Adds OpenTelemetry instrumentation to the . + /// + /// The . + /// An action with to be + /// included in the instrumentation. + /// + public static Builder WithOpenTelemetryMetrics(this Builder builder, Action options) + { + var metricsOptions = new DriverMetricsOptions(); + + metricsOptions.SetEnabledNodeMetrics(NodeMetric.AllNodeMetrics); + metricsOptions.SetEnabledSessionMetrics(SessionMetric.AllSessionMetrics); + + options?.Invoke(metricsOptions); + + return builder.WithMetrics(new CassandraDriverMetricsProvider(), metricsOptions); + } } } diff --git a/src/Extensions/Cassandra.OpenTelemetry/Metrics/CassandraDriverMetricsProvider.cs b/src/Extensions/Cassandra.OpenTelemetry/Metrics/CassandraDriverMetricsProvider.cs new file mode 100644 index 000000000..bcd9cc973 --- /dev/null +++ b/src/Extensions/Cassandra.OpenTelemetry/Metrics/CassandraDriverMetricsProvider.cs @@ -0,0 +1,56 @@ +// +// Copyright (C) DataStax Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using Cassandra.Metrics; +using Cassandra.Metrics.Abstractions; + +namespace Cassandra.OpenTelemetry.Metrics +{ + internal sealed class CassandraDriverMetricsProvider : IDriverMetricsProvider + { + private const string Prefix = "cassandra"; + + public IDriverTimer Timer(string bucket, IMetric metric) + { + return new DriverTimer($"{Prefix}.{metric.Name}"); + } + + public IDriverMeter Meter(string bucket, IMetric metric) + { + return new DriverMeter($"{Prefix}.{metric.Name}"); + } + + public IDriverCounter Counter(string bucket, IMetric metric) + { + return new DriverCounter($"{Prefix}.{metric.Name}"); + } + + public IDriverGauge Gauge(string bucket, IMetric metric, Func valueProvider) + { + return new DriverGauge($"{Prefix}.{metric.Name}", Value(valueProvider)); + } + + public void ShutdownMetricsBucket(string bucket) + { + } + + private static Func Value(Func valueProvider) + { + return () => valueProvider.Invoke() ?? 0; + } + } +} diff --git a/src/Extensions/Cassandra.OpenTelemetry/Metrics/CassandraMeter.cs b/src/Extensions/Cassandra.OpenTelemetry/Metrics/CassandraMeter.cs new file mode 100644 index 000000000..3000f718f --- /dev/null +++ b/src/Extensions/Cassandra.OpenTelemetry/Metrics/CassandraMeter.cs @@ -0,0 +1,25 @@ +// +// Copyright (C) DataStax Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics.Metrics; + +namespace Cassandra.OpenTelemetry.Metrics +{ + internal static class CassandraMeter + { + public static Meter Instance { get; } = new Meter(CassandraActivitySourceHelper.ActivitySourceName, CassandraActivitySourceHelper.Version); + } +} diff --git a/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverCounter.cs b/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverCounter.cs new file mode 100644 index 000000000..e5209e6b3 --- /dev/null +++ b/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverCounter.cs @@ -0,0 +1,41 @@ +// +// Copyright (C) DataStax Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics.Metrics; +using Cassandra.Metrics.Abstractions; + +namespace Cassandra.OpenTelemetry.Metrics +{ + internal sealed class DriverCounter : IDriverCounter + { + private readonly Counter _counter; + + public DriverCounter(string name) + { + _counter = CassandraMeter.Instance.CreateCounter(name); + } + + public void Increment() + { + _counter.Add(1); + } + + public void Increment(long value) + { + _counter.Add(value); + } + } +} diff --git a/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverGauge.cs b/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverGauge.cs new file mode 100644 index 000000000..8234c1e8d --- /dev/null +++ b/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverGauge.cs @@ -0,0 +1,34 @@ +// +// Copyright (C) DataStax Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Diagnostics.Metrics; +using Cassandra.Metrics.Abstractions; + +namespace Cassandra.OpenTelemetry.Metrics +{ + internal sealed class DriverGauge : IDriverGauge + { +#pragma warning disable IDE0052 // Remove unread private members + private readonly ObservableGauge _gauge; +#pragma warning restore IDE0052 // Remove unread private members + + public DriverGauge(string name, Func value) + { + _gauge = CassandraMeter.Instance.CreateObservableGauge(name, value); + } + } +} diff --git a/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverMeter.cs b/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverMeter.cs new file mode 100644 index 000000000..bbd432c85 --- /dev/null +++ b/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverMeter.cs @@ -0,0 +1,36 @@ +// +// Copyright (C) DataStax Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics.Metrics; +using Cassandra.Metrics.Abstractions; + +namespace Cassandra.OpenTelemetry.Metrics +{ + internal sealed class DriverMeter : IDriverMeter + { + private readonly Histogram _meter; + + public DriverMeter(string name) + { + _meter = CassandraMeter.Instance.CreateHistogram(name); + } + + public void Mark(long amount) + { + _meter.Record(amount); + } + } +} diff --git a/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverTimer.cs b/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverTimer.cs new file mode 100644 index 000000000..0b92e3bcf --- /dev/null +++ b/src/Extensions/Cassandra.OpenTelemetry/Metrics/DriverTimer.cs @@ -0,0 +1,38 @@ +// +// Copyright (C) DataStax Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics.Metrics; +using Cassandra.Metrics.Abstractions; + +namespace Cassandra.OpenTelemetry.Metrics +{ + internal sealed class DriverTimer : IDriverTimer + { + private readonly Histogram _timer; + + public DriverTimer(string name) + { + _timer = CassandraMeter.Instance.CreateHistogram(name, "ms"); + } + + public void Record(long elapsedNanoseconds) + { + var elapsedMilliseconds = elapsedNanoseconds * 0.000001; + + _timer.Record(elapsedMilliseconds); + } + } +}