diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonDataWriter.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonDataWriter.java index 54f4d7ff7b..676eea7bc7 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonDataWriter.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonDataWriter.java @@ -30,6 +30,8 @@ public class DslJsonDataWriter implements DataWriter { private final Reporter reporter; + private final StringBuilder replaceBuilder = new StringBuilder(); + public DslJsonDataWriter(JsonWriter jw, Reporter reporter) { this.jw = jw; this.reporter = reporter; @@ -62,6 +64,8 @@ public void write(StructureType type) { case NEW_LINE: jw.writeByte((byte) '\n'); break; + default: + throw new IllegalArgumentException(); } } @@ -70,6 +74,16 @@ public void writeFieldName(String name) { DslJsonSerializer.writeFieldName(name, jw); } + @Override + public void writeFieldName(String name, boolean sanitized) { + if (sanitized) { + DslJsonSerializer.writeStringValue(DslJsonSerializer.sanitizePropertyName(name, replaceBuilder), replaceBuilder, jw); + jw.writeByte(JsonWriter.SEMI); + } else { + writeFieldName(name); + } + } + @Override public void serialize(boolean value) { BoolConverter.serialize(value, jw); @@ -91,13 +105,17 @@ public void writeString(CharSequence value) { } @Override - public void writeStringValue(CharSequence value, StringBuilder replaceBuilder) { - DslJsonSerializer.writeStringValue(value, replaceBuilder, jw); + public void writeString(CharSequence value, boolean trimmed) { + if (trimmed) { + DslJsonSerializer.writeStringValue(value, replaceBuilder, jw); + } else { + writeString(value); + } } @Override - public CharSequence sanitizePropertyName(String key, StringBuilder replaceBuilder) { - return DslJsonSerializer.sanitizePropertyName(key, replaceBuilder); + public String sanitizePropertyName(String key) { + return DslJsonSerializer.sanitizePropertyName(key, replaceBuilder).toString(); } @Override diff --git a/apm-agent-plugins/apm-micrometer-plugin/src/main/java/co/elastic/apm/agent/micrometer/MicrometerMeterRegistrySerializer.java b/apm-agent-plugins/apm-micrometer-plugin/src/main/java/co/elastic/apm/agent/micrometer/MicrometerMeterRegistrySerializer.java index e06145f7b7..f66563cc53 100644 --- a/apm-agent-plugins/apm-micrometer-plugin/src/main/java/co/elastic/apm/agent/micrometer/MicrometerMeterRegistrySerializer.java +++ b/apm-agent-plugins/apm-micrometer-plugin/src/main/java/co/elastic/apm/agent/micrometer/MicrometerMeterRegistrySerializer.java @@ -18,7 +18,6 @@ */ package co.elastic.apm.agent.micrometer; -import co.elastic.apm.agent.report.serialize.DslJsonDataWriter; import co.elastic.apm.agent.sdk.internal.util.PrivilegedActionUtils; import co.elastic.apm.agent.sdk.logging.Logger; import co.elastic.apm.agent.sdk.logging.LoggerFactory; @@ -61,7 +60,6 @@ public class MicrometerMeterRegistrySerializer { private static final int BUFFER_SIZE_LIMIT = 2048; private static final Logger logger = LoggerFactory.getLogger(MicrometerMeterRegistrySerializer.class); - private final StringBuilder replaceBuilder = new StringBuilder(); private final MetricsConfiguration config; private final ReportingTracer tracer; private final WeakSet internallyDisabledMeters = WeakConcurrent.buildSet(); @@ -91,7 +89,7 @@ public List serialize(final Map metersById, final l } for (Map.Entry, List> entry : metersGroupedByTags.entrySet()) { DataWriter writer = tracer.newWriter(maxSerializedSize); - if (serializeMetricSet(entry.getKey(), entry.getValue(), epochMicros, replaceBuilder, writer)) { + if (serializeMetricSet(entry.getKey(), entry.getValue(), epochMicros, writer)) { serializedMeters.add(writer); maxSerializedSize = Math.max(Math.min(writer.size(), BUFFER_SIZE_LIMIT), maxSerializedSize); } @@ -99,7 +97,7 @@ public List serialize(final Map metersById, final l return serializedMeters; } - boolean serializeMetricSet(List tags, List meters, long epochMicros, StringBuilder replaceBuilder, DataWriter writer) { + boolean serializeMetricSet(List tags, List meters, long epochMicros, DataWriter writer) { boolean hasSamples = false; boolean dedotMetricName = config.isDedotCustomMetrics(); writer.write(OBJECT_START); @@ -110,7 +108,7 @@ boolean serializeMetricSet(List tags, List meters, long epochMicros, writer.writeFieldName("timestamp"); writer.serialize(epochMicros); writer.write(COMMA); - serializeTags(tags, replaceBuilder, writer); + serializeTags(tags, writer); writer.writeFieldName("samples"); writer.write(OBJECT_START); @@ -126,25 +124,25 @@ boolean serializeMetricSet(List tags, List meters, long epochMicros, PrivilegedActionUtils.setContextClassLoader(Thread.currentThread(), PrivilegedActionUtils.getClassLoader(meter.getClass())); if (meter instanceof Timer) { Timer timer = (Timer) meter; - hasSamples = serializeTimer(writer, timer.takeSnapshot(), timer.getId(), timer.count(), timer.totalTime(TimeUnit.MICROSECONDS), hasSamples, replaceBuilder, dedotMetricName); + hasSamples = serializeTimer(writer, timer.takeSnapshot(), timer.getId(), timer.count(), timer.totalTime(TimeUnit.MICROSECONDS), hasSamples, dedotMetricName); } else if (meter instanceof FunctionTimer) { FunctionTimer timer = (FunctionTimer) meter; - hasSamples = serializeTimer(writer, null, timer.getId(), (long) timer.count(), timer.totalTime(TimeUnit.MICROSECONDS), hasSamples, replaceBuilder, dedotMetricName); + hasSamples = serializeTimer(writer, null, timer.getId(), (long) timer.count(), timer.totalTime(TimeUnit.MICROSECONDS), hasSamples, dedotMetricName); } else if (meter instanceof LongTaskTimer) { LongTaskTimer timer = (LongTaskTimer) meter; - hasSamples = serializeTimer(writer, timer.takeSnapshot(), timer.getId(), timer.activeTasks(), timer.duration(TimeUnit.MICROSECONDS), hasSamples, replaceBuilder, dedotMetricName); + hasSamples = serializeTimer(writer, timer.takeSnapshot(), timer.getId(), timer.activeTasks(), timer.duration(TimeUnit.MICROSECONDS), hasSamples, dedotMetricName); } else if (meter instanceof DistributionSummary) { DistributionSummary summary = (DistributionSummary) meter; - hasSamples = serializeDistributionSummary(writer, summary.takeSnapshot(), summary.getId(), summary.count(), summary.totalAmount(), hasSamples, replaceBuilder, dedotMetricName); + hasSamples = serializeDistributionSummary(writer, summary.takeSnapshot(), summary.getId(), summary.count(), summary.totalAmount(), hasSamples, dedotMetricName); } else if (meter instanceof Gauge) { Gauge gauge = (Gauge) meter; - hasSamples = serializeValue(gauge.getId(), gauge.value(), hasSamples, writer, replaceBuilder, dedotMetricName); + hasSamples = serializeValue(gauge.getId(), gauge.value(), hasSamples, writer, dedotMetricName); } else if (meter instanceof Counter) { Counter counter = (Counter) meter; - hasSamples = serializeValue(counter.getId(), counter.count(), hasSamples, writer, replaceBuilder, dedotMetricName); + hasSamples = serializeValue(counter.getId(), counter.count(), hasSamples, writer, dedotMetricName); } else if (meter instanceof FunctionCounter) { FunctionCounter counter = (FunctionCounter) meter; - hasSamples = serializeValue(counter.getId(), counter.count(), hasSamples, writer, replaceBuilder, dedotMetricName); + hasSamples = serializeValue(counter.getId(), counter.count(), hasSamples, writer, dedotMetricName); } } catch (Throwable throwable) { String meterName = meter.getId().getName(); @@ -167,7 +165,7 @@ boolean serializeMetricSet(List tags, List meters, long epochMicros, return hasSamples; } - private static void serializeTags(List tags, StringBuilder replaceBuilder, DataWriter writer) { + private static void serializeTags(List tags, DataWriter writer) { if (tags.isEmpty()) { return; } @@ -178,9 +176,8 @@ private static void serializeTags(List tags, StringBuilder replaceBuilder, if (i > 0) { writer.write(COMMA); } - writer.writeStringValue(writer.sanitizePropertyName(tag.getKey(), replaceBuilder), replaceBuilder); - writer.write(SEMI); - writer.writeStringValue(tag.getValue(), replaceBuilder); + writer.writeFieldName(tag.getKey(), true); + writer.writeString(tag.getValue(), true); } writer.write(OBJECT_END); writer.write(COMMA); @@ -195,19 +192,18 @@ private static void serializeTags(List tags, StringBuilder replaceBuilder, * @param count count * @param totalTime total time * @param hasValue whether a value has already been written - * @param replaceBuilder * @param dedotMetricName * @return true if a value has been written before, including this one; false otherwise */ - private static boolean serializeTimer(DataWriter writer, HistogramSnapshot histogramSnapshot, Meter.Id id, long count, double totalTime, boolean hasValue, StringBuilder replaceBuilder, boolean dedotMetricName) { + private static boolean serializeTimer(DataWriter writer, HistogramSnapshot histogramSnapshot, Meter.Id id, long count, double totalTime, boolean hasValue, boolean dedotMetricName) { if (isValidValue(totalTime)) { if (hasValue) writer.write(COMMA); - serializeValue(id, ".count", count, writer, replaceBuilder, dedotMetricName); + serializeValue(id, ".count", count, writer, dedotMetricName); writer.write(COMMA); - serializeValue(id, ".sum.us", totalTime, writer, replaceBuilder, dedotMetricName); + serializeValue(id, ".sum.us", totalTime, writer, dedotMetricName); if (histogramSnapshot != null && histogramSnapshot.histogramCounts().length > 0) { writer.write(COMMA); - serializeHistogram(id, histogramSnapshot, writer, replaceBuilder, dedotMetricName); + serializeHistogram(id, histogramSnapshot, writer, dedotMetricName); } return true; } @@ -223,32 +219,31 @@ private static boolean serializeTimer(DataWriter writer, HistogramSnapshot histo * @param count count * @param totalAmount total amount of recorded events * @param hasValue whether a value has already been written - * @param replaceBuilder * @param dedotMetricName * @return true if a value has been written before, including this one; false otherwise */ - private static boolean serializeDistributionSummary(DataWriter writer, HistogramSnapshot histogramSnapshot, Meter.Id id, long count, double totalAmount, boolean hasValue, StringBuilder replaceBuilder, boolean dedotMetricName) { + private static boolean serializeDistributionSummary(DataWriter writer, HistogramSnapshot histogramSnapshot, Meter.Id id, long count, double totalAmount, boolean hasValue, boolean dedotMetricName) { if (isValidValue(totalAmount)) { if (hasValue) writer.write(COMMA); - serializeValue(id, ".count", count, writer, replaceBuilder, dedotMetricName); + serializeValue(id, ".count", count, writer, dedotMetricName); writer.write(COMMA); - serializeValue(id, ".sum", totalAmount, writer, replaceBuilder, dedotMetricName); + serializeValue(id, ".sum", totalAmount, writer, dedotMetricName); if (histogramSnapshot != null && histogramSnapshot.histogramCounts().length > 0) { writer.write(COMMA); - serializeHistogram(id, histogramSnapshot, writer, replaceBuilder, dedotMetricName); + serializeHistogram(id, histogramSnapshot, writer, dedotMetricName); } return true; } return hasValue; } - private static void serializeHistogram(Meter.Id id, HistogramSnapshot histogramSnapshot, DataWriter writer, StringBuilder replaceBuilder, boolean dedotMetricName) { + private static void serializeHistogram(Meter.Id id, HistogramSnapshot histogramSnapshot, DataWriter writer, boolean dedotMetricName) { if (histogramSnapshot == null) { return; } String suffix = ".histogram"; CountAtBucket[] bucket = histogramSnapshot.histogramCounts(); - serializeObjectStart(id.getName(), "values", suffix, writer, replaceBuilder, dedotMetricName); + serializeObjectStart(id.getName(), "values", suffix, writer, dedotMetricName); writer.write(ARRAY_START); if (bucket.length > 0) { writer.serialize(bucket[0].bucket()); @@ -284,8 +279,8 @@ private static void serializeHistogram(Meter.Id id, HistogramSnapshot histogramS writer.write(OBJECT_END); } - private static void serializeValue(Meter.Id id, String suffix, long value, DataWriter writer, StringBuilder replaceBuilder, boolean dedotMetricName) { - serializeValueStart(id.getName(), suffix, writer, replaceBuilder, dedotMetricName); + private static void serializeValue(Meter.Id id, String suffix, long value, DataWriter writer, boolean dedotMetricName) { + serializeValueStart(id.getName(), suffix, writer, dedotMetricName); writer.serialize(value); writer.write(OBJECT_END); } @@ -297,45 +292,36 @@ private static void serializeValue(Meter.Id id, String suffix, long value, DataW * @param value meter value * @param hasValue whether a value has already been written * @param writer writer - * @param replaceBuilder * @param dedotMetricName * @return true if a value has been written before, including this one; false otherwise */ - private static boolean serializeValue(Meter.Id id, double value, boolean hasValue, DataWriter writer, StringBuilder replaceBuilder, boolean dedotMetricName) { + private static boolean serializeValue(Meter.Id id, double value, boolean hasValue, DataWriter writer, boolean dedotMetricName) { if (isValidValue(value)) { if (hasValue) writer.write(COMMA); - serializeValue(id, "", value, writer, replaceBuilder, dedotMetricName); + serializeValue(id, "", value, writer, dedotMetricName); return true; } return hasValue; } - private static void serializeValue(Meter.Id id, String suffix, double value, DataWriter writer, StringBuilder replaceBuilder, boolean dedotMetricName) { - serializeValueStart(id.getName(), suffix, writer, replaceBuilder, dedotMetricName); + private static void serializeValue(Meter.Id id, String suffix, double value, DataWriter writer, boolean dedotMetricName) { + serializeValueStart(id.getName(), suffix, writer, dedotMetricName); writer.serialize(value); writer.write(OBJECT_END); } - private static void serializeValueStart(String key, String suffix, DataWriter writer, StringBuilder replaceBuilder, boolean dedotMetricName) { - serializeObjectStart(key, "value", suffix, writer, replaceBuilder, dedotMetricName); + private static void serializeValueStart(String key, String suffix, DataWriter writer, boolean dedotMetricName) { + serializeObjectStart(key, "value", suffix, writer, dedotMetricName); } - private static void serializeObjectStart(String key, String objectName, String suffix, DataWriter writer, StringBuilder replaceBuilder, boolean dedotMetricName) { - replaceBuilder.setLength(0); - if (dedotMetricName) { - writer.sanitizePropertyName(key, replaceBuilder); + private static void serializeObjectStart(String key, String objectName, String suffix, DataWriter writer, boolean dedotMetricName) { + String name; + if (suffix == null) { + name = key; } else { - replaceBuilder.append(key); + name = key + suffix; } - if (suffix != null) { - if (replaceBuilder.length() == 0) { - replaceBuilder.append(key); - } - replaceBuilder.append(suffix); - } - writer.writeString(replaceBuilder); - - writer.write(SEMI); + writer.writeFieldName(name, dedotMetricName); writer.write(OBJECT_START); writer.writeFieldName(objectName); } diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/MetricSetSerializer.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/MetricSetSerializer.java index 5dd92dd73d..a3d5f98d04 100644 --- a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/MetricSetSerializer.java +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/MetricSetSerializer.java @@ -41,12 +41,10 @@ class MetricSetSerializer { private static final int INITIAL_BUFFER_SIZE = 2048; - private final StringBuilder replaceBuilder; private final DataWriter writer; private boolean anySamplesWritten; - public MetricSetSerializer(ReportingTracer tracer, Attributes attributes, CharSequence instrumentationScopeName, long epochMicros, StringBuilder replaceBuilder) { - this.replaceBuilder = replaceBuilder; + public MetricSetSerializer(ReportingTracer tracer, Attributes attributes, CharSequence instrumentationScopeName, long epochMicros) { anySamplesWritten = false; writer = tracer.newWriter(INITIAL_BUFFER_SIZE); writer.write(OBJECT_START); @@ -203,8 +201,7 @@ private boolean serializeAttribute(AttributeKey key, @Nullable Object value, if (prependComma) { writer.write(COMMA); } - writer.writeStringValue(writer.sanitizePropertyName(key.getKey(), replaceBuilder), replaceBuilder); - writer.write(SEMI); + writer.writeFieldName(key.getKey(), true); AttributeType type = key.getType(); switch (type) { diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/OtelMetricSerializer.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/OtelMetricSerializer.java index 99f4f9b43c..525635315f 100644 --- a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/OtelMetricSerializer.java +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/OtelMetricSerializer.java @@ -44,8 +44,6 @@ public class OtelMetricSerializer { private static final Logger logger = LoggerFactory.getLogger(OtelMetricSerializer.class); private final ReporterConfiguration reporterConfig; private final ReportingTracer tracer; - private final StringBuilder serializationTempBuilder; - private final Set metricsWithBadAggregations = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Map> metricSets; @@ -57,7 +55,6 @@ public OtelMetricSerializer(ReporterConfiguration reporterConfig, ReportingTrace this.reporterConfig = reporterConfig; this.tracer = tracer; metricSets = new HashMap<>(); - serializationTempBuilder = new StringBuilder(); } public void addValues(MetricData metric) { @@ -152,7 +149,7 @@ private MetricSetSerializer getOrCreateMetricSet(CharSequence instrScopeName, lo MetricSetSerializer ms = timestampMetricSets.get(attributes); if (ms == null) { - ms = new MetricSetSerializer(tracer, attributes, key.instrumentationScopeName, key.timestamp, serializationTempBuilder); + ms = new MetricSetSerializer(tracer, attributes, key.instrumentationScopeName, key.timestamp); timestampMetricSets.put(attributes, ms); } return ms; diff --git a/apm-agent-tracer/src/main/java/co/elastic/apm/agent/tracer/reporting/DataWriter.java b/apm-agent-tracer/src/main/java/co/elastic/apm/agent/tracer/reporting/DataWriter.java index 0a0b2e9c0b..1ca1174c45 100644 --- a/apm-agent-tracer/src/main/java/co/elastic/apm/agent/tracer/reporting/DataWriter.java +++ b/apm-agent-tracer/src/main/java/co/elastic/apm/agent/tracer/reporting/DataWriter.java @@ -22,6 +22,8 @@ public interface DataWriter { void writeFieldName(String name); + void writeFieldName(String name, boolean sanitized); + void write(StructureType type); void serialize(boolean value); @@ -32,11 +34,11 @@ public interface DataWriter { void writeString(CharSequence value); - void writeStringValue(CharSequence value, StringBuilder replaceBuilder); + void writeString(CharSequence value, boolean trimmed); int size(); - CharSequence sanitizePropertyName(String key, StringBuilder replaceBuilder); + String sanitizePropertyName(String key); void report();