Repository: open-telemetry/opentelemetry-java
File: sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java (Java)

Code before:
    private final List<ReadableSpan> spansList;

    private Worker(
        SpanExporter spanExporter,
        long scheduleDelayMillis,
        int maxQueueSize,
        int maxExportBatchSize,
        int exporterTimeoutMillis) {
      this.spanExporter = spanExporter;
      this.scheduleDelayMillis = scheduleDelayMillis;
      this.maxQueueSize = maxQueueSize;
      this.halfMaxQueueSize = maxQueueSize >> 1;
      this.maxExportBatchSize = maxExportBatchSize;
      this.spansList = new ArrayList<>(maxQueueSize);
      this.exporterTimeoutMillis = exporterTimeoutMillis;

      Meter meter = OpenTelemetry.getMeterRegistry().get("opentelemetry");
      LongCounter droppedSpansCounter =
          meter
              .longCounterBuilder("droppedSpans")
              .setMonotonic(true)
              .setUnit("1")
              .setDescription(
                  "The number of spans dropped by the BatchSpansProcessor due to high throughput.")
              .build();
      this.droppedSpans =
          droppedSpansCounter.bind(
              meter.createLabelSet("spanProcessorType", BatchSpansProcessor.class.getSimpleName()));
    }

    private void addSpan(ReadableSpan span) {
      synchronized (monitor) {
        if (spansList.size() == maxQueueSize) {
          droppedSpans.add(1);
          return;
        }
        // TODO: Record a gauge for referenced spans.
        spansList.add(span);
        // Notify the worker thread that at half of the queue is available. It will take
        // time anyway for the thread to wake up.
        if (spansList.size() >= halfMaxQueueSize) {
          monitor.notifyAll();
        }
      }
    }

    @Override
    public void run() {
      while (!Thread.currentThread().isInterrupted()) {
        // Copy all the batched spans in a separate list to release the monitor lock asap to
        // avoid blocking the producer thread.

Review comment (bug):
As a limitation of the current API for metrics the bound needs to be static or you need to `unbind` it, maybe in shutdown?

Also creating the same instrument twice may fail (we have not implement that check yet). So the instrument can easily be static :) 

Code after:
              @Override
              public Thread newThread(Runnable r) {
                return new Thread(r, EXPORTER_THREAD_NAME);
              }
            });

    private static final Logger logger = Logger.getLogger(Worker.class.getName());
    private final SpanExporter spanExporter;
    private final long scheduleDelayMillis;
    private final int maxQueueSize;
    private final int maxExportBatchSize;
    private final int halfMaxQueueSize;
    private final Object monitor = new Object();
    private final int exporterTimeoutMillis;

    @GuardedBy("monitor")
    private final List<ReadableSpan> spansList;

    private Worker(
        SpanExporter spanExporter,
        long scheduleDelayMillis,
        int maxQueueSize,
        int maxExportBatchSize,
        int exporterTimeoutMillis) {
      this.spanExporter = spanExporter;
      this.scheduleDelayMillis = scheduleDelayMillis;
      this.maxQueueSize = maxQueueSize;
      this.halfMaxQueueSize = maxQueueSize >> 1;
      this.maxExportBatchSize = maxExportBatchSize;
      this.spansList = new ArrayList<>(maxQueueSize);
      this.exporterTimeoutMillis = exporterTimeoutMillis;
    }

    private void addSpan(ReadableSpan span) {
      synchronized (monitor) {
        if (spansList.size() == maxQueueSize) {
          droppedSpans.add(1);
          return;
        }
        // TODO: Record a gauge for referenced spans.
        spansList.add(span);
        // Notify the worker thread that at half of the queue is available. It will take
        // time anyway for the thread to wake up.
        if (spansList.size() >= halfMaxQueueSize) {
          monitor.notifyAll();
        }
      }
    }

    @Override
    public void run() {