Example #1
0
        public void Upgrade(EventUpgraderContext ctx)
        {
            if (ctx.Version > new Version(1, 0, 0, 500))
            {
                return;
            }

            foreach (var doc in ctx.Documents)
            {
                if (!(doc["ExceptionlessClientInfo"] is JObject clientInfo) || !clientInfo.HasValues || clientInfo["InstallDate"] == null)
                {
                    return;
                }

                // This shouldn't hurt using DateTimeOffset to try and parse a date. It insures you won't lose any info.
                if (DateTimeOffset.TryParse(clientInfo["InstallDate"].ToString(), out var date))
                {
                    clientInfo.Remove("InstallDate");
                    clientInfo.Add("InstallDate", new JValue(date));
                }
                else
                {
                    clientInfo.Remove("InstallDate");
                }
            }
        }
Example #2
0
        public void Upgrade(EventUpgraderContext ctx)
        {
            if (ctx.Version > new Version(1, 0, 0, 850))
            {
                return;
            }

            foreach (var doc in ctx.Documents.OfType <JObject>())
            {
                JObject current = doc;
                while (current != null)
                {
                    var extendedData = doc["ExtendedData"] as JObject;
                    if (extendedData != null)
                    {
                        if (extendedData["ExtraExceptionProperties"] != null)
                        {
                            extendedData.Rename("ExtraExceptionProperties", "__ExceptionInfo");
                        }

                        if (extendedData["ExceptionInfo"] != null)
                        {
                            extendedData.Rename("ExceptionInfo", "__ExceptionInfo");
                        }

                        if (extendedData["TraceInfo"] != null)
                        {
                            extendedData.Rename("TraceInfo", "TraceLog");
                        }
                    }

                    current = current["Inner"] as JObject;
                }
            }
        }
        public void Upgrade(EventUpgraderContext ctx)
        {
            if (ctx.Version > new Version(1, 0, 0, 844))
                return;

            foreach (var doc in ctx.Documents) {
                var requestInfo = doc["RequestInfo"] as JObject;

                if (requestInfo == null || !requestInfo.HasValues)
                    return;

                if (requestInfo["Cookies"] != null && requestInfo["Cookies"].HasValues) {
                    var cookies = requestInfo["Cookies"] as JObject;
                    if (cookies != null)
                        cookies.Remove("");
                }

                if (requestInfo["Form"] != null && requestInfo["Form"].HasValues) {
                    var form = requestInfo["Form"] as JObject;
                    if (form != null)
                        form.Remove("");
                }

                if (requestInfo["QueryString"] != null && requestInfo["QueryString"].HasValues) {
                    var queryString = requestInfo["QueryString"] as JObject;
                    if (queryString != null)
                        queryString.Remove("");
                }
            }
        }
Example #4
0
        public void Upgrade(EventUpgraderContext ctx)
        {
            if (ctx.Version != null)
                return;

            if (ctx.Documents.Count == 0 || !ctx.Documents.First().HasValues) {
                ctx.Version = new Version();
                return;
            }

            var doc = ctx.Documents.First();
            var clientInfo = doc["ExceptionlessClientInfo"] as JObject;
            if (clientInfo == null || !clientInfo.HasValues || clientInfo["Version"] == null) {
                ctx.Version = new Version();
                return;
            }

            if (clientInfo["Version"].ToString().Contains(" ")) {
                string version = clientInfo["Version"].ToString().Split(' ').First();
                ctx.Version = new Version(version);
                return;
            }

            if (clientInfo["Version"].ToString().Contains("-")) {
                string version = clientInfo["Version"].ToString().Split('-').First();
                ctx.Version = new Version(version);
                return;
            }

            // old version format
            ctx.Version = new Version(clientInfo["Version"].ToString());
        }
        public void Upgrade(EventUpgraderContext ctx) {
            if (ctx.Version > new Version(2, 0))
                return;

            bool isNotFound = ctx.Document.GetPropertyStringValue("Code") == "404";
            if (isNotFound)
                ctx.Document.Remove("Id");
            else
                ctx.Document.RenameOrRemoveIfNullOrEmpty("Id", "ReferenceId");

            ctx.Document.RenameOrRemoveIfNullOrEmpty("OccurrenceDate", "Date");
            ctx.Document.Remove("OrganizationId");
            ctx.Document.Remove("ProjectId");
            ctx.Document.Remove("ErrorStackId");
            ctx.Document.Remove("ExceptionlessClientInfo");
            ctx.Document.Remove("IsFixed");
            ctx.Document.Remove("IsHidden");
            ctx.Document.RemoveIfNullOrEmpty("Tags");
            ctx.Document.RenameOrRemoveIfNullOrEmpty("RequestInfo", "req");
            ctx.Document.RenameOrRemoveIfNullOrEmpty("EnvironmentInfo", "env");

            ctx.Document.RenameAll("ExtendedData", "Data");
            var extendedData = ctx.Document.Property("Data") != null ? ctx.Document.Property("Data").Value as JObject : null;
            if (extendedData != null)
                extendedData.RenameOrRemoveIfNullOrEmpty("TraceLog", "trace");

            var error = new JObject();
            error.MoveOrRemoveIfNullOrEmpty(ctx.Document, "Code");
            error.MoveOrRemoveIfNullOrEmpty(ctx.Document, "Type");
            error.CopyOrRemoveIfNullOrEmpty(ctx.Document, "Message");
            error.MoveOrRemoveIfNullOrEmpty(ctx.Document, "Inner");
            error.MoveOrRemoveIfNullOrEmpty(ctx.Document, "StackTrace");
            error.MoveOrRemoveIfNullOrEmpty(ctx.Document, "TargetMethod");
            error.MoveOrRemoveIfNullOrEmpty(ctx.Document, "Modules");

            MoveExtraExceptionProperties(error, extendedData);
            var inner = error["inner"] as JObject;
            while (inner != null) {
                MoveExtraExceptionProperties(inner);
                inner = inner["inner"] as JObject;
            }

            ctx.Document.Add("Type", new JValue(isNotFound ? "404" : "error"));
            ctx.Document.Add("err", error);

            string emailAddress = ctx.Document.GetPropertyStringValueAndRemove("UserEmail");
            string userDescription = ctx.Document.GetPropertyStringValueAndRemove("UserDescription");
            if (!String.IsNullOrWhiteSpace(emailAddress) && !String.IsNullOrWhiteSpace(userDescription))
                ctx.Document.Add("desc", JObject.FromObject(new UserDescription(emailAddress, userDescription)));

            string identity = ctx.Document.GetPropertyStringValueAndRemove("UserName");
            if (!String.IsNullOrWhiteSpace(identity))
                ctx.Document.Add("user", JObject.FromObject(new UserInfo(identity)));

            ctx.Document.RemoveAllIfNullOrEmpty("Data");
            ctx.Document.RemoveAllIfNullOrEmpty("GenericArguments");
            ctx.Document.RemoveAllIfNullOrEmpty("Parameters");
        }
        public void ParseEvents(string errorFilePath) {
            var json = File.ReadAllText(errorFilePath);
            var ctx = new EventUpgraderContext(json);

            _eventUpgraderPluginManager.Upgrade(ctx);
            ApprovalsUtility.VerifyFile(Path.ChangeExtension(errorFilePath, ".expected.json"), ctx.Document.ToString());

            var events = _eventParserPluginManager.ParseEvents(ctx.Document.ToString(), 2, "exceptionless/2.0.0.0");
            Assert.Equal(1, events.Count);
        }
 /// <summary>
 /// Runs all of the event upgrade plugins upgrade method.
 /// </summary>
 public void Upgrade(EventUpgraderContext context)
 {
     foreach (var plugin in Plugins.Values.ToList())
     {
         try {
             plugin.Upgrade(context);
         } catch (Exception ex) {
             Logger.Error().Exception(ex).Message("Error calling upgrade in plugin \"{0}\": {1}", plugin.GetType().FullName, ex.Message).Property("Context", context).Write();
             throw;
         }
     }
 }
        public List<PersistentEvent> ParseEvents(string input, int apiVersion, string userAgent) {
            if (apiVersion != 1)
                return null;

            try {
                var ctx = new EventUpgraderContext(input);
                _manager.Upgrade(ctx);

                return ctx.Documents.FromJson<PersistentEvent>(_settings);
            } catch (Exception ex) {
                Logger.Error().Message("Error parsing event: {0}", ex.Message).Exception(ex).Write();
                return null;
            }
        }
Example #9
0
        /// <summary>
        /// Runs all of the event upgrade plugins upgrade method.
        /// </summary>
        public async Task UpgradeAsync(EventUpgraderContext context)
        {
            string metricPrefix = String.Concat(_metricPrefix, nameof(UpgradeAsync).ToLower(), ".");

            foreach (var plugin in Plugins.Values.ToList())
            {
                string metricName = String.Concat(metricPrefix, plugin.Name.ToLower());
                try {
                    await _metricsClient.TimeAsync(() => plugin.Upgrade(context), metricName).AnyContext();
                } catch (Exception ex) {
                    _logger.Error().Exception(ex).Message("Error calling upgrade in plugin \"{0}\": {1}", plugin.Name, ex.Message).Property("Context", context).Write();
                    throw;
                }
            }
        }
        public void Upgrade(EventUpgraderContext ctx) {
            if (ctx.Version > new Version(1, 0, 0, 500))
                return;

            var clientInfo = ctx.Document["ExceptionlessClientInfo"] as JObject;
            if (clientInfo == null || !clientInfo.HasValues || clientInfo["InstallDate"] == null)
                return;

            DateTimeOffset date; // This shouldn't hurt using DateTimeOffset to try and parse a date. It insures you won't lose any info.
            if (DateTimeOffset.TryParse(clientInfo["InstallDate"].ToString(), out date)) {
                clientInfo.Remove("InstallDate");
                clientInfo.Add("InstallDate", new JValue(date));
            } else
                clientInfo.Remove("InstallDate");
        }
Example #11
0
        public void Upgrade(EventUpgraderContext ctx)
        {
            if (ctx.Version > new Version(1, 0, 0, 844))
            {
                return;
            }

            foreach (var doc in ctx.Documents)
            {
                var requestInfo = doc["RequestInfo"] as JObject;

                if (requestInfo == null || !requestInfo.HasValues)
                {
                    return;
                }

                if (requestInfo["Cookies"] != null && requestInfo["Cookies"].HasValues)
                {
                    var cookies = requestInfo["Cookies"] as JObject;
                    if (cookies != null)
                    {
                        cookies.Remove("");
                    }
                }

                if (requestInfo["Form"] != null && requestInfo["Form"].HasValues)
                {
                    var form = requestInfo["Form"] as JObject;
                    if (form != null)
                    {
                        form.Remove("");
                    }
                }

                if (requestInfo["QueryString"] != null && requestInfo["QueryString"].HasValues)
                {
                    var queryString = requestInfo["QueryString"] as JObject;
                    if (queryString != null)
                    {
                        queryString.Remove("");
                    }
                }
            }
        }
        /// <summary>
        /// Runs all of the event upgrade plugins upgrade method.
        /// </summary>
        public void Upgrade(EventUpgraderContext context)
        {
            string metricPrefix = String.Concat(_metricPrefix, nameof(Upgrade).ToLower(), ".");

            foreach (var plugin in Plugins.Values.ToList())
            {
                string metricName = String.Concat(metricPrefix, plugin.Name.ToLower());
                try {
                    _metricsClient.Time(() => plugin.Upgrade(context), metricName);
                } catch (Exception ex) {
                    using (_logger.BeginScope(new Dictionary <string, object> {
                        { "Context", context }
                    }))
                        _logger.LogError(ex, "Error calling upgrade in plugin {PluginName}: {Message}", plugin.Name, ex.Message);

                    throw;
                }
            }
        }
        public List<PersistentEvent> ParseEvents(string input, int apiVersion, string userAgent)
        {
            if (apiVersion != 1)
                return null;

            try {
                var ctx = new EventUpgraderContext(input);
                _manager.Upgrade(ctx);

                var settings = new JsonSerializerSettings {
                    MissingMemberHandling = MissingMemberHandling.Ignore,
                    ContractResolver = new ExtensionContractResolver()
                };

                return ctx.Documents.FromJson<PersistentEvent>(settings);
            } catch (Exception ex) {
                ex.ToExceptionless().AddObject(input, "Error").AddObject(apiVersion, "Api Version").Submit();
                return null;
            }
        }
        public void Upgrade(EventUpgraderContext ctx) {
            if (ctx.Version > new Version(1, 0, 0, 850))
                return;

            JObject current = ctx.Document;
            while (current != null) {
                var extendedData = ctx.Document["ExtendedData"] as JObject;
                if (extendedData != null) {
                    if (extendedData["ExtraExceptionProperties"] != null)
                        extendedData.Rename("ExtraExceptionProperties", "__ExceptionInfo");

                    if (extendedData["ExceptionInfo"] != null)
                        extendedData.Rename("ExceptionInfo", "__ExceptionInfo");

                    if (extendedData["TraceInfo"] != null)
                        extendedData.Rename("TraceInfo", "TraceLog");
                }

                current = current["Inner"] as JObject;
            }
        }
        public void Upgrade(EventUpgraderContext ctx)
        {
            if (ctx.Version > new Version(1, 0, 0, 844))
            {
                return;
            }

            foreach (var doc in ctx.Documents)
            {
                if (!(doc["RequestInfo"] is JObject requestInfo) || !requestInfo.HasValues)
                {
                    return;
                }

                if (requestInfo["Cookies"] != null && requestInfo["Cookies"].HasValues)
                {
                    if (requestInfo["Cookies"] is JObject cookies)
                    {
                        cookies.Remove("");
                    }
                }

                if (requestInfo["Form"] != null && requestInfo["Form"].HasValues)
                {
                    if (requestInfo["Form"] is JObject form)
                    {
                        form.Remove("");
                    }
                }

                if (requestInfo["QueryString"] != null && requestInfo["QueryString"].HasValues)
                {
                    if (requestInfo["QueryString"] is JObject queryString)
                    {
                        queryString.Remove("");
                    }
                }
            }
        }
Example #16
0
        public void Upgrade(EventUpgraderContext ctx)
        {
            if (ctx.Version != null)
            {
                return;
            }

            if (ctx.Documents.Count == 0 || !ctx.Documents.First().HasValues)
            {
                ctx.Version = new Version();
                return;
            }

            var doc        = ctx.Documents.First();
            var clientInfo = doc["ExceptionlessClientInfo"] as JObject;

            if (clientInfo == null || !clientInfo.HasValues || clientInfo["Version"] == null)
            {
                ctx.Version = new Version();
                return;
            }

            if (clientInfo["Version"].ToString().Contains(" "))
            {
                string version = clientInfo["Version"].ToString().Split(' ').First();
                ctx.Version = new Version(version);
                return;
            }

            if (clientInfo["Version"].ToString().Contains("-"))
            {
                string version = clientInfo["Version"].ToString().Split('-').First();
                ctx.Version = new Version(version);
                return;
            }

            // old version format
            ctx.Version = new Version(clientInfo["Version"].ToString());
        }
Example #17
0
        public void Upgrade(EventUpgraderContext ctx) {
            if (ctx.Version > new Version(2, 0))
                return;

            foreach (var doc in ctx.Documents.OfType<JObject>()) {
                bool isNotFound = doc.GetPropertyStringValue("Code") == "404";

                if (ctx.IsMigration) {
                    doc.Rename("ErrorStackId", "StackId");
                } else {
                    doc.RenameOrRemoveIfNullOrEmpty("Id", "ReferenceId");
                    doc.Remove("OrganizationId");
                    doc.Remove("ProjectId");
                    doc.Remove("ErrorStackId");
                }

                doc.RenameOrRemoveIfNullOrEmpty("OccurrenceDate", "Date");
                doc.Remove("ExceptionlessClientInfo");
                if (!doc.RemoveIfNullOrEmpty("Tags")) {
                    var tags = doc.GetValue("Tags");
                    if (tags.Type == JTokenType.Array) {
                        foreach (JToken tag in tags.ToList()) {
                            string t = tag.ToString();
                            if (String.IsNullOrEmpty(t) || t.Length > 255)
                                tag.Remove();
                        }
                    }
                }

                doc.RenameOrRemoveIfNullOrEmpty("RequestInfo", "@request");
                bool hasRequestInfo = doc["@request"] != null;

                if (!isNotFound)
                    doc.RenameOrRemoveIfNullOrEmpty("EnvironmentInfo", "@environment");
                else
                    doc.Remove("EnvironmentInfo");

                doc.RenameAll("ExtendedData", "Data");

                var extendedData = doc.Property("Data") != null ? doc.Property("Data").Value as JObject : null;
                if (extendedData != null) {
                    if (!isNotFound)
                        extendedData.RenameOrRemoveIfNullOrEmpty("TraceLog", "@trace");
                    else
                        extendedData.Remove("TraceLog");
                }

                if (isNotFound && hasRequestInfo) {
                    doc.RemoveAll("Code", "Type", "Message", "Inner", "StackTrace", "TargetMethod", "Modules");
                    if (extendedData?["__ExceptionInfo"] != null)
                        extendedData.Remove("__ExceptionInfo");

                    doc.Add("Type", new JValue("404"));
                } else {
                    var error = new JObject();

                    if (!doc.RemoveIfNullOrEmpty("Message"))
                        error.Add("Message", doc["Message"].Value<string>());

                    error.MoveOrRemoveIfNullOrEmpty(doc, "Code", "Type", "Inner", "StackTrace", "TargetMethod", "Modules");

                    // Copy the exception info from root extended data to the current errors extended data.
                    if (extendedData?["__ExceptionInfo"] != null) {
                        error.Add("Data", new JObject());
                        ((JObject)error["Data"]).MoveOrRemoveIfNullOrEmpty(extendedData, "__ExceptionInfo");
                    }

                    string id = doc["Id"] != null ? doc["Id"].Value<string>(): null;
                    string projectId = doc["ProjectId"] != null ? doc["ProjectId"].Value<string>() : null;
                    RenameAndValidateExtraExceptionProperties(projectId, id, error);

                    var inner = error["Inner"] as JObject;
                    while (inner != null) {
                        RenameAndValidateExtraExceptionProperties(projectId, id, inner);
                        inner = inner["Inner"] as JObject;
                    }

                    doc.Add("Type", new JValue(isNotFound ? "404" : "error"));
                    doc.Add("@error", error);
                }

                string emailAddress = doc.GetPropertyStringValueAndRemove("UserEmail");
                string userDescription = doc.GetPropertyStringValueAndRemove("UserDescription");
                if (!String.IsNullOrWhiteSpace(emailAddress) && !String.IsNullOrWhiteSpace(userDescription))
                    doc.Add("@user_description", JObject.FromObject(new UserDescription(emailAddress, userDescription)));

                string identity = doc.GetPropertyStringValueAndRemove("UserName");
                if (!String.IsNullOrWhiteSpace(identity))
                    doc.Add("@user", JObject.FromObject(new UserInfo(identity)));

                doc.RemoveAllIfNullOrEmpty("Data", "GenericArguments", "Parameters");
            }
        }
Example #18
0
        public void Upgrade(EventUpgraderContext ctx)
        {
            if (ctx.Version > new Version(2, 0))
            {
                return;
            }

            foreach (var doc in ctx.Documents.OfType <JObject>())
            {
                bool isNotFound = doc.GetPropertyStringValue("Code") == "404";

                if (ctx.IsMigration)
                {
                    doc.Rename("ErrorStackId", "StackId");
                }
                else
                {
                    doc.RenameOrRemoveIfNullOrEmpty("Id", "ReferenceId");
                    doc.Remove("OrganizationId");
                    doc.Remove("ProjectId");
                    doc.Remove("ErrorStackId");
                }

                doc.RenameOrRemoveIfNullOrEmpty("OccurrenceDate", "Date");
                doc.Remove("ExceptionlessClientInfo");
                if (!doc.RemoveIfNullOrEmpty("Tags"))
                {
                    var tags = doc.GetValue("Tags");
                    if (tags.Type == JTokenType.Array)
                    {
                        foreach (JToken tag in tags.ToList())
                        {
                            string t = tag.ToString();
                            if (String.IsNullOrEmpty(t) || t.Length > 255)
                            {
                                tag.Remove();
                            }
                        }
                    }
                }

                doc.RenameOrRemoveIfNullOrEmpty("RequestInfo", "@request");
                bool hasRequestInfo = doc["@request"] != null;

                if (!isNotFound)
                {
                    doc.RenameOrRemoveIfNullOrEmpty("EnvironmentInfo", "@environment");
                }
                else
                {
                    doc.Remove("EnvironmentInfo");
                }

                doc.RenameAll("ExtendedData", "Data");

                var extendedData = doc.Property("Data") != null?doc.Property("Data").Value as JObject : null;

                if (extendedData != null)
                {
                    if (!isNotFound)
                    {
                        extendedData.RenameOrRemoveIfNullOrEmpty("TraceLog", "@trace");
                    }
                    else
                    {
                        extendedData.Remove("TraceLog");
                    }
                }

                if (isNotFound && hasRequestInfo)
                {
                    doc.RemoveAll("Code", "Type", "Message", "Inner", "StackTrace", "TargetMethod", "Modules");
                    if (extendedData != null && extendedData["__ExceptionInfo"] != null)
                    {
                        extendedData.Remove("__ExceptionInfo");
                    }

                    doc.Add("Type", new JValue("404"));
                }
                else
                {
                    var error = new JObject();

                    if (!doc.RemoveIfNullOrEmpty("Message"))
                    {
                        error.Add("Message", doc["Message"].Value <string>());
                    }

                    error.MoveOrRemoveIfNullOrEmpty(doc, "Code", "Type", "Inner", "StackTrace", "TargetMethod", "Modules");

                    // Copy the exception info from root extended data to the current errors extended data.
                    if (extendedData != null && extendedData["__ExceptionInfo"] != null)
                    {
                        error.Add("Data", new JObject());
                        ((JObject)error["Data"]).MoveOrRemoveIfNullOrEmpty(extendedData, "__ExceptionInfo");
                    }

                    string id        = doc["Id"] != null ? doc["Id"].Value <string>(): null;
                    string projectId = doc["ProjectId"] != null ? doc["ProjectId"].Value <string>() : null;
                    RenameAndValidateExtraExceptionProperties(projectId, id, error);

                    var inner = error["Inner"] as JObject;
                    while (inner != null)
                    {
                        RenameAndValidateExtraExceptionProperties(projectId, id, inner);
                        inner = inner["Inner"] as JObject;
                    }

                    doc.Add("Type", new JValue(isNotFound ? "404" : "error"));
                    doc.Add("@error", error);
                }

                string emailAddress    = doc.GetPropertyStringValueAndRemove("UserEmail");
                string userDescription = doc.GetPropertyStringValueAndRemove("UserDescription");
                if (!String.IsNullOrWhiteSpace(emailAddress) && !String.IsNullOrWhiteSpace(userDescription))
                {
                    doc.Add("@user_description", JObject.FromObject(new UserDescription(emailAddress, userDescription)));
                }

                string identity = doc.GetPropertyStringValueAndRemove("UserName");
                if (!String.IsNullOrWhiteSpace(identity))
                {
                    doc.Add("@user", JObject.FromObject(new UserInfo(identity)));
                }

                doc.RemoveAllIfNullOrEmpty("Data", "GenericArguments", "Parameters");
            }
        }
        protected override async Task<JobResult> RunInternalAsync(CancellationToken token) {
            OutputPublicIp();
            QueueEntry<EventMigrationBatch> queueEntry = null;
            try {
                queueEntry = _queue.Dequeue(TimeSpan.FromSeconds(1));
            } catch (Exception ex) {
                if (!(ex is TimeoutException)) {
                    Log.Error().Exception(ex).Message("Error trying to dequeue message: {0}", ex.Message).Write();
                    return JobResult.FromException(ex);
                }
            }

            if (queueEntry == null)
                return JobResult.Success;

            Log.Info().Message("Processing event migration jobs for date range: {0}-{1}", new DateTimeOffset(queueEntry.Value.StartTicks, TimeSpan.Zero).ToString("O"), new DateTimeOffset(queueEntry.Value.EndTicks, TimeSpan.Zero).ToString("O")).Write();
       
            int total = 0;
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            var errorCollection = GetErrorCollection();
            var knownStackIds = new List<string>();

            var userAgentParser = Parser.GetDefault();

            var query = Query.And(Query.GTE(ErrorFieldNames.OccurrenceDate_UTC, queueEntry.Value.StartTicks), Query.LT(ErrorFieldNames.OccurrenceDate_UTC, queueEntry.Value.EndTicks));
            var errors = errorCollection.Find(query).SetSortOrder(SortBy.Ascending(ErrorFieldNames.OccurrenceDate_UTC)).SetLimit(_batchSize).ToList();
            int batch = 0;
            while (errors.Count > 0) {
                Log.Info().Message("Migrating events {0}-{1} {2:N0} total {3:N0}/s...", errors.First().Id, errors.Last().Id, total, total > 0 ? total / stopwatch.Elapsed.TotalSeconds : 0).Write();

                var upgradedErrors = JArray.FromObject(errors);
                var ctx = new EventUpgraderContext(upgradedErrors, new Version(1, 5), true);
                _eventUpgraderPluginManager.Upgrade(ctx);

                var upgradedEvents = upgradedErrors.FromJson<PersistentEvent>(_settings);

                var stackIdsToCheck = upgradedEvents.Where(e => !knownStackIds.Contains(e.StackId)).Select(e => e.StackId).Distinct().ToArray();
                if (stackIdsToCheck.Length > 0)
                    knownStackIds.AddRange(_eventRepository.ExistsByStackIds(stackIdsToCheck));
                        
                upgradedEvents.ForEach(async e => {
                    if (e.Date.UtcDateTime > DateTimeOffset.UtcNow.AddHours(1))
                        e.Date = DateTimeOffset.Now;

                    e.CreatedUtc = e.Date.ToUniversalTime().DateTime;

                    // Truncate really large fields
                    if (e.Message != null && e.Message.Length > 2000) {
                        Log.Error().Project(e.ProjectId).Message("Event: {0} Message is Too Big: {1}", e.Id, e.Message.Length).Write();
                        e.Message = e.Message.Truncate(2000);
                    }

                    if (e.Source != null && e.Source.Length > 2000) {
                        Log.Error().Project(e.ProjectId).Message("Event: {0} Source is Too Big: {1}", e.Id, e.Source.Length).Write();
                        e.Source = e.Source.Truncate(2000);
                    }

                    if (!knownStackIds.Contains(e.StackId)) {
                        // We haven't processed this stack id yet in this run. Check to see if this stack has already been imported..
                        e.IsFirstOccurrence = true;
                        knownStackIds.Add(e.StackId);
                    }

                    var request = e.GetRequestInfo();
                    if (request != null) {
                        request = request.ApplyDataExclusions(RequestInfoPlugin.DefaultExclusions, RequestInfoPlugin.MAX_VALUE_LENGTH);

                        if (!String.IsNullOrEmpty(request.UserAgent)) {
                            try {
                                var info = userAgentParser.Parse(request.UserAgent);
                                if (!String.Equals(info.UserAgent.Family, "Other")) {
                                    request.Data[RequestInfo.KnownDataKeys.Browser] = info.UserAgent.Family;
                                    if (!String.IsNullOrEmpty(info.UserAgent.Major)) {
                                        request.Data[RequestInfo.KnownDataKeys.BrowserVersion] = String.Join(".", new[] { info.UserAgent.Major, info.UserAgent.Minor, info.UserAgent.Patch }.Where(v => !String.IsNullOrEmpty(v)));
                                        request.Data[RequestInfo.KnownDataKeys.BrowserMajorVersion] = info.UserAgent.Major;
                                    }
                                }

                                if (!String.Equals(info.Device.Family, "Other"))
                                    request.Data[RequestInfo.KnownDataKeys.Device] = info.Device.Family;


                                if (!String.Equals(info.OS.Family, "Other")) {
                                    request.Data[RequestInfo.KnownDataKeys.OS] = info.OS.Family;
                                    if (!String.IsNullOrEmpty(info.OS.Major)) {
                                        request.Data[RequestInfo.KnownDataKeys.OSVersion] = String.Join(".", new[] { info.OS.Major, info.OS.Minor, info.OS.Patch }.Where(v => !String.IsNullOrEmpty(v)));
                                        request.Data[RequestInfo.KnownDataKeys.OSMajorVersion] = info.OS.Major;
                                    }
                                }

                                request.Data[RequestInfo.KnownDataKeys.IsBot] = info.Device.IsSpider;
                            } catch (Exception ex) {
                                Log.Warn().Project(e.ProjectId).Message("Unable to parse user agent {0}. Exception: {1}", request.UserAgent, ex.Message).Write();
                            }
                        }

                        e.AddRequestInfo(request);
                    }

                    foreach (var ip in GetIpAddresses(e, request)) {
                        var location = await _geoIpResolver.ResolveIpAsync(ip, token);
                        if (location == null || !location.IsValid())
                            continue;

                        e.Geo = location.ToString();
                        break;
                    }

                    if (e.Type == Event.KnownTypes.NotFound && request != null) {
                        if (String.IsNullOrWhiteSpace(e.Source)) {
                            e.Message = null;
                            e.Source = request.GetFullPath(includeHttpMethod: true, includeHost: false, includeQueryString: false);
                        }

                        return;
                    }
                         
                    var error = e.GetError();
                    if (error == null) {
                        Debugger.Break();
                        Log.Error().Project(e.ProjectId).Message("Unable to get parse error model: {0}", e.Id).Write();
                        return;
                    }

                    var stackingTarget = error.GetStackingTarget();
                    if (stackingTarget != null && stackingTarget.Method != null && !String.IsNullOrEmpty(stackingTarget.Method.GetDeclaringTypeFullName()))
                        e.Source = stackingTarget.Method.GetDeclaringTypeFullName().Truncate(2000);

                    var signature = new ErrorSignature(error);
                    if (signature.SignatureInfo.Count <= 0)
                        return;

                    var targetInfo = new SettingsDictionary(signature.SignatureInfo);
                    if (stackingTarget != null && stackingTarget.Error != null && !targetInfo.ContainsKey("Message"))
                        targetInfo["Message"] = stackingTarget.Error.Message;

                    error.Data[Error.KnownDataKeys.TargetInfo] = targetInfo;
                });

                Log.Info().Message("Saving events {0}-{1} {2:N0} total", errors.First().Id, errors.Last().Id, upgradedEvents.Count).Write();
                try {
                    _eventRepository.Add(upgradedEvents, sendNotification: false);
                } catch (Exception) {
                    foreach (var persistentEvent in upgradedEvents) {
                        try {
                            _eventRepository.Add(persistentEvent, sendNotification: false);
                        } catch (Exception ex) {
                            //Debugger.Break();
                            Log.Error().Exception(ex).Project(persistentEvent.ProjectId).Message("An error occurred while migrating event '{0}': {1}", persistentEvent.Id, ex.Message).Write();
                        }
                    }
                }

                batch++;
                total += upgradedEvents.Count;

                Log.Info().Message("Getting next batch of events").Write();
                var sw = new Stopwatch();
                sw.Start();
                errors = errorCollection.Find(query).SetSortOrder(SortBy.Ascending(ErrorFieldNames.OccurrenceDate_UTC)).SetLimit(_batchSize).SetSkip(_batchSize * batch).ToList();
                sw.Stop();
                Log.Info().Message("Finished getting next batch of events in {0}ms", sw.ElapsedMilliseconds).Write();
            }

            Log.Info().Message("Finished processing event migration jobs for date range: {0}-{1}", new DateTimeOffset(queueEntry.Value.StartTicks, TimeSpan.Zero).ToString("O"), new DateTimeOffset(queueEntry.Value.EndTicks, TimeSpan.Zero).ToString("O")).Write();
            _cache.Set("migration-completedperiod", queueEntry.Value.EndTicks);
            queueEntry.Complete();

            return JobResult.Success;
        }