Instrumenting an application against a vendor SDK makes the vendor a dependency in the code; instrumenting against a collector makes it a line in a configuration file.
receivers:
otlp: { protocols: { grpc: {}, http: {} } }
processors:
batch: {}
tail_sampling:
policies:
- { type: status_code, status_code: { status_codes: [ERROR] } }
- { type: latency, latency: { threshold_ms: 2000 } }
exporters:
otlp/vendor: { endpoint: "..." }
Tail sampling in the collector is the other reason to run one: it buffers spans until a trace completes and can then keep every error and everything slow, which head-based sampling in the application cannot do. The buffering is memory and the collector becomes a component with its own capacity planning, which is the honest cost.