public async Task <Hipstershop.Cart> GetCartAsync(string userId) { Log.Information("GetCartAsync called with userId={userId}", userId); var transaction = Elastic.Apm.Agent.Tracer.CurrentTransaction; ISpan span = transaction.StartSpan("GetCartAsync", ApiConstants.TypeDb, ApiConstants.ActionQuery); span.Labels["userId"] = userId; try { EnsureRedisConnected(); var db = redis.GetDatabase(); // Access the cart from the cache var value = await db.HashGetAsync(userId, CART_FIELD_NAME); if (!value.IsNull) { return(Hipstershop.Cart.Parser.ParseFrom(value)); } // We decided to return empty cart in cases when user wasn't in the cache before return(new Hipstershop.Cart()); } catch (Exception ex) { Log.Error(ex, "Can't access cart storage"); span.CaptureException(ex); throw new RpcException(new Status(StatusCode.FailedPrecondition, $"Can't access cart storage. {ex}")); } finally { span.End(); } }
public async Task EmptyCartAsync(string userId) { Log.Information("EmptyCartAsync called with userId={userId}", userId); var transaction = Elastic.Apm.Agent.Tracer.CurrentTransaction; ISpan span = transaction.StartSpan("EmptyCartAsync", ApiConstants.TypeDb, ApiConstants.ActionQuery); span.Labels["userId"] = userId; try { EnsureRedisConnected(); var db = redis.GetDatabase(); // Update the cache with empty cart for given user await db.HashSetAsync(userId, new[] { new HashEntry(CART_FIELD_NAME, emptyCartBytes) }); } catch (Exception ex) { Log.Error(ex, "Can't access cart storage"); span.CaptureException(ex); throw new RpcException(new Status(StatusCode.FailedPrecondition, $"Can't access cart storage. {ex}")); } finally { span.End(); } }
public async Task AddItemAsync(string userId, string productId, int quantity) { Log.Information("AddItemAsync called with userId={userId}, productId={productId}, quantity={quantity}", userId, productId, quantity); var transaction = Elastic.Apm.Agent.Tracer.CurrentTransaction; ISpan span = transaction.StartSpan("AddItemAsync", ApiConstants.TypeDb, ApiConstants.ActionQuery); span.Labels["userId"] = userId; span.Labels["productId"] = productId; span.Labels["quantity"] = quantity.ToString(); try { EnsureRedisConnected(); var db = redis.GetDatabase(); // Access the cart from the cache var value = await db.HashGetAsync(userId, CART_FIELD_NAME); Hipstershop.Cart cart; if (value.IsNull) { cart = new Hipstershop.Cart(); cart.UserId = userId; cart.Items.Add(new Hipstershop.CartItem { ProductId = productId, Quantity = quantity }); } else { cart = Hipstershop.Cart.Parser.ParseFrom(value); var existingItem = cart.Items.SingleOrDefault(i => i.ProductId == productId); if (existingItem == null) { cart.Items.Add(new Hipstershop.CartItem { ProductId = productId, Quantity = quantity }); } else { existingItem.Quantity += quantity; } } await db.HashSetAsync(userId, new[] { new HashEntry(CART_FIELD_NAME, cart.ToByteArray()) }); } catch (Exception ex) { Log.Error(ex, "Can't access cart storage"); span.CaptureException(ex); throw new RpcException(new Status(StatusCode.FailedPrecondition, $"Can't access cart storage. {ex}")); } finally { span.End(); } }
internal static void EndSpan(ApmAgent agent, IDbCommand command, ISpan span, Exception exception) { if (span != null) { var outcome = Outcome.Success; if (exception != null) { span.CaptureException(exception); outcome = Outcome.Failure; } agent.TracerInternal.DbSpanCommon.EndSpan(span, command, outcome); } }
public bool Ping() { var transaction = Elastic.Apm.Agent.Tracer.CurrentTransaction; ISpan span = transaction.StartSpan("GetCartAsync", ApiConstants.TypeDb, ApiConstants.ActionQuery); try { var cache = redis.GetDatabase(); var res = cache.Ping(); return(res != TimeSpan.Zero); } catch (Exception ex) { span.CaptureException(ex); return(false); } finally { span.End(); } }
/// <summary> /// Registers a continuation on the task. /// Within the continuation it ends the transaction and captures errors /// </summary> /// <param name="task">Task.</param> /// <param name="transaction">Transaction.</param> private void RegisterContinuation(Task task, ISpan span) { task.ContinueWith((t) => { if (t.IsFaulted) { if (t.Exception != null) { if (t.Exception is AggregateException aggregateException) { ExceptionFilter.Capture( aggregateException.InnerExceptions.Count == 1 ? aggregateException.InnerExceptions[0] : aggregateException.Flatten(), span); } else { ExceptionFilter.Capture(t.Exception, span); } } else { span.CaptureError("Task faulted", "A task faulted", new StackTrace().GetFrames()); } } else if (t.IsCanceled) { if (t.Exception == null) { span.CaptureError("Task canceled", "A task was canceled", new StackTrace().GetFrames()); //TODO: this async stacktrace is hard to use, make it readable! } else { span.CaptureException(t.Exception); } } span.End(); }, System.Threading.CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); }
/// <summary> /// Helper method used by <see cref="CachedWrapperDelegate{TActionDelegate}"/> to create a delegate /// </summary> /// <param name="originalHandler">The original delivery report handler </param> /// <param name="span">A <see cref="ISpan"/> that can be manipulated when the action is invoked</param> /// <typeparam name="TDeliveryReport">Type of the delivery report</typeparam> /// <returns>The wrapped action</returns> public static Action <TDeliveryReport> WrapAction <TDeliveryReport>(Action <TDeliveryReport> originalHandler, ISpan span) => value => { if (value.TryDuckCast <IDeliveryReport>(out var report)) { var isError = report?.Error is not null && report.Error.IsError; if (isError) { // Set the error manually, as we don't have an exception + stack trace here // Should we create a stack trace manually? var ex = new Exception(report.Error.ToString()); span.CaptureException(ex); } if (report?.Partition is not null) { span.SetLabel("partition", report.Partition.ToString()); } // Won't have offset if is error if (!isError && report?.Offset is not null) { span.SetLabel("offset", report.Offset.ToString()); } } // call previous delegate try { originalHandler(value); } finally { span.End(); } };
internal static bool Capture(Exception e, ISpan span) { span.CaptureException(e); return(false); }
private static void RegisterError(ISpan span, IApiCallDetails response) { if (response.Success) { return; } var exception = response.OriginalException ?? response.AuditTrail.FirstOrDefault(a => a.Exception != null)?.Exception; var f = PipelineFailure.Unexpected; // report inner exception stack traces for these directly if possible if (exception is ElasticsearchClientException es) { f = es.FailureReason ?? f; exception = es.InnerException ?? es; } if (exception is UnexpectedElasticsearchClientException un) { f = un.FailureReason ?? f; exception = un.InnerException ?? un; } var culprit = "Client Error"; var message = $"{f.GetStringValue()} {exception?.Message}"; var stackFrames = exception == null ? null : new StackTrace(exception, true).GetFrames(); if (stackFrames == null || stackFrames.Length == 0) { stackFrames = new StackTrace(true).GetFrames(); } var causeOnServer = false; if (response.ResponseBodyInBytes != null) { using var memoryStream = new MemoryStream(response.ResponseBodyInBytes); if (ServerError.TryCreate(memoryStream, out var serverError) && serverError != null) { causeOnServer = true; culprit = $"Elasticsearch Server Error: {serverError.Error.Type}"; message = $"The server returned a ({response.HttpStatusCode}) and indicated: " + ( serverError.Error?.CausedBy?.Reason ?? serverError.Error?.CausedBy?.Type ?? serverError.Error?.RootCause.FirstOrDefault()?.Reason ?? serverError.Error?.Reason ?? "Response did not indicate a server error, usually means no json was with an error key was returned."); } } if (exception == null && !causeOnServer) { return; } if (causeOnServer && string.IsNullOrEmpty(message)) { return; } if (causeOnServer) { span.CaptureError(message, culprit, stackFrames); } else { span.CaptureException(exception); } }
public void Error(Exception ex) { span?.CaptureException(ex); }