You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8应用中使用OTEL实现长时间运行作业的可观测性方案咨询

.NET 8应用中使用OTEL实现长时间运行作业的可观测性方案咨询

嘿,针对你这个.NET 8里长时间运行集成作业的可观测性需求,我结合Azure Monitor OTEL的使用经验给你梳理下可行的方案,刚好能解决你提到的那些痛点:

先明确你的核心需求

  • 能够在Azure Insights中实时看到作业启动状态,不用等它跑完
  • 能追踪作业运行中的进度,比如“开始执行步骤X”这类细节
  • 作业相关的所有数据必须100%上报,不能被采样过滤
  • 常规Web请求流量遵循配置的采样率,不影响整体成本
  • 非作业类的日志只上报Warning及以上级别,减少冗余数据

你提到的核心挑战确实戳中了痛点

用单个Span/Activity包裹整个长作业确实不行,因为OTEL的Span默认是在结束时才上报,这样就没法实时看到进度了。那咱们换个思路来解决:

方案1:拆分作业为实时上报的事件/小Span

既然整段Span等不及,那就把作业拆成多个阶段的实时事件:

  • 作业启动瞬间,立刻打一条作业启动日志,带上唯一的JobId作为标识,这样Insights里马上就能看到作业已启动
  • 每个步骤开始、完成时都打对应的进度日志,同样带上JobId,方便后续通过这个ID关联整个作业的所有日志
  • 作业最终完成或失败时,打一条收尾日志,标记作业的最终状态

方案2:自定义采样规则,区分作业和Web流量

要实现作业数据不被采样、Web请求按配置采样,咱们可以给OTEL加个自定义采样器:

  • 给所有作业相关的Activity/日志打上专属标签,比如is_integration_job: true
  • 配置一个复合采样器:对带这个标签的流量强制RecordAndSample(全部上报);对Web请求用你预设的概率采样率(比如10%)

方案3:日志级别过滤,精准控制上报内容

针对非作业日志只上报Warning及以上的需求,可以通过OTEL的日志处理器来过滤:

  • 自定义一个日志处理器,检查每条日志的上下文:如果没有JobId标识(非作业日志),就把级别低于Warning的日志给屏蔽掉
  • 作业相关的日志则不受限制,所有级别都能上报,保证进度追踪的完整性

给你贴点实用的代码参考

OTEL采样配置

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation()
        .AddAzureMonitorInstrumentation()
        // 复合采样器:Web请求用10%采样,作业流量强制全量
        .SetSampler(new CompositeSampler(
            new ParentBasedSampler(new TraceIdRatioBasedSampler(0.1)),
            new AttributeBasedSampler("is_integration_job", "true", SamplingDecision.RecordAndSample)
        )));

作业执行时的日志示例

public async Task RunLongRunningIntegrationJob(Guid jobId)
{
    // 用日志Scope绑定JobId,后续所有日志都会自动带上这个标识
    using var jobScope = _logger.BeginScope(new Dictionary<string, object> { ["JobId"] = jobId });
    
    _logger.LogInformation("集成作业[{JobId}]已启动,开始执行", jobId);

    // 步骤1执行
    _logger.LogInformation("集成作业[{JobId}]开始执行步骤1:数据同步", jobId);
    await ExecuteDataSyncStep(jobId);
    _logger.LogInformation("集成作业[{JobId}]完成步骤1:数据同步", jobId);

    // 步骤2执行
    _logger.LogInformation("集成作业[{JobId}]开始执行步骤2:校验数据", jobId);
    var validationResult = await ValidateDataStep(jobId);
    if (!validationResult.IsValid)
    {
        _logger.LogWarning("集成作业[{JobId}]步骤2校验不通过:{ErrorMessage}", jobId, validationResult.ErrorMessage);
    }
    _logger.LogInformation("集成作业[{JobId}]完成步骤2:校验数据", jobId);

    // 后续步骤...

    _logger.LogInformation("集成作业[{JobId}]全部执行完成", jobId);
}

日志过滤处理器

builder.Logging.AddOpenTelemetry(options =>
{
    options.AddAzureMonitorLogExporter(exporterOptions => 
    {
        // 这里配置你的Azure Monitor导出器参数
    });
    // 添加自定义过滤处理器
    options.AddProcessor(new NonJobLogFilterProcessor());
});

public class NonJobLogFilterProcessor : BaseProcessor<LogRecord>
{
    public override void OnEnd(LogRecord logRecord)
    {
        // 非作业日志(没有JobId标识)只保留Warning及以上级别
        if (!logRecord.Properties.TryGetValue("JobId", out _) && logRecord.LogLevel < LogLevel.Warning)
        {
            logRecord.Suppress = true; // 屏蔽这条日志,不发送到Azure Monitor
        }
        base.OnEnd(logRecord);
    }
}

最后给你提个小建议

在Azure Monitor里,你可以用Kusto查询通过JobId快速定位整个作业的所有日志,比如:

traces
| where customDimensions.JobId == "你的作业ID"
| order by timestamp asc

这样就能完整看到作业从启动到结束的全流程进度啦。

备注:内容来源于stack exchange,提问作者mortenbock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 07:39:50