![JAR search and dependency download from the Maven repository](/logo.png)
com.netflix.spectator.tdigest.Json Maven / Gradle / Ivy
/**
* Copyright 2015 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spectator.tdigest;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
import com.fasterxml.jackson.dataformat.smile.SmileGenerator;
import com.fasterxml.jackson.dataformat.smile.SmileParser;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Registry;
import com.netflix.spectator.api.Tag;
import com.tdunning.math.stats.AVLTreeDigest;
import com.tdunning.math.stats.TDigest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Helper for encoding and decoding digest measurements.
*/
final class Json {
private static final SmileFactory FACTORY = new SmileFactory();
static {
FACTORY
.enable(SmileGenerator.Feature.WRITE_HEADER)
.disable(SmileGenerator.Feature.WRITE_END_MARKER)
.enable(SmileGenerator.Feature.CHECK_SHARED_NAMES)
.enable(SmileGenerator.Feature.CHECK_SHARED_STRING_VALUES)
.disable(SmileParser.Feature.REQUIRE_HEADER);
}
private final Registry registry;
/**
* Create a new instance that will use the specified registry to create id objects during
* deserialization.
*/
Json(Registry registry) {
this.registry = registry;
}
/** Create a new generator for the output stream. */
JsonGenerator newGenerator(OutputStream out) throws IOException {
return FACTORY.createGenerator(out);
}
/** Encode the measurement using the generator. */
void encode(Map commonTags, TDigestMeasurement m, JsonGenerator gen)
throws IOException {
TDigest digest = m.value();
digest.compress();
ByteBuffer buf = ByteBuffer.allocate(digest.byteSize());
digest.asBytes(buf);
gen.writeStartArray();
gen.writeStartObject();
gen.writeStringField("name", m.id().name());
for (Map.Entry e : commonTags.entrySet()) {
gen.writeStringField(e.getKey(), e.getValue());
}
for (Tag t : m.id().tags()) {
gen.writeStringField(t.key(), t.value());
}
gen.writeEndObject();
gen.writeNumber(m.timestamp());
gen.writeBinary(buf.array());
gen.writeEndArray();
}
/** Encode the measurements using the generator. */
void encode(Map commonTags, List ms, JsonGenerator gen)
throws IOException {
gen.writeStartArray();
for (TDigestMeasurement m : ms) {
encode(commonTags, m, gen);
}
gen.writeEndArray();
}
/** Return a byte-array with the encoded measurements. */
byte[] encode(Map commonTags, List ms) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (JsonGenerator gen = FACTORY.createGenerator(baos)) {
encode(commonTags, ms, gen);
}
return baos.toByteArray();
}
private void require(boolean condition, String msg) {
if (!condition) {
throw new IllegalArgumentException(msg);
}
}
private void expect(JsonParser parser, JsonToken expected) throws IOException {
JsonToken t = parser.nextToken();
if (t != expected) {
String msg = String.format("expected %s, but found %s", expected, t);
throw new IllegalArgumentException(msg);
}
}
private TDigestMeasurement decode(JsonParser parser) throws IOException {
expect(parser, JsonToken.START_OBJECT);
require("name".equals(parser.nextFieldName()), "expected name");
Id id = registry.createId(parser.nextTextValue());
while (parser.nextToken() == JsonToken.FIELD_NAME) {
id = id.withTag(parser.getText(), parser.nextTextValue());
}
long t = parser.nextLongValue(-1L);
expect(parser, JsonToken.VALUE_EMBEDDED_OBJECT);
TDigest v = AVLTreeDigest.fromBytes(ByteBuffer.wrap(parser.getBinaryValue()));
expect(parser, JsonToken.END_ARRAY);
return new TDigestMeasurement(id, t, v);
}
/** Decode a list of measurements from a byte array. */
List decode(byte[] data) throws IOException {
return decode(data, 0, data.length);
}
/** Decode a list of measurements from a range of a byte array. */
List decode(byte[] data, int offset, int length) throws IOException {
JsonParser parser = FACTORY.createParser(data, offset, length);
List ms = new ArrayList<>();
expect(parser, JsonToken.START_ARRAY);
while (parser.nextToken() == JsonToken.START_ARRAY) {
ms.add(decode(parser));
}
return ms;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy