Skip to main content

servers/otlp/
utils.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use api::v1::ColumnDataType;
16use api::v1::value::ValueData;
17use common_telemetry::warn;
18use jsonb::{Number as JsonbNumber, Value as JsonbValue};
19use opentelemetry_proto::tonic::common::v1::{KeyValue, any_value};
20
21use crate::otlp::trace::KEY_SERVICE_NAME;
22
23/// Reads the resource `service.name`. It is required by the semantic conventions,
24/// but senders may omit it; unrelated attributes are never used as a fallback.
25/// <https://opentelemetry.io/docs/specs/semconv/resource/service/>
26pub(crate) fn resource_service_name(resource_attrs: &[KeyValue]) -> Option<String> {
27    resource_attrs
28        .iter()
29        .find(|kv| kv.key == KEY_SERVICE_NAME)
30        .and_then(|kv| kv.value.as_ref()?.value.as_ref())
31        .and_then(|value| match value {
32            any_value::Value::StringValue(s) => Some(s.clone()),
33            any_value::Value::BytesValue(b) => Some(String::from_utf8_lossy(b).to_string()),
34            _ => None,
35        })
36}
37
38pub fn bytes_to_hex_string(bs: &[u8]) -> String {
39    hex::encode(bs)
40}
41
42pub fn any_value_to_jsonb(value: any_value::Value) -> JsonbValue<'static> {
43    match value {
44        any_value::Value::StringValue(s) => JsonbValue::String(s.into()),
45        any_value::Value::IntValue(i) => JsonbValue::Number(JsonbNumber::Int64(i)),
46        any_value::Value::DoubleValue(d) => JsonbValue::Number(JsonbNumber::Float64(d)),
47        any_value::Value::BoolValue(b) => JsonbValue::Bool(b),
48        any_value::Value::ArrayValue(a) => {
49            let values = a
50                .values
51                .into_iter()
52                .map(|v| match v.value {
53                    Some(value) => any_value_to_jsonb(value),
54                    None => JsonbValue::Null,
55                })
56                .collect();
57            JsonbValue::Array(values)
58        }
59        any_value::Value::KvlistValue(kv) => key_value_to_jsonb(kv.values),
60        any_value::Value::BytesValue(b) => JsonbValue::String(bytes_to_hex_string(&b).into()),
61        // `StringValueStrindex` references the Profiling signal's
62        // `ProfilesDictionary.string_table`, which is unavailable to logs/traces.
63        // Per the OTLP spec, non-Profiling receivers must treat it as a non-fatal
64        // issue and process the value as if it were absent.
65        any_value::Value::StringValueStrindex(_) => {
66            warn!(
67                "encountered a profiling-only `StringValueStrindex` value in a non-profiling signal; ignoring"
68            );
69            JsonbValue::Null
70        }
71    }
72}
73
74pub fn key_value_to_jsonb(key_values: Vec<KeyValue>) -> JsonbValue<'static> {
75    JsonbValue::Object(
76        key_values
77            .into_iter()
78            .map(|kv| {
79                (
80                    kv.key,
81                    kv.value
82                        .and_then(|v| v.value)
83                        .map_or(JsonbValue::Null, any_value_to_jsonb),
84                )
85            })
86            .collect(),
87    )
88}
89
90#[inline]
91pub(crate) fn make_string_column_data(
92    name: &str,
93    value: Option<String>,
94) -> (String, ColumnDataType, Option<ValueData>) {
95    make_column_data(
96        name,
97        ColumnDataType::String,
98        value.map(ValueData::StringValue),
99    )
100}
101
102#[inline]
103pub(crate) fn make_column_data(
104    name: &str,
105    data_type: ColumnDataType,
106    value: Option<ValueData>,
107) -> (String, ColumnDataType, Option<ValueData>) {
108    (name.to_string(), data_type, value)
109}