private static async Task <StatusCodeResult> RedisUpsert(ProcessingUpdate update, AppInsightsLogger appInsightsLogger, string redisOperation) { IDatabase db = null; try { db = RedisConnection.GetDatabase(); } catch (Exception ex) { appInsightsLogger.LogError(ex, update.ApiPath); appInsightsLogger.LogRedisUpsert("Redis upsert failed.", redisOperation, DateTime.UtcNow.ToString(), update.ApiPath); return(new StatusCodeResult(500)); } try { var keyname = APP_INSIGHTS_REQUESTS_KEY_NAME + "/" + update.ServiceCluster + update.ApiPath; var newCount = await db.StringIncrementAsync(keyname, update.IncrementBy - update.DecrementBy); appInsightsLogger.LogMetric(keyname, newCount, update.ApiPath); return(new StatusCodeResult(200)); } catch (Exception ex) { appInsightsLogger.LogError(ex, update.ApiPath); appInsightsLogger.LogRedisUpsert("Redis upsert failed.", redisOperation, DateTime.UtcNow.ToString(), update.ApiPath); return(new StatusCodeResult(500)); } }
private static async Task UpdateTaskStatus(string taskId, string backendUri, string taskBody, string statusDetail, string backendStatus, AppInsightsLogger appInsightsLogger) { IDatabase db = null; try { db = RedisConnection.GetDatabase(); } catch (Exception ex) { appInsightsLogger.LogError(ex, URL, taskId); } RedisValue storedStatus = RedisValue.Null; try { storedStatus = await db.StringGetAsync(taskId); } catch (Exception ex) { appInsightsLogger.LogError(ex, URL, taskId); } APITask task = null; if (storedStatus.HasValue) { task = JsonConvert.DeserializeObject <APITask>(storedStatus.ToString()); task.Status = statusDetail; task.Timestamp = DateTime.UtcNow.ToString(); } else { appInsightsLogger.LogWarning("Cannot find status in cache", URL, taskId); task = new APITask() { TaskId = Guid.NewGuid().ToString(), Status = statusDetail, BackendStatus = backendStatus, Body = taskBody, Timestamp = DateTime.UtcNow.ToString(), Endpoint = task.Endpoint, PublishToGrid = true }; } if (await db.StringSetAsync(task.TaskId, JsonConvert.SerializeObject(task)) == false) { var ex = new Exception("Unable to complete redis transaction."); appInsightsLogger.LogError(ex, task.Endpoint, task.TaskId); } }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger logger) { IDatabase db = null; AppInsightsLogger appInsightsLogger = new AppInsightsLogger(logger, LOGGING_SERVICE_NAME, LOGGING_SERVICE_VERSION); string cluster = string.Empty; string path = string.Empty; var apiParams = req.GetQueryParameterDictionary(); if (apiParams != null && apiParams.Keys.Contains(SERVICE_CLUSTER_KEY_NAME) && apiParams.Keys.Contains(API_PATH_KEY_NAME)) { cluster = apiParams[SERVICE_CLUSTER_KEY_NAME]; path = apiParams[API_PATH_KEY_NAME]; } else { return(new BadRequestObjectResult("The cluster and path parameters are requried.")); } try { db = RedisConnection.GetDatabase(); } catch (Exception ex) { appInsightsLogger.LogError(ex, cluster + "/" + path); return(new StatusCodeResult(500)); } RedisValue storedCount = RedisValue.Null; try { storedCount = await db.StringGetAsync(APP_INSIGHTS_REQUESTS_KEY_NAME + cluster + "/" + path); if (storedCount.HasValue) { appInsightsLogger.LogInformation("Found status in cache", cluster + "/" + path); return(new OkObjectResult(storedCount.ToString())); } else { appInsightsLogger.LogInformation("Found status in cache", cluster + "/" + path); return(new StatusCodeResult(204)); } } catch (Exception ex) { appInsightsLogger.LogError(ex, cluster + "/" + path); return(new StatusCodeResult(500)); } }
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, ILogger logger) { IDatabase db = null; AppInsightsLogger appInsightsLogger = new AppInsightsLogger(logger, LOGGING_SERVICE_NAME, LOGGING_SERVICE_VERSION); string uuid = "nil"; var apiParams = req.GetQueryParameterDictionary(); if (apiParams != null && apiParams.Keys.Contains(UUID_KEYNAME)) { uuid = apiParams[UUID_KEYNAME]; appInsightsLogger.LogInformation("Getting status for taskId: " + uuid, URL, uuid); } else { appInsightsLogger.LogWarning("Called without a taskId", URL); return(new BadRequestObjectResult("The taskId parameter is requried.")); } try { db = RedisConnection.GetDatabase(); } catch (Exception ex) { appInsightsLogger.LogError(ex, URL, uuid); return(new StatusCodeResult(500)); } RedisValue storedStatus = RedisValue.Null; try { storedStatus = db.StringGet(uuid); if (storedStatus.HasValue) { appInsightsLogger.LogInformation("Found status in cache", URL, uuid); return(new OkObjectResult(storedStatus.ToString())); } else { appInsightsLogger.LogWarning("Cannot find status in cache", URL, uuid); return(new StatusCodeResult(204)); } } catch (Exception ex) { appInsightsLogger.LogError(ex, URL, uuid); return(new StatusCodeResult(500)); } }
private static bool PublishEvent(APITask task, string taskBody, AppInsightsLogger appInsightsLogger) { string event_grid_topic_uri = Environment.GetEnvironmentVariable(EVENT_GRID_TOPIC_URI_VARIABLE_NAME, EnvironmentVariableTarget.Process); string event_grid_key = Environment.GetEnvironmentVariable(EVENT_GRID_KEY_VARIABLE_NAME, EnvironmentVariableTarget.Process); var ev = new EventGridEvent() { Id = task.TaskId, EventType = "task", Data = taskBody, EventTime = DateTime.Parse(task.Timestamp), Subject = task.Endpoint, DataVersion = "1.0" }; string topicHostname = new Uri(event_grid_topic_uri).Host; TopicCredentials topicCredentials = new TopicCredentials(event_grid_key); EventGridClient client = new EventGridClient(topicCredentials); try { client.PublishEventsAsync(topicHostname, new List <EventGridEvent>() { ev }).GetAwaiter().GetResult(); } catch (Exception ex) { appInsightsLogger.LogError(ex, task.Endpoint, task.TaskId); return(false); } return(true); }
public void Test_Telemetry_LogErrorExceptionWithObject() { // Assert AssertExtensions.DoesNotThrow(() => { // Arrange var logger = new AppInsightsLogger("test", LogLevel.Error); // Act logger.LogError(new Exception("test"), new Test { PropA = "propA", PropB = 1, PropC = true, PropD = new SubItem { PropE = "propE", PropF = new List <int> { 1, 2, 3 } } }); logger.Flush(); }); }
private static async Task <bool> PublishEventGridEvent(APITask task, string taskBody, string eventGridTopicUri, string eventGridKey, AppInsightsLogger appInsightsLogger) { var ev = new EventGridEvent() { Id = task.TaskId, EventType = "task", Data = taskBody, EventTime = DateTime.Parse(task.Timestamp), Subject = task.Endpoint, DataVersion = "1.0" }; string topicHostname = new Uri(eventGridTopicUri).Host; TopicCredentials topicCredentials = new TopicCredentials(eventGridKey); EventGridClient client = new EventGridClient(topicCredentials); try { await client.PublishEventsAsync(topicHostname, new List <EventGridEvent>() { ev }); } catch (Exception ex) { appInsightsLogger.LogError(ex, task.Endpoint, task.TaskId); return(false); } return(true); }
public static async Task ServiceBusQueueProcessorAsync( [ServiceBusTrigger("%SERVICE_BUS_QUEUE%")] Message message, MessageReceiver messageReceiver, ILogger logger) { var timestamp = DateTime.UtcNow; var queueName = Environment.GetEnvironmentVariable("SERVICE_BUS_QUEUE", EnvironmentVariableTarget.Process); logger.LogTrace($@"[{message.UserProperties[@"TaskId"]}]: Message received at {timestamp}: {JObject.FromObject(message)}"); AppInsightsLogger appInsightsLogger = new AppInsightsLogger(logger, LOGGING_SERVICE_NAME + ": " + queueName, LOGGING_SERVICE_VERSION); var enqueuedTime = message.ScheduledEnqueueTimeUtc; var elapsedTimeMs = (timestamp - enqueuedTime).TotalMilliseconds; var taskId = message.UserProperties["TaskId"].ToString(); var backendUri = message.UserProperties["Uri"].ToString(); var messageBody = Encoding.UTF8.GetString(message.Body); try { appInsightsLogger.LogInformation($"Sending request to {backendUri} for taskId {taskId} from queue {queueName}. Queued for {elapsedTimeMs/60} seconds.", backendUri, taskId); var client = new HttpClient(); client.DefaultRequestHeaders.Add("taskId", taskId); var httpContent = new StringContent(messageBody, Encoding.UTF8, "application/json"); var res = await client.PostAsync(backendUri, httpContent); if (res.StatusCode == (System.Net.HttpStatusCode) 429) // Special return value indicating that the service is busy. { var retryDelay = int.Parse(Environment.GetEnvironmentVariable(QUEUE_RETRY_DELAY_MS_VARIABLE_NAME, EnvironmentVariableTarget.Process)); appInsightsLogger.LogInformation($"Service is busy. Will try again in {retryDelay/1000} seconds.", backendUri, taskId); await UpdateTaskStatus(taskId, backendUri, messageBody, $"Awaiting service availability. Queued for {elapsedTimeMs/60} seconds.", "created", appInsightsLogger); // Artifical delay is needed since the ServiceBusTrigger will retry immediately. await Task.Delay(retryDelay); await messageReceiver.AbandonAsync(message.SystemProperties.LockToken); throw new ApplicationException($"Service is busy. Will try again in {retryDelay/1000} seconds."); } else if (!res.IsSuccessStatusCode) { await messageReceiver.CompleteAsync(message.SystemProperties.LockToken); //Need to complete even though we have failure. This removes it from the queue to avoid an infinite state. appInsightsLogger.LogWarning($"Unable to send request to backend. Status: {res.StatusCode.ToString()}, Reason: {res.ReasonPhrase}", backendUri, taskId); await UpdateTaskStatus(taskId, backendUri, messageBody, $"Unable to send request to backend.", "failed", appInsightsLogger); } else { await messageReceiver.CompleteAsync(message.SystemProperties.LockToken); appInsightsLogger.LogInformation($"taskId {taskId} has successfully been pushed to the backend from queue {queueName}. Queue time: {elapsedTimeMs/60} seconds.", backendUri, taskId); } } catch (Exception ex) { appInsightsLogger.LogError(ex, backendUri, taskId); } }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger logger) { AppInsightsLogger appInsightsLogger = new AppInsightsLogger(logger, LOGGING_SERVICE_NAME, LOGGING_SERVICE_VERSION); var redisOperation = "increment"; if (req.Body != null) { string body = string.Empty; try { using (StreamReader reader = new StreamReader(req.Body)) { if (reader.BaseStream.Length > 0) { body = reader.ReadToEnd(); ProcessingUpdate update = null; try { update = JsonConvert.DeserializeObject <ProcessingUpdate>(body); } catch { update = JsonConvert.DeserializeObject <ProcessingUpdate[]>(body)[0]; } if (update == null) { appInsightsLogger.LogWarning("Parameters missing. Unable to update processing count."); return(new BadRequestResult()); } else { return(await RedisUpsert(update, appInsightsLogger, redisOperation)); } } else { appInsightsLogger.LogWarning("Parameters missing. Unable to update processing count."); return(new BadRequestResult()); } } } catch (Exception ex) { appInsightsLogger.LogError(ex); appInsightsLogger.LogRedisUpsert("Redis upsert failed.", redisOperation, DateTime.UtcNow.ToString(), body); return(new StatusCodeResult(500)); } } else { return(new BadRequestResult()); } }
public void Test_Telemetry_LogExceptionWithMessage() { // Assert AssertExtensions.DoesNotThrow(() => { // Arrange var logger = new AppInsightsLogger("test", LogLevel.Trace); // Act logger.LogError(new Exception(), "Some error"); logger.Flush(); }); }
public void Test_Telemetry_ExceptionWithDictionary() { // Assert AssertExtensions.DoesNotThrow(() => { // Arrange var logger = new AppInsightsLogger("test", LogLevel.Trace); // Act logger.LogError(new Exception(), new Dictionary <string, string> { { "a", "a" } }); logger.Flush(); }); }
public void Test_Telemetry_LogExceptionWithMessageAndProperties() { // Assert AssertExtensions.DoesNotThrow(() => { // Arrange var logger = new AppInsightsLogger("test", LogLevel.Trace); // Act logger.LogError(new Exception(), "Some error", new Dictionary <string, string>() { { "test", "test" } }); logger.Flush(); }); }
public void Test_Telemetry_LogLevelNone() { // Assert AssertExtensions.DoesNotThrow(() => { // Arrange var logger = new AppInsightsLogger("test", LogLevel.None); // Act logger.Log(LogLevel.Trace, new EventId(1, "test"), "test", null, null); logger.LogError(new Exception(), null, null, null, null); logger.LogMetric("test", Double.MinValue); logger.Log(LogLevel.Trace, new EventId(1, "test"), "test", null, null); logger.Flush(); }); }
private static async Task <bool> PublishServiceBusQueueEvent(APITask task, string taskBody, AppInsightsLogger appInsightsLogger) { string serviceBusConnectionString = Environment.GetEnvironmentVariable(SERVICE_BUS_CONNECTION_STRING_VARIABLE_NAME, EnvironmentVariableTarget.Process); // A queue must be created for each endpoint, ex: // The queue name for http://52.224.89.22/v1/paws/consolidate should be http52.224.89.22v1pawsconsolidate var queueName = task.Endpoint.Replace(".", string.Empty); queueName = queueName.Replace("/", string.Empty); queueName = queueName.Replace(":", string.Empty); var messageProperties = new Dictionary <string, object> { { "TaskId", task.TaskId }, { "Uri", task.Endpoint } }; IQueueClient queueClient = new QueueClient(serviceBusConnectionString, queueName); try { Message message = new Message(Encoding.UTF8.GetBytes(taskBody)); message.UserProperties["TaskId"] = task.TaskId; message.UserProperties["Uri"] = task.Endpoint; // Write the body of the message to the console. Console.WriteLine($"Sending taskId {task.TaskId} to queue {queueName}"); // Send the message to the queue. await queueClient.SendAsync(message); await queueClient.CloseAsync(); appInsightsLogger.LogInformation($"Sent task {task.TaskId} to queue {queueName}.", task.Endpoint, task.TaskId); } catch (Exception ex) { appInsightsLogger.LogError(ex, task.Endpoint, task.TaskId); return(false); } return(true); }
private static void ExecuteTransaction(ITransaction transaction, APITask task, AppInsightsLogger appInsightsLogger) { int attempt = 1; TimeSpan delaySeconds = TimeSpan.FromSeconds(0); var tran = transaction.ExecuteAsync(); while (transaction.Wait(tran) == false) { Task.Delay(delaySeconds).GetAwaiter().GetResult(); if (attempt >= 5) { var ex = new Exception("Unable to complete redis transaction."); appInsightsLogger.LogError(ex, task.Endpoint, task.TaskId); throw ex; } delaySeconds.Add(TimeSpan.FromSeconds(2)); attempt++; } }
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger logger) { IDatabase db = null; AppInsightsLogger appInsightsLogger = new AppInsightsLogger(logger, LOGGING_SERVICE_NAME, LOGGING_SERVICE_VERSION); var redisOperation = "insert"; APITask task = null; if (req.Body != null) { string body = string.Empty; try { using (StreamReader reader = new StreamReader(req.Body)) { if (reader.BaseStream.Length > 0) { body = reader.ReadToEnd(); logger.LogInformation("body: " + body); try { appInsightsLogger.LogInformation("DeserializeObject<APITask>(body)"); task = JsonConvert.DeserializeObject <APITask>(body); } catch { appInsightsLogger.LogInformation("DeserializeObject<APITask>(body[])"); task = JsonConvert.DeserializeObject <APITask[]>(body)[0]; } appInsightsLogger.LogInformation("task.PublishToGrid: " + task.PublishToGrid.ToString(), task.Endpoint, task.TaskId); } else { appInsightsLogger.LogWarning("Parameters missing. Unable to create task."); return(new BadRequestResult()); } } } catch (Exception ex) { appInsightsLogger.LogError(ex); appInsightsLogger.LogRedisUpsert("Redis upsert failed.", redisOperation, task.Timestamp, body); return(new StatusCodeResult(500)); } } else { appInsightsLogger.LogWarning("Parameters missing. Unable to create task."); return(new BadRequestResult()); } if (!String.IsNullOrWhiteSpace(task.TaskId)) { appInsightsLogger.LogInformation("Updating status", task.Endpoint, task.TaskId); redisOperation = "update"; } else { task.TaskId = Guid.NewGuid().ToString(); appInsightsLogger.LogInformation("Generated new taskId: " + task.TaskId, task.Endpoint, task.TaskId); } task.Timestamp = DateTime.UtcNow.ToString(); try { db = RedisConnection.GetDatabase(); } catch (Exception ex) { appInsightsLogger.LogError(ex, task.Endpoint, task.TaskId); appInsightsLogger.LogRedisUpsert("Redis upsert failed.", redisOperation, task.Timestamp, task.Endpoint, task.TaskId); return(new StatusCodeResult(500)); } string serializedTask = string.Empty; try { var taskBody = task.Body; task.Body = null; serializedTask = JsonConvert.SerializeObject(task); RedisValue res = RedisValue.Null; appInsightsLogger.LogInformation("Setting Redis task record", task.Endpoint, task.TaskId); var upsertTransaction = db.CreateTransaction(); upsertTransaction.StringSetAsync(task.TaskId, serializedTask); // Get seconds since epoch TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1)); int timestamp = (int)ts.TotalSeconds; appInsightsLogger.LogInformation(string.Format("Adding backend status '{0}' for endpoint.", task.BackendStatus), task.Endpoint, task.TaskId); upsertTransaction.SortedSetAddAsync(string.Format("{0}_{1}", task.EndpointPath, task.BackendStatus), new SortedSetEntry[] { new SortedSetEntry(task.TaskId, timestamp) }); if (task.BackendStatus.Equals(BACKEND_STATUS_RUNNING)) { upsertTransaction.SortedSetRemoveAsync(string.Format("{0}_{1}", task.EndpointPath, BACKEND_STATUS_CREATED), task.TaskId); } else if (task.BackendStatus.Equals(BACKEND_STATUS_COMPLETED) || task.BackendStatus.Equals(BACKEND_STATUS_FAILED)) { upsertTransaction.SortedSetRemoveAsync(string.Format("{0}_{1}", task.EndpointPath, BACKEND_STATUS_RUNNING), task.TaskId); } bool isSubsequentPipelineCall = false; bool isPublish = false; bool.TryParse(task.PublishToGrid.ToString(), out isPublish); if (isPublish == true || task.PublishToGrid == true) { if (string.IsNullOrEmpty(taskBody)) { appInsightsLogger.LogInformation("It IS a pipeline call", task.Endpoint, task.TaskId); // This is a subsequent pipeline publish. isSubsequentPipelineCall = true; } else { appInsightsLogger.LogInformation("Adding body to redis: " + taskBody, task.Endpoint, task.TaskId); upsertTransaction.StringSetAsync(string.Format("{0}_{1}", task.TaskId, GRID_PUBLISH_RECORD_KEY), taskBody); } } ExecuteTransaction(upsertTransaction, task, appInsightsLogger); if (isSubsequentPipelineCall) { // We have to get the original body, since it's currently empty. taskBody = db.StringGet(string.Format("{0}_{1}", task.TaskId, GRID_PUBLISH_RECORD_KEY)); appInsightsLogger.LogInformation("Stored body: " + taskBody, task.Endpoint, task.TaskId); } if (task.PublishToGrid) { if (PublishEvent(task, taskBody, appInsightsLogger) == false) { // Move task to failed var updateTransaction = db.CreateTransaction(); task.Status = "Failed - unable to send to backend service."; task.BackendStatus = BACKEND_STATUS_FAILED; string updateBody = JsonConvert.SerializeObject(task); updateTransaction.StringSetAsync(task.TaskId, updateBody); updateTransaction.SortedSetAddAsync(string.Format("{0}_{1}", task.EndpointPath, task.BackendStatus), new SortedSetEntry[] { new SortedSetEntry(task.TaskId, timestamp) }); updateTransaction.SortedSetRemoveAsync(string.Format("{0}_{1}", task.EndpointPath, BACKEND_STATUS_CREATED), task.TaskId); ExecuteTransaction(updateTransaction, task, appInsightsLogger); } } //LogSetCount(string.Format("{0}_{1}", task.EndpointPath, task.BackendStatus), task, db, appInsightsLogger); appInsightsLogger.LogRedisUpsert("Redis upsert successful.", redisOperation, task.Timestamp, serializedTask, task.Endpoint, task.TaskId); } catch (Exception ex) { appInsightsLogger.LogError(ex, task.Endpoint, task.TaskId); appInsightsLogger.LogRedisUpsert("Redis upsert failed.", redisOperation, task.Timestamp, serializedTask, task.Endpoint, task.TaskId); return(new StatusCodeResult(500)); } return(new OkObjectResult(serializedTask)); }
public static async Task <IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger logger) { AppInsightsLogger appInsightsLogger = new AppInsightsLogger(logger, LOGGING_SERVICE_NAME, LOGGING_SERVICE_VERSION); string response = string.Empty; const string SubscriptionValidationEvent = "Microsoft.EventGrid.SubscriptionValidationEvent"; string requestContent = new StreamReader(req.Body).ReadToEnd(); EventGridEvent[] eventGridEvents = JsonConvert.DeserializeObject <EventGridEvent[]>(requestContent); // We should only have 1 event foreach (EventGridEvent eventGridEvent in eventGridEvents) { JObject dataObject = eventGridEvent.Data as JObject; // Deserialize the event data into the appropriate type based on event type if (string.Equals(eventGridEvent.EventType, SubscriptionValidationEvent, StringComparison.OrdinalIgnoreCase)) { var eventData = dataObject.ToObject <SubscriptionValidationEventData>(); appInsightsLogger.LogInformation($"Got SubscriptionValidation event data, validation code: {eventData.ValidationCode}, topic: {eventGridEvent.Topic}", string.Empty); // Do any additional validation (as required) and then return back the below response var responseData = new SubscriptionValidationResponse(); responseData.ValidationResponse = eventData.ValidationCode; return(new OkObjectResult(responseData)); } else { var backendUri = new Uri(eventGridEvent.Subject); var client = new HttpClient(); var stringContent = new StringContent(eventGridEvent.Data.ToString(), Encoding.UTF8, "application/json"); try { appInsightsLogger.LogInformation($"Sending request to {backendUri} for taskId {eventGridEvent.Id}.", eventGridEvent.Subject, eventGridEvent.Id); client.DefaultRequestHeaders.Add("taskId", eventGridEvent.Id); var res = await client.PostAsync(backendUri, stringContent); if (res.StatusCode == (System.Net.HttpStatusCode) 429) // Special return value indicating that the service is busy. Let event grid handle the retries. { appInsightsLogger.LogInformation("Backend service is busy. Event grid will retry with backoff.", eventGridEvent.Subject, eventGridEvent.Id); } else if (!res.IsSuccessStatusCode) { appInsightsLogger.LogWarning($"Unable to send request to backend. Status: {res.StatusCode.ToString()}, Reason: {res.ReasonPhrase}", eventGridEvent.Subject, eventGridEvent.Id); } appInsightsLogger.LogInformation("Request has successfully been pushed to the backend.", eventGridEvent.Subject, eventGridEvent.Id); return(new StatusCodeResult((int)(res.StatusCode))); } catch (Exception ex) { appInsightsLogger.LogError(ex, eventGridEvent.Subject, eventGridEvent.Id); return(new StatusCodeResult(500)); } } } return(new OkResult()); }