Skip to content

Commit 58214f5

Browse files
committed
Add metrics instrumentation
1 parent 1f3fbd6 commit 58214f5

17 files changed

Lines changed: 678 additions & 35 deletions

src/Autofac/Core/Activators/Reflection/AutowiringPropertyInjector.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

44
using System.Reflection;
5+
using Autofac.Diagnostics;
56
using Autofac.Util;
67

78
namespace Autofac.Core.Activators.Reflection;
@@ -49,11 +50,18 @@ public static void InjectProperties(IComponentContext context, object instance,
4950
}
5051

5152
var resolveParameters = parameters as Parameter[] ?? parameters.ToArray();
53+
var recordMetrics = AutofacMetrics.MetricsEnabled;
54+
ValueStopwatch instrumentationTimer = default;
55+
if (recordMetrics)
56+
{
57+
instrumentationTimer = ValueStopwatch.StartNew();
58+
}
5259

5360
var injectablePropertiesCache = ReflectionCacheSet.Shared.Internal.AutowiringInjectableProperties;
5461

5562
var instanceType = instance.GetType();
5663
var injectableProperties = injectablePropertiesCache.GetOrAdd(instanceType, type => GetInjectableProperties(type).ToList());
64+
var injectedProperties = 0;
5765

5866
for (var index = 0; index < injectableProperties.Count; index++)
5967
{
@@ -77,6 +85,7 @@ public static void InjectProperties(IComponentContext context, object instance,
7785
{
7886
var setter = ReflectionCacheSet.Shared.Internal.AutowiringPropertySetters.GetOrAdd(property, MakeFastPropertySetter);
7987
setter(instance, valueProvider!());
88+
injectedProperties++;
8089
continue;
8190
}
8291

@@ -86,8 +95,18 @@ public static void InjectProperties(IComponentContext context, object instance,
8695
{
8796
var setter = ReflectionCacheSet.Shared.Internal.AutowiringPropertySetters.GetOrAdd(property, MakeFastPropertySetter);
8897
setter(instance, propertyValue);
98+
injectedProperties++;
8999
}
90100
}
101+
102+
if (recordMetrics)
103+
{
104+
AutofacMetrics.RecordPropertyInjection(
105+
instanceType,
106+
injectableProperties.Count,
107+
injectedProperties,
108+
instrumentationTimer.ElapsedTicks);
109+
}
91110
}
92111

93112
private static IEnumerable<PropertyInfo> GetInjectableProperties(Type instanceType)

src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Reflection;
66
using System.Text;
77
using Autofac.Core.Resolving.Pipeline;
8+
using Autofac.Diagnostics;
89
using Autofac.Util;
910

1011
namespace Autofac.Core.Activators.Reflection;
@@ -218,12 +219,24 @@ private void UseSingleConstructorActivation(IResolvePipelineBuilder pipelineBuil
218219
{
219220
CheckNotDisposed();
220221

222+
var recordMetrics = AutofacMetrics.MetricsEnabled;
223+
ValueStopwatch instrumentationTimer = default;
224+
if (recordMetrics)
225+
{
226+
instrumentationTimer = ValueStopwatch.StartNew();
227+
}
228+
221229
var instance = boundConstructor.Instantiate();
222230

223231
InjectProperties(instance, context, boundConstructor, GetAllParameters(context.Parameters));
224232

225233
context.Instance = instance;
226234

235+
if (recordMetrics)
236+
{
237+
AutofacMetrics.RecordReflectionActivation(_implementationType, instrumentationTimer.ElapsedTicks);
238+
}
239+
227240
next(context);
228241
});
229242
}
@@ -233,6 +246,13 @@ private void UseSingleConstructorActivation(IResolvePipelineBuilder pipelineBuil
233246
{
234247
CheckNotDisposed();
235248

249+
var recordMetrics = AutofacMetrics.MetricsEnabled;
250+
ValueStopwatch instrumentationTimer = default;
251+
if (recordMetrics)
252+
{
253+
instrumentationTimer = ValueStopwatch.StartNew();
254+
}
255+
236256
var prioritizedParameters = GetAllParameters(context.Parameters);
237257

238258
var bound = singleConstructor.Bind(prioritizedParameters, context);
@@ -248,6 +268,11 @@ private void UseSingleConstructorActivation(IResolvePipelineBuilder pipelineBuil
248268

249269
context.Instance = instance;
250270

271+
if (recordMetrics)
272+
{
273+
AutofacMetrics.RecordReflectionActivation(_implementationType, instrumentationTimer.ElapsedTicks);
274+
}
275+
251276
next(context);
252277
});
253278
}
@@ -277,6 +302,13 @@ private object ActivateInstance(IComponentContext context, IEnumerable<Parameter
277302

278303
CheckNotDisposed();
279304

305+
var recordMetrics = AutofacMetrics.MetricsEnabled;
306+
ValueStopwatch instrumentationTimer = default;
307+
if (recordMetrics)
308+
{
309+
instrumentationTimer = ValueStopwatch.StartNew();
310+
}
311+
280312
var prioritizedParameters = GetAllParameters(parameters);
281313

282314
var allBindings = GetAllBindings(_constructorBinders!, context, prioritizedParameters);
@@ -295,6 +327,11 @@ private object ActivateInstance(IComponentContext context, IEnumerable<Parameter
295327

296328
InjectProperties(instance, context, selectedBinding, prioritizedParameters);
297329

330+
if (recordMetrics)
331+
{
332+
AutofacMetrics.RecordReflectionActivation(_implementationType, instrumentationTimer.ElapsedTicks);
333+
}
334+
298335
return instance;
299336
}
300337

src/Autofac/Core/Lifetime/LifetimeScope.cs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
using System.Diagnostics;
66
using System.Globalization;
77
using System.Runtime.CompilerServices;
8+
using System.Threading;
89
#if NET5_0_OR_GREATER
910
using System.Runtime.Loader;
1011
#endif
1112
using Autofac.Builder;
1213
using Autofac.Core.Registration;
1314
using Autofac.Core.Resolving;
15+
using Autofac.Diagnostics;
1416
using Autofac.Util;
1517

1618
namespace Autofac.Core.Lifetime;
@@ -269,8 +271,21 @@ public object CreateSharedInstance(Guid id, Func<object> creator)
269271
return tempResult;
270272
}
271273

272-
lock (_synchRoot)
274+
var instrumentationDetail = AutofacMetrics.MetricsEnabled ? $"Tag:{Tag},Id:{id}" : null;
275+
var lockTaken = false;
276+
try
273277
{
278+
if (AutofacMetrics.MetricsEnabled)
279+
{
280+
var wait = ValueStopwatch.StartNew();
281+
Monitor.Enter(_synchRoot, ref lockTaken);
282+
AutofacMetrics.RecordLockContention("LifetimeScopeSharedInstance", instrumentationDetail, wait.ElapsedTicks);
283+
}
284+
else
285+
{
286+
Monitor.Enter(_synchRoot, ref lockTaken);
287+
}
288+
274289
if (_sharedInstances.TryGetValue(id, out var result))
275290
{
276291
return result;
@@ -284,6 +299,13 @@ public object CreateSharedInstance(Guid id, Func<object> creator)
284299

285300
return result;
286301
}
302+
finally
303+
{
304+
if (lockTaken)
305+
{
306+
Monitor.Exit(_synchRoot);
307+
}
308+
}
287309
}
288310

289311
/// <inheritdoc/>
@@ -306,8 +328,21 @@ public object CreateSharedInstance(Guid primaryId, Guid? qualifyingId, Func<obje
306328
return tempResult;
307329
}
308330

309-
lock (_synchRoot)
331+
var instrumentationDetail = AutofacMetrics.MetricsEnabled ? $"Tag:{Tag},Key:{instanceKey}" : null;
332+
var lockTaken = false;
333+
try
310334
{
335+
if (AutofacMetrics.MetricsEnabled)
336+
{
337+
var wait = ValueStopwatch.StartNew();
338+
Monitor.Enter(_synchRoot, ref lockTaken);
339+
AutofacMetrics.RecordLockContention("LifetimeScopeQualifiedSharedInstance", instrumentationDetail, wait.ElapsedTicks);
340+
}
341+
else
342+
{
343+
Monitor.Enter(_synchRoot, ref lockTaken);
344+
}
345+
311346
if (_sharedQualifiedInstances.TryGetValue(instanceKey, out var result))
312347
{
313348
return result;
@@ -321,6 +356,13 @@ public object CreateSharedInstance(Guid primaryId, Guid? qualifyingId, Func<obje
321356

322357
return result;
323358
}
359+
finally
360+
{
361+
if (lockTaken)
362+
{
363+
Monitor.Exit(_synchRoot);
364+
}
365+
}
324366
}
325367

326368
/// <inheritdoc />

src/Autofac/Core/Registration/DefaultRegisteredServicesTracker.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Concurrent;
55
using System.Runtime.CompilerServices;
66
using Autofac.Core.Resolving.Pipeline;
7+
using Autofac.Diagnostics;
78
using Autofac.Util;
89

910
namespace Autofac.Core.Registration;
@@ -303,6 +304,7 @@ private ServiceRegistrationInfo GetInitializedServiceInfo(Service service)
303304
}
304305

305306
var info = GetServiceInfo(service);
307+
var instrumentationService = AutofacMetrics.MetricsEnabled ? service.ToString() : null;
306308
if (info.IsInitialized)
307309
{
308310
return info;
@@ -324,7 +326,16 @@ private ServiceRegistrationInfo GetInitializedServiceInfo(Service service)
324326
var lockTaken = false;
325327
try
326328
{
327-
Monitor.Enter(info, ref lockTaken);
329+
if (AutofacMetrics.MetricsEnabled)
330+
{
331+
var wait = ValueStopwatch.StartNew();
332+
Monitor.Enter(info, ref lockTaken);
333+
AutofacMetrics.RecordLockContention("Service", instrumentationService, wait.ElapsedTicks);
334+
}
335+
else
336+
{
337+
Monitor.Enter(info, ref lockTaken);
338+
}
328339

329340
if (info.IsInitialized)
330341
{

src/Autofac/Core/Resolving/Middleware/ActivatorErrorHandlingMiddleware.cs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Copyright (c) Autofac Project. All rights reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4+
using System.Diagnostics;
45
using System.Globalization;
56
using Autofac.Core.Resolving.Pipeline;
7+
using Autofac.Diagnostics;
68

79
namespace Autofac.Core.Resolving.Middleware;
810

@@ -28,23 +30,20 @@ private ActivatorErrorHandlingMiddleware()
2830
/// <inheritdoc />
2931
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
3032
{
31-
try
33+
if (!AutofacMetrics.MetricsEnabled)
3234
{
33-
next(context);
34-
35-
if (context.Instance is null)
36-
{
37-
// Exited the Activation Stage without creating an instance.
38-
throw new DependencyResolutionException(MiddlewareMessages.ActivatorDidNotPopulateInstance);
39-
}
35+
ExecuteCore(context, next);
36+
return;
4037
}
41-
catch (ObjectDisposedException)
38+
39+
var start = Stopwatch.GetTimestamp();
40+
try
4241
{
43-
throw;
42+
ExecuteCore(context, next);
4443
}
45-
catch (Exception ex)
44+
finally
4645
{
47-
throw PropagateActivationException(context.Registration.Activator, ex);
46+
AutofacMetrics.RecordMiddlewareExecution(nameof(ActivatorErrorHandlingMiddleware), Stopwatch.GetTimestamp() - start);
4847
}
4948
}
5049

@@ -67,4 +66,26 @@ private static DependencyResolutionException PropagateActivationException(IInsta
6766
result.Data[ActivatorChainExceptionData] = activatorChain;
6867
return result;
6968
}
69+
70+
private static void ExecuteCore(ResolveRequestContext context, Action<ResolveRequestContext> next)
71+
{
72+
try
73+
{
74+
next(context);
75+
76+
if (context.Instance is null)
77+
{
78+
// Exited the Activation Stage without creating an instance.
79+
throw new DependencyResolutionException(MiddlewareMessages.ActivatorDidNotPopulateInstance);
80+
}
81+
}
82+
catch (ObjectDisposedException)
83+
{
84+
throw;
85+
}
86+
catch (Exception ex)
87+
{
88+
throw PropagateActivationException(context.Registration.Activator, ex);
89+
}
90+
}
7091
}

src/Autofac/Core/Resolving/Middleware/CircularDependencyDetectorMiddleware.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Copyright (c) Autofac Project. All rights reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4+
using System.Diagnostics;
45
using System.Globalization;
56
using System.Runtime.CompilerServices;
67
using Autofac.Core.Resolving.Pipeline;
8+
using Autofac.Diagnostics;
79

810
namespace Autofac.Core.Resolving.Middleware;
911

@@ -38,6 +40,25 @@ public CircularDependencyDetectorMiddleware(int maxResolveDepth)
3840

3941
/// <inheritdoc/>
4042
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
43+
{
44+
if (!AutofacMetrics.MetricsEnabled)
45+
{
46+
ExecuteCore(context, next);
47+
return;
48+
}
49+
50+
var start = Stopwatch.GetTimestamp();
51+
try
52+
{
53+
ExecuteCore(context, next);
54+
}
55+
finally
56+
{
57+
AutofacMetrics.RecordMiddlewareExecution(nameof(CircularDependencyDetectorMiddleware), Stopwatch.GetTimestamp() - start);
58+
}
59+
}
60+
61+
private void ExecuteCore(ResolveRequestContext context, Action<ResolveRequestContext> next)
4162
{
4263
if (context.Operation is not IDependencyTrackingResolveOperation dependencyTrackingResolveOperation)
4364
{

0 commit comments

Comments
 (0)