Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- [#1](https://github.com/ericdallo/metrepl/pull/1) Enable exporting metrics from `otlp` exporter.

## 0.4.2

- Bump nrepl to 1.5.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Available exporters:

- `stdout`: Export the metric to current nREPL process stdout, useful for debugging.
- `file`: Export the metric to a file, appending each metric in a new line.
- `otlp`: Export the metric via [OpenTelemetry](https://opentelemetry.io/) to what user configured.
- `otlp`: Export metrics via [OpenTelemetry Protocol](https://opentelemetry.io/docs/specs/otlp/). Supports exporting as logs and/or metrics.

By default no exporter is enabled, you need to manually configure which one(s) you want to enable, example: `{:exporters {:stdout {:enabled? true}}}`.

Expand Down
3 changes: 3 additions & 0 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
:extra-deps {nubank/matcher-combinators {:mvn/version "3.9.1"}
lambdaisland/kaocha {:mvn/version "1.91.1392"}}
:main-opts ["-m" "kaocha.runner"]}
:repl {:extra-paths ["dev" "docs" "test"]
:extra-deps {nubank/matcher-combinators {:mvn/version "3.9.1"}
lambdaisland/kaocha {:mvn/version "1.91.1392"}}}
:build {:deps {io.github.clojure/tools.build {:tag "v0.10.7" :sha "573711e"}
slipset/deps-deploy {:mvn/version "0.2.2"}}
:extra-paths ["resources"]
Expand Down
65 changes: 40 additions & 25 deletions src/metrepl/exporters/otlp.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@
[clojure.string :as string]
[metrepl.format :as format])
(:import
[io.opentelemetry.api.common AttributeKey]
[io.opentelemetry.api.common AttributeKey Attributes]
[io.opentelemetry.api.logs Severity]
[io.opentelemetry.sdk OpenTelemetrySdk]
[io.opentelemetry.sdk.autoconfigure AutoConfiguredOpenTelemetrySdk]
[io.opentelemetry.sdk.logs SdkLoggerProvider]
[java.util.function Function]))

(set! *warn-on-reflection* true)

(defonce otlp-logger-provider* (atom nil))
(defonce ^:private otlp-provider* (atom nil))

(defn ^:private setup-logger [otlp-config]
(reset! otlp-logger-provider*
(defn ^:private setup-sdk [otel-config]
(reset! otlp-provider*
(-> (AutoConfiguredOpenTelemetrySdk/builder)
(.addPropertiesCustomizer (reify Function (apply [_ _]
otlp-config)))
(.addPropertiesCustomizer ^Function (constantly otel-config))
(.build)
.getOpenTelemetrySdk
.getSdkLoggerProvider)))
.getOpenTelemetrySdk)))

(defn ^:private ->severity [level]
(case level
Expand All @@ -30,21 +28,38 @@
:error Severity/ERROR
Severity/INFO))

(defn ^:private ->raw-value [value]
(cond
(keyword? value) (string/join "" (drop 1 (str value)))
(number? value) value
(boolean? value) value
:else (str value)))
(defn ^:private ->attributes
[m]
(let [builder (Attributes/builder)]
(doseq [[k v] m
:let [value (cond
(keyword? v) (string/join "" (drop 1 (str v)))
(number? v) v
(boolean? v) v
:else (str v))]]
(.put builder (AttributeKey/stringKey (name k)) value))
(.build builder)))

(defn export! [data _metric-cfg {:keys [config]}]
(when-not @otlp-logger-provider*
(setup-logger config))
(let [log-record-builder (-> (.get ^SdkLoggerProvider @otlp-logger-provider* (str *ns*))
(defn export!
[{:keys [payload level timestamp] :as data {:keys [op]} :payload} _metric-cfg {:keys [config]}]
(when-not @otlp-provider*
(setup-sdk config))
(let [^OpenTelemetrySdk sdk @otlp-provider*
base-atrributes (dissoc data :timestamp :level :payload)
log-attributes (->attributes base-atrributes)
log-record-builder (-> (.getSdkLoggerProvider sdk)
(.get (str *ns*))
(.logRecordBuilder)
(.setBody (format/parse-data (:payload data) :json))
(.setSeverity (->severity (:level data)))
(.setTimestamp (:timestamp data)))]
(doseq [[field value] (dissoc data :timestamp :level :payload)]
(.setAttribute log-record-builder (AttributeKey/stringKey (name field)) (->raw-value value)))
(.emit log-record-builder)))
(.setBody (format/parse-data payload :json))
(.setSeverity (->severity level))
(.setTimestamp timestamp)
(.setAllAttributes log-attributes))
long-counter (-> (.getSdkMeterProvider sdk)
(.get (str *ns*))
(.counterBuilder "metrepl.events.total")
(.setUnit "events")
(.setDescription "Total number of REPL events")
(.build))
metric-attributes (->attributes (cond-> base-atrributes op (assoc :operation op)))]
(.emit log-record-builder)
(.add long-counter 1 metric-attributes)))
Loading