Skip to content

Commit 5f485ab

Browse files
authored
Merge pull request #25 from rebus-org/forward-exceptions-to-activity
Forward exceptions and add a bit more data to traces
2 parents 7f5a83d + 9c5224b commit 5f485ab

File tree

7 files changed

+126
-8
lines changed

7 files changed

+126
-8
lines changed

Rebus.Diagnostics.Tests/Incoming/HandlerInvokerWrapperTests.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Diagnostics;
1+
using System;
2+
using System.Diagnostics;
3+
using System.Linq;
24
using System.Threading.Tasks;
35
using NUnit.Framework;
46
using Rebus.Diagnostics.Incoming;
@@ -41,6 +43,40 @@ public async Task CreatesNewSubActivityIfThereIsAnActiveActivity()
4143
Assert.That(hadActivity);
4244
}
4345

46+
[Test]
47+
public void MarksActivityAsFailedIfHandlerThrows()
48+
{
49+
using var activity = new Activity("MyActivity");
50+
51+
Activity? innerActivity = null;
52+
var innerInvoker = new TestInvoker(() =>
53+
{
54+
innerActivity = Activity.Current;
55+
throw new Exception("Look im failing");
56+
});
57+
58+
var wrapper = new HandlerInvokerWrapper(innerInvoker, "MyMessage");
59+
60+
activity.Start();
61+
62+
Assume.That(activity, Is.SameAs(Activity.Current));
63+
64+
Assert.That(async () =>
65+
{
66+
await wrapper.Invoke();
67+
}, Throws.Exception);
68+
69+
70+
Assert.That(innerActivity, Is.Not.Null);
71+
72+
Assert.That(innerActivity!.Status, Is.EqualTo(ActivityStatusCode.Error));
73+
Assert.That(innerActivity!.StatusDescription, Is.EqualTo("Look im failing"));
74+
Assert.That(innerActivity.Events, Has.Exactly(1).Items);
75+
var ev = innerActivity.Events.Single();
76+
Assert.That(ev.Tags.FirstOrDefault(t => t.Key == "exception.type").Value, Is.EqualTo("System.Exception"));
77+
Assert.That(ev.Tags.FirstOrDefault(t => t.Key == "exception.message").Value, Is.EqualTo("Look im failing"));
78+
}
79+
4480
[Test]
4581
public async Task CreatesNoNewActivityIfThereIsntAlreadyAnActiveActivity()
4682
{

Rebus.Diagnostics.Tests/Incoming/IncomingDiagnosticsStepTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ private string RenderString(string message, object[] objs)
331331
/// <summary>
332332
/// Formatter function that is invoked for each object value to be rendered into a string while interpolating log lines
333333
/// </summary>
334-
private string FormatObject(object obj, string format)
334+
private string FormatObject(object obj, string? format)
335335
{
336336
if (obj is string) return $@"""{obj}""";
337337
if (obj is IEnumerable)
@@ -345,7 +345,7 @@ private string FormatObject(object obj, string format)
345345
if (obj is DateTimeOffset) return ((DateTimeOffset) obj).ToString(format ?? "O");
346346
if (obj is IFormattable) return ((IFormattable) obj).ToString(format, CultureInfo.InvariantCulture);
347347
if (obj is IConvertible) return ((IConvertible) obj).ToString(CultureInfo.InvariantCulture);
348-
return obj.ToString();
348+
return obj.ToString()!;
349349
}
350350

351351
#endregion

Rebus.Diagnostics.Tests/Outgoing/OutgoingDiagnosticsStepTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace Rebus.Diagnostics.Tests.Outgoing
1515
[TestFixture]
1616
public class OutgoingDiagnosticsStepTests : FixtureBase
1717
{
18-
RebusTransactionScope? _scope;
18+
RebusTransactionScope _scope = null!;
1919

2020
[OneTimeSetUp]
2121
public static void ListenForRebus()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
5+
namespace Rebus.Diagnostics.Helpers;
6+
7+
internal static class ActivityExtensions
8+
{
9+
10+
const string ExceptionEventName = "exception";
11+
const string ExceptionMessageTag = "exception.message";
12+
const string ExceptionStackTraceTag = "exception.stacktrace";
13+
const string ExceptionTypeTag = "exception.type";
14+
15+
16+
// "Borrowed" from https://github.com/tarekgh/runtime/blob/d4464d7ae6d99d75ff89309fb56fd2a5a8f0c845/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs#L535
17+
// to avoid requiring version 9 of System.Diagnostics.DiagnosticSource
18+
public static Activity AddException(this Activity activity, Exception exception,
19+
ActivityTagsCollection? tags = null, DateTimeOffset timestamp = default)
20+
{
21+
if (exception == null)
22+
{
23+
throw new ArgumentNullException(nameof(exception));
24+
}
25+
26+
var exceptionTags = tags ?? new();
27+
28+
var hasMessage = false;
29+
var hasStackTrace = false;
30+
var hasType = false;
31+
32+
foreach (var pair in exceptionTags)
33+
{
34+
if (pair.Key == ExceptionMessageTag)
35+
{
36+
hasMessage = true;
37+
}
38+
else if (pair.Key == ExceptionStackTraceTag)
39+
{
40+
hasStackTrace = true;
41+
}
42+
else if (pair.Key == ExceptionTypeTag)
43+
{
44+
hasType = true;
45+
}
46+
}
47+
48+
if (!hasMessage)
49+
{
50+
exceptionTags.Add(new KeyValuePair<string, object?>(ExceptionMessageTag, exception.Message));
51+
}
52+
53+
if (!hasStackTrace)
54+
{
55+
exceptionTags.Add(new KeyValuePair<string, object?>(ExceptionStackTraceTag, exception.ToString()));
56+
}
57+
58+
if (!hasType)
59+
{
60+
exceptionTags.Add(new KeyValuePair<string, object?>(ExceptionTypeTag, exception.GetType().ToString()));
61+
}
62+
63+
return activity.AddEvent(new ActivityEvent(ExceptionEventName, timestamp, exceptionTags));
64+
}
65+
}

Rebus.Diagnostics/Diagnostics/Helpers/MessageWrapper.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ internal string GetCorrectionId()
2525
{
2626
return Headers.GetValueOrNull(Rebus.Messages.Headers.Intent) ?? GetMessageId();
2727
}
28+
29+
internal string GetMessageType()
30+
{
31+
return Headers.GetValueOrNull(Rebus.Messages.Headers.Type) ?? "";
32+
}
2833
}
2934

3035
internal class TransportMessageWrapper : MessageWrapper

Rebus.Diagnostics/Diagnostics/Helpers/TagHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal static ActivityTagsCollection ExtractInitialTags(MessageWrapper message
1818
initialTags.Add("messaging.destination_kind", kind);
1919
initialTags.Add("messaging.message_id", message.GetMessageId());
2020
initialTags.Add("messaging.conversation_id", message.GetCorrectionId());
21+
initialTags.Add("rebus.message.type", message.GetMessageType());
2122

2223
return initialTags;
2324
}

Rebus.Diagnostics/Diagnostics/Incoming/HandlerInvokerWrapper.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Diagnostics;
1+
using System;
2+
using System.Diagnostics;
23
using System.Threading.Tasks;
34
using Rebus.Diagnostics.Helpers;
45
using Rebus.Pipeline.Receive;
@@ -34,12 +35,22 @@ public override async Task Invoke()
3435
initialTags.Add(tag.Key, tag.Value);
3536
}
3637
initialTags["messaging.operation"] = "process";
37-
38+
initialTags["rebus.handler.type"] = _handlerInvokerImplementation.Handler?.GetType().FullName ?? "Unknown handler type";
39+
3840
using var activity = RebusDiagnosticConstants.ActivitySource.StartActivity($"{_messageType} process", ActivityKind.Internal, parentActivity.Context, initialTags);
3941

4042
TagHelper.CopyBaggage(parentActivity, activity);
41-
42-
await _handlerInvokerImplementation.Invoke();
43+
44+
try
45+
{
46+
await _handlerInvokerImplementation.Invoke();
47+
}
48+
catch (Exception e)
49+
{
50+
activity?.AddException(e);
51+
activity?.SetStatus(ActivityStatusCode.Error, e.Message);
52+
throw;
53+
}
4354
}
4455

4556
public override void SetSagaData(ISagaData sagaData)

0 commit comments

Comments
 (0)