/// <summary> /// Unhandled exception /// </summary> private static BadRequestObjectResult BadRequest(ILogger logger, ISupportNotifier supportNotifier, EventId eventId, string correlationId, Exception ex, string payload) { var message = $"Unhandled Exception. Operation: {OperationName}, Step: {eventId.Name}, Request body: {payload}, Message: {ex.Message}"; // log exception logger.LogError(eventId, ex, StaticSettings.Template, OperationName, correlationId, message); // return bad request with unknown exception return(new BadRequestObjectResult(new JObject()["error"] = "unknown error")); }
public static async Task Run( [CosmosDBTrigger(StaticSettings.Db, StaticSettings.Collection, ConnectionStringSetting = StaticSettings.DbConnectionStringSetting, LeaseCollectionName = StaticSettings.LeasesCollection)] IReadOnlyList <Event> events, [ServiceBus(Topic, Connection = ServiceBusSendConnectionStringSetting)] IAsyncCollector <Message> messageAsyncCollector, ISupportNotifier supportNotifier, ILogger logger) { foreach (var eventModel in events) { PublishEvent(logger, supportNotifier, messageAsyncCollector, eventModel); } }
/// <summary> /// Log an error for the document client exception /// Notify support of this error /// /// This exception can encapsulate many different types of errors. To determine the specific error /// always look at the StatusCode property. Some common codes you may get when creating a Document are: /// 400 BadRequest - This means something was wrong with the document supplied.It is likely that /// disableAutomaticIdGeneration was true and an id was not supplied /// 403 Forbidden - This likely means the collection in to which you were trying to create the document is full. /// 409 Conflict - This means a Document with an id matching the id field of document already existed /// 413 RequestEntityTooLarge - This means the Document exceeds the current max entity size.Consult /// documentation for limits and quotas. /// 429 TooManyRequests - This means you have exceeded the number of request units per second.Consult the /// DocumentClientException.RetryAfter value to see how long you should wait before /// retrying this operation. /// </summary> private static BadRequestObjectResult BadRequest(ILogger logger, ISupportNotifier supportNotifier, EventId eventId, string correlationId, DocumentClientException ex, string payload) { var message = $"{ProcessStep.CreateDocument.ToString()}, DocumentClientException status: {ex.StatusCode}, message: {ex.Message}"; // log exception when unknown exception logger.LogError(eventId, ex, StaticSettings.Template, EventTrigger, correlationId, EntityType, null, message); // notify support of unknown exception supportNotifier.Notify($"WJ Unhandled Exception. Operation: {OperationName}, Step: {ProcessStep.CreateDocument.ToString()}, Request body: {payload}, Message: {message}"); // return bad request with unknown exception return(new BadRequestObjectResult(new JObject()["error"] = "unknown error")); }
private static void PublishEvent(ILogger logger, ISupportNotifier supportNotifier, IAsyncCollector <Message> messageAsyncCollector, Event eventModel) { var eventId = StaticSettings.EventId(MethodEventBase, (int)EventType.ChangeFeed); try { messageAsyncCollector.AddAsync(eventModel.ToMessage()); logger.LogInformation(eventId, StaticSettings.Template, EventTrigger, eventModel.CorrelationId, EntityType, eventModel.EventId, PublishMessage.PublishedApplicationEvent.ToString()); } catch (Exception ex) { logger.LogError(eventId, ex, StaticSettings.Template, EventTrigger, eventModel.CorrelationId, EntityType, eventModel.EventId, ex.Message); // notify support of unknown exception supportNotifier.Notify($"WJ Unhandled Exception. Operation: {OperationName}, body: {JsonConvert.SerializeObject(eventModel)}, Message: {ex.Message}"); } }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, Method, Route = Route)] HttpRequestMessage request, [CosmosDBTrigger(StaticSettings.Db, StaticSettings.Collection, ConnectionStringSetting = StaticSettings.DbConnectionStringSetting)] IDocumentClient documentClient, ISupportNotifier supportNotifier, ILogger logger) { // get the correlation identifier request.Headers.TryGetValues(CorrelationIdHeaderKey, out var headerValues); var correlationId = headerValues == null?Guid.NewGuid().ToString() : headerValues.First(); // ProcessStep.Deserialize - deserialize request content EventRequest eventRequest; string payload = null; var eventId = StaticSettings.EventId(MethodLevelEventId, (int)ProcessStep.Deserialize, ProcessStep.Deserialize.ToString()); try { payload = await request.Content.ReadAsStringAsync(); eventRequest = JsonConvert.DeserializeObject <EventRequest>(payload); logger.LogInformation(eventId, StaticSettings.Template, OperationName, correlationId); } catch (ArgumentNullException ex) { return(BadRequest(logger, eventId, correlationId, ex.Message)); } catch (ArgumentException ex) { return(BadRequest(logger, eventId, correlationId, ex.Message)); } catch (Exception e) { return(BadRequest(logger, supportNotifier, eventId, correlationId, e, payload)); } // ProcessStep.Validation - validate the event request eventId = StaticSettings.EventId(MethodLevelEventId, (int)ProcessStep.Validation, ProcessStep.Validation.ToString()); try { Validator.ValidateObject(eventRequest, new ValidationContext(eventRequest)); logger.LogInformation(eventId, StaticSettings.Template, OperationName, correlationId); } catch (ValidationException) { return(BadRequest(logger, eventId, correlationId, "Invalid request content")); } catch (Exception e) { return(BadRequest(logger, supportNotifier, eventId, correlationId, e, payload)); } var docUri = UriFactory.CreateDocumentCollectionUri(StaticSettings.Db, StaticSettings.Collection); // check for valid event sequence of the event in our event store // This may result in a stale sequence number, but we catch that in the "create document" step if (eventRequest.EventPurpose != EventPurpose.CreateAggregate) { eventId = StaticSettings.EventId(MethodLevelEventId, (int)ProcessStep.SequenceValidation); var sequenceValidated = true; try { Policy.Handle <Exception>() .WaitAndRetry(RetryCount, RetryDuration, (ex, c) => logger.LogWarning(eventId, StaticSettings.Template, EventTrigger, correlationId, EntityType, eventRequest.EventId, $"{ProcessStep.SequenceValidation.ToString()} retryTime: {c}, {ex.Message}")) .Execute(ct => { sequenceValidated = TryExecuteSequenceValidation(logger, eventId, correlationId, documentClient, docUri, eventRequest); }, CancellationToken.None); } catch (Exception ex) { return(BadRequest(logger, supportNotifier, eventId, correlationId, ProcessStep.SequenceValidation, ex, payload)); } if (!sequenceValidated) { return(BadRequest(logger, eventId, correlationId, ProcessStep.SequenceValidation, payload)); } } // Create document // todo: implement retry eventId = StaticSettings.EventId(MethodLevelEventId, (int)ProcessStep.CreateDocument); try { await documentClient.CreateDocumentAsync(docUri, eventRequest); logger.LogInformation(eventId, StaticSettings.Template, EventTrigger, correlationId, EntityType, eventRequest.EventId, ProcessStep.CreateDocument.ToString()); return(new OkResult()); } // If either documentsFeedOrDatabaseLink or document is not set. catch (ArgumentNullException) { return(BadRequest(logger, eventId, correlationId, ProcessStep.CreateDocument, CreateDocumentMessage.DocumentNull.ToString())); } // Represents a consolidation of failures that occured during async processing. // Look within InnerExceptions to find the actual exception(s) catch (AggregateException) { // todo: log inner exceptions individually return(BadRequest(logger, eventId, correlationId, ProcessStep.CreateDocument, CreateDocumentMessage.Aggregate.ToString())); } catch (DocumentClientException ex) { return(BadRequest(logger, supportNotifier, eventId, correlationId, ex, payload)); } catch (Exception ex) { return(BadRequest(logger, supportNotifier, eventId, correlationId, ProcessStep.CreateDocument, ex, payload)); } }