diff --git a/docker/oap/docker-entrypoint.sh b/docker/oap/docker-entrypoint.sh index 5897b0078..205cee497 100755 --- a/docker/oap/docker-entrypoint.sh +++ b/docker/oap/docker-entrypoint.sh @@ -256,6 +256,8 @@ receiver-jvm: default: receiver-clr: default: +receiver-so11y: + default: service-mesh: default: bufferPath: \${SW_SERVICE_MESH_BUFFER_PATH:../mesh-buffer/} # Path to trace buffer files, suggest to use absolute path @@ -270,9 +272,10 @@ query: alarm: default: telemetry: - prometheus: - host: \${SW_TELEMETRY_PROMETHEUS_HOST:0.0.0.0} - port: \${SW_TELEMETRY_PROMETHEUS_PORT:1234} + so11y: + prometheusExporterEnabled: \${SW_TELEMETRY_SO11Y_PROMETHEUS_ENABLED:true} + prometheusExporterHost: \${SW_TELEMETRY_PROMETHEUS_HOST:0.0.0.0} + prometheusExporterPort: \${SW_TELEMETRY_PROMETHEUS_PORT:1234} EOT # generate configuration case ${SW_CONFIGURATION} in diff --git a/docs/en/setup/backend/backend-telemetry.md b/docs/en/setup/backend/backend-telemetry.md index 0034f13f1..2115478a1 100644 --- a/docs/en/setup/backend/backend-telemetry.md +++ b/docs/en/setup/backend/backend-telemetry.md @@ -27,4 +27,30 @@ telemetry: Provide two grafana dashboard settings. 1. Use [SkyWalking trace-mode dashboard](telemetry/trace-mode-grafana.json) when SkyWalking is used with tracing agent. 1. Use [SkyWalking mesh-mode dashboard](telemetry/mesh-mode-grafana.json) when SkyWalking is used with service mesh -telemetry, including istio, envoy. \ No newline at end of file +telemetry, including istio, envoy. + +## Self Observability + +SkyWalking supports to collect telemetry data into OAP backend directly. Users could check them out through UI or +GraphQL API then. + +Adding following configuration to enable `so11y`(self-observability) related modules. + +```yaml +receiver-so11y: + default: +telemetry: + so11y: +``` + +Another example represents how to combine `promethues` and `so11y`. Adding some items in `so11y` to make it happen. + +```yaml +telemetry: + so11y: + prometheusExporterEnabled: true + prometheusExporterHost: 0.0.0.0 + prometheusExporterPort: 1234 +``` + +Then prometheus exporter is listening on `0.0.0.0:1234`. diff --git a/oap-server/server-receiver-plugin/pom.xml b/oap-server/server-receiver-plugin/pom.xml index 156e03b27..f6ff8ff4e 100644 --- a/oap-server/server-receiver-plugin/pom.xml +++ b/oap-server/server-receiver-plugin/pom.xml @@ -39,6 +39,7 @@ skywalking-clr-receiver-plugin jaeger-receiver-plugin receiver-proto + skywalking-so11y-receiver-plugin diff --git a/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/pom.xml b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/pom.xml new file mode 100644 index 000000000..8f5e094b7 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/pom.xml @@ -0,0 +1,41 @@ + + + + + + server-receiver-plugin + org.apache.skywalking + 6.3.0-SNAPSHOT + + 4.0.0 + + skywalking-so11y-receiver-plugin + + + + org.apache.skywalking + telemetry-api + ${project.version} + + + + + \ No newline at end of file diff --git a/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverConfig.java b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverConfig.java new file mode 100644 index 000000000..c72c14d1c --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverConfig.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.so11y; + +import org.apache.skywalking.oap.server.library.module.ModuleConfig; + +/** + * Self observability receiver config. + */ +public class So11yReceiverConfig extends ModuleConfig { +} diff --git a/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverModule.java b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverModule.java new file mode 100644 index 000000000..454e9d407 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverModule.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.so11y; + +import org.apache.skywalking.oap.server.library.module.ModuleDefine; + +/** + * Self observability receiver module. + * + * @author gaohongtao + */ +public class So11yReceiverModule extends ModuleDefine { + public static final String NAME = "receiver-so11y"; + + public So11yReceiverModule() { + super(NAME); + } + + @Override + public Class[] services() { + return new Class[0]; + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverModuleProvider.java b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverModuleProvider.java new file mode 100644 index 000000000..346a9623c --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/so11y/So11yReceiverModuleProvider.java @@ -0,0 +1,381 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.receiver.so11y; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.apache.skywalking.oap.server.core.Const; +import org.apache.skywalking.oap.server.core.CoreModule; +import org.apache.skywalking.oap.server.core.analysis.TimeBucket; +import org.apache.skywalking.oap.server.core.register.service.IServiceInstanceInventoryRegister; +import org.apache.skywalking.oap.server.core.register.service.IServiceInventoryRegister; +import org.apache.skywalking.oap.server.core.source.*; +import org.apache.skywalking.oap.server.library.module.*; +import org.apache.skywalking.oap.server.telemetry.TelemetryModule; +import org.apache.skywalking.oap.server.telemetry.api.MetricFamily; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector; +import org.apache.skywalking.oap.server.telemetry.api.TelemetryRelatedContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; + +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.summingDouble; + +/** + * Self observability receiver provider. + * + * @author gaohongtao + */ +public class So11yReceiverModuleProvider extends ModuleProvider { + + private static final Logger logger = LoggerFactory.getLogger(So11yReceiverModuleProvider.class); + + private static final String SERVICE_NAME = "SkyWalking"; + + private static final int RUN_RATE_SECONDS = 5; + + private final long[] lastNewGc = new long[]{0L, 0L}; + + private final long[] lastOldGc = new long[]{0L, 0L}; + + private int serviceId; + + private int serviceInstanceId; + + private String serviceInstanceName; + + private double lastCpuSeconds = -1; + + private IServiceInventoryRegister serviceInventoryRegister; + + private IServiceInstanceInventoryRegister serviceInstanceInventoryRegister; + + private SourceReceiver sourceReceiver; + + + @Override + public String name() { + return "default"; + } + + @Override + public Class module() { + return So11yReceiverModule.class; + } + + @Override + public ModuleConfig createConfigBeanIfAbsent() { + return new So11yReceiverConfig(); + } + + @Override + public void prepare() throws ServiceNotProvidedException, ModuleStartException { + } + + @Override + public void start() throws ServiceNotProvidedException, ModuleStartException { + serviceInventoryRegister = getManager().find(CoreModule.NAME).provider().getService(IServiceInventoryRegister.class); + serviceInstanceInventoryRegister = getManager().find(CoreModule.NAME).provider().getService(IServiceInstanceInventoryRegister.class); + sourceReceiver = getManager().find(CoreModule.NAME).provider().getService(SourceReceiver.class); + MetricsCollector collector = getManager().find(TelemetryModule.NAME).provider().getService(MetricsCollector.class); + Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder() + .setDaemon(true).setNameFormat("So11y-receiver-%s").build()).scheduleAtFixedRate(() -> { + if (register()) { + heartbeat(); + } else { + return; + } + Iterable mfs = collector.collect(); + Map metricsIndex = new HashMap<>(); + for (MetricFamily each : mfs) { + if (each.samples.size() < 1) { + continue; + } + metricsIndex.put(each.name, each); + } + writeCpuUsage(metricsIndex); + writeJvmMemory(metricsIndex); + writeJvmMemoryPool(metricsIndex); + writeGC(metricsIndex); + }, RUN_RATE_SECONDS, RUN_RATE_SECONDS, TimeUnit.SECONDS); + } + + + + @Override + public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException { + + } + + @Override + public String[] requiredModules() { + return new String[] {TelemetryModule.NAME, CoreModule.NAME}; + } + + private void writeGC(Map metricsIndex) { + if (!metricsIndex.containsKey("jvm_gc_collection_seconds")) { + return; + } + List newGC = ImmutableList.of("PS Scavenge", "ParNew", "G1 Young Generation", "Copy"); + List oldGC = ImmutableList.of("PS MarkSweep", "ConcurrentMarkSweep", "G1 Old Generation", "MarkSweepCompact"); + metricsIndex.get("jvm_gc_collection_seconds").samples.stream() + .map(sample -> { + int index = Iterables.indexOf(sample.labelNames, i -> Objects.equals(i, "gc")); + if (index < 0) { + return null; + } + String gcPhrase = sample.labelValues.get(index); + GCMetricType type = sample.name.contains("sum") ? GCMetricType.SUM : GCMetricType.COUNT; + double value = type == GCMetricType.SUM ? sample.value * 1000 : sample.value; + if (newGC.contains(gcPhrase)) { + return new GCMetric(GCPhrase.NEW, type, value); + } else if (oldGC.contains(gcPhrase)) { + return new GCMetric(GCPhrase.OLD, type, value); + } + throw new RuntimeException(String.format("Unsupported gc phrase %s", gcPhrase)); + }) + .filter(Objects::nonNull) + .collect(groupingBy(GCMetric::getPhrase)) + .forEach((gcPhrase, gcMetrics) -> { + ServiceInstanceJVMGC gc = new ServiceInstanceJVMGC(); + gc.setId(serviceInstanceId); + gc.setName(serviceInstanceName); + gc.setServiceId(serviceId); + gc.setServiceName(SERVICE_NAME); + gc.setPhrase(gcPhrase); + long[] lastGc = gcPhrase == GCPhrase.NEW ? lastNewGc : lastOldGc; + gcMetrics.stream().filter(m -> m.type.equals(GCMetricType.COUNT)).findFirst().ifPresent(m -> { + gc.setCount(m.getValue().longValue() - lastGc[0]); + lastGc[0] = m.getValue().longValue(); + }); + gcMetrics.stream().filter(m -> m.type.equals(GCMetricType.SUM)).findFirst().ifPresent(m -> { + gc.setTime(m.getValue().longValue() - lastGc[1]); + lastGc[1] = m.getValue().longValue(); + }); + gc.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis())); + if (logger.isDebugEnabled()) { + logger.debug("Write {} {}counts {}ms to {}", gc.getPhrase(), gc.getCount(), gc.getTime(), gc.getName()); + } + sourceReceiver.receive(gc); + }); + } + + private void writeJvmMemoryPool(Map metricsIndex) { + List> setterList = ImmutableList.of( + new MetricSetter<>("jvm_memory_pool_bytes_used", (m, v) -> m.setUsed(v.longValue())), + new MetricSetter<>("jvm_memory_pool_bytes_committed", (m, v) -> m.setCommitted(v.longValue())), + new MetricSetter<>("jvm_memory_pool_bytes_max", (m, v) -> m.setMax(v.longValue())), + new MetricSetter<>("jvm_memory_pool_bytes_init", (m, v) -> m.setInit(v.longValue()))); + if (setterList.stream().anyMatch(i -> !metricsIndex.containsKey(i.name))) { + return; + } + Map poolMap = new HashMap<>(); + setterList.forEach(setter -> metricsIndex.get(setter.name).samples.stream() + .map(sample -> { + int index = Iterables.indexOf(sample.labelNames, i -> Objects.equals(i, "pool")); + if (index < 0) { + return null; + } + String poolType = sample.labelValues.get(index); + if (poolType.contains("Code")) { + return new PoolMetric(MemoryPoolType.CODE_CACHE_USAGE, sample.value); + } else if (poolType.contains("Eden")) { + return new PoolMetric(MemoryPoolType.NEWGEN_USAGE, sample.value); + } else if (poolType.contains("Survivor")) { + return new PoolMetric(MemoryPoolType.SURVIVOR_USAGE, sample.value); + } else if (poolType.contains("Old")) { + return new PoolMetric(MemoryPoolType.OLDGEN_USAGE, sample.value); + } else if (poolType.contains("Metaspace")) { + return new PoolMetric(MemoryPoolType.METASPACE_USAGE, sample.value); + } else if (poolType.contains("Perm") || poolType.contains("Compressed Class Space")) { + return new PoolMetric(MemoryPoolType.PERMGEN_USAGE, sample.value); + } + throw new RuntimeException(String.format("Unknown pool type %s", poolType)); + }) + .filter(Objects::nonNull) + .collect(groupingBy(PoolMetric::getType, summingDouble(PoolMetric::getValue))) + .forEach((memoryPoolType, value) -> { + if (!poolMap.containsKey(memoryPoolType)) { + ServiceInstanceJVMMemoryPool pool = new ServiceInstanceJVMMemoryPool(); + pool.setId(serviceInstanceId); + pool.setName(serviceInstanceName); + pool.setServiceId(serviceId); + pool.setServiceName(SERVICE_NAME); + pool.setPoolType(memoryPoolType); + pool.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis())); + poolMap.put(memoryPoolType, pool); + } + ServiceInstanceJVMMemoryPool pool = poolMap.get(memoryPoolType); + setter.delegated.accept(pool, value); + })); + poolMap.values().forEach(p -> { + if (logger.isDebugEnabled()) { + logger.debug("Write {} {}-{}-{}-{} to {}", p.getPoolType(), + humanReadableByteCount(p.getInit(), false), + humanReadableByteCount(p.getUsed(), false), + humanReadableByteCount(p.getCommitted(), false), + humanReadableByteCount(p.getMax(), false), p.getName()); + } + sourceReceiver.receive(p); + }); + } + + private void writeJvmMemory(final Map metricsIndex) { + List> setterList = ImmutableList.of( + new MetricSetter<>("jvm_memory_bytes_used", (m, v) -> m.setUsed(v.longValue())), + new MetricSetter<>("jvm_memory_bytes_committed", (m, v) -> m.setCommitted(v.longValue())), + new MetricSetter<>("jvm_memory_bytes_max", (m, v) -> m.setMax(v.longValue())), + new MetricSetter<>("jvm_memory_bytes_init", (m, v) -> m.setInit(v.longValue()))); + if (setterList.stream().anyMatch(i -> !metricsIndex.containsKey(i.name))) { + return; + } + ImmutableList.of(createJVMMemory(true), createJVMMemory(false)) + .forEach(memory -> { + String area = memory.isHeapStatus() ? "heap" : "nonheap"; + setterList.forEach(setter -> { + metricsIndex.get(setter.name).samples.stream() + .filter(input -> { + int index = Iterables.indexOf(input.labelNames, i -> Objects.equals(i, "area")); + if (index < 0) { + return false; + } + return Objects.equals(input.labelValues.get(index), area); + }) + .findFirst() + .ifPresent(sample -> setter.delegated.accept(memory, sample.value)); + }); + if (logger.isDebugEnabled()) { + logger.debug("Write {} {}-{}-{}-{} to {}", area, + humanReadableByteCount(memory.getInit(), false), + humanReadableByteCount(memory.getUsed(), false), + humanReadableByteCount(memory.getCommitted(), false), + humanReadableByteCount(memory.getMax(), false), memory.getName()); + } + sourceReceiver.receive(memory); + }); + } + + private ServiceInstanceJVMMemory createJVMMemory(boolean isHeap) { + ServiceInstanceJVMMemory memory = new ServiceInstanceJVMMemory(); + memory.setId(serviceInstanceId); + memory.setName(serviceInstanceName); + memory.setServiceId(serviceId); + memory.setServiceName(SERVICE_NAME); + memory.setHeapStatus(isHeap); + memory.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis())); + return memory; + } + + private void writeCpuUsage(Map metricsIndex) { + if (!metricsIndex.containsKey("process_cpu_seconds_total")) { + return; + } + double value = metricsIndex.get("process_cpu_seconds_total").samples.get(0).value; + if (lastCpuSeconds < 0) { + lastCpuSeconds = value; + return; + } + double percentage = (value - lastCpuSeconds) * 100 / (RUN_RATE_SECONDS * Runtime.getRuntime().availableProcessors()); + lastCpuSeconds = value; + ServiceInstanceJVMCPU serviceInstanceJVMCPU = new ServiceInstanceJVMCPU(); + serviceInstanceJVMCPU.setId(serviceInstanceId); + serviceInstanceJVMCPU.setName(serviceInstanceName); + serviceInstanceJVMCPU.setServiceId(serviceId); + serviceInstanceJVMCPU.setServiceName(SERVICE_NAME); + serviceInstanceJVMCPU.setUsePercent(percentage); + serviceInstanceJVMCPU.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis())); + logger.debug("Write so11y cpu usage {} to {}", percentage, serviceInstanceName); + sourceReceiver.receive(serviceInstanceJVMCPU); + } + + + private void heartbeat() { + long now = System.currentTimeMillis(); + serviceInventoryRegister.heartbeat(serviceId, now); + serviceInstanceInventoryRegister.heartbeat(serviceInstanceId, now); + } + + private boolean register() { + if (serviceId == Const.NONE) { + logger.debug("Register so11y service [{}].", SERVICE_NAME); + serviceId = serviceInventoryRegister.getOrCreate(SERVICE_NAME, null); + } + if (serviceId != Const.NONE && serviceInstanceId == Const.NONE) { + serviceInstanceName = TelemetryRelatedContext.INSTANCE.getId(); + logger.debug("Register so11y service instance [{}].", serviceInstanceName); + serviceInstanceId = serviceInstanceInventoryRegister.getOrCreate(serviceId, serviceInstanceName, serviceInstanceName, + System.currentTimeMillis(), null); + } + return serviceInstanceId != Const.NONE; + } + + @RequiredArgsConstructor + private class MetricSetter { + + final String name; + + final BiConsumer delegated; + + } + + private static String humanReadableByteCount(long bytes, boolean si) { + int unit = si ? 1000 : 1024; + if (bytes < unit) return bytes + " B"; + int exp = (int) (Math.log(bytes) / Math.log(unit)); + String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); + return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + } + + @RequiredArgsConstructor + @EqualsAndHashCode(of = "type") + @ToString + @Getter + private class PoolMetric { + private final MemoryPoolType type; + private final Double value; + } + + @RequiredArgsConstructor + @EqualsAndHashCode(of = "phrase") + @ToString + @Getter + private class GCMetric { + private final GCPhrase phrase; + private final GCMetricType type; + private final Double value; + } + + private enum GCMetricType { + SUM, COUNT + } + +} diff --git a/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine new file mode 100644 index 000000000..e6616e5ab --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +# + +org.apache.skywalking.oap.server.receiver.so11y.So11yReceiverModule \ No newline at end of file diff --git a/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider new file mode 100644 index 000000000..81ce655a3 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-so11y-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +# + +org.apache.skywalking.oap.server.receiver.so11y.So11yReceiverModuleProvider \ No newline at end of file diff --git a/oap-server/server-starter/pom.xml b/oap-server/server-starter/pom.xml index 761c37709..cd9b471f7 100644 --- a/oap-server/server-starter/pom.xml +++ b/oap-server/server-starter/pom.xml @@ -117,6 +117,11 @@ skywalking-clr-receiver-plugin ${project.version} + + org.apache.skywalking + skywalking-so11y-receiver-plugin + ${project.version} + @@ -163,6 +168,11 @@ telemetry-prometheus ${project.version} + + org.apache.skywalking + telemetry-so11y + ${project.version} + diff --git a/oap-server/server-starter/src/main/resources/application.yml b/oap-server/server-starter/src/main/resources/application.yml index 30d21342b..85a5b8bc3 100644 --- a/oap-server/server-starter/src/main/resources/application.yml +++ b/oap-server/server-starter/src/main/resources/application.yml @@ -106,6 +106,8 @@ receiver-jvm: default: receiver-clr: default: +#receiver-so11y: +# default: service-mesh: default: bufferPath: ${SW_SERVICE_MESH_BUFFER_PATH:../mesh-buffer/} # Path to trace buffer files, suggest to use absolute path @@ -132,9 +134,14 @@ query: alarm: default: telemetry: - prometheus: - host: ${SW_TELEMETRY_PROMETHEUS_HOST:0.0.0.0} - port: ${SW_TELEMETRY_PROMETHEUS_PORT:1234} + none: +# prometheus: +# host: ${SW_TELEMETRY_PROMETHEUS_HOST:0.0.0.0} +# port: ${SW_TELEMETRY_PROMETHEUS_PORT:1234} +# so11y: +# prometheusExporterEnabled: ${SW_TELEMETRY_SO11Y_PROMETHEUS_ENABLED:true} +# prometheusExporterHost: ${SW_TELEMETRY_PROMETHEUS_HOST:0.0.0.0} +# prometheusExporterPort: ${SW_TELEMETRY_PROMETHEUS_PORT:1234} configuration: none: # apollo: diff --git a/oap-server/server-starter/src/main/resources/log4j2.xml b/oap-server/server-starter/src/main/resources/log4j2.xml index e9a520ba7..cf83c5bb7 100644 --- a/oap-server/server-starter/src/main/resources/log4j2.xml +++ b/oap-server/server-starter/src/main/resources/log4j2.xml @@ -31,12 +31,12 @@ - - + + diff --git a/oap-server/server-telemetry/pom.xml b/oap-server/server-telemetry/pom.xml index 4f885aa2b..3c17ace1e 100644 --- a/oap-server/server-telemetry/pom.xml +++ b/oap-server/server-telemetry/pom.xml @@ -30,5 +30,6 @@ telemetry-prometheus telemetry-api + telemetry-so11y \ No newline at end of file diff --git a/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/TelemetryModule.java b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/TelemetryModule.java index 78cdce45b..f9a9d13bf 100644 --- a/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/TelemetryModule.java +++ b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/TelemetryModule.java @@ -19,6 +19,7 @@ package org.apache.skywalking.oap.server.telemetry; import org.apache.skywalking.oap.server.library.module.ModuleDefine; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector; import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator; /** @@ -34,6 +35,6 @@ public class TelemetryModule extends ModuleDefine { } @Override public Class[] services() { - return new Class[] {MetricsCreator.class}; + return new Class[] {MetricsCreator.class, MetricsCollector.class}; } } diff --git a/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/api/MetricFamily.java b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/api/MetricFamily.java new file mode 100644 index 000000000..eabb2c632 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/api/MetricFamily.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.telemetry.api; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +/** + * MetricFamily define a metric and all its samples. + * + * @author gaohongtao + */ +@AllArgsConstructor +@EqualsAndHashCode +@ToString +public class MetricFamily { + + public final String name; + public final Type type; + public final String help; + public final List samples; + + public enum Type { + COUNTER, + GAUGE, + SUMMARY, + HISTOGRAM, + UNTYPED, + } + + /** + * A single Sample, with a unique name and set of labels. + */ + @AllArgsConstructor + @EqualsAndHashCode + @ToString + public static class Sample { + public final String name; + public final List labelNames; + public final List labelValues; // Must have same length as labelNames. + public final double value; + public final Long timestampMs; // It's an epoch format with milliseconds value included (this field is subject to change). + + } +} diff --git a/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/api/MetricsCollector.java b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/api/MetricsCollector.java new file mode 100644 index 000000000..c0a988f64 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/api/MetricsCollector.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.telemetry.api; + +import org.apache.skywalking.oap.server.library.module.Service; + +/** + * Collect all metrics from telemetry. + * + * @author gaohongtao + */ +public interface MetricsCollector extends Service { + + /** + * Get all of metrics. + * + * @return all metrics + */ + Iterable collect(); +} diff --git a/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/none/MetricsCollectorNoop.java b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/none/MetricsCollectorNoop.java new file mode 100644 index 000000000..57eac8be9 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/none/MetricsCollectorNoop.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.telemetry.none; + +import org.apache.skywalking.oap.server.telemetry.api.MetricFamily; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector; + +import java.util.Collections; + +/** + * No-op MetricFamily Collector. + * + * @author gaohongtao + */ +public class MetricsCollectorNoop implements MetricsCollector { + @Override + public Iterable collect() { + return Collections.emptyList(); + } +} diff --git a/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/none/NoneTelemetryProvider.java b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/none/NoneTelemetryProvider.java index 25db52b49..96975e293 100644 --- a/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/none/NoneTelemetryProvider.java +++ b/oap-server/server-telemetry/telemetry-api/src/main/java/org/apache/skywalking/oap/server/telemetry/none/NoneTelemetryProvider.java @@ -20,6 +20,7 @@ package org.apache.skywalking.oap.server.telemetry.none; import org.apache.skywalking.oap.server.library.module.*; import org.apache.skywalking.oap.server.telemetry.TelemetryModule; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector; import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator; /** @@ -43,6 +44,7 @@ public class NoneTelemetryProvider extends ModuleProvider { @Override public void prepare() throws ServiceNotProvidedException, ModuleStartException { this.registerServiceImplementation(MetricsCreator.class, new MetricsCreatorNoop()); + this.registerServiceImplementation(MetricsCollector.class, new MetricsCollectorNoop()); } @Override public void start() throws ServiceNotProvidedException, ModuleStartException { diff --git a/oap-server/server-telemetry/telemetry-so11y/pom.xml b/oap-server/server-telemetry/telemetry-so11y/pom.xml new file mode 100644 index 000000000..5932145d4 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-so11y/pom.xml @@ -0,0 +1,41 @@ + + + + + + server-telemetry + org.apache.skywalking + 6.3.0-SNAPSHOT + + 4.0.0 + + telemetry-so11y + + + + org.apache.skywalking + telemetry-prometheus + ${project.version} + + + + + \ No newline at end of file diff --git a/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yConfig.java b/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yConfig.java new file mode 100644 index 000000000..82271bdf6 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yConfig.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.telemetry.so11y; + +import lombok.Getter; +import lombok.Setter; +import org.apache.skywalking.oap.server.library.module.ModuleConfig; + +/** + * The module configuration of self observability. + */ +@Setter +@Getter +public class So11yConfig extends ModuleConfig { + + private boolean prometheusExporterEnabled = false; + + private String prometheusExporterHost = "0.0.0.0"; + + private int prometheusExporterPort = 1234; + +} diff --git a/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yMetricsCollector.java b/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yMetricsCollector.java new file mode 100644 index 000000000..de88bc993 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yMetricsCollector.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.telemetry.so11y; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; +import org.apache.skywalking.oap.server.telemetry.api.MetricFamily; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.List; + +/** + * Implement MetricCollector to generate prometheus metrics. + * + * @author gaohongtao + */ +public class So11yMetricsCollector implements MetricsCollector { + @Override + public Iterable collect() { + Enumeration mfs = CollectorRegistry.defaultRegistry.metricFamilySamples(); + List result = new LinkedList<>(); + while (mfs.hasMoreElements()) { + Collector.MetricFamilySamples metricFamilySamples = mfs.nextElement(); + List samples = new ArrayList<>(metricFamilySamples.samples.size()); + MetricFamily m = new MetricFamily(metricFamilySamples.name, MetricFamily.Type.valueOf(metricFamilySamples.type.name()), + metricFamilySamples.help, samples); + result.add(m); + for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) { + samples.add(new MetricFamily.Sample(sample.name, sample.labelNames, sample.labelValues, sample.value, sample.timestampMs)); + } + } + return result; + } +} diff --git a/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yMetricsCreator.java b/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yMetricsCreator.java new file mode 100644 index 000000000..ddad834a6 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yMetricsCreator.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.telemetry.so11y; + +import org.apache.skywalking.oap.server.telemetry.api.CounterMetrics; +import org.apache.skywalking.oap.server.telemetry.api.GaugeMetrics; +import org.apache.skywalking.oap.server.telemetry.api.HistogramMetrics; +import org.apache.skywalking.oap.server.telemetry.api.MetricsTag; +import org.apache.skywalking.oap.server.telemetry.prometheus.PrometheusMetricsCreator; + +/** + * Delegate prometheus metrics creator. + * + * @author gaohongtao + */ +public class So11yMetricsCreator extends PrometheusMetricsCreator { + + @Override + public CounterMetrics createCounter(String name, String tips, MetricsTag.Keys tagKeys, MetricsTag.Values tagValues) { + return super.createCounter(name, tips, tagKeys, tagValues); + } + + @Override + public GaugeMetrics createGauge(String name, String tips, MetricsTag.Keys tagKeys, MetricsTag.Values tagValues) { + return super.createGauge(name, tips, tagKeys, tagValues); + } + + @Override + public HistogramMetrics createHistogramMetric(String name, String tips, MetricsTag.Keys tagKeys, MetricsTag.Values tagValues, double... buckets) { + return super.createHistogramMetric(name, tips, tagKeys, tagValues, buckets); + } +} diff --git a/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yTelemetryProvider.java b/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yTelemetryProvider.java new file mode 100644 index 000000000..27c887497 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-so11y/src/main/java/org/apache/skywalking/oap/server/telemetry/so11y/So11yTelemetryProvider.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.telemetry.so11y; + +import io.prometheus.client.exporter.HTTPServer; +import io.prometheus.client.hotspot.DefaultExports; +import org.apache.skywalking.oap.server.library.module.*; +import org.apache.skywalking.oap.server.telemetry.TelemetryModule; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator; + +import java.io.IOException; + +/** + * Self observability telemetry provider. + * + * @author gaohongtao + */ +public class So11yTelemetryProvider extends ModuleProvider { + private So11yConfig config; + + public So11yTelemetryProvider() { + config = new So11yConfig(); + } + + @Override public String name() { + return "so11y"; + } + + @Override public Class module() { + return TelemetryModule.class; + } + + @Override public ModuleConfig createConfigBeanIfAbsent() { + return config; + } + + @Override public void prepare() throws ServiceNotProvidedException, ModuleStartException { + this.registerServiceImplementation(MetricsCreator.class, new So11yMetricsCreator()); + this.registerServiceImplementation(MetricsCollector.class, new So11yMetricsCollector()); + if (config.isPrometheusExporterEnabled()) { + try { + new HTTPServer(config.getPrometheusExporterHost(), config.getPrometheusExporterPort()); + } catch (IOException e) { + throw new ModuleStartException(e.getMessage(), e); + } + } + DefaultExports.initialize(); + } + + @Override public void start() throws ServiceNotProvidedException, ModuleStartException { + + } + + @Override public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException { + } + + @Override public String[] requiredModules() { + return new String[0]; + } +} diff --git a/oap-server/server-telemetry/telemetry-so11y/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/oap-server/server-telemetry/telemetry-so11y/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider new file mode 100644 index 000000000..fed0b08a6 --- /dev/null +++ b/oap-server/server-telemetry/telemetry-so11y/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +# + + +org.apache.skywalking.oap.server.telemetry.so11y.So11yTelemetryProvider