private void Context_Error(object sender, EventArgs e)
        {
            if (sender is not HttpApplication httpApplication ||
                httpApplication.Context == null ||
                httpApplication.Context.AllErrors == null ||
                httpApplication.Context.AllErrors.Length == 0
                )
            {
                return;
            }

            AggregateException exception =
                new("RollbarHttpModule's context error(s)", httpApplication.Context.AllErrors);

            IRollbarPackage package =
                new ExceptionPackage(exception, "HttpApplication.Context.AllErrors", true);

            // The HttpContext decorator already takes care of the HttpRequest and HttpResponse internally:
            package =
                new HttpContextPackageDecorator(package, httpApplication.Context);

            if (httpApplication.User?.Identity != null && !string.IsNullOrWhiteSpace(httpApplication.User.Identity.Name))
            {
                package = new PersonPackageDecorator(package, httpApplication.User.Identity.Name, httpApplication.User.Identity.Name, null);
            }

            this._rollbar.Error(package);
        }
Пример #2
0
        public void ExceptionPackageTest()
        {
            const string rollbarDataTitle = "You have some coding to do...";
            const string exceptionMessage = "Forgotten method";

            System.Exception exception = new NotImplementedException(exceptionMessage);

            IRollbarPackage packagingStrategy =
                new ExceptionPackage(exception, rollbarDataTitle);

            Assert.IsFalse(packagingStrategy.MustApplySynchronously, "Expected to be an async strategy!");

            Data rollbarData = packagingStrategy.PackageAsRollbarData();

            Assert.AreEqual(rollbarDataTitle, rollbarData.Title, "Data title is properly set!");
            Assert.IsNotNull(rollbarData.Body);
            Assert.IsNotNull(rollbarData.Body.Trace);
            Assert.IsNull(rollbarData.Body.Message);
            Assert.IsNull(rollbarData.Body.TraceChain);
            Assert.IsNull(rollbarData.Body.CrashReport);

            Assert.IsNotNull(rollbarData.Body.Trace.Exception);
            Assert.AreEqual(exceptionMessage, rollbarData.Body.Trace.Exception.Message);
            Assert.AreEqual(exception.GetType().FullName, rollbarData.Body.Trace.Exception.Class);
        }
Пример #3
0
        /// <summary>
        /// Called when [exception].
        /// </summary>
        /// <param name="actionExecutedContext">The action executed context.</param>
        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            IRollbarPackage rollbarPackage =
                new ExceptionPackage(actionExecutedContext.Exception, nameof(this.OnException));

            RollbarLocator.RollbarInstance.Critical(rollbarPackage);

            base.OnException(actionExecutedContext);
        }
Пример #4
0
        public void Exception_WithExceptionSupplied_ReturnsSameInstance()
        {
            // Arrange
            var package = new ExceptionPackage(id, exception);

            // Act
            var exceptionProperty = package.Exception;

            // Assert
            Assert.AreSame(exceptionProperty, exception);
        }
Пример #5
0
        public void Body_WithExcepionSupplied_ReturnsSameInstance()
        {
            // Arrange
            var package = new ExceptionPackage(id, exception);

            // Act
            var body = package.Body;

            // Assert
            Assert.AreSame(body, exception);
        }
Пример #6
0
        public void UnpackGeneric_WithExceptionMessage_RethrowsException_2()
        {
            // Arrange
            var exceptionPackage = new ExceptionPackage(new GuidPackageId(), new Exception());

            // Act
            TestDelegate extract = () => factory.Unpack <Exception>(exceptionPackage);

            // Assert
            Assert.That(extract, Throws.Exception);
        }
Пример #7
0
        protected void Application_Error()
        {
            var             exception         = Server.GetLastError();
            var             httpContext       = HttpContext.Current;
            IRollbarPackage packagingStrategy = new ExceptionPackage(exception, "EXCEPTION intercepted by MvcApplication.Application_Error()");

            if (httpContext != null)
            {
                packagingStrategy = new HttpContextPackageDecorator(packagingStrategy, httpContext);
            }
            RollbarLocator.RollbarInstance.Critical(packagingStrategy);
        }
Пример #8
0
        /// <summary>
        /// Composes the rolbar package.
        /// </summary>
        /// <typeparam name="TState">The type of the t state.</typeparam>
        /// <param name="eventId">The event identifier.</param>
        /// <param name="state">The state.</param>
        /// <param name="exception">The exception.</param>
        /// <param name="formatter">The formatter.</param>
        /// <returns>IRollbarPackage (if any) or null.</returns>
        protected virtual IRollbarPackage ComposeRolbarPackage <TState>(
            mslogging.EventId eventId
            , TState state
            , Exception exception
            , Func <TState, Exception, string> formatter
            )
        {
            string message = null;

            if (formatter != null)
            {
                message = formatter(state, exception);
            }

            IRollbarPackage rollbarPackage = null;

            if (exception != null)
            {
                rollbarPackage = new ExceptionPackage(exception, exception.Message);
            }
            else if (!string.IsNullOrWhiteSpace(message))
            {
                rollbarPackage = new MessagePackage(message, message);
            }
            else
            {
                return(null); //nothing to report...
            }

            Dictionary <string, object> customProperties = new Dictionary <string, object>();

            customProperties.Add(
                "LogEventID"
                , $"{eventId.Id}" + (string.IsNullOrWhiteSpace(eventId.Name) ? string.Empty : $" ({eventId.Name})")
                );
            if (exception != null && message != null)
            {
                customProperties.Add("LogMessage", message);
            }
            if (!string.IsNullOrWhiteSpace(this._name))
            {
                customProperties.Add("RollbarLoggerName", this._name);
            }
            if (customProperties.Count > 0)
            {
                rollbarPackage = new CustomKeyValuePackageDecorator(rollbarPackage, customProperties);
            }

            return(rollbarPackage);
        }
Пример #9
0
        public ActionResult Get([FromBody] LoginData loginData)
        {
            try
            {
                throw new Exception("Some error happened.");
            }
            catch (Exception err)
            {
                var rollbarPackage   = new ExceptionPackage(err);
                var packageDecorator = new HttpRequestPackageDecorator(rollbarPackage, HttpContext.Request, true);
                RollbarLocator.RollbarInstance.Log(ErrorLevel.Error, packageDecorator);
            }

            return(Ok());
        }
Пример #10
0
        private void Context_Error(object sender, EventArgs e)
        {
            HttpApplication httpApplication = sender as HttpApplication;

            if (httpApplication == null ||
                httpApplication.Context == null ||
                httpApplication.Context.AllErrors == null ||
                httpApplication.Context.AllErrors.Length == 0
                )
            {
                return;
            }

            // potential objects to snap as a Rollbar payload:
            //************************************************

            //httpApplication.Context.AllErrors;
            //httpApplication.Context;
            //httpApplication.Request;
            //httpApplication.Response;
            //httpApplication.Session;
            //httpApplication.User;
            //httpApplication.Server;
            //httpApplication.Application;
            //httpApplication.Modules;

            AggregateException exception =
                new AggregateException("RollbarHttpModule's context error(s)", httpApplication.Context.AllErrors);

            IRollbarPackage package =
                new ExceptionPackage(exception, "HttpApplication.Context.AllErrors", true);

            // The HttpContext decorator already takes care of the HttpRequest and HttpResponse internally:
            package =
                new HttpContextPackageDecorator(package, httpApplication.Context);

            if (httpApplication.User?.Identity != null && !string.IsNullOrWhiteSpace(httpApplication.User.Identity.Name))
            {
                package = new PersonPackageDecorator(package, httpApplication.User.Identity.Name, httpApplication.User.Identity.Name, null);
            }

            this._rollbar.Error(package);
        }
Пример #11
0
        /// <summary>
        /// Captures tha provided data with Rollbar.
        /// </summary>
        /// <param name="exception">
        /// The ex.
        /// </param>
        /// <param name="context">
        /// The context.
        /// </param>
        private static void CaptureWithRollbar(System.Exception exception, HttpContext context)
        {
            IRollbarPackage rollbarPackage = new ExceptionPackage(exception, $"{nameof(RollbarMiddleware)} processed uncaught exception.");

            if (context != null)
            {
                if (context.Request != null)
                {
                    rollbarPackage = new HttpRequestPackageDecorator(rollbarPackage, context.Request, true);
                }

                if (context.Response != null)
                {
                    rollbarPackage = new HttpResponsePackageDecorator(rollbarPackage, context.Response, true);
                }
            }

            RollbarLocator.RollbarInstance.Critical(rollbarPackage);
        }
Пример #12
0
        /// <summary>
        /// Produces the rollbar data.
        /// </summary>
        /// <returns>Rollbar Data DTO or null (if packaging is not applicable in some cases).</returns>
        protected override Data ProduceRollbarData()
        {
            if (this._exceptionContext == null)
            {
                return(null);
            }

            // let's use composition of available strategies:

            IRollbarPackage packagingStrategy = new ExceptionPackage(this._exceptionContext.Exception, this._message);

            if (this._exceptionContext?.RequestContext?.HttpContext != null)
            {
                packagingStrategy = new HttpContextPackageDecorator(packagingStrategy, this._exceptionContext.RequestContext.HttpContext);
            }

            Data rollbarData = packagingStrategy.PackageAsRollbarData();

            return(rollbarData);
        }
Пример #13
0
        public void AggregateExceptionPackageTest()
        {
            const string rollbarDataTitle       = "You have some coding to do...";
            const string innerExceptionMessage1 = "Forgotten method";

            System.Exception innerException1        = new NotImplementedException(innerExceptionMessage1);
            const string     innerExceptionMessage2 = "Forgotten null-check";

            System.Exception innerException2  = new NullReferenceException(innerExceptionMessage2);
            const string     exceptionMessage = "Application level exception";

            System.Exception exception = new AggregateException(exceptionMessage, innerException1, innerException2);

            IRollbarPackage packagingStrategy =
                new ExceptionPackage(exception, rollbarDataTitle);

            Assert.IsFalse(packagingStrategy.MustApplySynchronously, "Expected to be an async strategy!");

            Data rollbarData = packagingStrategy.PackageAsRollbarData();

            Assert.AreEqual(rollbarDataTitle, rollbarData.Title, "Data title is properly set!");
            Assert.IsNotNull(rollbarData.Body);
            Assert.IsNotNull(rollbarData.Body.TraceChain);
            Assert.IsNull(rollbarData.Body.Trace);
            Assert.IsNull(rollbarData.Body.Message);
            Assert.IsNull(rollbarData.Body.CrashReport);

            Assert.AreEqual(2, rollbarData.Body.TraceChain.Length);

            Assert.IsNotNull(rollbarData.Body.TraceChain[0]);
            Assert.IsNotNull(rollbarData.Body.TraceChain[0].Exception);
            Assert.AreEqual(innerExceptionMessage1, rollbarData.Body.TraceChain[0].Exception.Message);
            Assert.AreEqual(innerException1.GetType().FullName, rollbarData.Body.TraceChain[0].Exception.Class);

            Assert.IsNotNull(rollbarData.Body.TraceChain[1]);
            Assert.IsNotNull(rollbarData.Body.TraceChain[1].Exception);
            Assert.AreEqual(innerExceptionMessage2, rollbarData.Body.TraceChain[1].Exception.Message);
            Assert.AreEqual(innerException2.GetType().FullName, rollbarData.Body.TraceChain[1].Exception.Class);
        }
        /// <summary>
        /// Creates the rollbar package.
        /// </summary>
        /// <returns>IRollbarPackage.</returns>
        private IRollbarPackage?CreateRollbarPackage(object?payloadObject)
        {
            IRollbarPackage?package = null;

            switch (payloadObject)
            {
            case IRollbarPackage packageObject:
                package = packageObject;
                package = ApplyCustomKeyValueDecorator(package);
                return(package);

            case Data dataObject:
                package = new DataPackage(dataObject);
                package = ApplyCustomKeyValueDecorator(package);
                return(package);

            case Body bodyObject:
                package = new BodyPackage(this._rollbarLogger?.Config, bodyObject, this._custom);
                return(package);

            case System.Exception exceptionObject:
                package = new ExceptionPackage(exceptionObject);
                package = ApplyCustomKeyValueDecorator(package);
                return(package);

            case string messageObject:
                package = new MessagePackage(messageObject, this._custom);
                return(package);

            case ITraceable traceable:
                package = new MessagePackage(traceable.TraceAsString(), this._custom);
                return(package);

            default:
                package = new MessagePackage(payloadObject?.ToString(), this._custom);
                return(package);
            }
        }
Пример #15
0
        private void Page_Error(object sender, EventArgs e)
        {
            // Get last error from the server.
            Exception exception = Server.GetLastError();

            // Let's report to Rollbar on the Page Level:
            var metaData = new Dictionary <string, object>();

            metaData.Add("reportLevel", "PageLevel");
            metaData.Add("failedPage", this.AppRelativeVirtualPath);
            IRollbarPackage package = new ExceptionPackage(exception, "Page_Error", true);

            package = new CustomKeyValuePackageDecorator(package, metaData);
            package = new HttpContextPackageDecorator(package, this.Context);
            RollbarLocator.RollbarInstance.Error(package);

            // Handle specific exception.
            if (exception is InvalidOperationException)
            {
                // Pass the error on to the error page.
                Server.Transfer("ErrorPage.aspx?handler=Page_Error%20-%20Default.aspx", true);
            }
        }
Пример #16
0
        /// <summary>
        /// Invokes this middleware instance on the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>A middleware invocation/execution task.</returns>
        public async Task Invoke(HttpContext context)
        {
            // as we learned from a field issue, apparently a middleware can even be invoked without a valid HttPContext:
            string requestId = null;

            requestId = context?.Features?
                        .Get <IHttpRequestIdentifierFeature>()?
                        .TraceIdentifier;

#if (NETSTANDARD2_1 || NETCOREAPP3_0)
            context?.Request.EnableBuffering();
#else
            context?.Request.EnableRewind();
#endif

            using (_logger.BeginScope($"Request: {requestId ?? string.Empty}"))
            {
                NetworkTelemetry networkTelemetry = null;

                try
                {
                    if (TelemetryCollector.Instance.IsAutocollecting && context != null && context.Request != null)
                    {
                        int?telemetryStatusCode = null;
                        telemetryStatusCode = context?.Response?.StatusCode;

                        networkTelemetry = new NetworkTelemetry(
                            method: context.Request.Method,
                            url: context.Request.Host.Value + context.Request.Path,
                            eventStart: DateTime.UtcNow,
                            eventEnd: null,
                            statusCode: telemetryStatusCode
                            );
                        TelemetryCollector.Instance.Capture(new Telemetry(TelemetrySource.Server, TelemetryLevel.Info, networkTelemetry));
                    }

                    if (RollbarScope.Current != null && RollbarScope.Current.HttpContext != null)
                    {
                        RollbarScope.Current.HttpContext.HttpAttributes = new RollbarHttpAttributes(context);
                    }

                    await this._nextRequestProcessor(context);
                }
                catch (System.Exception ex)
                {
                    if (networkTelemetry != null)
                    {
                        networkTelemetry.StatusCode =
                            context?.Response?.StatusCode.ToString();
                        networkTelemetry.FinalizeEvent();
                    }

                    if (!RollbarLocator.RollbarInstance.Config.CaptureUncaughtExceptions)
                    {
                        // just rethrow since the Rollbar SDK is configured not to auto-capture
                        // uncaught exceptions:
                        throw;
                    }

                    if (RollbarScope.Current != null &&
                        RollbarLocator.RollbarInstance.Config.MaxItems > 0
                        )
                    {
                        RollbarScope.Current.IncrementLogItemsCount();
                        if (RollbarScope.Current.LogItemsCount == RollbarLocator.RollbarInstance.Config.MaxItems)
                        {
                            // the Rollbar SDK just reached MaxItems limit, report this fact and pause further logging within this scope:
                            RollbarLocator.RollbarInstance.Warning(RollbarScope.MaxItemsReachedWarning);
                            throw;
                        }
                        else if (RollbarScope.Current.LogItemsCount > RollbarLocator.RollbarInstance.Config.MaxItems)
                        {
                            // just rethrow since the Rollbar SDK already exceeded MaxItems limit:
                            throw;
                        }
                    }
                    else
                    {
                        IRollbarPackage rollbarPackage = new ExceptionPackage(ex, $"{nameof(RollbarMiddleware)} processed uncaught exception.");
                        if (context != null)
                        {
                            if (context.Request != null)
                            {
                                rollbarPackage = new HttpRequestPackageDecorator(rollbarPackage, context.Request, true);
                            }
                            if (context.Response != null)
                            {
                                rollbarPackage = new HttpResponsePackageDecorator(rollbarPackage, context.Response, true);
                            }
                        }
                        RollbarLocator.RollbarInstance.Critical(rollbarPackage);
                    }

                    throw new RollbarMiddlewareException(ex);
                }
                finally
                {
                    if (context != null &&
                        context.Response != null &&
                        RollbarScope.Current != null &&
                        RollbarScope.Current.HttpContext != null &&
                        RollbarScope.Current.HttpContext.HttpAttributes != null
                        )
                    {
                        RollbarScope.Current.HttpContext.HttpAttributes.ResponseStatusCode = context.Response.StatusCode;
                    }

                    if (networkTelemetry != null)
                    {
                        if (string.IsNullOrWhiteSpace(networkTelemetry.StatusCode))
                        {
                            networkTelemetry.StatusCode = context?.Response?.StatusCode.ToString();
                        }
                        networkTelemetry.FinalizeEvent();
                    }
                }
            }
        }
        public void BasicTest()
        {
            const string rollbarDataTitle = "Let's test some strategy decoration...";
            const string exceptionMessage = "Someone forgot null-test!";

            System.Exception exception = new NullReferenceException(exceptionMessage);

            IRollbarPackage packagingStrategy =
                new ExceptionPackage(exception, rollbarDataTitle);

            Assert.IsFalse(packagingStrategy.MustApplySynchronously, "Expected to be an async strategy!");

            Data rollbarData = packagingStrategy.PackageAsRollbarData();

            Assert.AreEqual(rollbarDataTitle, rollbarData.Title, "Data title is properly set!");
            Assert.IsNotNull(rollbarData.Body);
            Assert.IsNotNull(rollbarData.Body.Trace);
            Assert.IsNull(rollbarData.Body.Message);
            Assert.IsNull(rollbarData.Body.TraceChain);
            Assert.IsNull(rollbarData.Body.CrashReport);

            Assert.IsNotNull(rollbarData.Body.Trace.Exception);
            Assert.AreEqual(exceptionMessage, rollbarData.Body.Trace.Exception.Message);
            Assert.AreEqual(exception.GetType().FullName, rollbarData.Body.Trace.Exception.Class);

            Assert.IsTrue(rollbarData.Timestamp.HasValue);
            long initialTimestamp = rollbarData.Timestamp.Value;


            const string customKey1   = "customKey1";
            const string customValue1 = "customValue1";

            const string customKey2   = "customKey2";
            const string customValue2 = "customValue2";

            Dictionary <string, object> customData = new Dictionary <string, object>()
            {
                { customKey1, customValue1 },
                { customKey2, customValue2 },
            };

            packagingStrategy = new CustomKeyValuePackageDecorator(packagingStrategy, customData);

            // All the asserts prior to the decoration should be good again:

            Assert.IsFalse(packagingStrategy.MustApplySynchronously, "Expected to be an async strategy!");

            rollbarData = packagingStrategy.PackageAsRollbarData();

            Assert.AreEqual(rollbarDataTitle, rollbarData.Title, "Data title is properly set!");
            Assert.IsNotNull(rollbarData.Body);
            Assert.IsNotNull(rollbarData.Body.Trace);
            Assert.IsNull(rollbarData.Body.Message);
            Assert.IsNull(rollbarData.Body.TraceChain);
            Assert.IsNull(rollbarData.Body.CrashReport);

            Assert.IsNotNull(rollbarData.Body.Trace.Exception);
            Assert.AreEqual(exceptionMessage, rollbarData.Body.Trace.Exception.Message);
            Assert.AreEqual(exception.GetType().FullName, rollbarData.Body.Trace.Exception.Class);

            // Custom data decoration specific asserts:

            Assert.IsNotNull(rollbarData.Custom);
            Assert.AreEqual(customData.Count, rollbarData.Custom.Count);
            Assert.IsTrue(rollbarData.Custom.ContainsKey(customKey1));
            Assert.IsTrue(rollbarData.Custom.ContainsKey(customKey2));
            Assert.AreEqual(customValue1, rollbarData.Custom[customKey1]);
            Assert.AreEqual(customValue2, rollbarData.Custom[customKey2]);

            // Make sure the Data.Timestamp was not overwritten:

            Assert.IsTrue(rollbarData.Timestamp.HasValue);
            Assert.AreEqual(initialTimestamp, rollbarData.Timestamp.Value);
            System.Diagnostics.Debug.WriteLine($"Initial timestamp: {initialTimestamp}");
            System.Diagnostics.Debug.WriteLine($"Decorated timestamp: {rollbarData.Timestamp.Value}");
        }
Пример #18
0
        public static IRollbarPackage FakePackege()
        {
            var package = new ExceptionPackage(new NotImplementedException("Fake exception"));

            return(package);
        }
Пример #19
0
        /// <summary>
        /// Writes a log entry.
        /// </summary>
        /// <typeparam name="TState"></typeparam>
        /// <param name="logLevel">Entry will be written on this level.</param>
        /// <param name="eventId">Id of the event.</param>
        /// <param name="state">The entry to be written. Can be also an object.</param>
        /// <param name="exception">The exception related to this entry.</param>
        /// <param name="formatter">Function to create a <c>string</c> message of the <paramref name="state" /> and <paramref name="exception" />.</param>
        public void Log <TState>(
            mslogging.LogLevel logLevel
            , mslogging.EventId eventId
            , TState state
            , Exception exception
            , Func <TState, Exception, string> formatter
            )
        {
            if (!this.IsEnabled(logLevel))
            {
                return;
            }

            if (object.Equals(state, default(TState)) && exception == null)
            {
                return;
            }

            if (RollbarScope.Current != null &&
                RollbarLocator.RollbarInstance.Config.MaxItems > 0
                )
            {
                RollbarScope.Current.IncrementLogItemsCount();
                if (RollbarScope.Current.LogItemsCount == RollbarLocator.RollbarInstance.Config.MaxItems)
                {
                    // the Rollbar SDK just reached MaxItems limit, report this fact and pause further logging within this scope:
                    RollbarLocator.RollbarInstance.Warning(RollbarScope.MaxItemsReachedWarning);
                    return;
                }
                else if (RollbarScope.Current.LogItemsCount > RollbarLocator.RollbarInstance.Config.MaxItems)
                {
                    // the Rollbar SDK already exceeded MaxItems limit, do not log for this scope:
                    return;
                }
            }

            // let's custom build the Data object that includes the exception
            // along with the current HTTP request context:

            string message = null;

            if (formatter != null)
            {
                message = formatter(state, exception);
            }

            IRollbarPackage rollbarPackage = null;

            if (exception != null)
            {
                rollbarPackage = new ExceptionPackage(exception, exception.Message);
            }
            else if (!string.IsNullOrWhiteSpace(message))
            {
                rollbarPackage = new MessagePackage(message, message);
            }
            else
            {
                return; //nothing to report...
            }

            Dictionary <string, object> customProperties = new Dictionary <string, object>();

            customProperties.Add(
                "LogEventID"
                , $"{eventId.Id}" + (string.IsNullOrWhiteSpace(eventId.Name) ? string.Empty : $" ({eventId.Name})")
                );
            if (exception != null && message != null)
            {
                customProperties.Add("LogMessage", message);
            }
            if (customProperties.Count > 0)
            {
                rollbarPackage = new CustomKeyValuePackageDecorator(rollbarPackage, customProperties);
            }

            var currentContext = GetCurrentContext();

            if (currentContext != null)
            {
                rollbarPackage = new RollbarHttpContextPackageDecorator(rollbarPackage, currentContext, true);
            }

            var rollbarErrorLevel = RollbarLogger.Convert(logLevel);

            RollbarLocator.RollbarInstance.Log(rollbarErrorLevel, rollbarPackage);
        }
Пример #20
0
        public void PersonPackageDecoratorTest()
        {
            const string rollbarDataTitle = "Let's test some strategy decoration...";
            const string exceptionMessage = "Someone forgot null-test!";

            System.Exception exception = new NullReferenceException(exceptionMessage);

            IRollbarPackage packagingStrategy =
                new ExceptionPackage(exception, rollbarDataTitle);

            Assert.IsFalse(packagingStrategy.MustApplySynchronously, "Expected to be an async strategy!");

            Data rollbarData = packagingStrategy.PackageAsRollbarData();

            Assert.AreEqual(rollbarDataTitle, rollbarData.Title, "Data title is properly set!");
            Assert.IsNotNull(rollbarData.Body);
            Assert.IsNotNull(rollbarData.Body.Trace);
            Assert.IsNull(rollbarData.Body.Message);
            Assert.IsNull(rollbarData.Body.TraceChain);
            Assert.IsNull(rollbarData.Body.CrashReport);

            Assert.IsNotNull(rollbarData.Body.Trace.Exception);
            Assert.AreEqual(exceptionMessage, rollbarData.Body.Trace.Exception.Message);
            Assert.AreEqual(exception.GetType().FullName, rollbarData.Body.Trace.Exception.Class);

            Assert.IsTrue(rollbarData.Timestamp.HasValue);
            long initialTimestamp = rollbarData.Timestamp.Value;

            const string personID       = "007";
            const string personUsername = "******";
            const string personEmail    = "*****@*****.**";

            packagingStrategy = new PersonPackageDecorator(packagingStrategy, personID, personUsername, personEmail);

            // All the asserts prior to the decoration should be good again:

            Assert.IsFalse(packagingStrategy.MustApplySynchronously, "Expected to be an async strategy!");

            rollbarData = packagingStrategy.PackageAsRollbarData();

            Assert.AreEqual(rollbarDataTitle, rollbarData.Title, "Data title is properly set!");
            Assert.IsNotNull(rollbarData.Body);
            Assert.IsNotNull(rollbarData.Body.Trace);
            Assert.IsNull(rollbarData.Body.Message);
            Assert.IsNull(rollbarData.Body.TraceChain);
            Assert.IsNull(rollbarData.Body.CrashReport);

            Assert.IsNotNull(rollbarData.Body.Trace.Exception);
            Assert.AreEqual(exceptionMessage, rollbarData.Body.Trace.Exception.Message);
            Assert.AreEqual(exception.GetType().FullName, rollbarData.Body.Trace.Exception.Class);

            // Person decoration specific asserts:

            Assert.IsNotNull(rollbarData.Person);
            Assert.AreEqual(personID, rollbarData.Person.Id);
            Assert.AreEqual(personUsername, rollbarData.Person.UserName);
            Assert.AreEqual(personEmail, rollbarData.Person.Email);

            // Make sure the Data.Timestamp was not overwritten:

            Assert.IsTrue(rollbarData.Timestamp.HasValue);
            Assert.AreEqual(initialTimestamp, rollbarData.Timestamp.Value);
            System.Diagnostics.Debug.WriteLine($"Initial timestamp: {initialTimestamp}");
            System.Diagnostics.Debug.WriteLine($"Decorated timestamp: {rollbarData.Timestamp.Value}");
        }