Skip to content

Instantly share code, notes, and snippets.

@odinserj
Created November 15, 2024 07:55
Show Gist options
  • Save odinserj/06e18950b5bf3083a5aed0ed06d3d18a to your computer and use it in GitHub Desktop.
Save odinserj/06e18950b5bf3083a5aed0ed06d3d18a to your computer and use it in GitHub Desktop.
using System.Diagnostics;
using Hangfire.Client;
using Hangfire.Server;
using Hangfire.States;
using Hangfire.Storage;
namespace ActivitiesSupport
{
public sealed class DiagnosticsActivityFilter : IClientFilter, IServerFilter, IApplyStateFilter
{
private const string ActivityItemsKeyName = "Diagnostics.Activity";
private const string ParentIdParameterName = "My.Activity.ParentId";
private readonly ActivitySource _activitySource;
public DiagnosticsActivityFilter(ActivitySource activitySource)
{
_activitySource = activitySource ?? throw new ArgumentNullException(nameof(activitySource));
}
public void OnCreating(CreatingContext context)
{
using var parentActivity = _activitySource.StartActivity(
$"background job '{context.Job.Type.Name}.{context.Job.Method.Name}'");
parentActivity?.SetTag("job.type", context.Job.Type.FullName);
parentActivity?.SetTag("job.method", context.Job.Method.Name);
parentActivity?.SetTag("job.state", context.InitialState.Name);
parentActivity?.SetTag("job.storage", context.Storage.ToString());
if (parentActivity?.Id != null)
{
context.SetJobParameter(ParentIdParameterName, parentActivity.Id);
}
var activity = _activitySource.StartActivity(
$"create job '{context.Job.Type.Name}.{context.Job.Method.Name}'",
ActivityKind.Producer);
context.Items[ActivityItemsKeyName] = activity;
}
public void OnCreated(CreatedContext context)
{
if (context.Items.TryGetValue(ActivityItemsKeyName, out var item) &&
item is Activity activity)
{
if (context.Exception == null)
{
activity.SetStatus(ActivityStatusCode.Ok, "Created");
activity.SetTag("job.id", context.BackgroundJob.Id);
activity.SetTag("job.created", context.BackgroundJob.CreatedAt.ToString("O"));
}
else
{
activity.SetStatus(ActivityStatusCode.Error, "Exception");
activity.SetTag("exception.type", context.Exception.GetType().FullName);
activity.SetTag("exception.message", context.Exception.Message);
activity.SetTag("exception.stacktrace", context.Exception.ToString());
}
activity.Dispose();
}
}
public void OnPerforming(PerformingContext context)
{
var parentId = context.GetJobParameter<string>(ParentIdParameterName);
ActivityContext.TryParse(parentId, null, isRemote: true, out var parentCtx);
var activity = _activitySource.StartActivity(
$"perform job '{context.BackgroundJob.Job.Type.Name}.{context.BackgroundJob.Job.Method.Name}'",
ActivityKind.Consumer,
parentCtx);
context.Items[ActivityItemsKeyName] = activity;
}
public void OnPerformed(PerformedContext context)
{
if (context.Items.TryGetValue(ActivityItemsKeyName, out var item) && item is Activity activity)
{
if (context.Exception == null)
{
activity.SetStatus(ActivityStatusCode.Ok, "Performed");
}
else
{
activity.SetStatus(ActivityStatusCode.Error, "Exception");
activity.SetTag("exception.type", context.Exception.GetType().FullName);
activity.SetTag("exception.message", context.Exception.Message);
activity.SetTag("exception.stacktrace", context.Exception.ToString());
}
activity.Dispose();
}
}
public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
var parentId = context.GetJobParameter<string>(ParentIdParameterName);
ActivityContext.TryParse(parentId, null, isRemote: true, out var parentCtx);
using var activity = _activitySource.StartActivity(
$"state change to '{context.NewState.Name}'",
ActivityKind.Consumer,
parentCtx);
activity?.SetTag("state.old", context.OldStateName);
activity?.SetTag("state.new", context.NewState.Name);
activity?.SetTag("state.reason", context.NewState.Reason);
}
public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment