/reactor-netty-http/src/main/java/reactor/netty/http/MicrometerHttpMetricsRecorder.java

https://github.com/reactor/reactor-netty · Java · 126 lines · 86 code · 19 blank · 21 comment · 6 complexity · 67fef6a24075c5ae9c5ff57c1d3ad245 MD5 · raw file

  1. /*
  2. * Copyright (c) 2011-Present VMware, Inc. or its affiliates, All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package reactor.netty.http;
  17. import io.micrometer.core.instrument.Counter;
  18. import io.micrometer.core.instrument.DistributionSummary;
  19. import io.micrometer.core.instrument.Timer;
  20. import io.netty.util.internal.PlatformDependent;
  21. import reactor.netty.Metrics;
  22. import reactor.netty.channel.MeterKey;
  23. import reactor.netty.channel.MicrometerChannelMetricsRecorder;
  24. import java.net.SocketAddress;
  25. import java.util.concurrent.ConcurrentMap;
  26. import static reactor.netty.Metrics.DATA_RECEIVED;
  27. import static reactor.netty.Metrics.DATA_RECEIVED_TIME;
  28. import static reactor.netty.Metrics.DATA_SENT;
  29. import static reactor.netty.Metrics.DATA_SENT_TIME;
  30. import static reactor.netty.Metrics.ERRORS;
  31. import static reactor.netty.Metrics.REGISTRY;
  32. import static reactor.netty.Metrics.REMOTE_ADDRESS;
  33. import static reactor.netty.Metrics.RESPONSE_TIME;
  34. import static reactor.netty.Metrics.URI;
  35. /**
  36. * An {@link HttpMetricsRecorder} implementation for integration with Micrometer.
  37. *
  38. * @author Violeta Georgieva
  39. * @since 0.9
  40. */
  41. public class MicrometerHttpMetricsRecorder extends MicrometerChannelMetricsRecorder implements HttpMetricsRecorder {
  42. protected final Timer.Builder dataReceivedTimeBuilder;
  43. protected final ConcurrentMap<MeterKey, Timer> dataReceivedTimeCache = PlatformDependent.newConcurrentHashMap();
  44. protected final Timer.Builder dataSentTimeBuilder;
  45. protected final ConcurrentMap<MeterKey, Timer> dataSentTimeCache = PlatformDependent.newConcurrentHashMap();
  46. protected final Timer.Builder responseTimeBuilder;
  47. protected final ConcurrentMap<MeterKey, Timer> responseTimeCache = PlatformDependent.newConcurrentHashMap();
  48. protected final DistributionSummary.Builder dataReceivedBuilder;
  49. protected final ConcurrentMap<MeterKey, DistributionSummary> dataReceivedCache = PlatformDependent.newConcurrentHashMap();
  50. protected final DistributionSummary.Builder dataSentBuilder;
  51. protected final ConcurrentMap<MeterKey, DistributionSummary> dataSentCache = PlatformDependent.newConcurrentHashMap();
  52. protected final Counter.Builder errorsBuilder;
  53. protected final ConcurrentMap<MeterKey, Counter> errorsCache = PlatformDependent.newConcurrentHashMap();
  54. protected MicrometerHttpMetricsRecorder(String name, String protocol) {
  55. super(name, protocol);
  56. this.dataReceivedTimeBuilder =
  57. Timer.builder(name + DATA_RECEIVED_TIME)
  58. .description("Time spent in consuming incoming data");
  59. this.dataSentTimeBuilder =
  60. Timer.builder(name + DATA_SENT_TIME)
  61. .description("Time spent in sending outgoing data");
  62. this.responseTimeBuilder =
  63. Timer.builder(name + RESPONSE_TIME)
  64. .description("Total time for the request/response");
  65. this.dataReceivedBuilder =
  66. DistributionSummary.builder(name + DATA_RECEIVED)
  67. .baseUnit("bytes")
  68. .description("Amount of the data received, in bytes");
  69. this.dataSentBuilder =
  70. DistributionSummary.builder(name + DATA_SENT)
  71. .baseUnit("bytes")
  72. .description("Amount of the data sent, in bytes");
  73. this.errorsBuilder =
  74. Counter.builder(name + ERRORS)
  75. .description("Number of errors that occurred");
  76. }
  77. @Override
  78. public void recordDataReceived(SocketAddress remoteAddress, String uri, long bytes) {
  79. String address = Metrics.formatSocketAddress(remoteAddress);
  80. DistributionSummary dataReceived = dataReceivedCache.computeIfAbsent(new MeterKey(uri, address, null, null),
  81. key -> filter(dataReceivedBuilder.tags(REMOTE_ADDRESS, address, URI, uri)
  82. .register(REGISTRY)));
  83. if (dataReceived != null) {
  84. dataReceived.record(bytes);
  85. }
  86. }
  87. @Override
  88. public void recordDataSent(SocketAddress remoteAddress, String uri, long bytes) {
  89. String address = Metrics.formatSocketAddress(remoteAddress);
  90. DistributionSummary dataSent = dataSentCache.computeIfAbsent(new MeterKey(uri, address, null, null),
  91. key -> filter(dataSentBuilder.tags(REMOTE_ADDRESS, address, URI, uri)
  92. .register(REGISTRY)));
  93. if (dataSent != null) {
  94. dataSent.record(bytes);
  95. }
  96. }
  97. @Override
  98. public void incrementErrorsCount(SocketAddress remoteAddress, String uri) {
  99. String address = Metrics.formatSocketAddress(remoteAddress);
  100. Counter errors = errorsCache.computeIfAbsent(new MeterKey(uri, address, null, null),
  101. key -> filter(errorsBuilder.tags(REMOTE_ADDRESS, address, URI, uri)
  102. .register(REGISTRY)));
  103. if (errors != null) {
  104. errors.increment();
  105. }
  106. }
  107. }